pwa: add settings page with offline parameters (#25496)
This commit is contained in:
parent
00b6001be7
commit
c26b473c78
|
@ -15,6 +15,9 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import django.apps
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class AppConfig(django.apps.AppConfig):
|
||||
name = 'combo.apps.pwa'
|
||||
|
@ -26,4 +29,11 @@ class AppConfig(django.apps.AppConfig):
|
|||
from . import urls
|
||||
return urls.urlpatterns
|
||||
|
||||
def get_extra_manager_actions(self):
|
||||
from django.conf import settings
|
||||
if settings.TEMPLATE_VARS.get('pwa_display') in ('standalone', 'fullscreen'):
|
||||
return [{'href': reverse('pwa-manager-homepage'),
|
||||
'text': _('Mobile Application (PWA)')}]
|
||||
return []
|
||||
|
||||
default_app_config = 'combo.apps.pwa.AppConfig'
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# combo - content management system
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.views.generic import UpdateView
|
||||
|
||||
from .models import PwaSettings
|
||||
|
||||
|
||||
class ManagerHomeView(UpdateView):
|
||||
template_name = 'combo/pwa/manager_home.html'
|
||||
model = PwaSettings
|
||||
fields = '__all__'
|
||||
success_url = reverse_lazy('pwa-manager-homepage')
|
||||
|
||||
def get_object(self):
|
||||
return PwaSettings.singleton()
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.12 on 2018-12-27 08:44
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import combo.data.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pwa', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PwaSettings',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('offline_text', combo.data.fields.RichTextField(default='You are currently offline.', verbose_name='Offline Information Text')),
|
||||
('offline_retry_button', models.BooleanField(default=True, verbose_name='Include Retry Button')),
|
||||
('last_update_timestamp', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -14,10 +14,46 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.core import serializers
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from jsonfield import JSONField
|
||||
from combo.data.fields import RichTextField
|
||||
|
||||
|
||||
class PwaSettings(models.Model):
|
||||
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 PushSubscription(models.Model):
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
.manager-mobile-home-layout {
|
||||
display: flex;
|
||||
div.sections {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
div#mobile-case {
|
||||
background: url(../img/mobile-case.svg) top left no-repeat;
|
||||
width: 400px;
|
||||
height: 720px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
div.screen {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
left: 12px;
|
||||
top: 52px;
|
||||
bottom: 67px;
|
||||
right: 28px;
|
||||
div.mobile-top-bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
color: white;
|
||||
box-sizing: border-box;
|
||||
padding-right: 5px;
|
||||
height: 20px;
|
||||
}
|
||||
div.mobile-app-content {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
div.splash,
|
||||
iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
opacity: 0;
|
||||
transition: all ease-out 0.4s;
|
||||
}
|
||||
div.splash {
|
||||
z-index: 100;
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
&.splash-off {
|
||||
div.splash {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transform: scale(10);
|
||||
}
|
||||
iframe {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
div.appicon {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
text-align: center;
|
||||
img {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
div.applabel {
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 30px;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
xml:space="preserve"
|
||||
width="386.93579"
|
||||
height="707.42499"
|
||||
viewBox="0 0 386.93579 707.42499"
|
||||
sodipodi:docname="mobile-case.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs6"><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath18"><path
|
||||
d="M 0,410 H 1028 V 0 H 0 Z"
|
||||
id="path16"
|
||||
inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1043"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.5"
|
||||
inkscape:cx="-114.43322"
|
||||
inkscape:cy="238.84637"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g10"
|
||||
inkscape:measure-start="27,428"
|
||||
inkscape:measure-end="387,428"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" /><g
|
||||
id="g10"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="x"
|
||||
transform="matrix(1.3333333,0,0,-1.3333333,-3.6475299,539.99784)"><g
|
||||
id="g20"
|
||||
transform="matrix(1.8601685,0,0,1.8601685,309.66345,-151.14733)"
|
||||
style="stroke-width:0.5"><path
|
||||
d="m -10.548622,33.67008 c 0,-16.568 -13.431,-19.920268 -29.999,-19.920268 H -134.999 c -16.569,0 -30.001,3.352268 -30.001,19.920268 V 280.665 c 0,9.72565 13.432,18.31099 30.001,18.31099 h 94.451378 c 16.568,0 29.999,-9.44063 29.999,-18.31099 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5"
|
||||
id="path22"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssssssss" /></g><g
|
||||
id="g24"
|
||||
transform="matrix(1.8601685,0,0,1.8601685,300.67326,-142.46964)"
|
||||
style="stroke-width:0.5"><path
|
||||
d="m -10.120975,32.690552 c 0,-16.568 -13.432,-20.726646 -30,-20.726646 H -125.333 c -16.569,0 -30,4.158646 -30,20.726646 V 271.334 c 0,16.569 13.431,20.71282 30,20.71282 h 85.212025 c 16.568,0 30,-4.14382 30,-20.71282 z"
|
||||
style="fill:#232323;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5"
|
||||
id="path26"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssssssss" /></g><g
|
||||
id="g30"
|
||||
transform="matrix(1.8601685,0,0,1.8601685,189.06408,381.94033)"
|
||||
style="stroke-width:0.5"><path
|
||||
d="m 0,0 c 0,-1 -1,-2 -2,-2 h -30 c -1,0 -2,1 -2,2 0,1 1,2 2,2 H -2 C -1,2 0,1 0,0"
|
||||
style="fill:#0e0e0e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5"
|
||||
id="path32"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g38"
|
||||
transform="matrix(1.8601685,0,0,1.8601685,111.24393,381.94125)"
|
||||
style="stroke-width:0.5"><path
|
||||
d="m 0,0 c 0,-2 -1,-4 -4,-4 -2,0 -4,1 -4,4 0,2 1,4 4,4 2,0 4,-1 4,-4"
|
||||
style="fill:#0e0e0e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5"
|
||||
id="path40"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g42"
|
||||
transform="matrix(1.8601685,0,0,1.8601685,108.45368,381.94125)"
|
||||
style="stroke-width:0.5"><path
|
||||
d="m 0,0 c 0,-1 -1,-2 -2,-2 -1,0 -3,1 -3,2 0,1 2,2 3,2 1,0 2,-1 2,-2"
|
||||
style="fill:#2c2c2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5"
|
||||
id="path44"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g46"
|
||||
transform="matrix(1.8601685,0,0,1.8601685,106.43911,381.94125)"
|
||||
style="stroke-width:0.5"><path
|
||||
d="m 0,0 c 0,0 0,-1 -1,-1 0,0 -1,0 -1,1 0,0 0,1 1,1 1,0 1,-1 1,-1"
|
||||
style="fill:#373737;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5"
|
||||
id="path48"
|
||||
inkscape:connector-curvature="0" /></g><path
|
||||
d="m 292.9375,309.4375 h -3 v 41 h 3 z"
|
||||
style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1"
|
||||
id="path50"
|
||||
inkscape:connector-curvature="0" /><rect
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.8;fill:#00ffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28346458;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
|
||||
id="rect831"
|
||||
width="270"
|
||||
height="450"
|
||||
x="11.735635"
|
||||
y="-365.1778"
|
||||
transform="scale(1,-1)" /></g></svg>
|
After Width: | Height: | Size: 5.2 KiB |
|
@ -0,0 +1,16 @@
|
|||
{% extends "combo/manager_base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block css %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}css/combo.manager.pwa.css"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans 'Mobile Application' %}</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{{ block.super }}
|
||||
<a href="{% url 'pwa-manager-homepage' %}">{% trans 'Mobile Application' %}</a>
|
||||
{% endblock %}
|
|
@ -0,0 +1,54 @@
|
|||
{% extends "combo/pwa/manager_base.html" %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="manager-mobile-home-layout">
|
||||
<div id="mobile-case">
|
||||
<div class="screen" style="background: {{ theme_color }};">
|
||||
<div class="mobile-top-bar"><span class="clock">--:--</span></div>
|
||||
<div class="mobile-app-content">
|
||||
<div class="splash">
|
||||
<div class="appicon">
|
||||
<img src="{% static "" %}{{ css_variant }}/{{ icon_prefix }}{{icon_sizes|last}}px.png" alt="">
|
||||
</div>
|
||||
<div class="applabel">{% firstof global_title "Compte Citoyen" %}</div>
|
||||
</div>
|
||||
<iframe scrolling="no"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sections">
|
||||
|
||||
<div class="section settings">
|
||||
<h3>{% trans "Settings" %}</h3>
|
||||
<div>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<div class="buttons">
|
||||
<button class="submit-button">{% trans "Save" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> {# .sections #}
|
||||
</div> {# .manager-mobile-home-layout #}
|
||||
|
||||
<script>
|
||||
setInterval(function() {
|
||||
var $clock = $('#mobile-case .clock');
|
||||
var date = new Date();
|
||||
$clock.text(('0' + date.getHours()).slice(-2) + ':' + ('0' + date.getMinutes()).slice(-2));
|
||||
}, 500);
|
||||
|
||||
$(function() {
|
||||
$('.mobile-app-content .splash').on('click', function() {
|
||||
$('.mobile-app-content iframe').attr('src', '/');
|
||||
$('.mobile-app-content').addClass('splash-off');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,53 @@
|
|||
{% load i18n static %}<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0; padding: 1rem;
|
||||
font-family: sans-serif;
|
||||
background: {{theme_color}};
|
||||
}
|
||||
|
||||
div.info-text {
|
||||
background: white;
|
||||
padding: 1rem;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
p.retry {
|
||||
margin-top: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p.retry a {
|
||||
border: 1px solid {{theme_color}};
|
||||
text-decoration: none;
|
||||
background: white;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 3px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="info-text">
|
||||
<img src="{% static "" %}{{ css_variant }}/{{ icon_prefix }}{{icon_sizes|last}}px.png" alt="">
|
||||
{{ pwa_settings.offline_text|safe }}
|
||||
|
||||
{% if pwa_settings.offline_retry_button %}
|
||||
<p class="retry">
|
||||
<a href=".">{% trans "Retry" %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -25,11 +25,11 @@ function urlB64ToUint8Array(base64String) {
|
|||
var config = {
|
||||
version: 'v{% start_timestamp %}',
|
||||
staticCacheItems: [
|
||||
'/offline/'
|
||||
'/__pwa__/offline/'
|
||||
],
|
||||
cachePathPattern: /^\/static\/.*/,
|
||||
handleFetchPathPattern: /.*/,
|
||||
offlinePage: '/offline/'
|
||||
offlinePage: '/__pwa__/offline/'
|
||||
};
|
||||
|
||||
function cacheName (key, opts) {
|
||||
|
|
|
@ -14,18 +14,32 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf.urls import url
|
||||
from django.conf.urls import url, include
|
||||
|
||||
from combo.urls_utils import decorated_includes, manager_required
|
||||
|
||||
from .manager_views import (
|
||||
ManagerHomeView,
|
||||
)
|
||||
from .views import (
|
||||
manifest_json,
|
||||
service_worker_js,
|
||||
service_worker_registration_js,
|
||||
subscribe_push,
|
||||
offline_page,
|
||||
)
|
||||
|
||||
|
||||
pwa_manager_urls = [
|
||||
url('^$', ManagerHomeView.as_view(), name='pwa-manager-homepage'),
|
||||
]
|
||||
|
||||
urlpatterns = [
|
||||
url('^manifest.json$', manifest_json),
|
||||
url('^service-worker.js$', service_worker_js),
|
||||
url('^service-worker-registration.js$', service_worker_registration_js),
|
||||
url('^api/pwa/push/subscribe$', subscribe_push, name='pwa-subscribe-push'),
|
||||
url('^__pwa__/offline/$', offline_page),
|
||||
url(r'^manage/pwa/', decorated_includes(manager_required,
|
||||
include(pwa_manager_urls))),
|
||||
]
|
||||
|
|
|
@ -21,8 +21,9 @@ from django.conf import settings
|
|||
from django.http import HttpResponse, HttpResponseForbidden, Http404, JsonResponse
|
||||
from django.template.loader import get_template, TemplateDoesNotExist
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from .models import PushSubscription
|
||||
from .models import PushSubscription, PwaSettings
|
||||
|
||||
|
||||
def manifest_json(request, *args, **kwargs):
|
||||
|
@ -67,3 +68,14 @@ def subscribe_push(request, *args, **kwargs):
|
|||
subscription_info=subscription_data)
|
||||
subscription.save()
|
||||
return JsonResponse({'err': 0})
|
||||
|
||||
|
||||
class OfflinePage(TemplateView):
|
||||
template_name = 'combo/pwa/offline.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(OfflinePage, self).get_context_data(**kwargs)
|
||||
context['pwa_settings'] = PwaSettings.singleton()
|
||||
return context
|
||||
|
||||
offline_page = OfflinePage.as_view()
|
||||
|
|
|
@ -22,6 +22,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from combo.apps.assets.models import Asset
|
||||
from combo.apps.maps.models import MapLayer
|
||||
from combo.apps.pwa.models import PwaSettings
|
||||
from .models import Page
|
||||
|
||||
|
||||
|
@ -38,7 +39,11 @@ def export_site():
|
|||
'''Dump site objects to JSON-dumpable dictionnary'''
|
||||
return {'pages': Page.export_all_for_json(),
|
||||
'map-layers': MapLayer.export_all_for_json(),
|
||||
'assets': Asset.export_all_for_json(),}
|
||||
'assets': Asset.export_all_for_json(),
|
||||
'pwa': {
|
||||
'settings': PwaSettings.export_for_json(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def import_site(data, if_empty=False, clean=False):
|
||||
|
@ -68,6 +73,7 @@ def import_site(data, if_empty=False, clean=False):
|
|||
MapLayer.objects.all().delete()
|
||||
Asset.objects.all().delete()
|
||||
Page.objects.all().delete()
|
||||
PwaSettings.objects.all().delete()
|
||||
|
||||
with transaction.atomic():
|
||||
MapLayer.load_serialized_objects(data.get('map-layers') or [])
|
||||
|
@ -77,3 +83,6 @@ def import_site(data, if_empty=False, clean=False):
|
|||
|
||||
with transaction.atomic():
|
||||
Page.load_serialized_pages(data.get('pages') or [])
|
||||
|
||||
with transaction.atomic():
|
||||
PwaSettings.load_serialized_settings((data.get('pwa') or {}).get('settings'))
|
||||
|
|
|
@ -23,6 +23,7 @@ The local settings file should exist, at least to set a suitable SECRET_KEY,
|
|||
and to disable DEBUG mode in production.
|
||||
"""
|
||||
|
||||
import copy
|
||||
import os
|
||||
from django.conf import global_settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
@ -177,6 +178,9 @@ CKEDITOR_CONFIGS = {
|
|||
},
|
||||
}
|
||||
|
||||
CKEDITOR_CONFIGS['small'] = copy.copy(CKEDITOR_CONFIGS['default'])
|
||||
CKEDITOR_CONFIGS['small']['height'] = 150
|
||||
|
||||
HAYSTACK_CONNECTIONS = {
|
||||
'default': {
|
||||
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
|
||||
|
|
|
@ -14,6 +14,7 @@ from django.utils.six import BytesIO, StringIO
|
|||
|
||||
from combo.apps.assets.models import Asset
|
||||
from combo.apps.maps.models import MapLayer, Map
|
||||
from combo.apps.pwa.models import PwaSettings
|
||||
from combo.data.models import Page, TextCell
|
||||
from combo.data.utils import export_site, import_site, MissingGroups
|
||||
|
||||
|
@ -198,3 +199,17 @@ def test_import_export_assets(app, some_assets):
|
|||
|
||||
import_site(data={}, if_empty=True)
|
||||
assert Asset.objects.count() == 2
|
||||
|
||||
def test_import_export_pwa_settings(app):
|
||||
output = get_output_of_command('export_site')
|
||||
pwa_settings = PwaSettings.singleton()
|
||||
pwa_settings.offline_text = 'Hello world'
|
||||
pwa_settings.offline_retry_button = False
|
||||
pwa_settings.save()
|
||||
output = get_output_of_command('export_site')
|
||||
import_site(data={}, clean=True)
|
||||
assert PwaSettings.objects.all().count() == 0
|
||||
|
||||
import_site(data=json.loads(output))
|
||||
assert PwaSettings.singleton().offline_retry_button is False
|
||||
assert PwaSettings.singleton().offline_text == 'Hello world'
|
||||
|
|
|
@ -13,7 +13,7 @@ from django.core.urlresolvers import reverse
|
|||
from django.test import override_settings
|
||||
|
||||
from combo.apps.notifications.models import Notification
|
||||
from combo.apps.pwa.models import PushSubscription
|
||||
from combo.apps.pwa.models import PushSubscription, PwaSettings
|
||||
|
||||
from .test_manager import login
|
||||
|
||||
|
@ -59,3 +59,35 @@ def test_webpush_notification(app, john_doe):
|
|||
notification = Notification.notify(john_doe, 'test', body='hello world')
|
||||
assert webpush.call_count == 1
|
||||
assert webpush.call_args[1]['subscription_info'] == {'sample': 'content'}
|
||||
|
||||
def test_no_pwa_manager(app, admin_user):
|
||||
app = login(app)
|
||||
resp = app.get('/manage/', status=200)
|
||||
assert not '/manage/pwa/' in resp.text
|
||||
|
||||
def test_pwa_manager(app, admin_user):
|
||||
app = login(app)
|
||||
with override_settings(TEMPLATE_VARS={'pwa_display': 'standalone'}):
|
||||
resp = app.get('/manage/', status=200)
|
||||
assert '/manage/pwa/' in resp.text
|
||||
resp = app.get('/manage/pwa/')
|
||||
resp.form['offline_text'] = 'You are offline.'
|
||||
assert resp.form['offline_retry_button'].checked
|
||||
resp.form['offline_retry_button'].checked = False
|
||||
resp = resp.form.submit().follow()
|
||||
assert resp.form['offline_text'].value == 'You are offline.'
|
||||
assert resp.form['offline_retry_button'].checked is False
|
||||
|
||||
def test_pwa_offline_page(app):
|
||||
PwaSettings.objects.all().delete()
|
||||
resp = app.get('/__pwa__/offline/')
|
||||
assert 'You are currently offline.' in resp.text
|
||||
assert 'Retry' in resp.text
|
||||
pwa_settings = PwaSettings.singleton()
|
||||
pwa_settings.offline_text = 'You are offline.'
|
||||
pwa_settings.offline_retry_button = False
|
||||
pwa_settings.save()
|
||||
|
||||
resp = app.get('/__pwa__/offline/')
|
||||
assert 'You are offline.' in resp.text
|
||||
assert 'Retry' not in resp.text
|
||||
|
|
Loading…
Reference in New Issue