Tag pro odkazy na poslání mailu #22

Merged
ledoian merged 7 commits from maillink into master 2023-02-06 22:02:00 +01:00
5 changed files with 121 additions and 4 deletions

View file

@ -1,6 +1,7 @@
{% extends "base.html" %}
{% load static %}
{% load deadliny %}
{% load mail %}
{% block content %}
@ -14,7 +15,14 @@
{% if edit %}
<p>Řešitelé:
{% for r in object.resitele.all %}{{ r }} (<a href="mailto:{{ r.osoba.email }}?subject={{ "Oprava řešení M&M " | urlencode }}{{ object.problem.all.0.hlavni_problem | urlencode }}">{{ r.osoba.email }}</a>){% 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. #}
zelvuska marked this conversation as resolved Outdated

Přemýšlím, jestli to nechce komentář, proč máme mailsubject generovaný ve view, když je to prakticky čistě prezentační záležitost… Názory?

Přemýšlím, jestli to nechce komentář, proč máme `mailsubject` generovaný ve view, když je to prakticky čistě prezentační záležitost… Názory?

A možná v template komentář, kde se vzal mailsubject / co to je?

A možná v template komentář, kde se vzal mailsubject / co to je?

Template IMHO spíš komentář nepotřebuje. Je to evidentně subject mailu, a věci se typicky berou v kontextu (a v development toolbaru by měl být normálně vidět). Ta divná věc je to, že ho bastlíme ve view…

Template IMHO spíš komentář nepotřebuje. Je to evidentně subject mailu, a věci se typicky berou v kontextu (a v development toolbaru by měl být normálně vidět). Ta divná věc je to, že ho bastlíme ve view…

Ale možná by se mohl jmenovat česky, není důvod to tak neudělat (kromě toho, že jsem to nejspíš kódil v tramvaji a tam kódím většinou anglicky :-D)

Ale možná by se mohl jmenovat česky, není důvod to tak neudělat (kromě toho, že jsem to nejspíš kódil v tramvaji a tam kódím většinou anglicky :-D)
({% maillink r.osoba.email to=r.osoba.email subject=predmetmailu %}){% if forloop.revcounter0 != 0 %}, {% endif %}
{% endfor %}
</p>
<p>
{% maillink "Poslat mail všem řešitelům" bcc=maily_vsech_resitelu subject=predmetmailu %}
</p>
{% else %}
<p>Řešitelé: {{ object.resitele.all | join:", " }}</p>

View file

@ -235,8 +235,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):

View file

View file

@ -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, <https://en.wikipedia.org/wiki/Mailto>"""
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'<a href="{url}"{mezera}{attrs}>{text}</a>'
return mark_safe(full_link)

View file

@ -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'<a href="mailto:some@body.test">Hello</a>')
self.assertEquals(maillink('Hello', to=['some@body.test']), r'<a href="mailto:some@body.test">Hello</a>')
self.assertEquals(
maillink('Hello', to=['alice@test.test', 'bob@jinde.test']),
r'<a href="mailto:alice@test.test,bob@jinde.test">Hello</a>',
)
self.assertEquals(
maillink('Hello', to='some@body.test', attrs='class="trida" id="id"'),
r'<a href="mailto:some@body.test" class="trida" id="id">Hello</a>',
)
# 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'<a href="mailto:prijemce@wtf.test?subject=P%C5%99edm%C4%9Bt&body=%C4%8Cau">Text odkazu</a>',
)
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')
zelvuska marked this conversation as resolved Outdated

Není to tohle:
from django.template.loader import render_to_string

https://docs.djangoproject.com/en/3.2/topics/templates/#django.template.loader.render_to_string

Není to tohle: from django.template.loader import render_to_string https://docs.djangoproject.com/en/3.2/topics/templates/#django.template.loader.render_to_string

Aha, to bere soubor…

Aha, to bere soubor…
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'<a href="mailto:alice@test.test?subject=Oprava%20%C5%99e%C5%A1en%C3%AD">Text</a>',
)
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')