diff --git a/webpush/config.py b/webpush/config.py index fa058ee..e69de29 100644 --- a/webpush/config.py +++ b/webpush/config.py @@ -1,5 +0,0 @@ -from django.conf import settings - -MANIFEST = {} -if hasattr(settings,'WEBPUSH_SETTINGS'): - MANIFEST["gcm_sender_id"] = settings.WEBPUSH_SETTINGS['GCM_ID'] diff --git a/webpush/forms.py b/webpush/forms.py index 978d938..63fd5fc 100644 --- a/webpush/forms.py +++ b/webpush/forms.py @@ -3,7 +3,6 @@ from .models import Group, PushInformation, SubscriptionInfo - class WebPushForm(forms.Form): group = forms.CharField(max_length=255, required=False) status_type = forms.ChoiceField(choices=[ @@ -19,7 +18,7 @@ def save_or_delete(self, subscription, user, status_type, group_name): if group_name: group, created = Group.objects.get_or_create(name=group_name) - data["group"] = group + data["group"] = group data["subscription"] = subscription @@ -36,9 +35,8 @@ class SubscriptionForm(forms.ModelForm): class Meta: model = SubscriptionInfo - fields = ('browser', 'endpoint', 'auth', 'p256dh') - + fields = ('endpoint', 'auth', 'p256dh') - def get_or_save(self, subscription_data): - subscription, created = SubscriptionInfo.objects.get_or_create(**subscription_data) - return subscription \ No newline at end of file + def get_or_save(self): + subscription, created = SubscriptionInfo.objects.get_or_create(**self.cleaned_data) + return subscription diff --git a/webpush/models.py b/webpush/models.py index d4b9ba5..6e244de 100644 --- a/webpush/models.py +++ b/webpush/models.py @@ -4,6 +4,7 @@ # Create your models here. + class Group(models.Model): name = models.CharField(max_length=255, unique=True) @@ -20,12 +21,12 @@ class PushInformation(models.Model): subscription = models.ForeignKey(SubscriptionInfo, related_name='webpush_info') group = models.ForeignKey(Group, related_name='webpush_info', blank=True, null=True) - def save(self, force_insert=False, force_update=False, using=None): + def save(self, *args, **kwargs): # Check whether user or the group field is present # At least one field should be present there # Through from the functionality its not possible, just in case! ;) if self.user or self.group: - super(PushInformation, self).save() + super(PushInformation, self).save(*args, **kwargs) else: raise FieldError('At least user or group should be present') diff --git a/webpush/static/webpush/webpush.js b/webpush/static/webpush/webpush.js index 3cf85bf..fde8445 100644 --- a/webpush/static/webpush/webpush.js +++ b/webpush/static/webpush/webpush.js @@ -78,8 +78,7 @@ window.addEventListener('load', function() { function subscribe(reg) { // Get the Subscription or register one - getSubscription(reg) - .then( + getSubscription(reg).then( function(subscription) { postSubscribeObj('subscribe',subscription); } @@ -91,16 +90,40 @@ function subscribe(reg) { ) } +function urlB64ToUint8Array(base64String) { + const padding = '='.repeat((4 - base64String.length % 4) % 4); + const base64 = (base64String + padding) + .replace(/\-/g, '+') + .replace(/_/g, '/'); + + const rawData = window.atob(base64); + const outputArray = new Uint8Array(rawData.length); + + for (var i = 0; i < rawData.length; ++i) { + outputArray[i] = rawData.charCodeAt(i); + } + return outputArray; +} + function getSubscription(reg) { - return reg.pushManager.getSubscription() - .then( + return reg.pushManager.getSubscription().then( function(subscription) { + var metaObj, applicationServerKey, options; // Check if Subscription is available if (subscription) { return subscription; } + + metaObj = document.querySelector('meta[name="django-webpush-vapid-key"]'); + applicationServerKey = metaObj.content; + options = { + userVisibleOnly: true + }; + if (applicationServerKey){ + options.applicationServerKey = urlB64ToUint8Array(applicationServerKey) + } // If not, register one - return registration.pushManager.subscribe({ userVisibleOnly: true }); + return registration.pushManager.subscribe(options) } ) } diff --git a/webpush/static/webpush/webpush_serviceworker.js b/webpush/static/webpush/webpush_serviceworker.js index 2c81ff5..e3c879c 100644 --- a/webpush/static/webpush/webpush_serviceworker.js +++ b/webpush/static/webpush/webpush_serviceworker.js @@ -14,7 +14,7 @@ self.addEventListener('push', function(event) { // Show a notification with title 'ServiceWorker Cookbook' and use the payload // as the body. self.registration.showNotification(head, { - body: body, + body: body }) ); }); \ No newline at end of file diff --git a/webpush/templates/webpush.html b/webpush/templates/webpush.html index 3e9a026..f289f6c 100644 --- a/webpush/templates/webpush.html +++ b/webpush/templates/webpush.html @@ -3,5 +3,5 @@ - + {% endif %} diff --git a/webpush/templatetags/webpush_notifications.py b/webpush/templatetags/webpush_notifications.py index e1b84c3..14d62e5 100644 --- a/webpush/templatetags/webpush_notifications.py +++ b/webpush/templatetags/webpush_notifications.py @@ -1,4 +1,5 @@ from django import template +from django.conf import settings from django.core.urlresolvers import reverse register = template.Library() @@ -8,8 +9,9 @@ @register.inclusion_tag('webpush.html', takes_context=True) def webpush(context): group = context.get('webpush', {}).get('group') + vapid_public_key = getattr(settings, 'WEBPUSH_SETTINGS', {}).get('VAPID_PUBLIC_KEY', '') request = context['request'] - return {'group': group, 'request': request} + return {'group': group, 'request': request, 'vapid_public_key': vapid_public_key} @register.filter diff --git a/webpush/urls.py b/webpush/urls.py index 451ac6a..76750af 100644 --- a/webpush/urls.py +++ b/webpush/urls.py @@ -4,5 +4,4 @@ urlpatterns = [ url(r'^save_information', views.save_info, name='save_webpush_info'), - url(r'^manifest', views.generate_manifest, name='webpush_manifest_json'), ] diff --git a/webpush/utils.py b/webpush/utils.py index 0a86f7b..8dcb86f 100644 --- a/webpush/utils.py +++ b/webpush/utils.py @@ -1,7 +1,7 @@ from django.conf import settings from django.forms.models import model_to_dict -from pywebpush import WebPusher +from pywebpush import webpush def send_notification_to_user(user, payload, ttl=0): @@ -22,14 +22,24 @@ def send_notification_to_group(group_name, payload, ttl=0): def _send_notification(push_info, payload, ttl): subscription = push_info.subscription subscription_data = _process_subscription_info(subscription) - # Check if GCM info is provided in the settings - if hasattr(settings,'WEBPUSH_SETTINGS'): - gcm_key = settings.WEBPUSH_SETTINGS.get('GCM_KEY') - else: - gcm_key = None - req = WebPusher(subscription_data).send(data=payload, ttl=ttl, gcm_key=gcm_key) + vapid_data = {} + + webpush_settings = getattr(settings, 'WEBPUSH_SETTINGS', {}) + vapid_private_key = webpush_settings.get('VAPID_PRIVATE_KEY') + vapid_admin_email = webpush_settings.get('VAPID_ADMIN_EMAIL') + + # Vapid keys are optional, and mandatory only for Chrome. + # If Vapid key is provided, include vapid key and claims + if vapid_private_key: + vapid_data = { + 'vapid_private_key': vapid_private_key, + 'vapid_claims': {"sub": "mailto:{}".format(vapid_admin_email)} + } + + req = webpush(subscription_info=subscription_data, data=payload, ttl=ttl, **vapid_data) return req + def _process_subscription_info(subscription): subscription_data = model_to_dict(subscription, exclude=["browser", "id"]) endpoint = subscription_data.pop("endpoint") diff --git a/webpush/views.py b/webpush/views.py index 30fd2e4..d6ab4e5 100644 --- a/webpush/views.py +++ b/webpush/views.py @@ -1,13 +1,8 @@ import json - -from django.conf import settings -from django.core.exceptions import FieldError -from django.http import HttpResponse, JsonResponse -from django.shortcuts import render +from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST, require_GET -from .config import MANIFEST from .forms import WebPushForm, SubscriptionForm @@ -37,7 +32,7 @@ def save_info(request): if request.user.is_authenticated() or group_name: # Save the subscription info with subscription data # as the subscription data is a dictionary and its valid - subscription = subscription_form.get_or_save(subscription_data) + subscription = subscription_form.get_or_save() web_push_form.save_or_delete( subscription=subscription, user=request.user, status_type=status_type, group_name=group_name) @@ -52,14 +47,6 @@ def save_info(request): return HttpResponse(status=400) -@require_GET -def generate_manifest(request): - if hasattr(settings,'WEBPUSH_SETTINGS'): - return JsonResponse(MANIFEST) - else: - return HttpResponse(status=404) - - def process_subscription_data(post_data): """Process the subscription data according to out model""" subscription_data = post_data.pop("subscription", {})