# -*- coding: utf-8 -* # # combo - content management system # Copyright (C) 2015-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 base64 import json from django.conf import settings from django.core import serializers from django.core.files.base import ContentFile from django.core.files.storage import default_storage from django.db import models from django.utils import six from django.utils.encoding import force_text, force_bytes from django.utils.six import BytesIO from django.utils.translation import ugettext_lazy as _ from jsonfield import JSONField from combo.data.fields import RichTextField from combo import utils class PwaSettings(models.Model): APPLICATION_ICON_SIZES = ['%sx%s' % (x, x) for x in (48, 96, 192, 256, 512)] application_icon = models.FileField( verbose_name=_('Application Icon'), help_text=_(u'Should be a square of at least 512×512 pixels.'), upload_to='pwa', blank=True, null=True) offline_text = RichTextField( verbose_name=_('Offline Information Text'), default=_('You are currently offline.'), config_name='small') offline_retry_button = models.BooleanField(_('Include Retry Button'), default=True) last_update_timestamp = models.DateTimeField(auto_now=True) @classmethod def singleton(cls): return cls.objects.first() or cls() @classmethod def export_for_json(cls): obj = cls.singleton() if not obj.id: return {} serialized_settings = json.loads(serializers.serialize('json', [obj])) return serialized_settings[0].get('fields') @classmethod def load_serialized_settings(cls, json_settings): if not json_settings: return obj = cls.singleton() for attr in json_settings: setattr(obj, attr, json_settings[attr]) obj.save() class PwaNavigationEntry(models.Model): label = models.CharField(verbose_name=_('Label'), max_length=150, blank=True) url = models.CharField(verbose_name=_('External URL'), max_length=200, blank=True) link_page = models.ForeignKey('data.Page', blank=True, null=True, verbose_name=_('Internal link')) icon = models.FileField(_('Icon'), upload_to='pwa', blank=True, null=True) extra_css_class = models.CharField(_('Extra classes for CSS styling'), max_length=100, blank=True) order = models.PositiveIntegerField() notification_count = models.BooleanField( verbose_name=_('Display notification count'), default=False) use_user_name_as_label = models.BooleanField( verbose_name=_('Use user name as label'), default=False) class Meta: ordering = ('order',) def get_label(self): return self.label or self.link_page.title def get_url(self): if self.link_page: return self.link_page.get_online_url() else: return utils.get_templated_url(self.url) def css_class_names(self): css_class_names = self.extra_css_class or '' if self.link_page: css_class_names += ' page-%s' % self.link_page.slug return css_class_names @classmethod def export_all_for_json(cls): return [x.get_as_serialized_object() for x in cls.objects.all()] def get_as_serialized_object(self): serialized_entry = json.loads(serializers.serialize('json', [self], use_natural_foreign_keys=True, use_natural_primary_keys=True))[0] if self.icon: encode = base64.encodestring if six.PY2 else base64.encodebytes serialized_entry['icon:base64'] = force_text(encode(self.icon.read())) del serialized_entry['model'] del serialized_entry['pk'] return serialized_entry @classmethod def load_serialized_objects(cls, json_site): for json_entry in json_site: cls.load_serialized_object(json_entry) @classmethod def load_serialized_object(cls, json_entry): json_entry['model'] = 'pwa.pwanavigationentry' # deserialize once to get link_page by natural key fake_entry = [x for x in serializers.deserialize('json', json.dumps([json_entry]))][0] entry, created = cls.objects.get_or_create( label=json_entry['fields']['label'], url=json_entry['fields']['url'], link_page=fake_entry.object.link_page, defaults={'order': 0}) json_entry['pk'] = entry.id entry = [x for x in serializers.deserialize('json', json.dumps([json_entry]))][0] entry.save() if json_entry.get('icon:base64'): decode = base64.decodestring if six.PY2 else base64.decodebytes decoded_icon = decode(force_bytes(json_entry['icon:base64'])) if not default_storage.exists(entry.object.icon.name) or entry.object.icon.read() != decoded_icon: # save new file entry.object.icon.save(entry.object.icon.name, ContentFile(decoded_icon)) class PushSubscription(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) subscription_info = JSONField() creation_timestamp = models.DateTimeField(auto_now_add=True)