From 947f479949dbe9518bc5a489c0137b4c3fb60fc1 Mon Sep 17 00:00:00 2001 From: Elias Showk Date: Thu, 23 Aug 2018 16:26:46 +0200 Subject: [PATCH 2/6] pwa: create WebPushRecord and WebPushCell (#25462) --- combo/apps/pwa/migrations/0001_initial.py | 55 ++++++++ combo/apps/pwa/migrations/__init__.py | 0 combo/apps/pwa/models.py | 120 ++++++++++++++++++ .../apps/pwa/templates/combo/webpushcell.html | 17 +++ 4 files changed, 192 insertions(+) create mode 100644 combo/apps/pwa/migrations/0001_initial.py create mode 100644 combo/apps/pwa/migrations/__init__.py create mode 100644 combo/apps/pwa/models.py create mode 100644 combo/apps/pwa/templates/combo/webpushcell.html diff --git a/combo/apps/pwa/migrations/0001_initial.py b/combo/apps/pwa/migrations/0001_initial.py new file mode 100644 index 0000000..9073a60 --- /dev/null +++ b/combo/apps/pwa/migrations/0001_initial.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.14 on 2018-08-23 12:39 +from __future__ import unicode_literals + +import django +from django.db import migrations, models +import django.db.models.deletion +import jsonfield.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0008_alter_user_username_max_length'), + ('push_notifications', '0006_webpushdevice'), + ('notifications', '0005_auto_20180324_0025'), + ] + + operations = [ + migrations.CreateModel( + name='WebpushCell', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('placeholder', models.CharField(max_length=20)), + ('order', models.PositiveIntegerField()), + ('slug', models.SlugField(blank=True, verbose_name='Slug')), + ('extra_css_class', models.CharField(blank=True, max_length=100, verbose_name='Extra classes for CSS styling')), + ('public', models.BooleanField(default=True, verbose_name='Public')), + ('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')), + ('last_update_timestamp', models.DateTimeField(auto_now=True)), + ('groups', models.ManyToManyField(blank=True, to='auth.Group', verbose_name='Groups')), + ('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='data.Page')), + ], + options={ + 'verbose_name': 'Push notifications subscription cell', + }, + ), + migrations.CreateModel( + name='WebPushRecord', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('creation_date', models.DateTimeField(auto_now_add=True)), + ('status', models.CharField(blank=True, choices=[(b'NEW', b'Not-sent notification'), (b'SENT', b'Sent notification'), (b'ERR', b'Invalid notification')], default=b'NEW', max_length=4)), + ('response', jsonfield.fields.JSONField(null=True)), + ('notification', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='notifications.Notification')), + ('subscription', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='push_notifications.WebPushDevice')), + ], + ), + migrations.AlterUniqueTogether( + name='webpushrecord', + unique_together=set([('subscription', 'notification')]), + ), + ] diff --git a/combo/apps/pwa/migrations/__init__.py b/combo/apps/pwa/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/combo/apps/pwa/models.py b/combo/apps/pwa/models.py new file mode 100644 index 0000000..8bdec7b --- /dev/null +++ b/combo/apps/pwa/models.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +# combo - Combo PWA App +# Copyright (C) 2018 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import json + +from django.db import models +from django.conf import settings +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext_lazy as _ + +from jsonfield import JSONField + +from combo.apps.notifications.models import Notification +from combo.data.models import CellBase +from combo.data.library import register_cell_class + +from push_notifications.models import WebPushDevice + + +class WebPushRecord(models.Model): + ''' + Store the state of a user's push notification + ''' + DEFAULT_STATUS = 'NEW' + ERR_STATUS = 'ERR' + OK_STATUS = 'SENT' + STATUS = ( + (DEFAULT_STATUS, 'Not-sent notification'), + (OK_STATUS, 'Sent notification'), + (ERR_STATUS, 'Invalid notification'), + ) + + subscription = models.ForeignKey(WebPushDevice, null=False) + notification = models.ForeignKey(Notification, null=False) + creation_date = models.DateTimeField(blank=True, auto_now_add=True) + status = models.CharField(blank=True, max_length=4, choices=STATUS, default=DEFAULT_STATUS) + response = JSONField(null=True) + + class Meta: + unique_together = ('subscription', 'notification') + + @property + def ttl(self): + delta = self.notification.end_timestamp - self.notification.start_timestamp + return int(delta.total_seconds()) + + @property + def payload(self): + ''' + JSON string sent to the push service. + Every options are not supported by all browsers, adoption is progressive + ''' + return json.dumps({ + 'title': self.notification.summary, + 'body': "%s\n - %s" % (self.notification.body, self.notification.url), + 'actions': [ + {'action': 'ack', 'title': _('OK').encode('utf8'), 'icon': ''}, + {'action': 'forget', 'title': _('Forget').encode('utf8'), 'icon': ''} + ], + 'data': { + 'callback_url': reverse('webpush-notification-callback', args=[self.notification.id, self.notification.user.id, self.subscription.p256dh]), + 'open_url': self.notification.url + }, + 'vibrate': [200, 100, 400], + }) + + def set_status_ok(self): + self.status = self.OK_STATUS + self.save() + + def set_status_err(self): + self.status = self.ERR_STATUS + self.save() + +@register_cell_class +class WebpushCell(CellBase): + ''' + Combo Cell for webpush subscription management + ''' + user_dependant = True + loading_message = _('Loading your active subscriptions to push notifications') + + class Meta: + verbose_name = _('Push notifications subscription cell') + + def is_visible(self, user=None): + if user is None or not user.is_authenticated(): + return False + return super(WebpushCell, self).is_visible(user) + + def get_cell_extra_context(self, context): + extra_context = super(WebpushCell, self).get_cell_extra_context(context) + user = getattr(context.get('request'), 'user', None) + if user and user.is_authenticated(): + url = reverse('create_webpush_subscription') + user_active_devices = [{ + 'browser': device.browser, + 'registration_id': device.registration_id, + } for device in WebPushDevice.objects.filter(user=user, active=True)] + + extra_context.update({ + 'url': url, + 'application_server_key': settings.PUSH_NOTIFICATIONS_SETTINGS['APP_SERVER_KEY'], + 'user_active_devices': json.dumps(user_active_devices), + }) + return extra_context diff --git a/combo/apps/pwa/templates/combo/webpushcell.html b/combo/apps/pwa/templates/combo/webpushcell.html new file mode 100644 index 0000000..b7da5f2 --- /dev/null +++ b/combo/apps/pwa/templates/combo/webpushcell.html @@ -0,0 +1,17 @@ +{% load i18n static %} +{% block cell-content %} +
+{% if request.user.is_authenticated %} + + +{% else %} + +{% endif %} +
+{% endblock %} \ No newline at end of file -- 2.18.0