}
}
+#notifications {
+ width: 300px;
+}
+
.bold > a {
font-weight: bold;
}
.tab {
flex: 1;
}
+
+#notifications-count {
+ position: relative;
+ top: -50px;
+ left: 15px;
+}
import '../_global/index';
/* Tools */
-import { initExport, initFilters } from './js/tools';
+import { initExport, initFilters, initNotifications } from './js/tools';
/* Import shortcuts */
import './js/shortcuts/main';
});
initFilters();
initExport();
+ initNotifications();
$('#nav-btn-add-tag').on('click', () => {
$('.nav-panel-add-tag').toggle(100);
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');
+ });
+ });
});
}
}
-export { initExport, initFilters };
+function initNotifications() {
+ if ($('div').is('#notifications')) {
+ $('#button_notifications').show();
+ $('.js-notifications-action').sideNav({ edge: 'right' });
+ }
+}
+
+export { initExport, initFilters, initNotifications };
--- /dev/null
+<?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');
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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);
+
+
+}
--- /dev/null
+<?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);
+}
--- /dev/null
+<?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();
+ }
+}
search: 'Søg'
filter_entries: 'Filtrer artikler'
# export: 'Export'
+ # notifications: 'Notifications'
search_form:
input_label: 'Indtast søgning'
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:
search: 'Suche'
filter_entries: 'Artikel filtern'
export: 'Exportieren'
+ # notifications: 'Notifications'
search_form:
input_label: 'Suchbegriff hier eingeben'
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:
search: 'Search'
filter_entries: 'Filter entries'
export: 'Export'
+ notifications: 'Notifications'
search_form:
input_label: 'Enter your search here'
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:
search: 'Buscar'
filter_entries: 'Filtrar los artículos'
export: 'Exportar'
+ # notifications: 'Notifications'
search_form:
input_label: 'Introduzca su búsqueda aquí'
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:
search: 'جستجو'
filter_entries: 'فیلترکردن مقالهها'
export: 'برونبری'
+ # notifications: 'Notifications'
search_form:
input_label: 'جستجوی خود را اینجا بنویسید:'
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:
search: "Rechercher"
filter_entries: "Filtrer les articles"
export: "Exporter"
+ notifications: 'Notifications'
search_form:
input_label: "Saisissez votre terme de recherche"
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:
search: 'Cerca'
filter_entries: 'Filtra contenuti'
export: 'Esporta'
+ # notifications: 'Notifications'
search_form:
input_label: 'Inserisci qui la tua ricerca'
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:
search: 'Cercar'
filter_entries: 'Filtrar los articles'
export: 'Exportar'
+ # notifications: 'Notifications'
search_form:
input_label: 'Picatz vòstre mot-clau a cercar aquí'
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:
search: 'Szukaj'
filter_entries: 'Filtruj wpisy'
export: 'Eksportuj'
+ # notifications: 'Notifications'
search_form:
input_label: 'Wpisz swoje zapytanie tutaj'
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:
search: 'Pesquisa'
filter_entries: 'Filtrar entradas'
export: 'Exportar'
+ # notifications: 'Notifications'
search_form:
input_label: 'Digite aqui sua pesquisa'
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:
search: 'Căutare'
filter_entries: 'Filtrează articolele'
# export: 'Export'
+ # notifications: 'Notifications'
search_form:
input_label: 'Introdu căutarea ta'
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:
search: 'Ara'
filter_entries: 'Filtrele'
export: 'Dışa Aktar'
+ # notifications: 'Notifications'
search_form:
input_label: 'Aramak istediğiniz herhangi bir şey yazın'
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:
{% block title %}{{ 'developer.client.page_title'|trans }}{% endblock %}
{% block content %}
+{{ parent() }}
<div class="row">
<div class="col s12">
<div class="card-panel settings">
{% block title %}{{ 'developer.page_title'|trans }}{% endblock %}
{% block content %}
+{{ parent() }}
<div class="row">
<div class="col s12">
<div class="card-panel settings">
{% block title %}{{ 'about.page_title'|trans }}{% endblock %}
{% block content %}
-
+ {{ parent() }}
<div class="row">
<div class="col s12">
<div class="card-panel settings">
{% block title %}{{ 'howto.page_title'|trans }}{% endblock %}
{% block content %}
-
+ {{ parent() }}
<div class="row">
<div class="col s12">
<div class="card-panel settings">
{% block title %}{{ 'quickstart.page_title'|trans }}{% endblock %}
{% block content %}
-
+ {{ parent() }}
<div class="row quickstart">
<div class="col s12">
<div class="card-panel settings">
{% block title %}{{ 'config.page_title'|trans }}{% endblock %}
{% block content %}
-
+ {{ parent() }}
<div class="row">
<div class="col s12">
<div class="card-panel settings">
{% endblock %}
{% block content %}
+ {{ parent() }}
{% set listMode = app.user.config.listMode %}
<div class="results clearfix">
<div class="nb-results left">
--- /dev/null
+{% 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 %}
{% 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>
<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">
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;
{
return [
new \Twig_SimpleFilter('removeWww', [$this, 'removeWww']),
+ new \Twig_SimpleFilter('unread_notif', [$this, 'unreadNotif']),
];
}
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).
*
*/
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"})
*/
{
return $this->clients;
}
+
+ /**
+ * @return ArrayCollection<NotificationInterface>
+ */
+ public function getNotifications()
+ {
+ return $this->notifications;
+ }
+
+ /**
+ * @param ArrayCollection<NotificationInterface> $notifications
+ */
+ public function setNotifications($notifications)
+ {
+ $this->notifications = $notifications;
+ }
}
{% block title %}{{ 'user.manage.page_title'|trans }}{% endblock %}
{% block content %}
-
+ {{ parent() }}
<div class="row">
<div class="col s12">
<div class="card-panel">