Skip to content

Commit

Permalink
#15621: Support notifications for deletion of a subscribed object
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremystretch committed Aug 2, 2024
1 parent ca63aed commit 57fe207
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 14 deletions.
2 changes: 1 addition & 1 deletion netbox/core/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# Register core events
EventType(OBJECT_CREATED, _('Object created')).register()
EventType(OBJECT_UPDATED, _('Object updated')).register()
EventType(OBJECT_DELETED, _('Object deleted')).register()
EventType(OBJECT_DELETED, _('Object deleted'), destructive=True).register()
EventType(JOB_STARTED, _('Job started')).register()
EventType(JOB_COMPLETED, _('Job completed'), kind=EVENT_TYPE_KIND_SUCCESS).register()
EventType(JOB_FAILED, _('Job failed'), kind=EVENT_TYPE_KIND_WARNING).register()
Expand Down
1 change: 1 addition & 0 deletions netbox/extras/migrations/0118_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class Migration(migrations.Migration):
('object_id', models.PositiveBigIntegerField()),
('event_type', models.CharField(max_length=50)),
('object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
('object_repr', models.CharField(editable=False, max_length=200)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL)),
],
options={
Expand Down
19 changes: 16 additions & 3 deletions netbox/extras/models/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ class Notification(models.Model):
ct_field='object_type',
fk_field='object_id'
)
object_repr = models.CharField(
max_length=200,
editable=False
)
event_type = models.CharField(
verbose_name=_('event'),
max_length=50,
Expand All @@ -81,9 +85,7 @@ class Meta:
verbose_name_plural = _('notifications')

def __str__(self):
if self.object:
return str(self.object)
return super().__str__()
return self.object_repr

def get_absolute_url(self):
return reverse('account:notifications')
Expand All @@ -97,13 +99,24 @@ def clean(self):
_("Objects of this type ({type}) do not support notifications.").format(type=self.object_type)
)

def save(self, *args, **kwargs):
# Record a string representation of the associated object
if self.object:
self.object_repr = self.get_object_repr(self.object)

super().save(*args, **kwargs)

@cached_property
def event(self):
"""
Returns the registered Event which triggered this Notification.
"""
return registry['event_types'].get(self.event_type)

@classmethod
def get_object_repr(cls, obj):
return str(obj)[:200]


class NotificationGroup(ChangeLoggedModel):
"""
Expand Down
20 changes: 16 additions & 4 deletions netbox/extras/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,18 @@ def process_job_end_event_rules(sender, **kwargs):
# Notifications
#

@receiver(post_save)
def notify_object_changed(sender, instance, created, raw, **kwargs):
if created or raw:
@receiver((post_save, pre_delete))
def notify_object_changed(sender, instance, **kwargs):
# Skip for newly-created objects
if kwargs.get('created'):
return

# Determine event type
if 'created' in kwargs:
event_type = OBJECT_UPDATED
else:
event_type = OBJECT_DELETED

# Skip unsupported object types
ct = ContentType.objects.get_for_model(instance)
if ct.model not in registry['model_features']['notifications'].get(ct.app_label, []):
Expand All @@ -157,6 +164,11 @@ def notify_object_changed(sender, instance, created, raw, **kwargs):

# Create Notifications for Subscribers
Notification.objects.bulk_create([
Notification(user_id=user, object=instance, event_type=OBJECT_UPDATED)
Notification(
user_id=user,
object=instance,
object_repr=Notification.get_object_repr(instance),
event_type=event_type
)
for user in subscribed_users
])
15 changes: 10 additions & 5 deletions netbox/extras/tables/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@
<span class="text-{{ value.color }} fs-3"><i class="{{ value.icon }}"></i></span>
"""

NOTIFICATION_LINK = """
{% if not record.event.destructive %}
<a href="{% url 'extras:notification_read' pk=record.pk %}">{{ record.object_repr }}</a>
{% else %}
{{ record.object_repr }}
{% endif %}
"""


class CustomFieldTable(NetBoxTable):
name = tables.Column(
Expand Down Expand Up @@ -314,12 +322,9 @@ class NotificationTable(NetBoxTable):
object_type = columns.ContentTypeColumn(
verbose_name=_('Object Type'),
)
object = tables.Column(
object = columns.TemplateColumn(
verbose_name=_('Object'),
linkify={
'viewname': 'extras:notification_read',
'args': [tables.A('pk')],
},
template_code=NOTIFICATION_LINK,
orderable=False
)
created = columns.DateTimeColumn(
Expand Down
4 changes: 4 additions & 0 deletions netbox/netbox/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ class EventType:
name: The unique name under which the event is registered.
text: The human-friendly event name. This should support translation.
kind: The event's classification (info, success, warning, or danger). The default type is info.
destructive: Indicates that the associated object was destroyed as a result of the event (default: False).
"""
name: str
text: str
kind: str = EVENT_TYPE_KIND_INFO
destructive: bool = False

def __str__(self):
return self.text
Expand All @@ -58,6 +60,7 @@ def register(self):
raise Exception(f"An event type named {self.name} has already been registered!")
registry['event_types'][self.name] = self

@property
def color(self):
return {
EVENT_TYPE_KIND_INFO: 'blue',
Expand All @@ -66,6 +69,7 @@ def color(self):
EVENT_TYPE_KIND_DANGER: 'red',
}.get(self.kind)

@property
def icon(self):
return {
EVENT_TYPE_KIND_INFO: 'mdi mdi-information',
Expand Down
2 changes: 1 addition & 1 deletion netbox/templates/htmx/notifications.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<i class="{{ notification.event.icon }}"></i>
</div>
<div class="col text-truncate">
<a href="{% url 'extras:notification_read' pk=notification.pk %}" class="text-body d-block">{{ notification.object }}</a>
<a href="{% url 'extras:notification_read' pk=notification.pk %}" class="text-body d-block">{{ notification.object_repr }}</a>
<div class="d-block text-secondary fs-5">{{ notification.event }} {{ notification.created|timesince }} {% trans "ago" %}</div>
</div>
<div class="col-auto">
Expand Down

0 comments on commit 57fe207

Please sign in to comment.