Projet

Général

Profil

0001-pwa-add-settings-page-with-offline-parameters-25496.patch

Frédéric Péters, 27 décembre 2018 20:33

Télécharger (25,5 ko)

Voir les différences:

Subject: [PATCH 1/4] pwa: add settings page with offline parameters (#25496)

 combo/apps/pwa/__init__.py                    |  10 ++
 combo/apps/pwa/manager_views.py               |  30 +++++
 combo/apps/pwa/migrations/0002_pwasettings.py |  24 ++++
 combo/apps/pwa/models.py                      |  35 ++++++
 .../pwa/static/css/combo.manager.pwa.scss     |  84 +++++++++++++
 combo/apps/pwa/static/img/mobile-case.svg     | 112 ++++++++++++++++++
 .../pwa/templates/combo/pwa/manager_base.html |  16 +++
 .../pwa/templates/combo/pwa/manager_home.html |  54 +++++++++
 .../apps/pwa/templates/combo/pwa/offline.html |  53 +++++++++
 .../pwa/templates/combo/service-worker.js     |   4 +-
 combo/apps/pwa/urls.py                        |  16 ++-
 combo/apps/pwa/views.py                       |  14 ++-
 combo/data/utils.py                           |  11 +-
 combo/settings.py                             |   4 +
 tests/test_import_export.py                   |  15 +++
 tests/test_pwa.py                             |  34 +++++-
 16 files changed, 510 insertions(+), 6 deletions(-)
 create mode 100644 combo/apps/pwa/manager_views.py
 create mode 100644 combo/apps/pwa/migrations/0002_pwasettings.py
 create mode 100644 combo/apps/pwa/static/css/combo.manager.pwa.scss
 create mode 100644 combo/apps/pwa/static/img/mobile-case.svg
 create mode 100644 combo/apps/pwa/templates/combo/pwa/manager_base.html
 create mode 100644 combo/apps/pwa/templates/combo/pwa/manager_home.html
 create mode 100644 combo/apps/pwa/templates/combo/pwa/offline.html
combo/apps/pwa/__init__.py
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
import django.apps
18
from django.core.urlresolvers import reverse
19
from django.utils.translation import ugettext_lazy as _
20

  
18 21

  
19 22
class AppConfig(django.apps.AppConfig):
20 23
    name = 'combo.apps.pwa'
......
26 29
        from . import urls
27 30
        return urls.urlpatterns
28 31

  
32
    def get_extra_manager_actions(self):
33
        from django.conf import settings
34
        if settings.TEMPLATE_VARS.get('pwa_display') in ('standalone', 'fullscreen'):
35
            return [{'href': reverse('pwa-manager-homepage'),
36
                     'text': _('Mobile Application (PWA)')}]
37
        return []
38

  
29 39
default_app_config = 'combo.apps.pwa.AppConfig'
combo/apps/pwa/manager_views.py
1
# combo - content management system
2
# Copyright (C) 2018  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.core.urlresolvers import reverse_lazy
18
from django.views.generic import UpdateView
19

  
20
from .models import PwaSettings
21

  
22

  
23
class ManagerHomeView(UpdateView):
24
    template_name = 'combo/pwa/manager_home.html'
25
    model = PwaSettings
26
    fields = '__all__'
27
    success_url = reverse_lazy('pwa-manager-homepage')
28

  
29
    def get_object(self):
30
        return PwaSettings.singleton()
combo/apps/pwa/migrations/0002_pwasettings.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.12 on 2018-12-27 08:44
3
from __future__ import unicode_literals
4

  
5
import combo.data.fields
6
from django.db import migrations, models
7

  
8

  
9
class Migration(migrations.Migration):
10

  
11
    dependencies = [
12
        ('pwa', '0001_initial'),
13
    ]
14

  
15
    operations = [
16
        migrations.CreateModel(
17
            name='PwaSettings',
18
            fields=[
19
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20
                ('offline_text', combo.data.fields.RichTextField(default='You are currently offline.', verbose_name='Offline Information Text')),
21
                ('offline_retry_button', models.BooleanField(default=True, verbose_name='Include Retry Button')),
22
            ],
23
        ),
24
    ]
combo/apps/pwa/models.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
import json
18

  
17 19
from django.conf import settings
20
from django.core import serializers
18 21
from django.db import models
22
from django.utils.translation import ugettext_lazy as _
19 23

  
20 24
from jsonfield import JSONField
25
from combo.data.fields import RichTextField
26

  
27

  
28
class PwaSettings(models.Model):
29
    offline_text = RichTextField(
30
            verbose_name=_('Offline Information Text'),
31
            default=_('You are currently offline.'),
32
            config_name='small')
33
    offline_retry_button = models.BooleanField(_('Include Retry Button'), default=True)
34

  
35
    @classmethod
36
    def singleton(cls):
37
        return cls.objects.all().first() or cls()
38

  
39
    @classmethod
40
    def export_for_json(cls):
41
        obj = cls.singleton()
42
        if not obj.id:
43
            return {}
44
        serialized_settings = json.loads(serializers.serialize('json', [obj]))
45
        return serialized_settings[0].get('fields')
46

  
47
    @classmethod
48
    def load_serialized_settings(cls, json_settings):
49
        if not json_settings:
50
            return
51

  
52
        obj = cls.singleton()
53
        for attr in json_settings:
54
            setattr(obj, attr, json_settings[attr])
55
        obj.save()
21 56

  
22 57

  
23 58
class PushSubscription(models.Model):
combo/apps/pwa/static/css/combo.manager.pwa.scss
1
.manager-mobile-home-layout {
2
	display: flex;
3
	div.sections {
4
		flex: 1;
5
	}
6
}
7

  
8
div#mobile-case {
9
	background: url(../img/mobile-case.svg) top left no-repeat;
10
	width: 400px;
11
	height: 720px;
12
	position: relative;
13
	overflow: hidden;
14
	div.screen {
15
		position: absolute;
16
		overflow: hidden;
17
		left: 12px;
18
		top: 52px;
19
		bottom: 67px;
20
		right: 28px;
21
		div.mobile-top-bar {
22
			position: absolute;
23
			top: 0;
24
			left: 0;
25
			background: rgba(0, 0, 0, 0.7);
26
			width: 100%;
27
			text-align: right;
28
			color: white;
29
			box-sizing: border-box;
30
			padding-right: 5px;
31
			height: 20px;
32
		}
33
		div.mobile-app-content {
34
			position: absolute;
35
			top: 20px;
36
			left: 0;
37
			width: 100%;
38
			bottom: 0;
39
			div.splash,
40
			iframe {
41
				position: absolute;
42
				top: 0;
43
				left: 0;
44
				width: 100%;
45
				height: 100%;
46
				z-index: 0;
47
				opacity: 0;
48
				transition: all ease-out 0.4s;
49
			}
50
			div.splash {
51
				z-index: 100;
52
				opacity: 1;
53
				transform: scale(1);
54
			}
55
			&.splash-off {
56
				div.splash {
57
					pointer-events: none;
58
					opacity: 0;
59
					transform: scale(10);
60
				}
61
				iframe {
62
					opacity: 1;
63
				}
64
			}
65
		}
66
		div.appicon {
67
			position: absolute;
68
			top: 40%;
69
			text-align: center;
70
			img {
71
				width: 50%;
72
			}
73
		}
74
		div.applabel {
75
			position: absolute;
76
			bottom: 50px;
77
			left: 0;
78
			width: 100%;
79
			text-align: center;
80
			font-size: 30px;
81
			color: white;
82
		}
83
	}
84
}
combo/apps/pwa/static/img/mobile-case.svg
1
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
<!-- Created with Inkscape (http://www.inkscape.org/) -->
3

  
4
<svg
5
   xmlns:dc="http://purl.org/dc/elements/1.1/"
6
   xmlns:cc="http://creativecommons.org/ns#"
7
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8
   xmlns:svg="http://www.w3.org/2000/svg"
9
   xmlns="http://www.w3.org/2000/svg"
10
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
   version="1.1"
13
   id="svg2"
14
   xml:space="preserve"
15
   width="386.93579"
16
   height="707.42499"
17
   viewBox="0 0 386.93579 707.42499"
18
   sodipodi:docname="mobile-case.svg"
19
   inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
20
     id="metadata8"><rdf:RDF><cc:Work
21
         rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
22
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
23
     id="defs6"><clipPath
24
       clipPathUnits="userSpaceOnUse"
25
       id="clipPath18"><path
26
         d="M 0,410 H 1028 V 0 H 0 Z"
27
         id="path16"
28
         inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
29
     pagecolor="#ffffff"
30
     bordercolor="#666666"
31
     borderopacity="1"
32
     objecttolerance="10"
33
     gridtolerance="10"
34
     guidetolerance="10"
35
     inkscape:pageopacity="0"
36
     inkscape:pageshadow="2"
37
     inkscape:window-width="1920"
38
     inkscape:window-height="1043"
39
     id="namedview4"
40
     showgrid="false"
41
     inkscape:zoom="0.5"
42
     inkscape:cx="-114.43322"
43
     inkscape:cy="238.84637"
44
     inkscape:window-x="0"
45
     inkscape:window-y="0"
46
     inkscape:window-maximized="1"
47
     inkscape:current-layer="g10"
48
     inkscape:measure-start="27,428"
49
     inkscape:measure-end="387,428"
50
     fit-margin-top="0"
51
     fit-margin-left="0"
52
     fit-margin-right="0"
53
     fit-margin-bottom="0" /><g
54
     id="g10"
55
     inkscape:groupmode="layer"
56
     inkscape:label="x"
57
     transform="matrix(1.3333333,0,0,-1.3333333,-3.6475299,539.99784)"><g
58
       id="g20"
59
       transform="matrix(1.8601685,0,0,1.8601685,309.66345,-151.14733)"
60
       style="stroke-width:0.5"><path
61
         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"
62
         style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5"
63
         id="path22"
64
         inkscape:connector-curvature="0"
65
         sodipodi:nodetypes="sssssssss" /></g><g
66
       id="g24"
67
       transform="matrix(1.8601685,0,0,1.8601685,300.67326,-142.46964)"
68
       style="stroke-width:0.5"><path
69
         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"
70
         style="fill:#232323;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5"
71
         id="path26"
72
         inkscape:connector-curvature="0"
73
         sodipodi:nodetypes="sssssssss" /></g><g
74
       id="g30"
75
       transform="matrix(1.8601685,0,0,1.8601685,189.06408,381.94033)"
76
       style="stroke-width:0.5"><path
77
         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"
78
         style="fill:#0e0e0e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5"
79
         id="path32"
80
         inkscape:connector-curvature="0" /></g><g
81
       id="g38"
82
       transform="matrix(1.8601685,0,0,1.8601685,111.24393,381.94125)"
83
       style="stroke-width:0.5"><path
84
         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"
85
         style="fill:#0e0e0e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5"
86
         id="path40"
87
         inkscape:connector-curvature="0" /></g><g
88
       id="g42"
89
       transform="matrix(1.8601685,0,0,1.8601685,108.45368,381.94125)"
90
       style="stroke-width:0.5"><path
91
         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"
92
         style="fill:#2c2c2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5"
93
         id="path44"
94
         inkscape:connector-curvature="0" /></g><g
95
       id="g46"
96
       transform="matrix(1.8601685,0,0,1.8601685,106.43911,381.94125)"
97
       style="stroke-width:0.5"><path
98
         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"
99
         style="fill:#373737;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5"
100
         id="path48"
101
         inkscape:connector-curvature="0" /></g><path
102
       d="m 292.9375,309.4375 h -3 v 41 h 3 z"
103
       style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1"
104
       id="path50"
105
       inkscape:connector-curvature="0" /><rect
106
       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"
107
       id="rect831"
108
       width="270"
109
       height="450"
110
       x="11.735635"
111
       y="-365.1778"
112
       transform="scale(1,-1)" /></g></svg>
combo/apps/pwa/templates/combo/pwa/manager_base.html
1
{% extends "combo/manager_base.html" %}
2
{% load i18n %}
3

  
4
{% block css %}
5
{{ block.super }}
6
<link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}css/combo.manager.pwa.css"/>
7
{% endblock %}
8

  
9
{% block appbar %}
10
<h2>{% trans 'Mobile Application' %}</h2>
11
{% endblock %}
12

  
13
{% block breadcrumb %}
14
{{ block.super }}
15
<a href="{% url 'pwa-manager-homepage' %}">{% trans 'Mobile Application' %}</a>
16
{% endblock %}
combo/apps/pwa/templates/combo/pwa/manager_home.html
1
{% extends "combo/pwa/manager_base.html" %}
2
{% load i18n static %}
3

  
4
{% block content %}
5
<div class="manager-mobile-home-layout">
6
<div id="mobile-case">
7
  <div class="screen" style="background: {{ theme_color }};">
8
    <div class="mobile-top-bar"><span class="clock">--:--</span></div>
9
    <div class="mobile-app-content">
10
      <div class="splash">
11
      <div class="appicon">
12
        <img src="{% static "" %}{{ css_variant }}/{{ icon_prefix }}{{icon_sizes|last}}px.png" alt="">
13
      </div>
14
      <div class="applabel">{% firstof global_title "Compte Citoyen" %}</div>
15
      </div>
16
      <iframe scrolling="no"></iframe>
17
    </div>
18
  </div>
19
</div>
20

  
21
<div class="sections">
22

  
23
<div class="section settings">
24
<h3>{% trans "Settings" %}</h3>
25
<div>
26
  <form method="post" enctype="multipart/form-data">
27
    {% csrf_token %}
28
    {{ form.as_p }}
29
    <div class="buttons">
30
      <button class="submit-button">{% trans "Save" %}</button>
31
    </div>
32
  </form>
33
</div>
34
</div>
35

  
36
</div> {# .sections #}
37
</div> {# .manager-mobile-home-layout #}
38

  
39
<script>
40
setInterval(function() {
41
  var $clock = $('#mobile-case .clock');
42
  var date = new Date();
43
  $clock.text(('0' + date.getHours()).slice(-2) + ':' + ('0' + date.getMinutes()).slice(-2));
44
}, 500);
45

  
46
$(function() {
47
  $('.mobile-app-content .splash').on('click', function() {
48
    $('.mobile-app-content iframe').attr('src', '/');
49
    $('.mobile-app-content').addClass('splash-off');
50
  });
51
});
52
</script>
53

  
54
{% endblock %}
combo/apps/pwa/templates/combo/pwa/offline.html
1
{% load i18n static %}<!DOCTYPE html>
2
<html>
3
<head>
4
  <meta charset="utf-8"/>
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <style>
7
html, body {
8
  margin: 0; padding: 1rem;
9
  font-family: sans-serif;
10
  background: {{theme_color}};
11
}
12

  
13
div.info-text {
14
  background: white;
15
  padding: 1rem;
16
  border-radius: 3px;
17
}
18

  
19
img {
20
  max-width: 100%;
21
  margin: 0 auto;
22
  display: block;
23
}
24

  
25
p.retry {
26
  margin-top: 2rem;
27
  text-align: center;
28
}
29

  
30
p.retry a {
31
  border: 1px solid {{theme_color}};
32
  text-decoration: none;
33
  background: white;
34
  padding: 0.5rem 1rem;
35
  border-radius: 3px;
36
  color: inherit;
37
}
38

  
39
  </style>
40
</head>
41
<body>
42
<div class="info-text">
43
  <img src="{% static "" %}{{ css_variant }}/{{ icon_prefix }}{{icon_sizes|last}}px.png" alt="">
44
  {{ pwa_settings.offline_text|safe }}
45

  
46
  {% if pwa_settings.offline_retry_button %}
47
  <p class="retry">
48
  <a href=".">{% trans "Retry" %}</a>
49
  </p>
50
  {% endif %}
51
</div>
52
</body>
53
</html>
combo/apps/pwa/templates/combo/service-worker.js
25 25
var config = {
26 26
  version: 'v{% start_timestamp %}',
27 27
  staticCacheItems: [
28
    '/offline/'
28
    '/__pwa__/offline/'
29 29
  ],
30 30
  cachePathPattern: /^\/static\/.*/,
31 31
  handleFetchPathPattern: /.*/,
32
  offlinePage: '/offline/'
32
  offlinePage: '/__pwa__/offline/'
33 33
};
34 34

  
35 35
function cacheName (key, opts) {
combo/apps/pwa/urls.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
from django.conf.urls import url
17
from django.conf.urls import url, include
18 18

  
19
from combo.urls_utils import decorated_includes, manager_required
20

  
21
from .manager_views import (
22
        ManagerHomeView,
23
        )
19 24
from .views import (
20 25
        manifest_json,
21 26
        service_worker_js,
22 27
        service_worker_registration_js,
23 28
        subscribe_push,
29
        offline_page,
24 30
        )
25 31

  
32

  
33
pwa_manager_urls = [
34
    url('^$', ManagerHomeView.as_view(), name='pwa-manager-homepage'),
35
]
36

  
26 37
urlpatterns = [
27 38
    url('^manifest.json$', manifest_json),
28 39
    url('^service-worker.js$', service_worker_js),
29 40
    url('^service-worker-registration.js$', service_worker_registration_js),
30 41
    url('^api/pwa/push/subscribe$', subscribe_push, name='pwa-subscribe-push'),
42
    url('^__pwa__/offline/$', offline_page),
43
    url(r'^manage/pwa/', decorated_includes(manager_required,
44
        include(pwa_manager_urls))),
31 45
]
combo/apps/pwa/views.py
21 21
from django.http import HttpResponse, HttpResponseForbidden, Http404, JsonResponse
22 22
from django.template.loader import get_template, TemplateDoesNotExist
23 23
from django.views.decorators.csrf import csrf_exempt
24
from django.views.generic import TemplateView
24 25

  
25
from .models import PushSubscription
26
from .models import PushSubscription, PwaSettings
26 27

  
27 28

  
28 29
def manifest_json(request, *args, **kwargs):
......
67 68
                subscription_info=subscription_data)
68 69
        subscription.save()
69 70
    return JsonResponse({'err': 0})
71

  
72

  
73
class OfflinePage(TemplateView):
74
    template_name = 'combo/pwa/offline.html'
75

  
76
    def get_context_data(self, **kwargs):
77
        context = super(OfflinePage, self).get_context_data(**kwargs)
78
        context['pwa_settings'] = PwaSettings.singleton()
79
        return context
80

  
81
offline_page = OfflinePage.as_view()
combo/data/utils.py
22 22

  
23 23
from combo.apps.assets.models import Asset
24 24
from combo.apps.maps.models import MapLayer
25
from combo.apps.pwa.models import PwaSettings
25 26
from .models import Page
26 27

  
27 28

  
......
38 39
    '''Dump site objects to JSON-dumpable dictionnary'''
39 40
    return {'pages': Page.export_all_for_json(),
40 41
            'map-layers': MapLayer.export_all_for_json(),
41
            'assets': Asset.export_all_for_json(),}
42
            'assets': Asset.export_all_for_json(),
43
            'pwa': {
44
                'settings': PwaSettings.export_for_json(),
45
            }
46
           }
42 47

  
43 48

  
44 49
def import_site(data, if_empty=False, clean=False):
......
68 73
        MapLayer.objects.all().delete()
69 74
        Asset.objects.all().delete()
70 75
        Page.objects.all().delete()
76
        PwaSettings.objects.all().delete()
71 77

  
72 78
    with transaction.atomic():
73 79
        MapLayer.load_serialized_objects(data.get('map-layers') or [])
......
77 83

  
78 84
    with transaction.atomic():
79 85
        Page.load_serialized_pages(data.get('pages') or [])
86

  
87
    with transaction.atomic():
88
        PwaSettings.load_serialized_settings((data.get('pwa') or {}).get('settings'))
combo/settings.py
23 23
and to disable DEBUG mode in production.
24 24
"""
25 25

  
26
import copy
26 27
import os
27 28
from django.conf import global_settings
28 29
from django.utils.translation import ugettext_lazy as _
......
177 178
    },
178 179
}
179 180

  
181
CKEDITOR_CONFIGS['small'] = copy.copy(CKEDITOR_CONFIGS['default'])
182
CKEDITOR_CONFIGS['small']['height'] = 150
183

  
180 184
HAYSTACK_CONNECTIONS = {
181 185
    'default': {
182 186
        'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
tests/test_import_export.py
14 14

  
15 15
from combo.apps.assets.models import Asset
16 16
from combo.apps.maps.models import MapLayer, Map
17
from combo.apps.pwa.models import PwaSettings
17 18
from combo.data.models import Page, TextCell
18 19
from combo.data.utils import export_site, import_site, MissingGroups
19 20

  
......
198 199

  
199 200
    import_site(data={}, if_empty=True)
200 201
    assert Asset.objects.count() == 2
202

  
203
def test_import_export_pwa_settings(app):
204
    output = get_output_of_command('export_site')
205
    pwa_settings = PwaSettings.singleton()
206
    pwa_settings.offline_text = 'Hello world'
207
    pwa_settings.offline_retry_button = False
208
    pwa_settings.save()
209
    output = get_output_of_command('export_site')
210
    import_site(data={}, clean=True)
211
    assert PwaSettings.objects.all().count() == 0
212

  
213
    import_site(data=json.loads(output))
214
    assert PwaSettings.singleton().offline_retry_button is False
215
    assert PwaSettings.singleton().offline_text == 'Hello world'
tests/test_pwa.py
13 13
from django.test import override_settings
14 14

  
15 15
from combo.apps.notifications.models import Notification
16
from combo.apps.pwa.models import PushSubscription
16
from combo.apps.pwa.models import PushSubscription, PwaSettings
17 17

  
18 18
from .test_manager import login
19 19

  
......
59 59
        notification = Notification.notify(john_doe, 'test', body='hello world')
60 60
        assert webpush.call_count == 1
61 61
        assert webpush.call_args[1]['subscription_info'] == {'sample': 'content'}
62

  
63
def test_no_pwa_manager(app, admin_user):
64
    app = login(app)
65
    resp = app.get('/manage/', status=200)
66
    assert not '/manage/pwa/' in resp.text
67

  
68
def test_pwa_manager(app, admin_user):
69
    app = login(app)
70
    with override_settings(TEMPLATE_VARS={'pwa_display': 'standalone'}):
71
        resp = app.get('/manage/', status=200)
72
        assert '/manage/pwa/' in resp.text
73
        resp = app.get('/manage/pwa/')
74
        resp.form['offline_text'] = 'You are offline.'
75
        assert resp.form['offline_retry_button'].checked
76
        resp.form['offline_retry_button'].checked = False
77
        resp = resp.form.submit().follow()
78
        assert resp.form['offline_text'].value == 'You are offline.'
79
        assert resp.form['offline_retry_button'].checked is False
80

  
81
def test_pwa_offline_page(app):
82
    PwaSettings.objects.all().delete()
83
    resp = app.get('/__pwa__/offline/')
84
    assert 'You are currently offline.' in resp.text
85
    assert 'Retry' in resp.text
86
    pwa_settings = PwaSettings.singleton()
87
    pwa_settings.offline_text = 'You are offline.'
88
    pwa_settings.offline_retry_button = False
89
    pwa_settings.save()
90

  
91
    resp = app.get('/__pwa__/offline/')
92
    assert 'You are offline.' in resp.text
93
    assert 'Retry' not in resp.text
62
-