From e17ba704033aba068a935c1beb42d1f306e6a114 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Thu, 30 Aug 2018 12:38:08 +0200 Subject: [PATCH 1/1] push notifications --- requirements/requirements.txt | 4 +- src/push/__init__.py | 0 src/push/admin.py | 10 +++++ src/push/forms.py | 23 ++++++++++++ src/push/migrations/0001_initial.py | 24 ++++++++++++ .../migrations/0002_auto_20180830_1627.py | 37 +++++++++++++++++++ .../migrations/0003_auto_20180831_1135.py | 19 ++++++++++ src/push/migrations/__init__.py | 0 src/push/models.py | 20 ++++++++++ .../templates/push/notification_form.html | 17 +++++++++ .../templates/push/notification_sent.html | 10 +++++ src/push/urls.py | 12 ++++++ src/push/utils.py | 29 +++++++++++++++ src/push/views.py | 26 +++++++++++++ src/wolnelektury/settings/apps.py | 1 + src/wolnelektury/urls.py | 1 + 16 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 src/push/__init__.py create mode 100644 src/push/admin.py create mode 100644 src/push/forms.py create mode 100644 src/push/migrations/0001_initial.py create mode 100644 src/push/migrations/0002_auto_20180830_1627.py create mode 100644 src/push/migrations/0003_auto_20180831_1135.py create mode 100644 src/push/migrations/__init__.py create mode 100644 src/push/models.py create mode 100644 src/push/templates/push/notification_form.html create mode 100644 src/push/templates/push/notification_sent.html create mode 100644 src/push/urls.py create mode 100644 src/push/utils.py create mode 100644 src/push/views.py diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 05e0a8f3c..62c6180e9 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -71,4 +71,6 @@ paypalrestsdk django-haystack<2.8.0 django-migdal>=0.8.3 -python-slugify \ No newline at end of file +python-slugify + +firebase-admin diff --git a/src/push/__init__.py b/src/push/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/push/admin.py b/src/push/admin.py new file mode 100644 index 000000000..339e2e780 --- /dev/null +++ b/src/push/admin.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from django.contrib import admin + +from .models import Notification + + +admin.site.register(Notification) diff --git a/src/push/forms.py b/src/push/forms.py new file mode 100644 index 000000000..b49096db1 --- /dev/null +++ b/src/push/forms.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from django import forms +from django.contrib.sites.models import Site + +from push.models import Notification +from push.utils import send_fcm_push + + +class NotificationForm(forms.ModelForm): + + class Meta: + model = Notification + exclude = ('timestamp', 'message_id') + + def save(self, commit=True): + notification = super(NotificationForm, self).save(commit=commit) + wl_base = u'https://' + Site.objects.get_current().domain + notification.message_id = send_fcm_push(notification.title, notification.body, wl_base + notification.image.url) + if commit: + notification.save() diff --git a/src/push/migrations/0001_initial.py b/src/push/migrations/0001_initial.py new file mode 100644 index 000000000..86a33dc00 --- /dev/null +++ b/src/push/migrations/0001_initial.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Notification', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('title', models.CharField(max_length=256)), + ('body', models.CharField(max_length=2048)), + ('image_url', models.URLField()), + ('message_id', models.CharField(max_length=2048)), + ], + ), + ] diff --git a/src/push/migrations/0002_auto_20180830_1627.py b/src/push/migrations/0002_auto_20180830_1627.py new file mode 100644 index 000000000..bb22b652c --- /dev/null +++ b/src/push/migrations/0002_auto_20180830_1627.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('push', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='notification', + options={'ordering': ['-timestamp']}, + ), + migrations.RemoveField( + model_name='notification', + name='image_url', + ), + migrations.AddField( + model_name='notification', + name='image', + field=models.FileField(upload_to=b'push/img', verbose_name='image', blank=True), + ), + migrations.AlterField( + model_name='notification', + name='body', + field=models.CharField(max_length=2048, verbose_name='content'), + ), + migrations.AlterField( + model_name='notification', + name='title', + field=models.CharField(max_length=256, verbose_name='title'), + ), + ] diff --git a/src/push/migrations/0003_auto_20180831_1135.py b/src/push/migrations/0003_auto_20180831_1135.py new file mode 100644 index 000000000..40da3fe4a --- /dev/null +++ b/src/push/migrations/0003_auto_20180831_1135.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('push', '0002_auto_20180830_1627'), + ] + + operations = [ + migrations.AlterField( + model_name='notification', + name='image', + field=models.ImageField(upload_to=b'push/img', verbose_name='image', blank=True), + ), + ] diff --git a/src/push/migrations/__init__.py b/src/push/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/push/models.py b/src/push/models.py new file mode 100644 index 000000000..79e555173 --- /dev/null +++ b/src/push/models.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from django.db import models +from django.utils.translation import ugettext_lazy as _ + + +class Notification(models.Model): + timestamp = models.DateTimeField(auto_now_add=True) + title = models.CharField(max_length=256, verbose_name=_(u'title')) + body = models.CharField(max_length=2048, verbose_name=_(u'content')) + image = models.ImageField(verbose_name=_(u'image'), blank=True, upload_to='push/img') + message_id = models.CharField(max_length=2048) + + class Meta: + ordering = ['-timestamp'] + + def __unicode__(self): + return '%s: %s' % (self.timestamp, self.title) diff --git a/src/push/templates/push/notification_form.html b/src/push/templates/push/notification_form.html new file mode 100644 index 000000000..e926c9229 --- /dev/null +++ b/src/push/templates/push/notification_form.html @@ -0,0 +1,17 @@ +{% extends "base/base.html" %} +{% load i18n %} + +{% block titleextra %}{% trans "Notifications" %}{% endblock %} + + +{% block body %} +

Wyślij powiadomienie

+ +
+ {% csrf_token %} + + {{ form.as_table }} + +
+
+{% endblock %} \ No newline at end of file diff --git a/src/push/templates/push/notification_sent.html b/src/push/templates/push/notification_sent.html new file mode 100644 index 000000000..44c29c327 --- /dev/null +++ b/src/push/templates/push/notification_sent.html @@ -0,0 +1,10 @@ +{% extends "base/base.html" %} +{% load i18n %} + +{% block titleextra %}{% trans "Notifications" %}{% endblock %} + + +{% block body %} +

Wysłano powiadomienie

+

Gratulacje!

+{% endblock %} \ No newline at end of file diff --git a/src/push/urls.py b/src/push/urls.py new file mode 100644 index 000000000..70a8487be --- /dev/null +++ b/src/push/urls.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from django.conf.urls import patterns, url + +from push import views + +urlpatterns = ( + url(r'^wyslij/$', views.notification_form, name='notification_form'), + url(r'^wyslane/$', views.notification_sent, name='notification_sent'), +) diff --git a/src/push/utils.py b/src/push/utils.py new file mode 100644 index 000000000..98f035919 --- /dev/null +++ b/src/push/utils.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +import firebase_admin +from firebase_admin import credentials, messaging +from django.conf import settings + +cred = credentials.Certificate(settings.FCM_PRIVATE_KEY_PATH) +firebase_admin.initialize_app(cred) + +TOPIC = 'wolnelektury' + + +def send_fcm_push(title, body, image_url=None): + # See documentation on defining a message payload. + data = {} + if image_url: + data['imageUrl'] = image_url + message = messaging.Message( + notification=messaging.Notification( + title=title, + body=body, + ), + data=data, + topic=TOPIC, + ) + message_id = messaging.send(message) + return message_id diff --git a/src/push/views.py b/src/push/views.py new file mode 100644 index 000000000..bbcf89d6d --- /dev/null +++ b/src/push/views.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from django.contrib.auth.decorators import permission_required +from django.core.urlresolvers import reverse +from django.http.response import HttpResponseRedirect +from django.shortcuts import render + +from push.forms import NotificationForm + + +@permission_required('push.change_notification') +def notification_form(request): + if request.method == 'POST': + form = NotificationForm(data=request.POST, files=request.FILES or None) + if form.is_valid(): + form.save() + return HttpResponseRedirect(reverse('notification_sent')) + else: + form = NotificationForm() + return render(request, 'push/notification_form.html', {'form': form}) + + +def notification_sent(request): + return render(request, 'push/notification_sent.html') \ No newline at end of file diff --git a/src/wolnelektury/settings/apps.py b/src/wolnelektury/settings/apps.py index 7e293d3bd..87286d56a 100644 --- a/src/wolnelektury/settings/apps.py +++ b/src/wolnelektury/settings/apps.py @@ -32,6 +32,7 @@ INSTALLED_APPS_OUR = [ 'contact', 'isbn', 'paypal', + 'push', ] INSTALLED_APPS_CONTRIB = [ diff --git a/src/wolnelektury/urls.py b/src/wolnelektury/urls.py index 97002d193..403cc1fc8 100644 --- a/src/wolnelektury/urls.py +++ b/src/wolnelektury/urls.py @@ -52,6 +52,7 @@ urlpatterns += [ url(r'^formularz/', include('contact.urls')), url(r'^isbn/', include('isbn.urls')), url(r'^paypal/', include('paypal.urls')), + url(r'^powiadomienie/', include('push.urls')), # Admin panel url(r'^admin/catalogue/book/import$', catalogue.views.import_book, name='import_book'), -- 2.20.1