0001-pwa-allow-absolute-redirection-url-from-internal-pag.patch
combo/apps/pwa/models.py | ||
---|---|---|
13 | 13 |
# GNU Affero General Public License for more details. |
14 | 14 |
# |
15 | 15 |
# You should have received a copy of the GNU Affero General Public License |
16 | 16 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | 17 | |
18 | 18 |
import base64 |
19 | 19 |
import json |
20 | 20 |
import os |
21 |
import urllib.parse |
|
21 | 22 | |
22 | 23 |
from django.conf import settings |
23 | 24 |
from django.contrib.postgres.fields import JSONField |
24 | 25 |
from django.core import serializers |
25 | 26 |
from django.core.files.base import ContentFile |
26 | 27 |
from django.core.files.storage import default_storage |
27 | 28 |
from django.db import models |
28 | 29 |
from django.utils.encoding import force_bytes, force_text |
29 | 30 |
from django.utils.translation import ugettext_lazy as _ |
30 | 31 |
from py_vapid import Vapid |
31 | 32 | |
32 | 33 |
from combo import utils |
33 | 34 |
from combo.data.fields import RichTextField |
35 |
from combo.middleware import get_request |
|
34 | 36 | |
35 | 37 | |
36 | 38 |
class PwaSettings(models.Model): |
37 | 39 |
APPLICATION_ICON_SIZES = ['%sx%s' % (x, x) for x in (48, 96, 192, 256, 512)] |
38 | 40 |
application_name = models.CharField(verbose_name=_('Application Name'), max_length=64, blank=True) |
39 | 41 |
application_icon = models.FileField( |
40 | 42 |
verbose_name=_('Application Icon'), |
41 | 43 |
help_text=_( |
... | ... | |
132 | 134 |
class Meta: |
133 | 135 |
ordering = ('order',) |
134 | 136 | |
135 | 137 |
def get_label(self): |
136 | 138 |
return self.label or self.link_page.title |
137 | 139 | |
138 | 140 |
def get_url(self): |
139 | 141 |
if self.link_page: |
140 |
return self.link_page.get_online_url()
|
|
142 |
url = self.link_page.get_online_url()
|
|
141 | 143 |
else: |
142 |
return utils.get_templated_url(self.url) |
|
144 |
url = utils.get_templated_url(self.url) |
|
145 |
if not urllib.parse.urlparse(url).netloc and get_request(): |
|
146 |
site_base = get_request().build_absolute_uri() |
|
147 |
url = site_base + url.lstrip('/') |
|
148 |
return url |
|
143 | 149 | |
144 | 150 |
def css_class_names(self): |
145 | 151 |
css_class_names = self.extra_css_class or '' |
146 | 152 |
if self.link_page: |
147 | 153 |
css_class_names += ' page-%s' % self.link_page.slug |
148 | 154 |
return css_class_names |
149 | 155 | |
150 | 156 |
@classmethod |
combo/apps/pwa/templates/combo/pwa/navigation.html | ||
---|---|---|
2 | 2 |
<div class="pwa-navigation" id="pwa-navigation" |
3 | 3 |
{% if include_user_name %}data-pwa-user-name="{% skeleton_extra_placeholder user-name %}{{user.get_full_name}}{% end_skeleton_extra_placeholder %}"{% endif %}> |
4 | 4 |
<div> |
5 | 5 |
<ul> |
6 | 6 |
{% for entry in entries %} |
7 | 7 |
<li class="{{ entry.css_class_names }}{% if entry.link_page in page.get_parents_and_self %} selected{% endif %}" data-entry-pk="{{ entry.pk }}" |
8 | 8 |
{% if entry.notification_count %}data-notification-count-url="{{site_base}}/api/notification/count/"{% endif %} |
9 | 9 |
{% if entry.use_user_name_as_label %}data-include-user-name{% endif %}> |
10 |
<a href="{% if entry.link_page_id %}{{ site_base }}{% endif %}{{ entry.get_url }}"
|
|
10 |
<a href="{{ entry.get_url }}" |
|
11 | 11 |
{% if entry.icon %}style="background-image: url({{site_base}}{{entry.icon.url}});"{% endif %} |
12 | 12 |
><span>{{ entry.get_label }}</span></a></li> |
13 | 13 |
{% endfor %} |
14 | 14 |
</ul> |
15 | 15 |
</div> |
16 | 16 |
</div> |
17 | 17 |
<script> |
18 | 18 |
$('li[data-include-user-name]').each(function(idx, elem) { |
... | ... | |
31 | 31 |
crossDomain: true, |
32 | 32 |
success: function(data) { |
33 | 33 |
if (data.new) { |
34 | 34 |
$entry.find('span').append(' <span class="badge">' + data.new + '</span>'); |
35 | 35 |
} |
36 | 36 |
}}); |
37 | 37 |
}); |
38 | 38 |
</script> |
39 |
{% endif %} |
|
39 |
{% endif %} |
tests/test_pwa.py | ||
---|---|---|
1 | 1 |
import base64 |
2 |
from html.parser import HTMLParser |
|
2 | 3 |
from unittest import mock |
3 | 4 | |
4 | 5 |
import pytest |
5 | 6 |
from django.conf import settings |
6 | 7 |
from django.core.files import File |
7 | 8 |
from django.template import Context, Template |
8 | 9 |
from django.test import override_settings |
9 | 10 |
from django.test.client import RequestFactory |
... | ... | |
248 | 249 |
assert nav.count('background-image') == 1 |
249 | 250 |
assert nav.count('data-notification-count-url=') == 1 |
250 | 251 |
assert nav.count('data-pwa-user-name=""') == 1 |
251 | 252 | |
252 | 253 |
nav = t.render(Context({'request': request, 'render_skeleton': True})) |
253 | 254 |
assert 'data-pwa-user-name="{% block placeholder-user-name %}' in nav |
254 | 255 | |
255 | 256 | |
257 |
def test_pwa_navigation_templatetag_internal_redirection(app): |
|
258 |
page1 = Page(title='One', slug='one') |
|
259 |
page2 = Page(title='Two', slug='two', redirect_url='http://www.example.org/test') |
|
260 |
page3 = Page(title='Three', slug='three', redirect_url='../test') |
|
261 |
page4 = Page(title='Four', slug='four', redirect_url='{{test_url}}plop') |
|
262 |
page1.save() |
|
263 |
page2.save() |
|
264 |
page3.save() |
|
265 |
page4.save() |
|
266 |
entry1 = PwaNavigationEntry(link_page=page1, order=1) |
|
267 |
entry2 = PwaNavigationEntry(link_page=page2, order=2) |
|
268 |
entry3 = PwaNavigationEntry(link_page=page3, order=3) |
|
269 |
entry4 = PwaNavigationEntry(link_page=page4, order=4) |
|
270 |
entry1.save() |
|
271 |
entry2.save() |
|
272 |
entry3.save() |
|
273 |
entry4.save() |
|
274 |
t = Template('{% load pwa %}{% pwa_navigation %}') |
|
275 | ||
276 |
with override_settings(TEMPLATE_VARS={'pwa_display': 'standalone'}): |
|
277 |
request = RequestFactory().get('/') |
|
278 |
with mock.patch('combo.apps.pwa.models.get_request', return_value=request): |
|
279 |
nav = t.render(Context({'request': request})) |
|
280 | ||
281 |
class MyHTMLParser(HTMLParser): |
|
282 |
def handle_starttag(self, tag, attrs): |
|
283 |
if tag == 'a': |
|
284 |
results.append(attrs[0][1]) |
|
285 | ||
286 |
parser = MyHTMLParser() |
|
287 |
results = [] |
|
288 |
parser.feed(nav) |
|
289 |
assert results == [ |
|
290 |
'http://testserver/one/', |
|
291 |
'http://www.example.org/test', |
|
292 |
'http://testserver/three/', |
|
293 |
'http://testserver/four/', |
|
294 |
] |
|
295 | ||
296 | ||
256 | 297 |
def test_pwa_application_icon(app, admin_user): |
257 | 298 |
app = login(app) |
258 | 299 |
with override_settings(TEMPLATE_VARS={'pwa_display': 'standalone'}): |
259 | 300 |
PwaSettings.objects.all().delete() |
260 | 301 |
resp = app.get('/manage/pwa/') |
261 | 302 |
resp.form['application_icon'] = Upload( |
262 | 303 |
'test.png', |
263 | 304 |
base64.decodebytes( |
264 |
- |