]> git.immae.eu Git - github/wallabag/wallabag.git/commitdiff
First draft for notifications
authorThomas Citharel <tcit@tcit.fr>
Fri, 12 May 2017 13:02:32 +0000 (15:02 +0200)
committerThomas Citharel <tcit@tcit.fr>
Thu, 25 May 2017 17:09:52 +0000 (19:09 +0200)
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
35 files changed:
app/Resources/static/themes/material/css/sidenav.scss
app/Resources/static/themes/material/css/various.scss
app/Resources/static/themes/material/index.js
app/Resources/static/themes/material/js/tools.js
src/Wallabag/CoreBundle/Controller/NotificationsController.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Entity/Notification.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Notifications/Action.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Notifications/ActionInterface.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Notifications/NotificationInterface.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Repository/NotificationRepository.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/client.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/index.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/common/Static/about.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/common/Static/howto.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/common/Static/quickstart.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Notification/notifications.html.twig [new file with mode: 0644]
src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/tags.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
src/Wallabag/CoreBundle/Twig/WallabagExtension.php
src/Wallabag/UserBundle/Entity/User.php
src/Wallabag/UserBundle/Resources/views/manage.html.twig

index 416dc1c7b7283ef6434a74f33a1e0bc820bf8f46..f45970de17e4584dd94ab2c03d9d36f95bac59c7 100644 (file)
   }
 }
 
+#notifications {
+  width: 300px;
+}
+
 .bold > a {
   font-weight: bold;
 }
index 7daf40ec6ecb6891c367178a6dc23df6e44ab094..300015ab102a3e1f1630f0aa6a69baa0534612ce 100644 (file)
@@ -30,3 +30,9 @@ nav .input-field input {
 .tab {
   flex: 1;
 }
+
+#notifications-count {
+  position: relative;
+  top: -50px;
+  left: 15px;
+}
index d6afbb8a32eba702d7a68e255608e02c30889e2c..e16749f6d9c310f522324ab04b237bf8847c72fa 100755 (executable)
@@ -8,7 +8,7 @@ import 'materialize-css/dist/js/materialize';
 import '../_global/index';
 
 /* Tools */
-import { initExport, initFilters } from './js/tools';
+import { initExport, initFilters, initNotifications } from './js/tools';
 
 /* Import shortcuts */
 import './js/shortcuts/main';
@@ -34,6 +34,7 @@ $(document).ready(() => {
   });
   initFilters();
   initExport();
+  initNotifications();
 
   $('#nav-btn-add-tag').on('click', () => {
     $('.nav-panel-add-tag').toggle(100);
@@ -75,4 +76,13 @@ $(document).ready(() => {
     const scrollPercent = (s / (d - c)) * 100;
     $('.progress .determinate').css('width', `${scrollPercent}%`);
   });
+
+  $('.notification').on('click', () => {
+    $.ajax({
+      url: Routing.generate('notification-archive-all'),
+      method: 'GET',
+    }).done(() => {
+      $('#notifications').sideNav('hide');
+    });
+  });
 });
index 39398fd8a4c0fb4a15fd5ce2750ada6f2ca0e3ec..492640a0dc9199f14f7394436fe7cc8c5d61fb15 100644 (file)
@@ -21,4 +21,11 @@ function initExport() {
   }
 }
 
-export { initExport, initFilters };
+function initNotifications() {
+  if ($('div').is('#notifications')) {
+    $('#button_notifications').show();
+    $('.js-notifications-action').sideNav({ edge: 'right' });
+  }
+}
+
+export { initExport, initFilters, initNotifications };
diff --git a/src/Wallabag/CoreBundle/Controller/NotificationsController.php b/src/Wallabag/CoreBundle/Controller/NotificationsController.php
new file mode 100644 (file)
index 0000000..24870b3
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+namespace Wallabag\CoreBundle\Controller;
+
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+class NotificationsController extends Controller
+{
+    /**
+     * @param Request $request
+     *
+     * @Route("/notifications", name="notifications-all")
+     *
+     * @return \Symfony\Component\HttpFoundation\Response
+     */
+    public function getAllNotificationsAction(Request $request)
+    {
+        $notifications = $this->getDoctrine()->getRepository('WallabagCoreBundle:Notification')->findByUser($this->getUser());
+
+        return $this->render('WallabagCoreBundle:Notification:notifications.html.twig', ['notifications' => $notifications]);
+    }
+
+    /**
+     * @Route("/notifications/readall", name="notification-archive-all")
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function markAllNotificationsAsReadAction(Request $request)
+    {
+        $this->getDoctrine()->getRepository('WallabagCoreBundle:Notification')->markAllAsReadForUser($this->getUser()->getId());
+
+        return $this->redirectToRoute('notifications-all');
+    }
+}
diff --git a/src/Wallabag/CoreBundle/Entity/Notification.php b/src/Wallabag/CoreBundle/Entity/Notification.php
new file mode 100644 (file)
index 0000000..e1c1b44
--- /dev/null
@@ -0,0 +1,220 @@
+<?php
+
+namespace Wallabag\CoreBundle\Entity;
+
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\ORM\Mapping as ORM;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+use Wallabag\CoreBundle\Notifications\ActionInterface;
+use Wallabag\CoreBundle\Notifications\NotificationInterface;
+use Wallabag\UserBundle\Entity\User;
+
+/**
+ * Class Notification
+ *
+ * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\NotificationRepository")
+ * @ORM\Table(name="`notification`")
+ */
+class Notification implements NotificationInterface {
+
+    /**
+     * @var int
+     *
+     * @ORM\Column(name="id", type="integer")
+     * @ORM\Id
+     * @ORM\GeneratedValue(strategy="AUTO")
+     *
+     */
+    protected $id;
+
+    /**
+     * @var int $type
+     *
+     * @ORM\Column(name="type", type="integer")
+     */
+    protected $type;
+
+    /**
+     * @var User $user
+     *
+     * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="notifications")
+     */
+    protected $user;
+
+    /**
+     * @var \DateTime $timestamp
+     *
+     * @ORM\Column(name="timestamp", type="datetime")
+     */
+    protected $timestamp;
+
+    /**
+     * @var string $title
+     *
+     * @ORM\Column(name="title", type="string")
+     */
+    protected $title;
+
+    /**
+     * @var boolean $read
+     *
+     * @ORM\Column(name="read", type="boolean")
+     */
+    protected $read;
+
+    protected $logger;
+
+    /**
+     * @var ArrayCollection<ActionInterface>
+     *
+     * @ORM\Column(name="actions", type="array", nullable=true)
+     */
+    protected $actions;
+
+    protected $actionTypes = [];
+
+    const TYPE_ADMIN = 0;
+    const TYPE_USER = 1;
+    const TYPE_RELEASE = 2;
+
+    public function __construct(User $user = null)
+    {
+        $this->logger = new NullLogger();
+        $this->timestamp = new \DateTime();
+        $this->actions = new ArrayCollection();
+        $this->read = false;
+        $this->user = $user;
+    }
+
+    /**
+     * @param LoggerInterface $logger
+     * @return NotificationInterface
+     */
+    public function setLogger(LoggerInterface $logger)
+    {
+        $this->logger = $logger;
+        return $this;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * @param mixed $type
+     * @return NotificationInterface
+     */
+    public function setType($type)
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    /**
+     * @return User
+     */
+    public function getUser()
+    {
+        return $this->user;
+    }
+
+    /**
+     * @param User $user
+     * @return NotificationInterface
+     */
+    public function setUser(User $user)
+    {
+        $this->user = $user;
+        return $this;
+    }
+
+    /**
+     * @return \DateTime
+     */
+    public function getTimestamp()
+    {
+        return $this->timestamp;
+    }
+
+    /**
+     * @param \DateTime $timestamp
+     * @return NotificationInterface
+     */
+    public function setTimestamp(\DateTime $timestamp)
+    {
+        $this->timestamp = $timestamp;
+        return $this;
+    }
+
+    /**
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->title;
+    }
+
+    /**
+     * @param string $title
+     * @return NotificationInterface
+     */
+    public function setTitle($title)
+    {
+        $this->title = $title;
+        return $this;
+    }
+
+    /**
+     * @return bool
+     */
+    public function isRead()
+    {
+        return $this->read;
+    }
+
+    /**
+     * @param bool $read
+     * @return NotificationInterface
+     */
+    public function setRead($read)
+    {
+        $this->read = $read;
+        return $this;
+    }
+
+    /**
+     * @param ActionInterface $action
+     * @return NotificationInterface
+     * @throws \InvalidArgumentException
+     */
+    public function addAction(ActionInterface $action)
+    {
+        if (isset($this->actionTypes[$action->getType()])) {
+            throw new \InvalidArgumentException('The notification already has a primary action');
+        }
+        $this->actionTypes[$action->getType()] = true;
+        $this->actions->add($action);
+        return $this;
+    }
+
+    /**
+     * @return ArrayCollection<ActionInterface>
+     */
+    public function getActions()
+    {
+        return $this->actions;
+    }
+}
diff --git a/src/Wallabag/CoreBundle/Notifications/Action.php b/src/Wallabag/CoreBundle/Notifications/Action.php
new file mode 100644 (file)
index 0000000..8ef1860
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+
+namespace Wallabag\CoreBundle\Notifications;
+
+class Action implements ActionInterface {
+
+    /**
+     * @var string
+     */
+    protected $label;
+
+    /**
+     * @var int
+     */
+    protected $type;
+
+    const TYPE_OK = 1;
+    const TYPE_YES = 2;
+    const TYPE_NO = 3;
+    const TYPE_INFO = 4;
+
+    /**
+     * @var string
+     */
+    protected $link;
+
+    /**
+     * @return string
+     */
+    public function getLabel()
+    {
+        return $this->label;
+    }
+
+    /**
+     * @param string $label
+     * @return ActionInterface
+     */
+    public function setLabel($label)
+    {
+        $this->label = $label;
+        return $this;
+    }
+
+    /**
+     * @return int
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * @param int $type
+     * @return ActionInterface
+     * @throws \InvalidArgumentException
+     */
+    public function setType($type)
+    {
+        if ($type <= 0 || $type > 4) {
+            throw new \InvalidArgumentException('The given type option is invalid');
+        }
+        $this->type = $type;
+        return $this;
+    }
+
+    /**
+     * @return string
+     */
+    public function getLink()
+    {
+        return $this->link;
+    }
+
+    /**
+     * @param string $link
+     * @return ActionInterface
+     */
+    public function setLink($link)
+    {
+        $this->link = $link;
+        return $this;
+    }
+}
diff --git a/src/Wallabag/CoreBundle/Notifications/ActionInterface.php b/src/Wallabag/CoreBundle/Notifications/ActionInterface.php
new file mode 100644 (file)
index 0000000..0b40237
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+namespace Wallabag\CoreBundle\Notifications;
+
+interface ActionInterface {
+
+    /**
+     * @return string
+     */
+    public function getLabel();
+
+    /**
+     * @param string $label
+     * @return ActionInterface
+     */
+    public function setLabel($label);
+
+    /**
+     * @return int
+     */
+    public function getType();
+
+    /**
+     * @param int $type
+     * @return ActionInterface
+     */
+    public function setType($type);
+
+    /**
+     * @return string
+     */
+    public function getLink();
+
+    /**
+     * @param string $link
+     * @return ActionInterface
+     */
+    public function setLink($link);
+
+
+}
diff --git a/src/Wallabag/CoreBundle/Notifications/NotificationInterface.php b/src/Wallabag/CoreBundle/Notifications/NotificationInterface.php
new file mode 100644 (file)
index 0000000..d83e236
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+namespace Wallabag\CoreBundle\Notifications;
+
+use Psr\Log\LoggerAwareInterface;
+
+interface NotificationInterface extends LoggerAwareInterface {
+
+    /**
+     * Title of the notification
+     *
+     * @return string
+     */
+    public function getTitle();
+
+    /**
+     * @param string $title
+     * @return NotificationInterface
+     */
+    public function setTitle($title);
+
+    /**
+     * Type of the notification.
+     *
+     * @return string
+     */
+    public function getType();
+
+    /**
+     * @param int $type
+     * @return NotificationInterface
+     */
+    public function setType($type);
+
+    /**
+     * If the notification has been viewed / dismissed or not
+     *
+     * @return boolean
+     */
+    public function isRead();
+
+    /**
+     * @param boolean $read
+     * @return NotificationInterface
+     */
+    public function setRead($read);
+
+    /**
+     * When the notification was sent
+     *
+     * @return \DateTime
+     */
+    public function getTimestamp();
+
+    /**
+     * @param \DateTime $timestamp
+     * @return NotificationInterface
+     */
+    public function setTimestamp(\DateTime $timestamp);
+
+    /**
+     * @param ActionInterface $action
+     * @return NotificationInterface
+     */
+    public function addAction(ActionInterface $action);
+}
diff --git a/src/Wallabag/CoreBundle/Repository/NotificationRepository.php b/src/Wallabag/CoreBundle/Repository/NotificationRepository.php
new file mode 100644 (file)
index 0000000..eb8dbb3
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+
+namespace Wallabag\CoreBundle\Repository;
+
+use Doctrine\ORM\EntityRepository;
+
+class NotificationRepository extends EntityRepository {
+
+    public function markAllAsReadForUser($userId) {
+        return $this->getEntityManager()->createQueryBuilder()
+        ->update('WallabagCoreBundle:Notification', 'n')
+        ->set('n.read', true)
+        ->where('n.user = :userId')->setParameter('userId', $userId)
+        ->getQuery()
+        ->getResult();
+    }
+}
index 1bd0b8fdd7be155d573572250350fc09d9fb7341..4cfbd9ac8dfdbfd05311039266f98e0c9204fb2e 100644 (file)
@@ -37,6 +37,7 @@ menu:
         search: 'Søg'
         filter_entries: 'Filtrer artikler'
         # export: 'Export'
+        # notifications: 'Notifications'
     search_form:
         input_label: 'Indtast søgning'
 
@@ -241,6 +242,13 @@ entry:
     public:
         # shared_by_wallabag: "This article has been shared by <a href=%wallabag_instance%'>wallabag</a>"
 
+# notifications:
+#     sidebar:
+#         view_more: 'View more'
+#     list:
+#         page_title: 'Notifications'
+#         mark_all_as_read: 'Mark all as read'
+
 about:
     page_title: 'Om'
     top_menu:
index 94bb329517648e3084ce747149ae7a6a638fd91e..4874980efc116ed59c28fa3c28846e65d0e8b0b3 100644 (file)
@@ -37,6 +37,7 @@ menu:
         search: 'Suche'
         filter_entries: 'Artikel filtern'
         export: 'Exportieren'
+        # notifications: 'Notifications'
     search_form:
         input_label: 'Suchbegriff hier eingeben'
 
@@ -242,6 +243,13 @@ entry:
     public:
         shared_by_wallabag: "Dieser Artikel wurde mittels <a href='%wallabag_instance%'>wallabag</a> geteilt"
 
+# notifications:
+#     sidebar:
+#         view_more: 'View more'
+#     list:
+#         page_title: 'Notifications'
+#         mark_all_as_read: 'Mark all as read'
+
 about:
     page_title: 'Über'
     top_menu:
index 3a006a0ec28c4484447e0ed1c046d89739c905d7..308878f4ad0e81a3d86ae8a55c8f289ade381e66 100644 (file)
@@ -37,6 +37,7 @@ menu:
         search: 'Search'
         filter_entries: 'Filter entries'
         export: 'Export'
+        notifications: 'Notifications'
     search_form:
         input_label: 'Enter your search here'
 
@@ -242,6 +243,13 @@ entry:
     public:
         shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>"
 
+notifications:
+    sidebar:
+        view_more: 'View more'
+    list:
+        page_title: 'Notifications'
+        mark_all_as_read: 'Mark all as read'
+
 about:
     page_title: 'About'
     top_menu:
index ca5d9b2c0aa74b352a847159e33c899275370267..e669268cb3a32f90a2653e150774fb781959586e 100644 (file)
@@ -37,6 +37,7 @@ menu:
         search: 'Buscar'
         filter_entries: 'Filtrar los artículos'
         export: 'Exportar'
+        # notifications: 'Notifications'
     search_form:
         input_label: 'Introduzca su búsqueda aquí'
 
@@ -242,6 +243,13 @@ entry:
     public:
         shared_by_wallabag: "Este artículo se ha compartido con <a href='%wallabag_instance%'>wallabag</a>"
 
+# notifications:
+#     sidebar:
+#         view_more: 'View more'
+#     list:
+#         page_title: 'Notifications'
+#         mark_all_as_read: 'Mark all as read'
+
 about:
     page_title: 'Acerca de'
     top_menu:
index ecd8f64d0e8162c1eb0a7eade9829662cf6f5ca6..0b4d77265fbbf2b9d5837df61555385065be0e47 100644 (file)
@@ -37,6 +37,7 @@ menu:
         search: 'جستجو'
         filter_entries: 'فیلترکردن مقاله‌ها'
         export: 'برون‌بری'
+        # notifications: 'Notifications'
     search_form:
         input_label: 'جستجوی خود را این‌جا بنویسید:'
 
@@ -242,6 +243,13 @@ entry:
     public:
         # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>"
 
+# notifications:
+#     sidebar:
+#         view_more: 'View more'
+#     list:
+#         page_title: 'Notifications'
+#         mark_all_as_read: 'Mark all as read'
+
 about:
     page_title: 'درباره'
     top_menu:
index 84706459d078a52c610b9e9e6fcee5ac489ed3e6..660f4d85f9fc7b839d7986d9505e2d7adbdb7876 100644 (file)
@@ -37,6 +37,7 @@ menu:
         search: "Rechercher"
         filter_entries: "Filtrer les articles"
         export: "Exporter"
+        notifications: 'Notifications'
     search_form:
         input_label: "Saisissez votre terme de recherche"
 
@@ -242,6 +243,13 @@ entry:
     public:
         shared_by_wallabag: "Cet article a été partagé par <a href=\"%wallabag_instance%\">wallabag</a>"
 
+notifications:
+    sidebar:
+        view_more: 'Voir plus'
+    list:
+        page_title: 'Notifications'
+        mark_all_as_read: 'Marquer tout comme lu'
+
 about:
     page_title: "À propos"
     top_menu:
index a8baa96fd6fee0189eaad7b9bd7c6a5ac1b27686..56ff5bc0eb4b230ecfee739f7633c6370693a685 100644 (file)
@@ -37,6 +37,7 @@ menu:
         search: 'Cerca'
         filter_entries: 'Filtra contenuti'
         export: 'Esporta'
+        # notifications: 'Notifications'
     search_form:
         input_label: 'Inserisci qui la tua ricerca'
 
@@ -242,6 +243,13 @@ entry:
     public:
         # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>"
 
+# notifications:
+#     sidebar:
+#         view_more: 'View more'
+#     list:
+#         page_title: 'Notifications'
+#         mark_all_as_read: 'Mark all as read'
+
 about:
     page_title: 'About'
     top_menu:
index 8f39ce0569ba532b68b2bfcbf0739a17d0af8b8f..219478bcc4be5679b7fc1b42a0f22b02813ee72c 100644 (file)
@@ -37,6 +37,7 @@ menu:
         search: 'Cercar'
         filter_entries: 'Filtrar los articles'
         export: 'Exportar'
+        # notifications: 'Notifications'
     search_form:
         input_label: 'Picatz vòstre mot-clau a cercar aquí'
 
@@ -242,6 +243,13 @@ entry:
     public:
         shared_by_wallabag: "Aqueste article es estat partejat per <a href='%wallabag_instance%'>wallabag</a>"
 
+# notifications:
+#     sidebar:
+#         view_more: 'View more'
+#     list:
+#         page_title: 'Notifications'
+#         mark_all_as_read: 'Mark all as read'
+
 about:
     page_title: 'A prepaus'
     top_menu:
index a6e0c10fd127c7d4a207f09c69cdc02af08a6744..3662f9ab3c1df97a08a0dce8b9cd68ae550260a0 100644 (file)
@@ -37,6 +37,7 @@ menu:
         search: 'Szukaj'
         filter_entries: 'Filtruj wpisy'
         export: 'Eksportuj'
+        # notifications: 'Notifications'
     search_form:
         input_label: 'Wpisz swoje zapytanie tutaj'
 
@@ -242,6 +243,13 @@ entry:
     public:
         shared_by_wallabag: "Ten artykuł został udostępniony przez <a href='%wallabag_instance%'>wallabag</a>"
 
+# notifications:
+#     sidebar:
+#         view_more: 'View more'
+#     list:
+#         page_title: 'Notifications'
+#         mark_all_as_read: 'Mark all as read'
+
 about:
     page_title: 'O nas'
     top_menu:
index a9473591f391139f87d843c8a7ed10126e99fa61..fa504617c476265ac9339317e46a78dba652b490 100644 (file)
@@ -37,6 +37,7 @@ menu:
         search: 'Pesquisa'
         filter_entries: 'Filtrar entradas'
         export: 'Exportar'
+        # notifications: 'Notifications'
     search_form:
         input_label: 'Digite aqui sua pesquisa'
 
@@ -242,6 +243,13 @@ entry:
     public:
         shared_by_wallabag: "Este artigo foi compartilhado pelo <a href='%wallabag_instance%'>wallabag</a>"
 
+# notifications:
+#     sidebar:
+#         view_more: 'View more'
+#     list:
+#         page_title: 'Notifications'
+#         mark_all_as_read: 'Mark all as read'
+
 about:
     page_title: 'Sobre'
     top_menu:
index 80d78a01e491c60cbd02c626c280e5db7786e4a0..c5ecedc165fd497411640ec4f77452737576d818 100644 (file)
@@ -37,6 +37,7 @@ menu:
         search: 'Căutare'
         filter_entries: 'Filtrează articolele'
         # export: 'Export'
+        # notifications: 'Notifications'
     search_form:
         input_label: 'Introdu căutarea ta'
 
@@ -242,6 +243,13 @@ entry:
     public:
         # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>"
 
+# notifications:
+#     sidebar:
+#         view_more: 'View more'
+#     list:
+#         page_title: 'Notifications'
+#         mark_all_as_read: 'Mark all as read'
+
 about:
     page_title: 'Despre'
     top_menu:
index 2896c82344900f89465bee857ae5012ec729965d..62d584289d16b4a0b5c58c0a0662e87745316a1b 100644 (file)
@@ -37,6 +37,7 @@ menu:
         search: 'Ara'
         filter_entries: 'Filtrele'
         export: 'Dışa Aktar'
+        # notifications: 'Notifications'
     search_form:
         input_label: 'Aramak istediğiniz herhangi bir şey yazın'
 
@@ -242,6 +243,13 @@ entry:
     public:
         # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>"
 
+# notifications:
+#     sidebar:
+#         view_more: 'View more'
+#     list:
+#         page_title: 'Notifications'
+#         mark_all_as_read: 'Mark all as read'
+
 about:
     page_title: 'Hakkımızda'
     top_menu:
index 8a5da71a9233d581f4f2c583abca6ba719556dfe..d566795dbef9b961b21a044a03b2d4eb5f5737ed 100644 (file)
@@ -3,6 +3,7 @@
 {% block title %}{{ 'developer.client.page_title'|trans }}{% endblock %}
 
 {% block content %}
+{{ parent() }}
 <div class="row">
     <div class="col s12">
         <div class="card-panel settings">
index b3f0affb5678b3a953753a69575e5832b845c913..575770d6d16039563fea800a53af424f972bc13f 100644 (file)
@@ -3,6 +3,7 @@
 {% block title %}{{ 'developer.page_title'|trans }}{% endblock %}
 
 {% block content %}
+{{ parent() }}
 <div class="row">
     <div class="col s12">
         <div class="card-panel settings">
index 1cd3485c06c3fc90baecb49c329dce89bc84aaf0..ffd9b1b8b63d9020a649b6897bbf3ae4596ccdd3 100644 (file)
@@ -3,7 +3,7 @@
 {% block title %}{{ 'about.page_title'|trans }}{% endblock %}
 
 {% block content %}
-
+    {{ parent() }}
     <div class="row">
         <div class="col s12">
             <div class="card-panel settings">
index 231f9bdf27ed8ffd4628239cd8b6ad12ac1e22ab..4c598b851cb660a70d1cc7a37b1cf11bea1b7582 100644 (file)
@@ -3,7 +3,7 @@
 {% block title %}{{ 'howto.page_title'|trans }}{% endblock %}
 
 {% block content %}
-
+    {{ parent() }}
     <div class="row">
         <div class="col s12">
             <div class="card-panel settings">
index 4580813c2dd67ca5efab04863ffc060bd00104cf..70265be236182f8e279543218262b051007780b5 100644 (file)
@@ -3,7 +3,7 @@
 {% block title %}{{ 'quickstart.page_title'|trans }}{% endblock %}
 
 {% block content %}
-
+    {{ parent() }}
     <div class="row quickstart">
         <div class="col s12">
             <div class="card-panel settings">
index 9b0816eb51eda730be31ee4c8f288c1a5c292607..67073c7d156f1fe0eb377f727f168a9ce05d1dba 100644 (file)
@@ -3,7 +3,7 @@
 {% block title %}{{ 'config.page_title'|trans }}{% endblock %}
 
 {% block content %}
-
+    {{ parent() }}
     <div class="row">
         <div class="col s12">
             <div class="card-panel settings">
index b2d91c9cc3d5f5c22ba9b40b7a5cc4f5c06a855a..b74d32b7c34bc1c2f535bc151f9cc26914d2a88e 100644 (file)
@@ -12,6 +12,7 @@
 {% endblock %}
 
 {% block content %}
+    {{ parent() }}
     {% set listMode = app.user.config.listMode %}
     <div class="results clearfix">
         <div class="nb-results left">
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Notification/notifications.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Notification/notifications.html.twig
new file mode 100644 (file)
index 0000000..bc72c70
--- /dev/null
@@ -0,0 +1,42 @@
+{% extends "WallabagCoreBundle::layout.html.twig" %}
+
+{% block title %}{{ 'notifications.list.page_title' | trans }}{% endblock %}
+
+{% block content %}
+    <div class="row">
+        <div class="col l6 offset-l3">
+            <ul class="collection">
+            {% for notification in notifications | slice(0, 10) %}
+                <li class="collection-item avatar{% if not notification.read %} light-blue lighten-5{% else %} grey-text{% endif %}">
+                    <i class="material-icons circle">{% spaceless %}
+                            {% if notification.type == constant('TYPE_ADMIN', notification) %}
+                                build
+                            {% elseif notification.type == constant('TYPE_USER', notification) %}
+                                person
+                            {% elseif notification.type == constant('TYPE_RELEASE', notification) %}
+                                new_releases
+                            {% endif %}
+                        {% endspaceless %}</i>
+                    <span class="title">{{ notification.title }}</span>
+                    <p><em>{{ notification.timestamp | date }}</p>
+                    <div class="secondary-content">
+                        {% for action in notification.actions %}
+                            <a class="btn waves-effect waves-light {% spaceless %}
+                               {% if action.type == constant('TYPE_OK', action) %}
+                               {% elseif action.type == constant('TYPE_YES', action) %}
+                                    cyan
+                               {% elseif action.type == constant('TYPE_NO', action) %}
+                                    red
+                               {% elseif action.type == constant('TYPE_INFO', action) %}
+                                    blue-grey
+                               {% endif %}
+                                {% endspaceless %}" href="{{ action.link }}">{{ action.label }}</a>
+                        {% endfor %}
+                    </div>
+                </li>
+            {% endfor %}
+            </ul>
+            <a href="{{ path('notification-archive-all') }}" class="btn waves-effect waves-light ">{{ 'notifications.list.mark_all_as_read' | trans }}</a>
+        </div>
+    </div>
+{% endblock %}
index c83543acaaf4e38d7a72556be1cf28572b0810ac..01dc4c6a7fdb6850d41fb0bad9e17ef1cecdd272 100644 (file)
@@ -3,6 +3,7 @@
 {% block title %}{{ 'tag.page_title'|trans }}{% endblock %}
 
 {% block content %}
+    {{ parent() }}
     <div class="results clearfix">
         <div class="nb-results left">{{ 'tag.list.number_on_the_page'|transchoice(tags|length) }}</div>
     </div>
index 2dab1c186d60e9fd6f2c9b372b2113f3f898928d..6e2828de438b03cdf429389bb121795f865594fb 100644 (file)
                         <i class="material-icons">search</i>
                     </a>
                 </li>
+                <li id="button_notifications">
+                    {% set unreadNotifs = app.user.notifications | unread_notif | length %}
+                    <a class="nav-panel-menu button-collapse-right tooltipped js-notifications-action" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.notifications' | trans }}" href="#" data-activates="notifications">
+                        <i class="material-icons">notifications{% if unreadNotifs == 0 %}_none{% endif %}</i>
+                        {% if unreadNotifs > 0 %}<span id="notifications-count" class="red-text text-accent-2">{{ unreadNotifs }}</span>{% endif %}
+                    </a>
+                </li>
                 <li id="button_filters">
                     <a class="nav-panel-menu button-collapse-right tooltipped js-filters-action" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.filter_entries'|trans }}" href="#" data-activates="filters">
                         <i class="material-icons">filter_list</i>
     </nav>
 {% endblock %}
 
+{% block content %}
+
+    <div id="notifications" class="side-nav">
+        <ul class="collection">
+            {% for notification in app.user.notifications | slice(0, 10) %}
+            <li class="notification collection-item avatar{% if not notification.read %} light-blue lighten-5{% else %} grey-text{% endif %}">
+                <i class="material-icons circle">{% spaceless %}
+                    {% if notification.type == constant('TYPE_ADMIN', notification) %}
+                        build
+                    {% elseif notification.type == constant('TYPE_USER', notification) %}
+                        person
+                    {% elseif notification.type == constant('TYPE_RELEASE', notification) %}
+                        new_releases
+                    {% endif %}
+                    {% endspaceless %}</i>
+                <span class="title">{{ notification.title }}</span>
+                <p><em>{{ notification.timestamp | date }}</em></p>
+                <div>
+                    {% for action in notification.actions %}
+                        <a class="btn waves-effect waves-light {% spaceless %}
+                               {% if action.type == constant('TYPE_OK', action) %}
+                               {% elseif action.type == constant('TYPE_YES', action) %}
+                                    cyan
+                               {% elseif action.type == constant('TYPE_NO', action) %}
+                                    red
+                               {% elseif action.type == constant('TYPE_INFO', action) %}
+                                    blue-grey
+                               {% endif %}
+                                {% endspaceless %}" href="{{ action.link }}">{{ action.label }}</a>
+                    {% endfor %}
+                </div>
+            </li>
+            {% endfor %}
+        </ul>
+        <a class="waves-effect waves-light btn" href="{{ path('notifications-all') }}">{{ 'notifications.sidebar.view_more' | trans }}</a>
+    </div>
+{% endblock %}
+
 {% block footer %}
     <footer class="page-footer cyan darken-2">
         <div class="footer-copyright">
index a305c53fc1819a50733746578b9bd30792ab66ef..ad224b6c3342d5065f7737c1f1cb6786369b081c 100644 (file)
@@ -2,7 +2,9 @@
 
 namespace Wallabag\CoreBundle\Twig;
 
+use Doctrine\Common\Collections\Collection;
 use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use Wallabag\CoreBundle\Notifications\NotificationInterface;
 use Wallabag\CoreBundle\Repository\EntryRepository;
 use Wallabag\CoreBundle\Repository\TagRepository;
 use Symfony\Component\Translation\TranslatorInterface;
@@ -28,6 +30,7 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa
     {
         return [
             new \Twig_SimpleFilter('removeWww', [$this, 'removeWww']),
+            new \Twig_SimpleFilter('unread_notif', [$this, 'unreadNotif']),
         ];
     }
 
@@ -45,6 +48,13 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa
         return preg_replace('/^www\./i', '', $url);
     }
 
+    public function unreadNotif(Collection $notifs)
+    {
+        return $notifs->filter(function(NotificationInterface $notif) {
+           return !$notif->isRead();
+        });
+    }
+
     /**
      * Return number of entries depending of the type (unread, archive, starred or all).
      *
index 3a167de740608567ae03b6e42f88ab8d7d512bf6..2a8c99263c7233641618fb81943ee017b88d3b73 100644 (file)
@@ -64,6 +64,11 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
      */
     protected $entries;
 
+    /**
+     * @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\Notification", mappedBy="user", cascade={"remove"})
+     */
+    protected $notifications;
+
     /**
      * @ORM\OneToOne(targetEntity="Wallabag\CoreBundle\Entity\Config", mappedBy="user", cascade={"remove"})
      */
@@ -266,4 +271,20 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
     {
         return $this->clients;
     }
+
+    /**
+     * @return ArrayCollection<NotificationInterface>
+     */
+    public function getNotifications()
+    {
+        return $this->notifications;
+    }
+
+    /**
+     * @param ArrayCollection<NotificationInterface> $notifications
+     */
+    public function setNotifications($notifications)
+    {
+        $this->notifications = $notifications;
+    }
 }
index c614c55fd9415e1fa71115ed3e7487fcb7a4c81f..10d8050c499f2333c17ccd3fc0f264daa6660d95 100644 (file)
@@ -3,7 +3,7 @@
 {% block title %}{{ 'user.manage.page_title'|trans }}{% endblock %}
 
 {% block content %}
-
+    {{ parent() }}
     <div class="row">
         <div class="col s12">
             <div class="card-panel">