Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Improve UX of the Notifications Module #344

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/user/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ include:

- :doc:`sending-notifications`
- :ref:`notifications_web_notifications`
- :ref:`notifications_email_notifications`
- :ref:`notifications_email_notifications` and
:ref:`notifications_batches`
- :doc:`notification-types`
- :doc:`User notification preferences <notification-preferences>`
- :ref:`Silencing notifications for specific objects temporarily or
Expand Down
25 changes: 23 additions & 2 deletions docs/user/notification-preferences.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Notification Preferences
========================

.. image:: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/notification-settings.png
:target: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/notification-settings.png
.. image:: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/25/notifications/preference-page.png
:target: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/25/notifications/preference-page.png
:align: center

OpenWISP Notifications enables users to customize their notification
Expand All @@ -12,6 +12,27 @@ organized by notification type and organization, allowing users to tailor
their notification experience by opting to receive updates only from
specific organizations or notification types.

Users can access and manage their notification preferences directly from
the notification widget by clicking the button highlighted in the
screenshot below:

.. image:: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/25/notifications/notification-preferences-button.png
:target: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/25/notifications/notification-preferences-button.png
:align: center

Alternatively, you can also visit ``/notification/preferences/`` to manage
your settings.

.. note::

- You can disable notifications globally while still enabling them for
specific organizations.
- Notification settings are now linked: disabling web notifications
will automatically disable email notifications, and enabling email
notifications will automatically enable web notifications.
- Deleting notification settings is no longer possible via the web
interface (please use the REST API if removal is needed).

Notification settings are automatically generated for all notification
types and organizations for every user. Superusers have the ability to
manage notification settings for all users, including adding or deleting
Expand Down
37 changes: 31 additions & 6 deletions docs/user/rest-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,19 @@ Delete a Notification

DELETE /api/v1/notifications/notification/{pk}/

Notification Read Redirect
~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: text

GET /api/v1/notifications/notification/{pk}/redirect/

List User's Notification Setting
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: text

GET /api/v1/notifications/notification/user-setting/
GET /api/v1/notifications/user/{user_id}/user-setting/

**Available Filters**

Expand All @@ -130,35 +137,42 @@ You can filter the list of user's notification setting based on their

.. code-block:: text

GET /api/v1/notifications/notification/user-setting/?organization={organization_id}
GET /api/v1/notifications/user/{user_id}/user-setting/?organization={organization_id}

You can filter the list of user's notification setting based on their
``organization_slug``.

.. code-block:: text

GET /api/v1/notifications/notification/user-setting/?organization_slug={organization_slug}
GET /api/v1/notifications/user/{user_id}/user-setting/?organization_slug={organization_slug}

You can filter the list of user's notification setting based on their
``type``.

.. code-block:: text

GET /api/v1/notifications/notification/user-setting/?type={type}
GET /api/v1/notifications/user/{user_id}/user-setting/?type={type}

Get Notification Setting Details
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: text

GET /api/v1/notifications/notification/user-setting/{pk}/
GET /api/v1/notifications/user/{user_id}/user-setting/{pk}/

Update Notification Setting Details
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: text

PATCH /api/v1/notifications/notification/user-setting/{pk}/
PATCH /api/v1/notifications/user/{user_id}/user-setting/{pk}/

Organization Notification Setting
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: text

POST /api/v1/notifications/user/{user_id}/organization/{organization_id}/setting/

List User's Object Notification Setting
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -187,3 +201,14 @@ Delete Object Notification Setting
.. code-block:: text

DELETE /api/v1/notifications/notification/ignore/{app_label}/{model_name}/{object_id}/

Deprecated Endpoints
~~~~~~~~~~~~~~~~~~~~

The following endpoints are deprecated and will be removed in future
releases:

.. code-block:: text

GET /api/v1/notifications/notification/user-setting/
GET /api/v1/notifications/notification/user-setting/{pk}/
31 changes: 31 additions & 0 deletions docs/user/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,34 @@ The default configuration is as follows:
# Maximum interval after which the notification widget should get updated (in seconds)
"max_allowed_backoff": 15,
}

.. _openwisp_notifications_email_batch_interval:

``OPENWISP_NOTIFICATIONS_EMAIL_BATCH_INTERVAL``
-----------------------------------------------

======= =================================
Type ``int``
Default ``1800`` (30 minutes, in seconds)
======= =================================

This setting determines the :ref:`interval of the email batching feature
<notifications_batches>`.

The interval is specified in seconds.

To send email notifications immediately without batching, set this value
to ``0``.

.. _openwisp_notifications_email_batch_display_limit:

``OPENWISP_NOTIFICATIONS_EMAIL_BATCH_DISPLAY_LIMIT``
----------------------------------------------------

======= =======
Type ``int``
Default ``15``
======= =======

This setting specifies the maximum number of email notifications that can
be included in a single :ref:`email batch <notifications_batches>`.
52 changes: 50 additions & 2 deletions docs/user/web-email-notifications.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,58 @@ notification toast.
Email Notifications
-------------------

.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/email-template.png
:target: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/email-template.png
.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/25/emails/template.png
:target: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/25/emails/template.png
:align: center

Along with web notifications OpenWISP Notifications also sends email
notifications leveraging the :ref:`send_email feature of OpenWISP Utils
<utils_send_email>`.

.. _notifications_batches:

Email Batches
~~~~~~~~~~~~~

.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/25/emails/batch-email.png
:target: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/25/emails/batch-email.png
:align: center

Batching email notifications helps manage the flow of emails sent to
users, especially during periods of increased alert activity. By grouping
emails into batches, the system minimizes the risk of emails being marked
as spam and prevents inboxes from rejecting alerts due to high volumes.

Key aspects of the batch email notification feature include:

- When multiple emails are triggered for the same user within a short time
frame, subsequent emails are grouped into a summary.
- The sending of individual emails is paused for a specified batch
interval when batching is enabled.

.. note::

If new alerts are received while a batch is pending, they will be
added to the current summary without resetting the timer. The batched
email will be sent when the initial batch interval expires.

You can customize the behavior of batch email notifications using the
following settings:

- :ref:`OPENWISP_NOTIFICATIONS_EMAIL_BATCH_INTERVAL
<openwisp_notifications_email_batch_interval>`.
- :ref:`OPENWISP_NOTIFICATIONS_EMAIL_BATCH_DISPLAY_LIMIT
<openwisp_notifications_email_batch_display_limit>`.

Unsubscribing from Email Notifications
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In addition to updating notification preferences via the :ref:`preferences
page <notification-preferences>`, users can opt out of receiving email
notifications using the unsubscribe link included in every notification
email.

Furthermore, email notifications include `List-Unsubscribe headers
<https://www.ietf.org/rfc/rfc2369.txt>`_, enabling modern email clients to
provide an unsubscribe button directly within their interface, offering a
seamless opt-out experience.
22 changes: 0 additions & 22 deletions openwisp_notifications/admin.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
from django.contrib import admin

from openwisp_notifications.base.admin import NotificationSettingAdminMixin
from openwisp_notifications.swapper import load_model
from openwisp_notifications.widgets import _add_object_notification_widget
from openwisp_users.admin import UserAdmin
from openwisp_utils.admin import AlwaysHasChangedMixin

Notification = load_model('Notification')
NotificationSetting = load_model('NotificationSetting')


class NotificationSettingInline(
NotificationSettingAdminMixin, AlwaysHasChangedMixin, admin.TabularInline
):
model = NotificationSetting
extra = 0

def has_change_permission(self, request, obj=None):
return request.user.is_superuser or request.user == obj


UserAdmin.inlines = [NotificationSettingInline] + UserAdmin.inlines

_add_object_notification_widget()
16 changes: 16 additions & 0 deletions openwisp_notifications/api/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from rest_framework.permissions import BasePermission


class PreferencesPermission(BasePermission):
"""
Permission class for the notification preferences.

Permission is granted only in these two cases:
1. Superusers can change the notification preferences of any user.
2. Regular users can only change their own preferences.
"""

def has_permission(self, request, view):
return request.user.is_superuser or request.user.id == view.kwargs.get(
'user_id'
)
16 changes: 16 additions & 0 deletions openwisp_notifications/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ class Meta(NotificationSerializer.Meta):


class NotificationSettingSerializer(serializers.ModelSerializer):
organization_name = serializers.CharField(
source='organization.name', read_only=True
)
type_label = serializers.CharField(source='get_type_display', read_only=True)

class Meta:
model = NotificationSetting
exclude = ['user']
Expand All @@ -87,3 +92,14 @@ class Meta:
'object_content_type',
'object_id',
]


class NotificationSettingUpdateSerializer(serializers.Serializer):
email = serializers.BooleanField(required=False)
web = serializers.BooleanField(required=False)

def validate(self, attrs):
attrs = super().validate(attrs)
if 'email' not in attrs and attrs.get('web') is False:
attrs['email'] = False
return attrs
44 changes: 34 additions & 10 deletions openwisp_notifications/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,56 @@ def get_api_urls(api_views=None):
if not api_views:
api_views = views
return [
path('', views.notifications_list, name='notifications_list'),
path('read/', views.notifications_read_all, name='notifications_read_all'),
path('<uuid:pk>/', views.notification_detail, name='notification_detail'),
path('notification/', views.notifications_list, name='notifications_list'),
path(
'<uuid:pk>/redirect/',
'notification/read/',
views.notifications_read_all,
name='notifications_read_all',
),
path(
'notification/<uuid:pk>/',
views.notification_detail,
name='notification_detail',
),
path(
'notification/<uuid:pk>/redirect/',
views.notification_read_redirect,
name='notification_read_redirect',
),
path(
'user-setting/',
'user/<uuid:user_id>/user-setting/',
views.notification_setting_list,
name='notification_setting_list',
name='user_notification_setting_list',
),
path(
'user-setting/<uuid:pk>/',
'user/<uuid:user_id>/user-setting/<uuid:pk>/',
views.notification_setting,
name='notification_setting',
name='user_notification_setting',
),
path(
'ignore/',
'notification/ignore/',
views.ignore_object_notification_list,
name='ignore_object_notification_list',
),
path(
'ignore/<str:app_label>/<str:model_name>/<uuid:object_id>/',
'notification/ignore/<str:app_label>/<str:model_name>/<uuid:object_id>/',
views.ignore_object_notification,
name='ignore_object_notification',
),
path(
'user/<uuid:user_id>/organization/<uuid:organization_id>/setting/',
views.organization_notification_setting,
name='organization_notification_setting',
),
# DEPRECATED
path(
'user/user-setting/',
views.notification_setting_list,
name='notification_setting_list',
),
path(
'user/user-setting/<uuid:pk>/',
views.notification_setting,
name='notification_setting',
),
]
Loading
Loading