Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • djangoldp-packages/djangoldp-notification
  • decentral1se/djangoldp-notification
  • femmefaytale/djangoldp-notification
  • 3wc/djangoldp-notification
4 results
Show changes
Showing
with 783 additions and 52 deletions
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2020-05-01 12:07
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('djangoldp_notification', '0003_auto_20200429_1346'),
]
operations = [
migrations.AddField(
model_name='notification',
name='backlink_created',
field=models.BooleanField(default=False, help_text='set automatically to indicate the Model is a backlink'),
),
migrations.AddField(
model_name='subscription',
name='backlink_created',
field=models.BooleanField(default=False, help_text='set automatically to indicate the Model is a backlink'),
),
]
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2019-05-07 08:58 # Generated by Django 1.11 on 2020-05-05 17:33
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations from django.db import migrations
...@@ -8,13 +8,18 @@ from django.db import migrations ...@@ -8,13 +8,18 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('djangoldp_notification', '0004_auto_20190425_1141'), ('djangoldp_notification', '0004_auto_20200501_1207'),
] ]
operations = [ operations = [
migrations.RenameField( migrations.RenameField(
model_name='notification', model_name='notification',
old_name='author_user', old_name='backlink_created',
new_name='author', new_name='is_backlink',
),
migrations.RenameField(
model_name='subscription',
old_name='backlink_created',
new_name='is_backlink',
), ),
] ]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-06-02 10:35
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('djangoldp_notification', '0005_auto_20200505_1733'),
]
operations = [
migrations.AddField(
model_name='subscription',
name='parent',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='djangoldp_notification.Subscription'),
),
]
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2019-04-04 03:43 # Generated by Django 1.11.29 on 2020-06-04 10:55
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
...@@ -8,17 +8,17 @@ from django.db import migrations, models ...@@ -8,17 +8,17 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('djangoldp_notification', '0002_auto_20190301_0418'), ('djangoldp_notification', '0006_subscription_parent'),
] ]
operations = [ operations = [
migrations.RemoveField( migrations.RemoveField(
model_name='notification', model_name='subscription',
name='read', name='parent',
), ),
migrations.AddField( migrations.AddField(
model_name='notification', model_name='subscription',
name='unread', name='field',
field=models.BooleanField(default=True), field=models.CharField(blank=True, help_text='if set to a field name on the object model, the field will be passed instead of the object instance', max_length=255, null=True),
), ),
] ]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-06-10 13:23
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('djangoldp_notification', '0007_auto_20200604_1055'),
]
operations = [
migrations.AlterField(
model_name='notification',
name='is_backlink',
field=models.BooleanField(default=False, help_text='(DEPRECIATED) set automatically to indicate the Model is a backlink'),
),
migrations.AlterField(
model_name='subscription',
name='is_backlink',
field=models.BooleanField(default=False, help_text='(DEPRECIATED) set automatically to indicate the Model is a backlink'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-06-19 08:02
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('djangoldp_notification', '0008_auto_20200610_1323'),
]
operations = [
migrations.AlterField(
model_name='notification',
name='is_backlink',
field=models.BooleanField(default=False, help_text='set automatically to indicate the Model is a backlink'),
),
migrations.AlterField(
model_name='subscription',
name='is_backlink',
field=models.BooleanField(default=False, help_text='set automatically to indicate the Model is a backlink'),
),
]
# Generated by Django 2.2.16 on 2020-11-16 09:19
import django.db.models.deletion
import djangoldp.fields
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('djangoldp_notification', '0014_subscription_disable_automatic_notifications'),
]
def create_settings(apps, schema_editor):
Users = apps.get_model('djangoldp_account', 'LDPUser')
Settings = apps.get_model('djangoldp_notification', 'NotificationSetting')
# iterate over all objects
for user in Users.objects.all():
if user.urlid.startswith(settings.SITE_URL):
Settings.objects.create(user=user)
operations = [
migrations.CreateModel(
name='NotificationSetting',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('urlid', djangoldp.fields.LDPUrlField(blank=True, null=True, unique=True)),
('is_backlink', models.BooleanField(default=False, help_text='set automatically to indicate the Model is a backlink')),
('allow_create_backlink', models.BooleanField(default=True, help_text='set to False to disable backlink creation after Model save')),
('receiveMail', models.BooleanField(default=True, help_text='if set to True the user will receive an email on notification receipt')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='settings', to=settings.AUTH_USER_MODEL)),
],
),
migrations.RunPython(create_settings),
]
# -*- coding: utf-8 -*- # Generated by Django 2.2.18 on 2021-02-18 10:45
# Generated by Django 1.11 on 2019-04-25 11:41
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations
...@@ -8,16 +6,16 @@ from django.db import migrations ...@@ -8,16 +6,16 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('djangoldp_notification', '0003_auto_20190404_0343'), ('djangoldp_notification', '0009_auto_20200619_0802'),
] ]
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='notification', name='notification',
options={'default_permissions': ('add', 'change', 'delete', 'view', 'control'), 'ordering': ['date']}, options={'default_permissions': ['add', 'change', 'delete', 'view', 'control'], 'ordering': ['-date']},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='subscription', name='subscription',
options={'default_permissions': ('add', 'change', 'delete', 'view', 'control')}, options={'default_permissions': ['add', 'change', 'delete', 'view', 'control']},
), ),
] ]
# Generated by Django 2.2.23 on 2021-07-29 17:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('djangoldp_notification', '0011_auto_20210218_1145'),
]
operations = [
migrations.AlterField(
model_name='notification',
name='summary',
field=models.TextField(blank=True, default=''),
),
]
# Generated by Django 2.2.23 on 2021-10-16 10:03
import django.db.models.deletion
import djangoldp.fields
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('djangoldp_notification', '0012_auto_20210729_1912'),
]
operations = [
migrations.AlterField(
model_name='notification',
name='author',
field=djangoldp.fields.LDPUrlField(help_text='the sender of the notification'),
),
migrations.AlterField(
model_name='notification',
name='object',
field=djangoldp.fields.LDPUrlField(help_text='the urlid of the saved object being transmitted by the notification'),
),
migrations.AlterField(
model_name='notification',
name='unread',
field=models.BooleanField(default=True, help_text='set to False after the user has seen the notification'),
),
migrations.AlterField(
model_name='notification',
name='user',
field=models.ForeignKey(help_text='the recipient of the notification', on_delete=django.db.models.deletion.CASCADE, related_name='inbox', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='subscription',
name='inbox',
field=models.URLField(help_text='the inbox of the recipient of the notification'),
),
migrations.AlterField(
model_name='subscription',
name='object',
field=models.URLField(help_text='the urlid of the object being subscribed'),
),
]
# Generated by Django 2.2.23 on 2021-10-16 10:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('djangoldp_notification', '0013_auto_20211016_1203'),
]
operations = [
migrations.AddField(
model_name='subscription',
name='disable_automatic_notifications',
field=models.BooleanField(default=False, help_text='By default, notifications will be sent to this inbox everytime the target object/container is updated. Setting this flag to true prevents this behaviour, meaning that notifications will have to be triggered manually'),
),
]
# -*- coding: utf-8 -*- # Generated by Django 4.2.3 on 2023-07-13 15:10
# Generated by Django 1.11 on 2019-06-19 08:43
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations
...@@ -8,12 +6,12 @@ from django.db import migrations ...@@ -8,12 +6,12 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('djangoldp_notification', '0005_auto_20190507_0858'), ('djangoldp_notification', '0010_notificationsetting'),
] ]
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='notification', name='subscription',
options={'default_permissions': ('add', 'change', 'delete', 'view', 'control'), 'ordering': ['-date']}, options={'default_permissions': ['add', 'change', 'delete', 'view', 'control'], 'ordering': ['pk']},
), ),
] ]
# Generated by Django 4.2.3 on 2023-10-12 09:14
from django.db import migrations
import djangoldp.fields
class Migration(migrations.Migration):
dependencies = [
('djangoldp_notification', '0015_alter_subscription_options'),
]
operations = [
migrations.AlterModelOptions(
name='notification',
options={'default_permissions': {'control', 'delete', 'view', 'add', 'change'}, 'ordering': ['-date']},
),
migrations.AlterModelOptions(
name='subscription',
options={'default_permissions': {'control', 'delete', 'view', 'add', 'change'}, 'ordering': ['pk']},
),
migrations.AlterField(
model_name='notification',
name='urlid',
field=djangoldp.fields.LDPUrlField(blank=True, db_index=True, null=True, unique=True),
),
migrations.AlterField(
model_name='notificationsetting',
name='urlid',
field=djangoldp.fields.LDPUrlField(blank=True, db_index=True, null=True, unique=True),
),
migrations.AlterField(
model_name='subscription',
name='urlid',
field=djangoldp.fields.LDPUrlField(blank=True, db_index=True, null=True, unique=True),
),
]
import logging
from threading import Thread
import requests import requests
from django.conf import settings from django.conf import settings
from django.contrib.admin.models import LogEntry from django.contrib.auth import get_user_model
from django.contrib.sessions.models import Session
from django.core.mail import send_mail from django.core.mail import send_mail
from django.core.exceptions import ObjectDoesNotExist
from django.db import models from django.db import models
from django.db.models.signals import post_save from django.db.models.signals import post_save, post_delete, m2m_changed
from django.dispatch import receiver from django.dispatch import receiver
from oidc_provider.models import Token from django.template import loader
from django.urls import NoReverseMatch from django.urls import NoReverseMatch, get_resolver
from django.utils.translation import gettext_lazy as _
from djangoldp.fields import LDPUrlField from djangoldp.fields import LDPUrlField
from djangoldp.models import Model from djangoldp.models import Model
from djangoldp.permissions import InboxPermissions from djangoldp.permissions import CreateOnly, AuthenticatedOnly, ReadAndCreate, OwnerPermissions
from djangoldp.activities.services import ActivityQueueService, activity_sending_finished
from djangoldp_notification.middlewares import get_current_user
from djangoldp_notification.views import LDPNotificationsViewSet
import logging
from django.template import loader
logger = logging.getLogger('djangoldp')
class Notification(Model): class Notification(Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='inbox', on_delete=models.deletion.CASCADE) user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='inbox', on_delete=models.deletion.CASCADE, help_text='the recipient of the notification')
author = LDPUrlField() author = LDPUrlField(help_text='the sender of the notification')
object = LDPUrlField() object = LDPUrlField(help_text='the urlid of the saved object being transmitted by the notification')
type = models.CharField(max_length=255) type = models.CharField(max_length=255)
summary = models.TextField() summary = models.TextField(blank=True, default='')
date = models.DateTimeField(auto_now_add=True) date = models.DateTimeField(auto_now_add=True)
unread = models.BooleanField(default=True) unread = models.BooleanField(default=True, help_text='set to False after the user has seen the notification')
class Meta(Model.Meta): class Meta(Model.Meta):
#permission_classes = [InboxPermissions] owner_field = 'user'
depth = 0
ordering = ['-date'] ordering = ['-date']
permission_classes = [CreateOnly|OwnerPermissions]
view_set = LDPNotificationsViewSet
# NOTE: this would be our ideal cache behaviour
# the functionality for optimising it was removed because of an issue with extensibility
# https://git.startinblox.com/djangoldp-packages/djangoldp-notification/merge_requests/42#note_58559
'''def clear_djangoldp_cache(self, cache, cache_entry):
# should only clear the users/x/inbox
lookup_arg = LDPViewSet.get_lookup_arg(model=get_user_model())
url = reverse('{}-{}-list'.format(self.user.__class__.__name__.lower(), self.__class__.__name__.lower()),
args=[getattr(self.user, lookup_arg)])
url = '{}{}'.format(settings.SITE_URL, url)
cache.invalidate(cache_entry, url)
# invalidate the global /notifications/ container also
url = '{}{}'.format(settings.SITE_URL, reverse('{}-list'.format(self.__class__.__name__.lower())))
cache.invalidate(cache_entry, url)'''
def __str__(self): def __str__(self):
return '{}'.format(self.type) return '{}'.format(self.type)
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)
class NotificationSetting(Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="settings")
receiveMail = models.BooleanField(default=True, help_text='if set to True the user will receive an email on notification receipt')
class Meta:
auto_author = 'user'
owner_field = 'user'
permission_classes = [OwnerPermissions]
container_path = 'settings/'
serializer_fields = ['@id', 'receiveMail']
rdf_type = 'sib:usersettings'
def __str__(self):
return '{} ({})'.format(self.user.get_full_name(), self.user.urlid)
class Subscription(Model): class Subscription(Model):
object = models.URLField() object = models.URLField(help_text='the urlid of the object being subscribed')
inbox = models.URLField() inbox = models.URLField(help_text='the inbox of the recipient of the notification')
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')
disable_automatic_notifications = models.BooleanField(default=False,
help_text='By default, notifications will be sent to this inbox everytime the target object/container is updated. Setting this flag to true prevents this behaviour, meaning that notifications will have to be triggered manually')
def __str__(self): def __str__(self):
return '{}'.format(self.object) return '{}'.format(self.object)
class Meta(Model.Meta):
ordering = ['pk']
permission_classes = [AuthenticatedOnly, ReadAndCreate]
# --- SUBSCRIPTION SYSTEM ---
@receiver(post_save, dispatch_uid="callback_notif") @receiver(post_save, sender=Subscription, dispatch_uid="nested_subscriber_check")
def send_notification(sender, instance, **kwargs): def create_nested_subscribers(sender, instance, created, **kwargs):
if sender != Notification: # save subscriptions for one-to-many nested fields
threads = [] if created and not instance.is_backlink and instance.object.startswith(settings.SITE_URL):
try: try:
urlContainer = settings.BASE_URL + Model.container_id(instance) # object is a WebID.. convert to local representation
urlResource = settings.BASE_URL + Model.resource_id(instance) local = Model.resolve(instance.object.replace(settings.SITE_URL, ''))[0]
except NoReverseMatch: nested_fields = getattr(local._meta, 'nested_fields', [])
return
# Don't create nested subscriptions for user model (Notification loop issue)
if local._meta.model_name == get_user_model()._meta.model_name:
return
for nested_field in nested_fields:
try:
field = local._meta.get_field(nested_field)
nested_container = field.related_model
nested_container_url = Model.absolute_url(nested_container)
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
for subscription in Subscription.objects.filter(models.Q(object=urlResource)|models.Q(object=urlContainer)): # --- SUBSCRIPTION SYSTEM ---
process = Thread(target=send_request, args=[subscription.inbox, urlResource]) @receiver(post_save, dispatch_uid="callback_notif")
process.start() @receiver(post_delete, dispatch_uid="delete_callback_notif")
threads.append(process) @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", "Migration"] 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')
def send_request(target, object_iri): def send_notifications(instance, request_type):
try: try:
req = requests.post(target, url_container = settings.BASE_URL + Model.container_id(instance)
json={"@context": "https://cdn.happy-dev.fr/owl/hdcontext.jsonld", url_resource = settings.BASE_URL + Model.resource_id(instance)
"object": object_iri, "type": "update"}, except NoReverseMatch:
headers={"Content-Type": "application/ld+json"}) return
except: recipients = []
logging.error('Djangoldp_notifications: Error with request') # don't send notifications for foreign resources
return True 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
send_request(subscription.inbox, url_resource, instance, request_type)
recipients.append(subscription.inbox)
def send_request(target, object_iri, instance, request_type):
author = getattr(get_current_user(), 'urlid', 'unknown')
# 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)
@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
'''
email_from = (getattr(settings, 'DEFAULT_FROM_EMAIL', False) or getattr(settings, 'EMAIL_HOST_USER', False))
if not email_from:
jabber_host = getattr(settings, 'JABBER_DEFAULT_HOST', False)
if jabber_host:
return "noreply@" + jabber_host
return None
return email_from
@receiver(post_save, sender=Notification) @receiver(post_save, sender=Notification)
def send_email_on_notification(sender, instance, created, **kwargs): def send_email_on_notification(sender, instance, created, **kwargs):
if created and instance.summary and settings.JABBER_DEFAULT_HOST and instance.user.email: if created \
and instance.summary \
and instance.user.email \
and instance.type in ('Message', 'Mention'):
email_from = get_default_email_sender_djangoldp_instance()
if email_from is None or not instance.user.settings.receiveMail:
return
# get author name, and store in who
try:
# local author
if instance.author.startswith(settings.SITE_URL):
who = str(Model.resolve_id(instance.author.replace(settings.SITE_URL, '')).get_full_name())
# external author
else:
who = requests.get(instance.author).json()['name']
except:
who = _("Quelqu'un")
# get identifier for resource triggering notification, and store in where
try:
if instance.object.startswith(settings.SITE_URL):
if hasattr(Model.resolve_id(instance.object.replace(settings.SITE_URL, '')), 'get_full_name'):
where = Model.resolve_id(instance.object.replace(settings.SITE_URL, '')).get_full_name()
else:
where = str(Model.resolve_id(instance.object.replace(settings.SITE_URL, '')).name)
else:
where = requests.get(instance.object).json()['name']
except:
where = _("le chat")
if who == where:
where = _("t'a envoyé un message privé")
else:
where = _("t'a mentionné sur ") + where
on = (getattr(settings, 'INSTANCE_DEFAULT_CLIENT', False) or settings.JABBER_DEFAULT_HOST)
html_message = loader.render_to_string( html_message = loader.render_to_string(
'email.html', 'email.html',
{ {
'on': settings.JABBER_DEFAULT_HOST, 'on': on,
'instance': instance, 'instance': instance,
'author': requests.get(instance.author).json()['name'] or 'Unknown user' 'author': who,
'object': where
} }
) )
send_mail( send_mail(
'Notification on ' + settings.JABBER_DEFAULT_HOST, _('Notification sur ') + on,
instance.summary, instance.summary,
settings.EMAIL_HOST_USER or "noreply@" + settings.JABBER_DEFAULT_HOST, email_from,
[instance.user.email], [instance.user.email],
fail_silently=True, fail_silently=True,
html_message=html_message html_message=html_message
) )
else:
raise Exception('Misconfiguration, missing JABBER_DEFAULT_HOST or incomplete instance') @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
USER_NESTED_FIELDS = ['inbox'] USER_NESTED_FIELDS = ['inbox', 'settings']
\ No newline at end of file USER_EMPTY_CONTAINERS = ['inbox']
\ No newline at end of file
{% load i18n %}
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
</head> </head>
<body> <body>
<div> <div>
<p style='font-style:italic;color:#777'>A new {{ instance.type }} from <b>{{ author }}</b> on {{ on }}</p> <p style='font-style:italic;color:#777'><b>{{ author }}</b> {{ object }} {% trans "de" %} {{ on }}</p>
<p>{{ instance.summary }}</p> <p>{{ instance.summary }}</p>
</div> </div>
<div style='margin-top:10px;'> <div style='margin-top:10px;'>
<p style='font-size:small;color:#777'> <p style='font-size:small;color:#777'>
-- --
<br> <br>
Do not reply to this email directly, go to <a href='http://{{ on }}' target='_blank'>http://{{ on }}</a> instead. {% trans "Ne réponds pas directement à cet e-mail, à la place rends-toi sur" %} <a href='{{ on }}' target='_blank'>{{ on }}</a>.
<br> <br>
<a href='#'>Unsubscribe</a>
</p> </p>
</div> </div>
</body> </body>
......
import sys
import yaml
import django
from django.conf import settings as django_settings
from djangoldp.conf.ldpsettings import LDPSettings
from djangoldp.tests.server_settings import yaml_config
# override config loading
config = {
# add the packages to the reference list
'ldppackages': ['djangoldp_account', 'djangoldp_notification', 'djangoldp_notification.tests'],
# required values for server
'server': {
'SECRET_KEY': "$r&)p-4k@h5b!1yrft6&q%j)_p$lxqh6#)jeeu0z1iag&y&wdu",
'AUTH_USER_MODEL': 'djangoldp_account.LDPUser',
'REST_FRAMEWORK': {
'DEFAULT_PAGINATION_CLASS': 'djangoldp.pagination.LDPPagination',
'PAGE_SIZE': 5
},
# map the config of the core settings (avoid asserts to fail)
'SITE_URL': 'http://happy-dev.fr',
'BASE_URL': 'http://happy-dev.fr',
'SEND_BACKLINKS': False,
'JABBER_DEFAULT_HOST': None,
'PERMISSIONS_CACHE': False,
'ANONYMOUS_USER_NAME': None,
'SERIALIZER_CACHE': True,
'USER_NESTED_FIELDS': ['inbox', 'settings'],
'USER_EMPTY_CONTAINERS': ['inbox'],
'EMAIL_BACKEND': 'django.core.mail.backends.console.EmailBackend'
}
}
ldpsettings = LDPSettings(config)
ldpsettings.config = yaml.safe_load(yaml_config)
django_settings.configure(ldpsettings)
django.setup()
from django.test.runner import DiscoverRunner
test_runner = DiscoverRunner(verbosity=1)
failures = test_runner.run_tests([
'djangoldp_notification.tests.test_models',
'djangoldp_notification.tests.test_subscriptions',
'djangoldp_notification.tests.test_cache',
])
if failures:
sys.exit(failures)
import json
import argparse
from pathlib import Path
from copy import deepcopy
from datetime import datetime
'''
A script which generates and outputs placeholder notifications attached to a parametrised user, output
into a parameterised file (json), which can be used as a Django fixture or imported into a live database
e.g. python manage.py loaddata fixture.json
for help run python generate_inbox_fixture.py -h
'''
# starting from offset ensures that existing users etc are not disturbed
parser = argparse.ArgumentParser(description='generates and outputs random test data, into a file used by the performance unit tests')
parser.add_argument(dest='count', metavar='N', type=int, help='the number of users (and projects) to generate')
parser.add_argument(dest='user', metavar='U', type=str, help='the primary key of the user whose inbox should store the notifications')
parser.add_argument('--offset', dest='offset', type=int, default=100, help='an offset to start primary keys at (should be larger than the largest pre-existing project/user primary key)')
parser.add_argument('-f', dest='file_dest', type=str, default="../fixtures/inbox.json", help='the file destination to write to')
args = parser.parse_args()
count = args.count
user = args.user
OFFSET = args.offset
notification_template = {
'model': 'djangoldp_notification.notification',
'pk': 0,
'fields': {
'user': user,
'author': 'Test',
'object': 'http://localhost:8000/users/admin/',
'type': 'Update',
'summary': 'Test',
'date': str(datetime.date(datetime.now()))
}
}
fixture = list()
for i in range(count):
notification = deepcopy(notification_template)
notification['pk'] = OFFSET + i
fixture.append(notification)
with open(Path(__file__).parent / args.file_dest, 'w') as output:
json.dump(fixture, output)
print(str(count))
import uuid
import json
from rest_framework.test import APITestCase, APIClient
from djangoldp_account.models import LDPUser
from djangoldp_notification.models import Notification
class TestSubscription(APITestCase):
def _get_random_user(self):
return LDPUser.objects.create(email='{}@test.co.uk'.format(str(uuid.uuid4())), first_name='Test',
last_name='Test', username=str(uuid.uuid4()))
def _get_random_notification(self, recipient, author):
return Notification.objects.create(user=recipient, author=author.urlid, object=author.urlid,
unread=True)
def setUpLoggedInUser(self):
self.user = self._get_random_user()
self.client.force_authenticate(user=self.user)
def setUp(self):
self.client = APIClient()
def test_indirect_cache(self):
self.setUpLoggedInUser()
author_user = self._get_random_user()
notification = self._get_random_notification(recipient=self.user, author=author_user)
self.assertEqual(notification.unread, True)
# GET the inbox - should set the cache
response = self.client.get("/users/{}/inbox/".format(self.user.username))
self.assertEqual(response.status_code, 200)
notif_serialized = response.data["ldp:contains"][0]
self.assertEqual(notif_serialized["unread"], True)
# PATCH the notification - should wipe the cache
patch = {
"unread": False,
"@context": {
"@vocab":"https://cdn.startinblox.com/owl#",
"unread": "https://cdn.startinblox.com/owl#unread"
}
}
response = self.client.patch("/notifications/{}/".format(notification.pk), data=json.dumps(patch),
content_type="application/ld+json")
notif_obj = Notification.objects.get(pk=notification.pk)
self.assertEqual(notif_obj.unread, False)
# GET the inbox - should now be read
response = self.client.get("/users/{}/inbox/".format(self.user.username))
self.assertEqual(response.status_code, 200)
notif_serialized = response.data["ldp:contains"][0]
self.assertEqual(notif_serialized["unread"], False)
# NOTE: this would be our ideal cache behaviour
# the functionality for optimising it was removed because of an issue with extensibility
# https://git.startinblox.com/djangoldp-packages/djangoldp-notification/merge_requests/42#note_58559
'''def test_custom_cache_clear(self):
# going to create two notifications in two different inboxes
self.setUpLoggedInUser()
other_user = self._get_random_user()
notification = self._get_random_notification(recipient=self.user, author=other_user)
notification2 = self._get_random_notification(recipient=other_user, author=self.user)
# GET the inboxes and asser that the cache is set for both
self.client.get("/users/{}/inbox/".format(self.user.username))
self.client.get("/users/{}/inbox/".format(other_user.username))
# assert cache is set
my_container_urlid = '{}/users/{}/inbox/'.format(settings.SITE_URL, self.user.username)
their_container_urlid = '{}/users/{}/inbox/'.format(settings.SITE_URL, other_user.username)
self.assertTrue(GLOBAL_SERIALIZER_CACHE.has(getattr(Notification._meta, 'label', None), my_container_urlid))
self.assertTrue(GLOBAL_SERIALIZER_CACHE.has(getattr(Notification._meta, 'label', None), their_container_urlid))
# save my notification - should wipe the cache for my inbox...
notification.unread = False
notification.save()
self.assertFalse(GLOBAL_SERIALIZER_CACHE.has(getattr(Notification._meta, 'label', None), my_container_urlid))
# ...but not for theirs
self.assertTrue(GLOBAL_SERIALIZER_CACHE.has(getattr(Notification._meta, 'label', None), their_container_urlid))'''