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] Added generic message notification type (shown in dialg box) #275

Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
c207c9d
[feature] Description of notification shown in a dialog admin site
Dhanus3133 May 14, 2024
1025bfb
[qa] Fix
Dhanus3133 May 14, 2024
2605a09
[chore] Add trans
Dhanus3133 May 14, 2024
bf3ba25
Merge branch 'master' into feat/display-description-notification-dialog
Dhanus3133 May 18, 2024
a15a284
[chore] Increase page_size
Dhanus3133 May 20, 2024
3a0377d
[chore] Fix relative URL error
Dhanus3133 May 22, 2024
2be7037
[chore] Improvements
Dhanus3133 May 23, 2024
581cc90
[chore] Show description with md support
Dhanus3133 May 23, 2024
a7c792d
[chore] Rename general to generic message types
Dhanus3133 May 23, 2024
9e7c1fc
[chore] Fix menu bar
Dhanus3133 May 25, 2024
2608c11
[chore] Rename rendered_description
Dhanus3133 May 25, 2024
2a922a6
[chore] Fix test
Dhanus3133 May 25, 2024
0686c2d
[chore] Just use description
Dhanus3133 May 26, 2024
8106f3e
[fix] Rendered description shows error for None description
Dhanus3133 May 26, 2024
cc528df
[fix] Level icon repeat
Dhanus3133 May 26, 2024
aca39bc
[chore] JQuery way
Dhanus3133 May 26, 2024
1e6b840
[fix] Fixed tests
pandafy May 27, 2024
00ec345
[chore] Bump changes
Dhanus3133 May 27, 2024
e69600f
[chore] Add tests for notification level
Dhanus3133 May 27, 2024
bbff510
[chore] Remove comment
Dhanus3133 May 27, 2024
8dfcfce
Merge branch 'master' into feat/display-description-notification-dialog
Dhanus3133 May 28, 2024
df10915
[chore] Focus fix
Dhanus3133 May 28, 2024
6e5db98
[chores] UI/CSS improvements
nemesifier May 29, 2024
6de368f
[fix] Toast notifications dialog
Dhanus3133 May 30, 2024
b0725b1
[fix] Use relative path also for notification toast
nemesifier May 30, 2024
9ed5982
[chore] Add generic_message test
Dhanus3133 May 30, 2024
4a9956b
[docs] Added generic_message
nemesifier May 30, 2024
4593ef3
[feature] Support markdown in message and description of generic noti…
pandafy May 30, 2024
9f11de6
[fix] Open button display only when target_url is available
Dhanus3133 May 31, 2024
b09cd6f
[changes] Use context manager to handle temporary attribtues
pandafy May 31, 2024
8229268
[chore] Add Tests for open button visibility
Dhanus3133 Jun 1, 2024
08ab21b
[fix] Keyup event handler
Dhanus3133 Jun 7, 2024
69bfb39
Merge branch 'master' into feat/display-description-notification-dialog
nemesifier Jun 7, 2024
0020c21
[chore] Refactor message conversion to improve readability
Dhanus3133 Jun 7, 2024
859734e
[chore] Comment changes
Dhanus3133 Jun 8, 2024
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
67 changes: 62 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -527,11 +527,61 @@ value you want but it needs to be unique. For more details read `preventing dupl
Notification Types
------------------

**OpenWISP Notifications** simplifies configuring individual notification by
using notification types. You can think of a notification type as a template
**OpenWISP Notifications** allows defining notification types for
recurring events. Think of a notification type as a template
for notifications.

These properties can be configured for each notification type:
``generic_message``
~~~~~~~~~~~~~~~~~~~

.. figure:: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/1.1/generic_message.png
:target: https://raw.githubusercontent.com/openwisp/openwisp-notifications/docs/docs/images/1.1/generic_message.png
:align: center

This module includes a notification type called ``generic_message``.

This notification type is designed to deliver custom messages in the
user interface for infrequent events or errors that occur during
background operations and cannot be communicated easily to the user
in other ways.

These messages may require longer explanations and are therefore
displayed in a dialog overlay, as shown in the screenshot above.
This notification type does not send emails.

The following code example demonstrates how to send a notification
of this type:

.. code-block:: python

from openwisp_notifications.signals import notify
notify.send(
type='generic_message',
level='error',
message='An unexpected error happened!',
sender=User.objects.first(),
target=User.objects.last(),
description="""Lorem Ipsum is simply dummy text
of the printing and typesetting industry.

### Heading 3

Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a
type specimen book.

It has survived not only **five centuries**, but also the leap into
electronic typesetting, remaining essentially unchanged.

It was popularised in the 1960s with the release of Letraset sheets
containing Lorem Ipsum passages, and more recently with desktop publishing
software like Aldus PageMaker including versions of *Lorem Ipsum*."""
)

Properties of Notification Types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The following properties can be configured for each notification type:

+------------------------+----------------------------------------------------------------+
| **Property** | **Description** |
Expand Down Expand Up @@ -569,8 +619,15 @@ These properties can be configured for each notification type:
+------------------------+----------------------------------------------------------------+


**Note**: A notification type configuration should contain atleast one of ``message`` or ``message_template``
settings. If both of them are present, ``message`` is given preference over ``message_template``.
**Note**: It is recommended that a notification type configuration
for recurring events contains either the ``message`` or
``message_template`` properties. If both are present,
``message`` is given preference over ``message_template``.

If you don't plan on using ``message`` or ``message_template``,
it may be better to use the existing ``generic_message`` type.
However, it's advised to do so only if the event being notified
is infrequent.

**Note**: The callable for ``actor_link``, ``action_object_link`` and ``target_link`` should
have the following signature:
Expand Down
3 changes: 3 additions & 0 deletions openwisp_notifications/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,13 @@ def data(self):


class NotificationListSerializer(NotificationSerializer):
description = serializers.CharField(source='rendered_description')

class Meta(NotificationSerializer.Meta):
fields = [
'id',
'message',
'description',
'unread',
'target_url',
'email_subject',
Expand Down
62 changes: 50 additions & 12 deletions openwisp_notifications/base/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,36 +105,74 @@ def target_url(self):
def message(self):
return self.get_message()

@cached_property
def rendered_description(self):
Dhanus3133 marked this conversation as resolved.
Show resolved Hide resolved
if not self.description:
return
md_description = self._get_formatted_string(self.description)
return mark_safe(markdown(md_description))

@property
def email_message(self):
return self.get_message(email_message=True)

def _set_extra_attributes(self, email_message=False):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought of performing these operations in the __init__, but the target_link is dynamic based on whether we are using the message in the email.

"""
This method sets extra attributes for the notification
which are required to render the notification correctly.
"""
self.actor_link = self.actor_url
self.action_link = self.action_url
self.target_link = (
self.target_url if not email_message else self.redirect_view_url
)

def _delete_extra_attributes(self):
"""
This method removes the extra_attributes set by
_set_extra_attributes
"""
for attr in ('actor_link', 'action_link', 'target_link'):
delattr(self, attr)

def _get_formatted_string(self, string, email_message=False):
self._set_extra_attributes(email_message=email_message)
data = self.data or {}
try:
formatted_output = string.format(notification=self, **data)
except AttributeError as exception:
self._invalid_notification(
self.pk,
exception,
'Error encountered in rendering notification message',
)
self._delete_extra_attributes()
return formatted_output
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required because we handle the target URL differently when we send the email.

The email includes a ridrect URL, which flags the notification as read and then redirects the user to the target object.


def get_message(self, email_message=False):
if self.type:
# setting links in notification object for message rendering
self.actor_link = self.actor_url
self.action_link = self.action_url
self.target_link = (
self.target_url if not email_message else self.redirect_view_url
)
try:
config = get_notification_configuration(self.type)
data = self.data or {}
if 'message' in config:
md_text = config['message'].format(notification=self, **data)
if 'message' in data:
md_text = data['message']
elif 'message' in config:
md_text = config['message']
else:
self._set_extra_attributes(email_message=email_message)
md_text = render_to_string(
config['message_template'], context=dict(notification=self)
).strip()
except (AttributeError, KeyError, NotificationRenderException) as exception:
self._delete_extra_attributes()
except (KeyError, NotificationRenderException) as exception:
self._invalid_notification(
self.pk,
exception,
'Error encountered in rendering notification message',
)
# clean up
self.actor_link = self.action_link = self.target_link = None
return mark_safe(markdown(md_text))
return mark_safe(
markdown(self._get_formatted_string(md_text, email_message))
)
else:
return self.description

Expand Down
4 changes: 2 additions & 2 deletions openwisp_notifications/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ def notify_handler(**kwargs):
except NotificationRenderException as error:
logger.error(f'Error encountered while creating notification: {error}')
return
level = notification_template.get(
'level', kwargs.pop('level', Notification.LEVELS.info)
level = kwargs.pop(
'level', notification_template.get('level', Notification.LEVELS.info)
)
verb = notification_template.get('verb', kwargs.pop('verb', None))
user_app_name = User._meta.app_label
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@
.ow-notification-level-text {
padding: 0px 6px;
text-transform: uppercase;
font-weight: bold;
}
.ow-notification-elem .icon,
.ow-notification-toast .icon {
Expand Down Expand Up @@ -300,6 +301,66 @@
}
.ow-notification-elem:last-child .ow-notification-inner { border-bottom: none }

/* Generic notification dialog */
.ow-overlay-notification {
background-color: rgba(0, 0, 0, 0.6);
display: flex;
justify-content: center;
align-items: center;
transition: opacity 0.3s;
}
.ow-dialog-notification {
position: relative;
background-color: white;
padding: 20px;
padding-top: 20px;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
width: 90%;
max-width: 500px;
text-align: left;
}
.ow-dialog-notification .ow-notification-date {
padding-right: 15px;
}
.ow-dialog-notification .button {
margin-right: 10px;
}
.ow-dialog-notification-level-wrapper {
display: flex;
justify-content: space-between;
}
.ow-dialog-notification .icon {
min-height: 15;
min-width: 15px;
background-repeat: no-repeat;
}
.ow-dialog-close-x {
cursor: pointer;
font-size: 1.75em;
position: absolute;
display: block;
font-weight: bold;
top: 3px;
right: 10px;
}
.ow-dialog-close-x:hover {
color: #df5d43;
}
.ow-message-title {
color: #333;
margin-bottom: 10px;
}
.ow-message-title a {
color: #df5d43;
}
.ow-message-title a:hover {
text-decoration: underline;
}
.ow-dialog-buttons {
line-height: 3em;
}

@media screen and (max-width: 600px) {
.ow-notification-dropdown {
width: 98%;
Expand Down
Loading
Loading