diff --git a/plinth/context_processors.py b/plinth/context_processors.py
index 3af5d73a0..1b3edfc70 100644
--- a/plinth/context_processors.py
+++ b/plinth/context_processors.py
@@ -18,9 +18,11 @@
Django context processors to provide common data to templates.
"""
-from django.utils.translation import ugettext as _, ugettext_noop
import re
+from django.utils.translation import ugettext as _
+from django.utils.translation import ugettext_noop
+
from plinth import cfg, menu
from plinth.utils import is_user_admin
@@ -35,6 +37,9 @@ def common(request):
# the brand name 'FreedomBox' itself to be translated.
ugettext_noop('FreedomBox')
+ from plinth.notification import Notification
+ notifications_context = Notification.get_display_context(user=request.user)
+
slash_indices = [match.start() for match in re.finditer('/', request.path)]
active_menu_urls = [request.path[:index + 1] for index in slash_indices]
return {
@@ -42,5 +47,7 @@ def common(request):
'submenu': menu.main_menu.active_item(request),
'active_menu_urls': active_menu_urls,
'box_name': _(cfg.box_name),
- 'user_is_admin': is_user_admin(request, True)
+ 'user_is_admin': is_user_admin(request, True),
+ 'notifications': notifications_context['notifications'],
+ 'notifications_max_severity': notifications_context['max_severity']
}
diff --git a/plinth/templates/base.html b/plinth/templates/base.html
index e75df0f3b..74131a205 100644
--- a/plinth/templates/base.html
+++ b/plinth/templates/base.html
@@ -98,6 +98,11 @@
+
+
+ {% include "notifications-dropdown.html" %}
+
+
{% block mainmenu_left %}
-
+
+ {% include "notifications.html" %}
diff --git a/plinth/templates/notifications-dropdown.html b/plinth/templates/notifications-dropdown.html
new file mode 100644
index 000000000..a096fdce4
--- /dev/null
+++ b/plinth/templates/notifications-dropdown.html
@@ -0,0 +1,36 @@
+{% comment %}
+#
+# This file is part of FreedomBox.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+{% endcomment %}
+
+{# Template to display a drop down button for notifications in the navbar #}
+
+{% load i18n %}
+
+{% if notifications %}
+
+
+
+
+ {{ notifications|length }}
+
+
+
+
+{% endif %}
diff --git a/plinth/templates/notifications.html b/plinth/templates/notifications.html
new file mode 100644
index 000000000..b264131b1
--- /dev/null
+++ b/plinth/templates/notifications.html
@@ -0,0 +1,75 @@
+{% comment %}
+#
+# This file is part of FreedomBox.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+{% endcomment %}
+
+{# Template to display notifications under the navbar #}
+
+{% load i18n %}
+{% load static %}
+
+{% if notifications %}
+
+
+
+{% endif %}
diff --git a/plinth/tests/test_context_processors.py b/plinth/tests/test_context_processors.py
index d2782bd0e..0df93bcf2 100644
--- a/plinth/tests/test_context_processors.py
+++ b/plinth/tests/test_context_processors.py
@@ -18,7 +18,7 @@
Test module for custom context processors.
"""
-from unittest.mock import MagicMock, Mock
+from unittest.mock import MagicMock, Mock, patch
import pytest
from django.http import HttpRequest
@@ -34,7 +34,8 @@ def fixture_menu():
menu_module.init()
-def test_common():
+@patch('plinth.notification.Notification')
+def test_common(Notification):
"""Verify that the common() function returns the correct values."""
cfg.read() # initialize config settings
@@ -62,7 +63,8 @@ def test_common():
assert response['user_is_admin']
-def test_common_border_conditions():
+@patch('plinth.notification.Notification')
+def test_common_border_conditions(Notification):
"""Verify that the common() function works for border conditions."""
request = HttpRequest()
request.path = ''
diff --git a/static/themes/default/css/plinth.css b/static/themes/default/css/plinth.css
index f9e461b53..6c3808c7e 100644
--- a/static/themes/default/css/plinth.css
+++ b/static/themes/default/css/plinth.css
@@ -609,3 +609,77 @@ a.menu_link_active {
transform: scale(1.2);
}
}
+
+/*
+ * Notifications
+ */
+.notifications {
+ margin-left: -15px;
+ margin-right: -15px;
+}
+
+.notifications .dropdown-menu {
+ width: 100%;
+ padding: 0;
+ float: none;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ margin-top: 0;
+}
+
+/* Style for individual notification */
+.notification {
+ padding: 1rem 1.5rem;
+ font-size: 15px;
+}
+
+.notification:not(:first-child) {
+ border-top: 1px solid #ddd;
+}
+
+.notification-title {
+ font-weight: bold;
+}
+
+img.notification-icon {
+ display: inline-block;
+ width: 1.4rem;
+ height: 1.4rem;
+ margin-top: -0.4rem;
+}
+
+/* Show badge with various colors and overlap it onto icon */
+.notifications-dropdown .badge {
+ padding: 2px 5px;
+ margin-left: -12px;
+ color: #fff;
+}
+
+.badge-exception, .badge-error {
+ background-color: #d9534f;
+}
+
+.badge-warning {
+ background-color: #ec971f;
+}
+
+.badge-info, badge-debug {
+ background-color: #5bc0de;
+}
+
+/* Don't collapse notifications on small screens */
+.collapsing .notifications-dropdown,
+.collapse.in .notifications-dropdown {
+ display: none;
+}
+
+.navbar-header .navbar-nav {
+ float: right;
+ margin: 6px 10px;
+}
+
+@media(min-width:768px) {
+ .navbar-header .navbar-nav {
+ display: none;
+ }
+}