diff --git a/aleksis/core/menus.py b/aleksis/core/menus.py index fc9ba63604dabf57e1c4101fc8d7f5700548cc6c..8b0afd86085db5628dcd66b60a2e86133cd59bc2 100644 --- a/aleksis/core/menus.py +++ b/aleksis/core/menus.py @@ -1,6 +1,8 @@ from django.conf import settings from django.utils.translation import gettext_lazy as _ +from .util.core_helpers import unread_notifications_badge + MENUS = { "NAV_MENU_CORE": [ { @@ -15,6 +17,15 @@ MENUS = { "icon": "home", "validators": ["menu_generator.validators.is_authenticated"], }, + { + "name": _("Notifications"), + "url": "notifications", + "icon": "notifications", + "badge": unread_notifications_badge, + "validators": [ + ("aleksis.core.util.predicates.permission_validator", "core.view_notifications",), + ], + }, { "name": _("Account"), "url": "#", diff --git a/aleksis/core/rules.py b/aleksis/core/rules.py index ac713bbe54a937d529da6c4e0266209dae593fd5..c118d9790fdeebe7d94906e1d045ea455a1cc8ee 100644 --- a/aleksis/core/rules.py +++ b/aleksis/core/rules.py @@ -16,6 +16,9 @@ rules.add_perm("core", rules.always_allow) # View dashboard rules.add_perm("core.view_dashboard", has_person) +# View notifications +rules.add_perm("core.view_notifications", has_person) + # Use search search_predicate = has_person & has_global_perm("core.search") rules.add_perm("core.search", search_predicate) diff --git a/aleksis/core/templates/core/notifications.html b/aleksis/core/templates/core/notifications.html new file mode 100644 index 0000000000000000000000000000000000000000..a49bc9e8e2a8ad87041acbf20e79e0486714cde6 --- /dev/null +++ b/aleksis/core/templates/core/notifications.html @@ -0,0 +1,32 @@ +{% extends 'core/base.html' %} +{% load i18n static dashboard %} + +{% block browser_title %}{% blocktrans %}Notifications{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Notifications{% endblocktrans %}{% endblock %} + + +{% block content %} + {% if object_list %} + <ul class="collection"> + {% for notification in object_list %} + <li class="collection-item"> + <span class="badge new primary-color">{{ notification.sender }}</span> + <span class="title">{{ notification.title }}</span> + <p> + <i class="material-icons left">access_time</i> {{ notification.created }} + </p> + <p> + {{ notification.description }} + </p> + {% if notification.link %} + <p> + <a href="{{ notification.link }}">{% blocktrans %}More information →{% endblocktrans %}</a> + </p> + {% endif %} + </li> + {% endfor %} + </ul> + {% else %} + <p>{% blocktrans %}No notifications available yet.{% endblocktrans %}</p> + {% endif %} +{% endblock %} diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py index 30c38b11c9c1e884f62f88a0f62b369f55fe2969..9fa05b3e46f1efab08e7b7452a06ce5534dc42eb 100644 --- a/aleksis/core/urls.py +++ b/aleksis/core/urls.py @@ -57,6 +57,7 @@ urlpatterns = [ path("group/<int:id_>/edit", views.edit_group, name="edit_group_by_id"), path("group/<int:id_>/delete", views.delete_group, name="delete_group_by_id"), path("", views.index, name="index"), + path("notifications/", views.Notifications.as_view(), name="notifications"), path("dashboard/edit/", views.EditDashboardView.as_view(), name="edit_dashboard"), path( "notifications/mark-read/<int:id_>", diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py index 18fcfc241ea76d96312e1339fbde6fd526218307..e1fceb34aae41bcb8a957801c2601e3eead74ee1 100644 --- a/aleksis/core/util/core_helpers.py +++ b/aleksis/core/util/core_helpers.py @@ -401,3 +401,8 @@ def queryset_rules_filter( wanted_objects.add(item.pk) return queryset.filter(pk__in=wanted_objects) + + +def unread_notifications_badge(request: HttpRequest) -> int: + """Generate badge content with the number of unread notifications.""" + return request.user.person.notifications.all().filter(read=False).count() diff --git a/aleksis/core/views.py b/aleksis/core/views.py index 41b36acfbcff7573366a2328d2d0f9f2a92fe1df..56eececce3f61d1be4d6f4f195ce7b0186e11fc2 100644 --- a/aleksis/core/views.py +++ b/aleksis/core/views.py @@ -109,6 +109,18 @@ def index(request: HttpRequest) -> HttpResponse: return render(request, "core/index.html", context) +class Notifications(PermissionRequiredMixin, ListView): + permission_required = "core.view_notifications" + template_name = "core/notifications.html" + + def get_queryset(self) -> QuerySet: + return self.request.user.person.notifications.order_by("-created") + + def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: + self.get_queryset().filter(read=False).update(read=True) + return super().get_context_data(**kwargs) + + def about(request: HttpRequest) -> HttpResponse: """About page listing all apps.""" context = {}