diff --git a/netbox/extras/api/serializers_/notifications.py b/netbox/extras/api/serializers_/notifications.py index 6ba7f0284..71791e71f 100644 --- a/netbox/extras/api/serializers_/notifications.py +++ b/netbox/extras/api/serializers_/notifications.py @@ -26,7 +26,7 @@ class NotificationSerializer(ValidatedModelSerializer): class Meta: model = Notification fields = [ - 'id', 'url', 'display', 'object_type', 'object_id', 'object', 'user', 'created', 'read', 'event', + 'id', 'url', 'display', 'object_type', 'object_id', 'object', 'user', 'created', 'read', 'event_name', ] brief_fields = ('id', 'url', 'display', 'object_type', 'object_id', 'user', 'event') diff --git a/netbox/extras/choices.py b/netbox/extras/choices.py index 6759f6e90..387716c85 100644 --- a/netbox/extras/choices.py +++ b/netbox/extras/choices.py @@ -148,25 +148,6 @@ class JournalEntryKindChoices(ChoiceSet): ] -# -# Notifications -# - -# TODO: Support dynamic entries from plugins -class NotificationEventChoices(ChoiceSet): - key = 'Notification.event' - - OBJECT_CREATED = 'object_created' - OBJECT_CHANGED = 'object_changed' - OBJECT_DELETED = 'object_deleted' - - CHOICES = [ - (OBJECT_CREATED, _('Object created')), - (OBJECT_CHANGED, _('Object changed')), - (OBJECT_DELETED, _('Object deleted')), - ] - - # # Reports and Scripts # diff --git a/netbox/extras/events.py b/netbox/extras/events.py index 0c3f6015f..cf76b3564 100644 --- a/netbox/extras/events.py +++ b/netbox/extras/events.py @@ -12,8 +12,8 @@ from core.choices import ObjectChangeActionChoices from core.models import Job from netbox.config import get_config from netbox.constants import RQ_QUEUE_DEFAULT +from netbox.events import Event from netbox.registry import registry -from users.models import User from utilities.api import get_serializer_for_model from utilities.rqworker import get_rq_retry from utilities.serialization import serialize_object @@ -22,6 +22,15 @@ from .models import EventRule logger = logging.getLogger('netbox.events_processor') +EVENT_OBJECT_CREATED = 'object_created' +EVENT_OBJECT_UPDATED = 'object_updated' +EVENT_OBJECT_DELETED = 'object_deleted' + +# Register event types +Event(name=EVENT_OBJECT_CREATED, text=_('Object created')).register() +Event(name=EVENT_OBJECT_UPDATED, text=_('Object updated')).register() +Event(name=EVENT_OBJECT_DELETED, text=_('Object deleted')).register() + def serialize_for_event(instance): """ diff --git a/netbox/extras/migrations/0118_notifications.py b/netbox/extras/migrations/0118_notifications.py index cbb32dabc..b498b8d8f 100644 --- a/netbox/extras/migrations/0118_notifications.py +++ b/netbox/extras/migrations/0118_notifications.py @@ -52,7 +52,7 @@ class Migration(migrations.Migration): ('created', models.DateTimeField(auto_now_add=True)), ('read', models.DateTimeField(null=True)), ('object_id', models.PositiveBigIntegerField()), - ('event', models.CharField(max_length=30)), + ('event_name', models.CharField(max_length=50)), ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL)), ], diff --git a/netbox/extras/models/notifications.py b/netbox/extras/models/notifications.py index 9e5f755e5..33fda8b9b 100644 --- a/netbox/extras/models/notifications.py +++ b/netbox/extras/models/notifications.py @@ -1,3 +1,5 @@ +from functools import cached_property + from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey from django.core.exceptions import ValidationError @@ -6,9 +8,9 @@ from django.urls import reverse from django.utils.translation import gettext_lazy as _ from core.models import ObjectType -from extras.choices import * from extras.querysets import NotificationQuerySet from netbox.models import ChangeLoggedModel +from netbox.registry import registry from utilities.querysets import RestrictedQuerySet __all__ = ( @@ -44,10 +46,9 @@ class Notification(models.Model): ct_field='object_type', fk_field='object_id' ) - event = models.CharField( + event_name = models.CharField( verbose_name=_('event'), - max_length=30, - choices=NotificationEventChoices + max_length=50 ) objects = NotificationQuerySet.as_manager() @@ -89,6 +90,10 @@ class Notification(models.Model): _("Objects of this type ({type}) do not support notifications.").format(type=self.object_type) ) + @cached_property + def event(self): + return registry['events'].get(self.event_name) + class NotificationGroup(ChangeLoggedModel): """ diff --git a/netbox/extras/signals.py b/netbox/extras/signals.py index 6bcc398e7..c6895bec5 100644 --- a/netbox/extras/signals.py +++ b/netbox/extras/signals.py @@ -12,7 +12,6 @@ from django_prometheus.models import model_deletes, model_inserts, model_updates from core.choices import ObjectChangeActionChoices from core.models import ObjectChange, ObjectType from core.signals import job_end, job_start -from extras.choices import NotificationEventChoices from extras.constants import EVENT_JOB_END, EVENT_JOB_START from extras.events import process_event_rules from extras.models import EventRule, Notification, Subscription @@ -22,7 +21,7 @@ from netbox.models.features import ChangeLoggingMixin from netbox.registry import registry from netbox.signals import post_clean from utilities.exceptions import AbortRequest -from .events import enqueue_object +from .events import EVENT_OBJECT_UPDATED, enqueue_object from .models import CustomField, TaggedItem from .validators import CustomValidator @@ -310,7 +309,7 @@ def notify_object_changed(sender, instance, created, raw, **kwargs): Notification( user_id=sub['user'], object=instance, - event=NotificationEventChoices.OBJECT_CHANGED + event_name=EVENT_OBJECT_UPDATED ) for sub in subscriptions ] diff --git a/netbox/netbox/events.py b/netbox/netbox/events.py new file mode 100644 index 000000000..7412527c8 --- /dev/null +++ b/netbox/netbox/events.py @@ -0,0 +1,46 @@ +from dataclasses import dataclass + +from netbox.registry import registry + +__all__ = ( + 'EVENT_TYPE_DANGER', + 'EVENT_TYPE_INFO', + 'EVENT_TYPE_SUCCESS', + 'EVENT_TYPE_WARNING', + 'Event', +) + + +EVENT_TYPE_INFO = 'info' +EVENT_TYPE_SUCCESS = 'success' +EVENT_TYPE_WARNING = 'warning' +EVENT_TYPE_DANGER = 'danger' + + +@dataclass +class Event: + name: str + text: str + type: str = EVENT_TYPE_INFO + + def __str__(self): + return self.text + + def register(self): + registry['events'][self.name] = self + + def color(self): + return { + EVENT_TYPE_INFO: 'blue', + EVENT_TYPE_SUCCESS: 'green', + EVENT_TYPE_WARNING: 'orange', + EVENT_TYPE_DANGER: 'red', + }.get(self.type) + + def icon(self): + return { + EVENT_TYPE_INFO: 'mdi mdi-information', + EVENT_TYPE_SUCCESS: 'mdi mdi-check-circle', + EVENT_TYPE_WARNING: 'mdi mdi-alert-box', + EVENT_TYPE_DANGER: 'mdi mdi-alert-octagon', + }.get(self.type) diff --git a/netbox/netbox/registry.py b/netbox/netbox/registry.py index d783647ec..44cdfb92b 100644 --- a/netbox/netbox/registry.py +++ b/netbox/netbox/registry.py @@ -25,6 +25,7 @@ registry = Registry({ 'counter_fields': collections.defaultdict(dict), 'data_backends': dict(), 'denormalized_fields': collections.defaultdict(list), + 'events': dict(), 'model_features': dict(), 'models': collections.defaultdict(set), 'plugins': dict(), diff --git a/netbox/templates/htmx/notifications.html b/netbox/templates/htmx/notifications.html index c9230b81e..0fb5501a2 100644 --- a/netbox/templates/htmx/notifications.html +++ b/netbox/templates/htmx/notifications.html @@ -3,9 +3,12 @@ {% for notification in notifications %}
+
+ +
{{ notification.object }} -
{{ notification.get_event_display }} {{ notification.created|timesince }} {% trans "ago" %}
+
{{ notification.event }} {{ notification.created|timesince }} {% trans "ago" %}