From 71a85571c13d63490d42e8bee0ed5ca49d67d8c6 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Thu, 5 Jan 2023 04:40:16 +0100 Subject: [PATCH] =?UTF-8?q?P=C5=99id=C3=A1n=C3=AD=20tagu=20{%=20maillink?= =?UTF-8?q?=20%}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vyrábí odkazy, které vedou na poslání mailu. Psal jsem to spíš po paměti, nejsem si jistý, že to takhle je přesně podle příslušného RFC, ale jako PoC dobrý a když to nebude fungovat, tak se implementace opraví. Všimněte si, že to je otestované, takže když někdo opraví testy (=předpis chování), tak je pak snadné z diffu a všeho odvodit úpravu. V Django dokumentaci se píše něco o tom, že by se měl použít spíš `format_html` a `conditional_escape`, ale zatím jsem to víc nezkoumal. Je žádoucí z tagu {% maillink %} odddělit i tag {% mailurl %}, který by vracel samotnou URL. Obojí dává smysl umět (speciálně bastlení odkazů z URL je stejně strašně nepřehledné, takže je lepší to zavřít do {% maillink %} a nikdy nevidět), ale zatím to oddělené není… (Ale jsou na to testy, takže by se mělo aspoň dát poznat, že rozdělení nerozbije chování.) --- various/templatetags/__init__.py | 0 various/templatetags/mail.py | 30 ++++++++++++++++++++++ various/tests.py | 43 +++++++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 various/templatetags/__init__.py create mode 100644 various/templatetags/mail.py diff --git a/various/templatetags/__init__.py b/various/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/various/templatetags/mail.py b/various/templatetags/mail.py new file mode 100644 index 00000000..fe11d218 --- /dev/null +++ b/various/templatetags/mail.py @@ -0,0 +1,30 @@ +from django import template +from django.utils.safestring import mark_safe +from urllib.request import quote as urlencode +register = template.Library() + +@register.simple_tag +def maillink(text: str, subject=None, body=None, to=[], attrs=None): + """TODO: Dokumentace""" + if isinstance(to, str): + to = [to] + assert isinstance(to, list) + parts = [ + f'mailto:{str.join(",", to)}', + ] + if len(to) < 1: + raise ValueError('Cannot mail to empty set of people') + + if subject: + parts.append(f'subject={urlencode(subject)}') + if body: + parts.append(f'body={urlencode(body)}') + + if len(parts) > 1: + url = parts[0] + '?' + str.join('&', parts[1:]) + else: + url = parts[0] + if not attrs: attrs = '' + mezera = ' '*bool(attrs) + full_link = f'{text}' + return mark_safe(full_link) diff --git a/various/tests.py b/various/tests.py index 7ce503c2..7884e618 100644 --- a/various/tests.py +++ b/various/tests.py @@ -1,3 +1,44 @@ from django.test import TestCase +# TODO: Možná vyrobit separátní soubory v tests/… než mít všechny testy v jednom souboru? +from various.templatetags.mail import maillink -# Create your tests here. +class MailTagsTest(TestCase): + """Testuje template tagy ohledně mailů.""" + def test_maillink(self): + # Tohle nedává smysl dělit do víc funkcí, bylo by v nich víc boilerplatu než užitečného kódu. + self.assertEquals(maillink('Hello', to='some@body.test'), r'Hello') + self.assertEquals(maillink('Hello', to=['some@body.test']), r'Hello') + self.assertEquals( + maillink('Hello', to=['alice@test.test', 'bob@jinde.test']), + r'Hello', + ) + self.assertEquals( + maillink('Hello', to='some@body.test', attrs='class="trida" id="id"'), + r'Hello', + ) + # Následující test toho testuje moc zároveň, měly by předcházet dedikované testy… (kašlu na ně :-P) + self.assertEquals( + maillink('Text odkazu', to='prijemce@wtf.test', subject="Předmět", body="Čau"), + r'Text odkazu', + ) + self.assertRaises(ValueError, lambda: maillink('Nemám příjemce')) + self.assertRaises(TypeError, lambda: maillink()) # Nemá text, takže to shodí python + + def test_render_in_template(self): + # Pomocná funkce: vykreslí template do stringu + # Ref: https://stackoverflow.com/a/1690879 + def render_template(template, context=None): + from django.template import Template, Context + context = context or {} + context = Context(context) + return Template(template).render(context) + + template=( + r'{% load mail %}' + # TODO: Vyzkoušet i víc adresátů. (Nepamatuji si z hlavy syntaxi…) + r'{% maillink "Text" to="alice@test.test" subject="Oprava řešení" %}' + ) + self.assertEquals( + render_template(template), + r'Text', + )