From 7b80ce179f6ee9091e858fc2c0840d263e260c64 Mon Sep 17 00:00:00 2001 From: Sylvain Le Bon <sylvain@startinblox.com> Date: Thu, 12 Oct 2023 10:55:56 +0200 Subject: [PATCH] feature: notification on m2m change --- djangoldp_notification/models.py | 105 ++++++++++++++++--------------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/djangoldp_notification/models.py b/djangoldp_notification/models.py index 5e70105..bd43a28 100644 --- a/djangoldp_notification/models.py +++ b/djangoldp_notification/models.py @@ -4,7 +4,7 @@ from django.contrib.auth import get_user_model from django.core.mail import send_mail from django.core.exceptions import ObjectDoesNotExist from django.db import models -from django.db.models.signals import post_save, post_delete +from django.db.models.signals import post_save, post_delete, m2m_changed from django.dispatch import receiver from django.template import loader from django.urls import NoReverseMatch, get_resolver, reverse @@ -153,63 +153,55 @@ def create_nested_subscribers(sender, instance, created, **kwargs): # --- SUBSCRIPTION SYSTEM --- @receiver(post_save, dispatch_uid="callback_notif") @receiver(post_delete, dispatch_uid="delete_callback_notif") -def send_notification(sender, instance, **kwargs): - if(type(instance).__name__ != "ScheduledActivity" and type(instance).__name__ != "LogEntry" and type(instance).__name__ != "Activity"): - if sender != Notification: - # don't send notifications for foreign resources - if hasattr(instance, 'urlid') and Model.is_external(instance.urlid): - return - - recipients = [] - try: - url_container = settings.BASE_URL + Model.container_id(instance) - url_resource = settings.BASE_URL + Model.resource_id(instance) - except NoReverseMatch: - return - - # dispatch a notification for every Subscription on this resource - for subscription in Subscription.objects.filter(models.Q(disable_automatic_notifications=False) & (models.Q(object=url_resource) | models.Q(object=url_container))): - if subscription.inbox not in recipients and (not subscription.is_backlink or not kwargs.get("created")): - # I may have configured to send the subscription to a foreign key - if subscription.field is not None and len(subscription.field) > 1: - try: - if kwargs.get("created"): - continue +@receiver(m2m_changed, dispatch_uid="m2m_callback_notif") +def notify(sender, instance, created=None, model=None, pk_set=set(), action='', **kwargs): + if type(instance).__name__ not in ["ScheduledActivity", "LogEntry", "Activity"] and sender != Notification \ + and action not in ['pre_add', 'pre_remove']: + if action or created is False: + request_type = 'update' #M2M change or post_save + elif created: + request_type = 'creation' + else: + request_type = "deletion" + send_notifications(instance, request_type) + if model and pk_set: + # Notify the reverse relations + send_notifications(model.objects.get(id=pk_set.pop()), 'update') - instance = getattr(instance, subscription.field, instance) - # don't send notifications for foreign resources - if hasattr(instance, 'urlid') and Model.is_external(instance.urlid): - continue +def send_notifications(instance, request_type): + try: + url_container = settings.BASE_URL + Model.container_id(instance) + url_resource = settings.BASE_URL + Model.resource_id(instance) + except NoReverseMatch: + return + recipients = [] + # don't send notifications for foreign resources + if hasattr(instance, 'urlid') and Model.is_external(instance.urlid): + return + # dispatch a notification for every Subscription on this resource + for subscription in Subscription.objects.filter(models.Q(disable_automatic_notifications=False) & (models.Q(object=url_resource) | models.Q(object=url_container))): + if subscription.inbox not in recipients and (not subscription.is_backlink or request_type != 'creation'): + # I may have configured to send the subscription to a foreign key + if subscription.field is not None and len(subscription.field) > 1 and request_type != 'creation': + try: + instance = getattr(instance, subscription.field, instance) + # don't send notifications for foreign resources + if hasattr(instance, 'urlid') and Model.is_external(instance.urlid): + continue - url_resource = settings.BASE_URL + Model.resource_id(instance) - except NoReverseMatch: - continue - except ObjectDoesNotExist: - continue + url_resource = settings.BASE_URL + Model.resource_id(instance) + except NoReverseMatch: + continue + except ObjectDoesNotExist: + continue - send_request(subscription.inbox, url_resource, instance, kwargs.get("created")) - recipients.append(subscription.inbox) + send_request(subscription.inbox, url_resource, instance, request_type) + recipients.append(subscription.inbox) -@receiver(activity_sending_finished, sender=ActivityQueueService) -def _handle_prosody_response(sender, response, saved_activity, **kwargs): - '''callback function for handling a response from Prosody on a notification''' - # if text is defined in the response body then it's an error - if saved_activity is not None: - response_body = saved_activity.response_to_json() - if 'condition' in response_body: - logger.error("[DjangoLDP-Notification.models._handle_prosody_response] error in Prosody response " + - str(response_body)) - - -def send_request(target, object_iri, instance, created): +def send_request(target, object_iri, instance, request_type): author = getattr(getattr(instance, MODEL_MODIFICATION_USER_FIELD, None), "urlid", str(_("Auteur inconnu"))) - if(created is not None): - request_type = "creation" if created else "update" - else: - request_type = "deletion" - # local inbox if target.startswith(settings.SITE_URL): user = Model.resolve_parent(target.replace(settings.SITE_URL, '')) @@ -225,6 +217,17 @@ def send_request(target, object_iri, instance, created): ActivityQueueService.send_activity(target, json) +@receiver(activity_sending_finished, sender=ActivityQueueService) +def _handle_prosody_response(sender, response, saved_activity, **kwargs): + '''callback function for handling a response from Prosody on a notification''' + # if text is defined in the response body then it's an error + if saved_activity is not None: + response_body = saved_activity.response_to_json() + if 'condition' in response_body: + logger.error("[DjangoLDP-Notification.models._handle_prosody_response] error in Prosody response " + + str(response_body)) + + def get_default_email_sender_djangoldp_instance(): ''' :return: the configured email host if it can find one, or None -- GitLab