2019-03-14 15:40:39 +00:00
|
|
|
|
import requests
|
|
|
|
|
from django.conf import settings
|
2020-10-01 09:40:46 +00:00
|
|
|
|
from django.contrib.auth import get_user_model
|
2019-03-18 11:57:57 +00:00
|
|
|
|
from django.core.mail import send_mail
|
2020-09-21 17:31:00 +00:00
|
|
|
|
from django.core.exceptions import ObjectDoesNotExist
|
2019-01-09 09:27:54 +00:00
|
|
|
|
from django.db import models
|
2020-06-01 19:49:28 +00:00
|
|
|
|
from django.db.models.signals import post_save, post_delete
|
2019-03-14 15:40:39 +00:00
|
|
|
|
from django.dispatch import receiver
|
2019-12-13 16:20:54 +00:00
|
|
|
|
from django.template import loader
|
2020-06-08 13:02:19 +00:00
|
|
|
|
from django.urls import NoReverseMatch, get_resolver
|
2019-12-13 16:06:25 +00:00
|
|
|
|
from django.utils.translation import ugettext_lazy as _
|
2019-02-28 01:21:03 +00:00
|
|
|
|
from djangoldp.fields import LDPUrlField
|
2019-02-20 03:18:46 +00:00
|
|
|
|
from djangoldp.models import Model
|
2020-10-23 12:29:26 +00:00
|
|
|
|
from djangoldp.activities.services import ActivityQueueService, ActivityPubService, activity_sending_finished
|
2019-12-13 14:51:36 +00:00
|
|
|
|
from djangoldp_notification.middlewares import MODEL_MODIFICATION_USER_FIELD
|
2019-12-13 16:20:54 +00:00
|
|
|
|
from djangoldp_notification.permissions import InboxPermissions, SubscriptionsPermissions
|
2020-08-26 09:17:10 +00:00
|
|
|
|
from djangoldp_notification.views import LDPNotificationsViewSet
|
2020-10-23 12:29:26 +00:00
|
|
|
|
import logging
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger('djangoldp')
|
2019-05-02 14:36:08 +00:00
|
|
|
|
|
2019-03-14 15:40:39 +00:00
|
|
|
|
|
|
|
|
|
class Notification(Model):
|
2019-04-30 15:07:20 +00:00
|
|
|
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='inbox', on_delete=models.deletion.CASCADE)
|
2019-05-07 09:01:26 +00:00
|
|
|
|
author = LDPUrlField()
|
2019-03-01 04:20:21 +00:00
|
|
|
|
object = LDPUrlField()
|
2019-01-09 09:27:54 +00:00
|
|
|
|
type = models.CharField(max_length=255)
|
|
|
|
|
summary = models.TextField()
|
|
|
|
|
date = models.DateTimeField(auto_now_add=True)
|
2019-04-04 10:18:24 +00:00
|
|
|
|
unread = models.BooleanField(default=True)
|
2019-03-14 15:40:39 +00:00
|
|
|
|
|
2019-04-25 11:44:00 +00:00
|
|
|
|
class Meta(Model.Meta):
|
2019-08-07 13:57:57 +00:00
|
|
|
|
owner_field = 'user'
|
2019-06-17 01:34:38 +00:00
|
|
|
|
ordering = ['-date']
|
2019-08-23 12:56:03 +00:00
|
|
|
|
permission_classes = [InboxPermissions]
|
2019-08-01 12:03:00 +00:00
|
|
|
|
anonymous_perms = ['add']
|
2019-08-07 13:57:57 +00:00
|
|
|
|
authenticated_perms = ['inherit']
|
|
|
|
|
owner_perms = ['view', 'change', 'control']
|
2020-08-26 09:17:10 +00:00
|
|
|
|
view_set = LDPNotificationsViewSet
|
2019-01-09 09:27:54 +00:00
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return '{}'.format(self.type)
|
2019-01-31 09:44:41 +00:00
|
|
|
|
|
2020-02-04 14:44:11 +00:00
|
|
|
|
def save(self, *args, **kwargs):
|
|
|
|
|
# I cannot send a notification to myself
|
|
|
|
|
if self.author.startswith(settings.SITE_URL):
|
|
|
|
|
try:
|
|
|
|
|
# author is a WebID.. convert to local representation
|
|
|
|
|
author = Model.resolve(self.author.replace(settings.SITE_URL, ''))[1]
|
|
|
|
|
except NoReverseMatch:
|
|
|
|
|
author = None
|
|
|
|
|
if author == self.user:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
super(Notification, self).save(*args, **kwargs)
|
|
|
|
|
|
2020-11-16 10:44:15 +00:00
|
|
|
|
class NotificationSetting(Model):
|
|
|
|
|
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="settings")
|
|
|
|
|
receiveMail = models.BooleanField(default=True)
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
auto_author = 'user'
|
|
|
|
|
owner_field = 'user'
|
|
|
|
|
anonymous_perms = []
|
|
|
|
|
authenticated_perms = []
|
|
|
|
|
owner_perms = ['view', 'change']
|
|
|
|
|
container_path = 'settings/'
|
|
|
|
|
serializer_fields = ['@id', 'receiveMail']
|
|
|
|
|
rdf_type = 'sib:usersettings'
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return '{} ({})'.format(self.user.get_full_name(), self.user.urlid)
|
2019-03-14 15:40:39 +00:00
|
|
|
|
|
|
|
|
|
class Subscription(Model):
|
2019-01-31 09:44:41 +00:00
|
|
|
|
object = models.URLField()
|
|
|
|
|
inbox = models.URLField()
|
2020-06-08 13:02:19 +00:00
|
|
|
|
field = models.CharField(max_length=255, blank=True, null=True,
|
|
|
|
|
help_text='if set to a field name on the object model, the field will be passed instead of the object instance')
|
2019-01-31 09:44:41 +00:00
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return '{}'.format(self.object)
|
|
|
|
|
|
2019-12-13 16:20:54 +00:00
|
|
|
|
class Meta(Model.Meta):
|
|
|
|
|
anonymous_perms = []
|
|
|
|
|
authenticated_perms = ["add", "view", "delete"]
|
|
|
|
|
permission_classes = [SubscriptionsPermissions]
|
|
|
|
|
|
2020-05-22 13:36:49 +00:00
|
|
|
|
|
2020-06-08 13:02:19 +00:00
|
|
|
|
@receiver(post_save, sender=Subscription, dispatch_uid="nested_subscriber_check")
|
|
|
|
|
def create_nested_subscribers(sender, instance, created, **kwargs):
|
|
|
|
|
# save subscriptions for one-to-many nested fields
|
|
|
|
|
if created and not instance.is_backlink and instance.object.startswith(settings.SITE_URL):
|
|
|
|
|
try:
|
|
|
|
|
# object is a WebID.. convert to local representation
|
|
|
|
|
local = Model.resolve(instance.object.replace(settings.SITE_URL, ''))[0]
|
|
|
|
|
nested_fields = Model.get_meta(local, 'nested_fields', [])
|
|
|
|
|
|
2020-10-01 09:40:46 +00:00
|
|
|
|
# Don't create nested subscriptions for user model (Notification loop issue)
|
|
|
|
|
if local._meta.model_name == get_user_model()._meta.model_name:
|
|
|
|
|
return
|
|
|
|
|
|
2020-06-08 13:02:19 +00:00
|
|
|
|
for nested_field in nested_fields:
|
|
|
|
|
try:
|
|
|
|
|
field = local._meta.get_field(nested_field)
|
|
|
|
|
nested_container = field.related_model
|
2020-05-22 13:36:49 +00:00
|
|
|
|
nested_container_url = Model.absolute_url(nested_container)
|
|
|
|
|
|
2020-06-08 13:02:19 +00:00
|
|
|
|
if field.one_to_many:
|
|
|
|
|
# get the nested view set
|
|
|
|
|
nested_url = str(instance.object) + '1/' + nested_field + '/'
|
|
|
|
|
view, args, kwargs = get_resolver().resolve(nested_url.replace(settings.SITE_URL, ''))
|
|
|
|
|
# get the reverse name for the field
|
|
|
|
|
field_name = view.initkwargs['nested_related_name']
|
|
|
|
|
|
|
|
|
|
if field_name is not None and field_name != '':
|
|
|
|
|
# check that this nested-field subscription doesn't already exist
|
|
|
|
|
existing_subscriptions = Subscription.objects.filter(object=nested_container_url, inbox=instance.inbox,
|
|
|
|
|
field=field_name)
|
|
|
|
|
# save a Subscription on this container
|
|
|
|
|
if not existing_subscriptions.exists():
|
|
|
|
|
Subscription.objects.create(object=nested_container_url, inbox=instance.inbox, is_backlink=True,
|
|
|
|
|
field=field_name)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2020-05-22 13:36:49 +00:00
|
|
|
|
|
|
|
|
|
|
2020-06-01 19:49:28 +00:00
|
|
|
|
|
2019-01-31 09:44:41 +00:00
|
|
|
|
# --- SUBSCRIPTION SYSTEM ---
|
2019-02-20 03:18:46 +00:00
|
|
|
|
@receiver(post_save, dispatch_uid="callback_notif")
|
2020-06-01 19:49:28 +00:00
|
|
|
|
@receiver(post_delete, dispatch_uid="delete_callback_notif")
|
|
|
|
|
def send_notification(sender, instance, **kwargs):
|
2021-02-11 13:07:12 +00:00
|
|
|
|
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
|
2020-10-23 12:29:26 +00:00
|
|
|
|
|
2021-02-11 13:07:12 +00:00
|
|
|
|
recipients = []
|
|
|
|
|
try:
|
|
|
|
|
url_container = settings.BASE_URL + Model.container_id(instance)
|
|
|
|
|
url_resource = settings.BASE_URL + Model.resource_id(instance)
|
|
|
|
|
except NoReverseMatch:
|
|
|
|
|
return
|
2021-02-11 12:25:39 +00:00
|
|
|
|
|
2021-02-11 13:07:12 +00:00
|
|
|
|
# dispatch a notification for every Subscription on this resource
|
|
|
|
|
for subscription in Subscription.objects.filter(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
|
2020-10-23 12:29:26 +00:00
|
|
|
|
|
2021-02-11 13:07:12 +00:00
|
|
|
|
instance = getattr(instance, subscription.field, instance)
|
2020-10-23 12:29:26 +00:00
|
|
|
|
|
2021-02-11 13:07:12 +00:00
|
|
|
|
# don't send notifications for foreign resources
|
|
|
|
|
if hasattr(instance, 'urlid') and Model.is_external(instance.urlid):
|
|
|
|
|
continue
|
2020-06-08 13:02:19 +00:00
|
|
|
|
|
2021-02-11 13:07:12 +00:00
|
|
|
|
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)
|
2019-03-14 15:40:39 +00:00
|
|
|
|
|
|
|
|
|
|
2020-10-23 12:29:26 +00:00
|
|
|
|
@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))
|
|
|
|
|
|
|
|
|
|
|
2019-12-13 14:51:36 +00:00
|
|
|
|
def send_request(target, object_iri, instance, created):
|
2021-02-11 13:07:12 +00:00
|
|
|
|
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"
|
2019-12-13 14:51:36 +00:00
|
|
|
|
|
2020-10-23 12:29:26 +00:00
|
|
|
|
# local inbox
|
|
|
|
|
if target.startswith(settings.SITE_URL):
|
|
|
|
|
user = Model.resolve_parent(target.replace(settings.SITE_URL, ''))
|
|
|
|
|
Notification.objects.create(user=user, object=object_iri, type=request_type, author=author)
|
|
|
|
|
# external inbox
|
|
|
|
|
else:
|
|
|
|
|
json = {
|
|
|
|
|
"@context": settings.LDP_RDF_CONTEXT,
|
|
|
|
|
"object": object_iri,
|
|
|
|
|
"author": author,
|
|
|
|
|
"type": request_type
|
|
|
|
|
}
|
|
|
|
|
ActivityQueueService.send_activity(target, json)
|
2019-03-18 11:57:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@receiver(post_save, sender=Notification)
|
2019-06-11 02:06:44 +00:00
|
|
|
|
def send_email_on_notification(sender, instance, created, **kwargs):
|
2020-08-13 15:42:07 +00:00
|
|
|
|
if created and instance.summary and getattr(settings,'JABBER_DEFAULT_HOST',False) and instance.user.email:
|
2020-02-04 14:44:11 +00:00
|
|
|
|
# get author name, and store in who
|
2019-10-21 15:35:37 +00:00
|
|
|
|
try:
|
2020-02-04 14:44:11 +00:00
|
|
|
|
# local author
|
2019-10-21 15:35:37 +00:00
|
|
|
|
if instance.author.startswith(settings.SITE_URL):
|
2021-03-26 18:11:20 +00:00
|
|
|
|
who = str(Model.resolve_id(instance.author.replace(settings.SITE_URL, '')).get_full_name())
|
2020-02-04 14:44:11 +00:00
|
|
|
|
# external author
|
2019-10-21 15:35:37 +00:00
|
|
|
|
else:
|
|
|
|
|
who = requests.get(instance.author).json()['name']
|
|
|
|
|
except:
|
2021-02-26 14:18:29 +00:00
|
|
|
|
who = "Quelqu'un"
|
2019-10-21 15:35:37 +00:00
|
|
|
|
|
2020-02-04 14:44:11 +00:00
|
|
|
|
# get identifier for resource triggering notification, and store in where
|
2019-10-21 15:35:37 +00:00
|
|
|
|
try:
|
|
|
|
|
if instance.object.startswith(settings.SITE_URL):
|
2021-03-26 18:11:20 +00:00
|
|
|
|
where = str(Model.resolve_id(instance.object.replace(settings.SITE_URL, '')).name)
|
2019-10-02 14:08:05 +00:00
|
|
|
|
else:
|
2019-10-21 15:35:37 +00:00
|
|
|
|
where = requests.get(instance.object).json()['name']
|
2019-10-02 14:08:05 +00:00
|
|
|
|
except:
|
2021-02-26 14:18:29 +00:00
|
|
|
|
where = "le chat"
|
2019-10-21 15:35:37 +00:00
|
|
|
|
|
2019-12-13 14:51:36 +00:00
|
|
|
|
if who == where:
|
2020-04-23 16:19:42 +00:00
|
|
|
|
where = "t'a envoyé un message privé"
|
2019-10-21 15:35:37 +00:00
|
|
|
|
else:
|
2020-04-23 16:19:42 +00:00
|
|
|
|
where = "t'a mentionné sur " + where
|
2019-10-21 15:35:37 +00:00
|
|
|
|
|
2020-08-13 15:42:07 +00:00
|
|
|
|
on = (getattr(settings, 'INSTANCE_DEFAULT_CLIENT', False) or settings.JABBER_DEFAULT_HOST)
|
|
|
|
|
|
2019-10-21 15:35:37 +00:00
|
|
|
|
html_message = loader.render_to_string(
|
|
|
|
|
'email.html',
|
|
|
|
|
{
|
2020-08-13 15:42:07 +00:00
|
|
|
|
'on': on,
|
2019-10-21 15:35:37 +00:00
|
|
|
|
'instance': instance,
|
|
|
|
|
'author': who,
|
|
|
|
|
'object': where
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
2020-11-16 10:44:15 +00:00
|
|
|
|
if instance.user.settings.receiveMail:
|
|
|
|
|
send_mail(
|
|
|
|
|
'Notification sur ' + on,
|
|
|
|
|
instance.summary,
|
|
|
|
|
(getattr(settings, 'EMAIL_HOST_USER', False) or "noreply@" + settings.JABBER_DEFAULT_HOST),
|
|
|
|
|
[instance.user.email],
|
|
|
|
|
fail_silently=True,
|
|
|
|
|
html_message=html_message
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
|
|
|
|
|
def create_user_settings(sender, instance, created, **kwargs):
|
|
|
|
|
try:
|
|
|
|
|
if created and instance.urlid.startswith(settings.SITE_URL):
|
|
|
|
|
NotificationSetting.objects.create(user=instance)
|
|
|
|
|
except:
|
|
|
|
|
pass
|