mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-09-19 04:59:01 +00:00
notification: Show a drop down from main navbar for notifications
Closes: #1042. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
afe179d91d
commit
8529022f63
@ -18,9 +18,11 @@
|
|||||||
Django context processors to provide common data to templates.
|
Django context processors to provide common data to templates.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _, ugettext_noop
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
from django.utils.translation import ugettext_noop
|
||||||
|
|
||||||
from plinth import cfg, menu
|
from plinth import cfg, menu
|
||||||
from plinth.utils import is_user_admin
|
from plinth.utils import is_user_admin
|
||||||
|
|
||||||
@ -35,6 +37,9 @@ def common(request):
|
|||||||
# the brand name 'FreedomBox' itself to be translated.
|
# the brand name 'FreedomBox' itself to be translated.
|
||||||
ugettext_noop('FreedomBox')
|
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)]
|
slash_indices = [match.start() for match in re.finditer('/', request.path)]
|
||||||
active_menu_urls = [request.path[:index + 1] for index in slash_indices]
|
active_menu_urls = [request.path[:index + 1] for index in slash_indices]
|
||||||
return {
|
return {
|
||||||
@ -42,5 +47,7 @@ def common(request):
|
|||||||
'submenu': menu.main_menu.active_item(request),
|
'submenu': menu.main_menu.active_item(request),
|
||||||
'active_menu_urls': active_menu_urls,
|
'active_menu_urls': active_menu_urls,
|
||||||
'box_name': _(cfg.box_name),
|
'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']
|
||||||
}
|
}
|
||||||
|
|||||||
@ -98,6 +98,11 @@
|
|||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<ul class="nav navbar-nav">
|
||||||
|
{% include "notifications-dropdown.html" %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
{% block mainmenu_left %}
|
{% block mainmenu_left %}
|
||||||
<a href="{% url 'index' %}" class="navbar-brand
|
<a href="{% url 'index' %}" class="navbar-brand
|
||||||
{% if not submenu.url %} menu_link_active {% else %}
|
{% if not submenu.url %} menu_link_active {% else %}
|
||||||
@ -143,7 +148,11 @@
|
|||||||
{% block mainmenu_right %}
|
{% block mainmenu_right %}
|
||||||
|
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
|
|
||||||
|
{% include "notifications-dropdown.html" %}
|
||||||
|
|
||||||
{% include "help-menu.html" %}
|
{% include "help-menu.html" %}
|
||||||
|
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a href="{% url 'users:edit' request.user.username %}"
|
<a href="{% url 'users:edit' request.user.username %}"
|
||||||
class="dropdown-toggle" data-toggle="dropdown"
|
class="dropdown-toggle" data-toggle="dropdown"
|
||||||
@ -215,8 +224,9 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% include "notifications.html" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
36
plinth/templates/notifications-dropdown.html
Normal file
36
plinth/templates/notifications-dropdown.html
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{# Template to display a drop down button for notifications in the navbar #}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% if notifications %}
|
||||||
|
<li class="dropdown notifications-dropdown">
|
||||||
|
<a href="#" title="{% trans "Notifications" %}" class="dropdown-toggle"
|
||||||
|
data-toggle="dropdown" role="button" aria-expanded="false"
|
||||||
|
data-target=".notifications">
|
||||||
|
<span class="fa fa-bell nav-icon"></span>
|
||||||
|
<span class="badge badge-{{ notifications_max_severity }}">
|
||||||
|
{{ notifications|length }}
|
||||||
|
</span>
|
||||||
|
<span class="caret"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
75
plinth/templates/notifications.html
Normal file
75
plinth/templates/notifications.html
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{# Template to display notifications under the navbar #}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% if notifications %}
|
||||||
|
<div class="notifications dropdown">
|
||||||
|
<ul class="dropdown-menu" role="menu">
|
||||||
|
{% for note in notifications %}
|
||||||
|
<li class="notification">
|
||||||
|
{% if note.data.app_name %}
|
||||||
|
<div class="app-name">
|
||||||
|
{% if note.data.app_icon %}
|
||||||
|
<div class="app-icon fa {{ note.data.app_icon }}"></div>
|
||||||
|
{% elif note.data.app_icon_filename %}
|
||||||
|
<img src="{% static 'theme/icons/' %}{{ note.data.app_icon_filename }}.svg"
|
||||||
|
alt="{{ note.data.app_name }}"
|
||||||
|
class="notification-icon" />
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{{ note.data.app_name }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if note.body %}
|
||||||
|
{{ note.body.content.decode|safe }}
|
||||||
|
{% else %}
|
||||||
|
<div class="notification-title">{{ note.title }}</div>
|
||||||
|
|
||||||
|
{% if note.message %}
|
||||||
|
<p>{{ note.message }}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if note.actions %}
|
||||||
|
<p>
|
||||||
|
{% for action in note.actions %}
|
||||||
|
{% if action.type == "dismiss" %}
|
||||||
|
<a href="{% url 'notification_dismiss' id=note.id %}?next={{ request.path|iriencode }}"
|
||||||
|
role="button" class="btn btn-default">
|
||||||
|
{% trans "Dismiss" %}
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{% url action.url %}" role="button"
|
||||||
|
class="btn btn-{{ action.class|default:'default' }}">
|
||||||
|
{{ action.text }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
@ -18,7 +18,7 @@
|
|||||||
Test module for custom context processors.
|
Test module for custom context processors.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from unittest.mock import MagicMock, Mock
|
from unittest.mock import MagicMock, Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
@ -34,7 +34,8 @@ def fixture_menu():
|
|||||||
menu_module.init()
|
menu_module.init()
|
||||||
|
|
||||||
|
|
||||||
def test_common():
|
@patch('plinth.notification.Notification')
|
||||||
|
def test_common(Notification):
|
||||||
"""Verify that the common() function returns the correct values."""
|
"""Verify that the common() function returns the correct values."""
|
||||||
cfg.read() # initialize config settings
|
cfg.read() # initialize config settings
|
||||||
|
|
||||||
@ -62,7 +63,8 @@ def test_common():
|
|||||||
assert response['user_is_admin']
|
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."""
|
"""Verify that the common() function works for border conditions."""
|
||||||
request = HttpRequest()
|
request = HttpRequest()
|
||||||
request.path = ''
|
request.path = ''
|
||||||
|
|||||||
@ -609,3 +609,77 @@ a.menu_link_active {
|
|||||||
transform: scale(1.2);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user