diff --git a/netbox/extras/forms/bulk_import.py b/netbox/extras/forms/bulk_import.py index 516a6766e..2ebba365a 100644 --- a/netbox/extras/forms/bulk_import.py +++ b/netbox/extras/forms/bulk_import.py @@ -3,7 +3,6 @@ import re from django import forms from django.contrib.postgres.forms import SimpleArrayField from django.core.exceptions import ObjectDoesNotExist -from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ from core.models import ObjectType diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py index d7e8fd1bd..a8406b671 100644 --- a/netbox/extras/forms/model_forms.py +++ b/netbox/extras/forms/model_forms.py @@ -257,6 +257,15 @@ class NotificationGroupForm(forms.ModelForm): model = NotificationGroup fields = ('name', 'description', 'groups', 'users') + def clean(self): + super().clean() + + # At least one User or Group must be assigned + if not self.cleaned_data['groups'] and not self.cleaned_data['users']: + raise forms.ValidationError(_("A notification group specify at least one user or group.")) + + return self.cleaned_data + class SubscriptionForm(forms.ModelForm): object_type = ContentTypeChoiceField( diff --git a/netbox/extras/models/notifications.py b/netbox/extras/models/notifications.py index d3fa77ec1..dba059ea7 100644 --- a/netbox/extras/models/notifications.py +++ b/netbox/extras/models/notifications.py @@ -88,12 +88,6 @@ class Notification(models.Model): def get_absolute_url(self): return reverse('account:notifications') - def get_read_url(self): - return reverse('extras:notification_read', kwargs={'pk': self.pk}) - - def get_dismiss_url(self): - return reverse('extras:notification_dismiss', kwargs={'pk': self.pk}) - def clean(self): super().clean() @@ -105,6 +99,9 @@ class Notification(models.Model): @cached_property def event(self): + """ + Returns the registered Event which triggered this Notification. + """ return registry['events'].get(self.event_type) @@ -165,6 +162,7 @@ class NotificationGroup(ChangeLoggedModel): Notification(user=member, **kwargs) for member in self.members ]) + notify.alters_data = True class Subscription(models.Model): diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index 477fb13a5..db4472313 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -31,13 +31,17 @@ __all__ = ( 'WebhookTable', ) -IMAGEATTACHMENT_IMAGE = ''' +IMAGEATTACHMENT_IMAGE = """ {% if record.image %} {{ record }} {% else %} — {% endif %} -''' +""" + +NOTIFICATION_ICON = """ + +""" class CustomFieldTable(NetBoxTable): @@ -276,19 +280,23 @@ class SubscriptionTable(NetBoxTable): linkify=True, orderable=False ) + user = tables.Column( + verbose_name=_('User'), + linkify=True + ) actions = columns.ActionsColumn( actions=('delete',) ) class Meta(NetBoxTable.Meta): model = Subscription - fields = ('pk', 'object', 'object_type', 'created') + fields = ('pk', 'object', 'object_type', 'created', 'user') default_columns = ('object', 'object_type', 'created') class NotificationTable(NetBoxTable): icon = columns.TemplateColumn( - template_code='', + template_code=NOTIFICATION_ICON, accessor=tables.A('event'), attrs={ 'td': {'class': 'w-1'}, @@ -315,14 +323,18 @@ class NotificationTable(NetBoxTable): timespec='minutes', verbose_name=_('Read'), ) + user = tables.Column( + verbose_name=_('User'), + linkify=True + ) actions = NotificationActionsColumn( actions=('dismiss',) ) class Meta(NetBoxTable.Meta): model = Notification - fields = ('pk', 'icon', 'object', 'object_type', 'event_type', 'created', 'read') - default_columns = ('icon', 'object', 'object_type', 'event_type', 'created', 'read') + fields = ('pk', 'icon', 'object', 'object_type', 'event_type', 'created', 'read', 'user') + default_columns = ('icon', 'object', 'object_type', 'event_type', 'created') row_attrs = { 'data-read': lambda record: bool(record.read), } diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 0675d1b0e..d3e346feb 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -407,7 +407,9 @@ class NotificationGroupBulkDeleteView(generic.BulkDeleteView): # class NotificationsView(LoginRequiredMixin, View): - + """ + HTMX-only user-specific notifications list. + """ def get(self, request): return render(request, 'htmx/notifications.html', { 'notifications': request.user.notifications.unread(), diff --git a/netbox/templates/htmx/notifications.html b/netbox/templates/htmx/notifications.html index ece463a9f..bd2de4e6e 100644 --- a/netbox/templates/htmx/notifications.html +++ b/netbox/templates/htmx/notifications.html @@ -7,11 +7,11 @@
- {{ notification.object }} + {{ notification.object }}
{{ notification.event }} {{ notification.created|timesince }} {% trans "ago" %}
- +