diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index db2b1630..87e52632 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -1,6 +1,7 @@ {% extends "base.html" %} {% load static %} {% load deadliny %} +{% load mail %} {# Přišlo mi to hezčí, než psát všude if. #} {% block custom_css %} @@ -21,7 +22,14 @@ {% if edit %}

Řešitelé: - {% for r in object.resitele.all %}{{ r }} ({{ r.osoba.email }}){% if forloop.revcounter0 != 0 %}, {% endif %}{% endfor %} + {% for r in object.resitele.all %} + {{ r }} + {# DjangoTemplates neumí spojovat řetězce (https://stackoverflow.com/q/4386168), tak si necháváme vyrobit subject mailu ve view. #} + ({% maillink r.osoba.email to=r.osoba.email subject=predmetmailu %}){% if forloop.revcounter0 != 0 %}, {% endif %} + {% endfor %} +

+

+ {% maillink "Poslat mail všem řešitelům" bcc=maily_vsech_resitelu subject=predmetmailu %}

{% else %}

Řešitelé: {{ object.resitele.all | join:", " }}

diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 746335fc..9f1c45c0 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -241,8 +241,12 @@ class DetailReseniView(DetailView): def get_context_data(self, **kw): self.check_access() ctx = super().get_context_data(**kw) - hodnoceni = self.aktualni_hodnoceni() - ctx["hodnoceni"] = hodnoceni + detaily_hodnoceni = self.aktualni_hodnoceni() + ctx["hodnoceni"] = detaily_hodnoceni + + # Subject případného mailu (template neumí použitelně spojovat řetězce: https://stackoverflow.com/q/4386168) + ctx["predmetmailu"] = "Oprava řešení M&M "+self.reseni.problem.first().hlavni_problem.nazev + ctx["maily_vsech_resitelu"] = [y for x in self.reseni.resitele.all().values_list('osoba__email') for y in x] return ctx def get(self, request, *args, **kwargs): 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..ecbb2a39 --- /dev/null +++ b/various/templatetags/mail.py @@ -0,0 +1,48 @@ +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 mailurl(*, subject=None, body=None, to=[], cc=[], bcc=[]): + """Tag na vytváření správně zakódované mailto: adresy + + Ref: RFC 6068, """ + if isinstance(to, str): + to = [to] + if isinstance(cc, str): + cc = [cc] + if isinstance(bcc, str): + bcc = [bcc] + assert isinstance(to, list) + assert isinstance(cc, list) + assert isinstance(bcc, list) + # FIXME: adresa není správně zakódovaná, rozbije se to na adresách s divnými znaky + parts = [ + f'mailto:{str.join(",", to)}', + ] + if len(to) + len(cc) + len(bcc) < 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(cc) > 0: + parts.append(f'cc={str.join(",", cc)}') + if len(bcc) > 0: + parts.append(f'bcc={str.join(",", bcc)}') + + if len(parts) > 1: + url = parts[0] + '?' + str.join('&', parts[1:]) + else: + url = parts[0] + return url + +@register.simple_tag +def maillink(text, subject=None, body=None, to=[], cc=[], bcc=[], attrs=None): + url = mailurl(subject=subject, body=body, to=to, cc=cc, bcc=bcc) + 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..0abf4e26 100644 --- a/various/tests.py +++ b/various/tests.py @@ -1,3 +1,60 @@ 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, mailurl -# 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_mailurl(self): + self.assertEquals(mailurl(to='some@body.test'), r'mailto:some@body.test') + self.assertEquals(mailurl(to=['some@body.test']), r'mailto:some@body.test') + self.assertEquals(mailurl(to=['alice@test.test', 'bob@jinde.test']), r'mailto:alice@test.test,bob@jinde.test') + self.assertEquals( + mailurl(to='some@body.test', body='Tělo', subject='Předmět'), + r'mailto:some@body.test?subject=P%C5%99edm%C4%9Bt&body=T%C4%9Blo', + ) + self.assertRaises(ValueError, lambda: mailurl()) + + 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', + ) + + mailurltemplate = ( + r'{% load mail %}' + r'{% mailurl to="alice@test.test" subject="Čau Alice" %}' + ) + self.assertEquals(render_template(mailurltemplate), r'mailto:alice@test.test?subject=%C4%8Cau%20Alice')