reminders: allow template syntax in message extra info (#61234)
This commit is contained in:
parent
fc34aab1a8
commit
064c9a4ea3
|
@ -23,6 +23,7 @@ from django.core.mail import send_mail
|
|||
from django.core.management.base import BaseCommand
|
||||
from django.db.models import F
|
||||
from django.db.transaction import atomic
|
||||
from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import timezone, translation
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
@ -88,11 +89,17 @@ class Command(BaseCommand):
|
|||
'booking': booking,
|
||||
'in_x_days': _('tomorrow') if days == 1 else _('in %s days') % days,
|
||||
'date': booking.event.start_datetime,
|
||||
'email_extra_info': agenda.reminder_settings.email_extra_info,
|
||||
'sms_extra_info': agenda.reminder_settings.sms_extra_info,
|
||||
}
|
||||
ctx.update(settings.TEMPLATE_VARS)
|
||||
|
||||
for extra_info in ('email_extra_info', 'sms_extra_info'):
|
||||
try:
|
||||
ctx[extra_info] = Template(getattr(agenda.reminder_settings, extra_info)).render(
|
||||
Context({'booking': booking}, autoescape=False)
|
||||
)
|
||||
except (VariableDoesNotExist, TemplateSyntaxError):
|
||||
pass
|
||||
|
||||
if msg_type == 'email':
|
||||
emails = set(booking.extra_emails)
|
||||
if booking.user_email:
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
import chrono
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
|
@ -41,7 +43,14 @@ class Migration(migrations.Migration):
|
|||
'email_extra_info',
|
||||
models.TextField(
|
||||
blank=True,
|
||||
help_text='Basic information such as event name, time and date are already included.',
|
||||
validators=[chrono.agendas.models.booking_template_validator],
|
||||
help_text=(
|
||||
'Basic information such as event name, time and date are already included. '
|
||||
'Booking object can be accessed using standard template syntax. '
|
||||
'This allows to access agenda name via {{ booking.event.agenda.label }}, '
|
||||
'meeting type name via {{ booking.event.meeting_type.label }}, or any extra '
|
||||
'parameter passed on booking creation via {{ booking.extra_data.xxx }}.'
|
||||
),
|
||||
verbose_name='Additional text to include in emails',
|
||||
),
|
||||
),
|
||||
|
@ -50,7 +59,14 @@ class Migration(migrations.Migration):
|
|||
'sms_extra_info',
|
||||
models.TextField(
|
||||
blank=True,
|
||||
help_text='Basic information such as event name, time and date are already included.',
|
||||
validators=[chrono.agendas.models.booking_template_validator],
|
||||
help_text=(
|
||||
'Basic information such as event name, time and date are already included. '
|
||||
'Booking object can be accessed using standard template syntax. '
|
||||
'This allows to access agenda name via {{ booking.event.agenda.label }}, '
|
||||
'meeting type name via {{ booking.event.meeting_type.label }}, or any extra '
|
||||
'parameter passed on booking creation via {{ booking.extra_data.xxx }}.'
|
||||
),
|
||||
verbose_name='Additional text to include in SMS',
|
||||
),
|
||||
),
|
||||
|
|
|
@ -167,6 +167,23 @@ def event_template_validator(value):
|
|||
raise ValidationError(_('syntax error: %s') % e)
|
||||
|
||||
|
||||
def booking_template_validator(value):
|
||||
example_event = Event(
|
||||
start_datetime=now(),
|
||||
publication_datetime=now(),
|
||||
recurrence_end_date=now().date(),
|
||||
places=1,
|
||||
duration=1,
|
||||
)
|
||||
example_booking = Booking(event=example_event)
|
||||
try:
|
||||
Template(value).render(Context({'booking': example_booking}))
|
||||
except TemplateSyntaxError as e:
|
||||
raise ValidationError(_('syntax error: %s') % e)
|
||||
except VariableDoesNotExist:
|
||||
pass
|
||||
|
||||
|
||||
class ICSError(Exception):
|
||||
pass
|
||||
|
||||
|
@ -2866,7 +2883,14 @@ class AgendaReminderSettings(models.Model):
|
|||
email_extra_info = models.TextField(
|
||||
blank=True,
|
||||
verbose_name=_('Additional text to include in emails'),
|
||||
help_text=_('Basic information such as event name, time and date are already included.'),
|
||||
validators=[booking_template_validator],
|
||||
help_text=_(
|
||||
'Basic information such as event name, time and date are already included. '
|
||||
'Booking object can be accessed using standard template syntax. '
|
||||
'This allows to access agenda name via {{ booking.event.agenda.label }}, '
|
||||
'meeting type name via {{ booking.event.meeting_type.label }}, or any extra '
|
||||
'parameter passed on booking creation via {{ booking.extra_data.xxx }}.'
|
||||
),
|
||||
)
|
||||
days_before_sms = models.IntegerField(
|
||||
null=True,
|
||||
|
@ -2881,7 +2905,8 @@ class AgendaReminderSettings(models.Model):
|
|||
sms_extra_info = models.TextField(
|
||||
blank=True,
|
||||
verbose_name=_('Additional text to include in SMS'),
|
||||
help_text=_('Basic information such as event name, time and date are already included.'),
|
||||
validators=[booking_template_validator],
|
||||
help_text=email_extra_info.help_text,
|
||||
)
|
||||
|
||||
def display_info(self):
|
||||
|
|
|
@ -11,7 +11,7 @@ You have a booking for event "{{ event }}", on {{ date }} at {{ time }}.
|
|||
</p>
|
||||
|
||||
{% if email_extra_info %}
|
||||
<p>{{ email_extra_info }}</p>
|
||||
<p>{{ email_extra_info|force_escape|linebreaks }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if booking.event.description %}
|
||||
|
|
|
@ -17,7 +17,7 @@ Your meeting "{{ meeting }}" is scheduled on {{ date }} at {{ time }}.
|
|||
</p>
|
||||
|
||||
{% if email_extra_info %}
|
||||
<p>{{ email_extra_info }}</p>
|
||||
<p>{{ email_extra_info|force_escape|linebreaks }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if booking.form_url %}
|
||||
|
|
|
@ -2553,6 +2553,32 @@ def test_manager_reminders(app, admin_user):
|
|||
assert not 'Booking reminders' in resp.text
|
||||
|
||||
|
||||
@override_settings(SMS_URL='https://passerelle.test.org/sms/send/', SMS_SENDER='EO', TIME_ZONE='UTC')
|
||||
@pytest.mark.parametrize('extra_info_field', ('sms_extra_info', 'email_extra_info'))
|
||||
def test_manager_reminders_templated_extra_info(app, admin_user, extra_info_field):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events')
|
||||
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
||||
|
||||
login(app)
|
||||
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
|
||||
resp = resp.click('Configure', href='reminder')
|
||||
|
||||
extra_info = 'test {{ booking.extra_data.xxx }} {{ booking.event.label|default:booking.extra_data.yyy }}'
|
||||
resp.form[extra_info_field] = extra_info
|
||||
resp = resp.form.submit().follow()
|
||||
assert getattr(agenda.reminder_settings, extra_info_field) == extra_info
|
||||
|
||||
invalid_templates = [
|
||||
'{{ syntax error }}',
|
||||
'{{ booking.label|invalidfilter }}',
|
||||
]
|
||||
for template in invalid_templates:
|
||||
resp = app.get('/manage/agendas/%s/reminder' % agenda.id)
|
||||
resp.form[extra_info_field] = template
|
||||
resp = resp.form.submit()
|
||||
assert 'syntax error' in resp.text
|
||||
|
||||
|
||||
def test_manager_reminders_preview(app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events')
|
||||
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
||||
|
@ -2591,6 +2617,19 @@ def test_manager_reminders_preview(app, admin_user):
|
|||
in resp.text
|
||||
)
|
||||
|
||||
# templates in extra info should not be interpreted
|
||||
agenda.reminder_settings.sms_extra_info = '{{ booking.extra_data.xxx }}'
|
||||
agenda.reminder_settings.email_extra_info = '{{ booking.extra_data.xxx }}'
|
||||
agenda.reminder_settings.save()
|
||||
|
||||
resp = resp.click('Return to settings')
|
||||
resp = resp.click('Preview SMS')
|
||||
assert '{{ booking.extra_data.xxx }}' in resp.text
|
||||
|
||||
resp = resp.click('Return to settings')
|
||||
resp = resp.click('Preview email')
|
||||
assert '{{ booking.extra_data.xxx }}' in resp.text
|
||||
|
||||
|
||||
def test_manager_agenda_roles(app, admin_user, manager_user):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events')
|
||||
|
|
|
@ -1930,6 +1930,60 @@ def test_agenda_reminders_sms_content(freezer):
|
|||
)
|
||||
|
||||
|
||||
@override_settings(SMS_URL='https://passerelle.test.org/sms/send/', SMS_SENDER='EO', TIME_ZONE='UTC')
|
||||
def test_agenda_reminders_templated_content(mailoutbox, freezer):
|
||||
freezer.move_to('2020-01-01 14:00')
|
||||
agenda = Agenda.objects.create(label='Main Center', kind='events')
|
||||
AgendaReminderSettings.objects.create(
|
||||
agenda=agenda,
|
||||
days_before_email=1,
|
||||
days_before_sms=1,
|
||||
email_extra_info='Go to {{ booking.event.agenda.label }}.\nTake your {{ booking.extra_data.document_type }}.',
|
||||
sms_extra_info='Take your {{ booking.extra_data.document_type }}.',
|
||||
)
|
||||
start_datetime = now() + datetime.timedelta(days=2)
|
||||
event = Event.objects.create(agenda=agenda, start_datetime=start_datetime, places=10, label='Pool party')
|
||||
|
||||
Booking.objects.create(
|
||||
event=event,
|
||||
user_email='t@test.org',
|
||||
user_phone_number='+336123456789',
|
||||
extra_data={'document_type': '"receipt"'},
|
||||
)
|
||||
|
||||
freezer.move_to('2020-01-02 15:00')
|
||||
with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
|
||||
mock_response = mock.Mock(status_code=200)
|
||||
mock_send.return_value = mock_response
|
||||
call_command('send_booking_reminders')
|
||||
|
||||
mail = mailoutbox[0]
|
||||
assert 'Go to Main Center.\nTake your "receipt".' in mail.body
|
||||
assert '<p>Go to Main Center.<br>Take your "receipt".</p>' in mail.alternatives[0][0]
|
||||
|
||||
body = json.loads(mock_send.call_args[0][0].body.decode())
|
||||
assert 'Take your "receipt".' in body['message']
|
||||
|
||||
# in case of invalid template, send anyway
|
||||
freezer.move_to('2020-01-01 14:00')
|
||||
Booking.objects.create(event=event, user_email='t@test.org', user_phone_number='+336123456789')
|
||||
agenda.reminder_settings.email_extra_info = 'Take your {{ syntax error }}'
|
||||
agenda.reminder_settings.sms_extra_info = 'Take your {{ syntax error }}'
|
||||
agenda.reminder_settings.save()
|
||||
|
||||
freezer.move_to('2020-01-02 15:00')
|
||||
with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
|
||||
mock_response = mock.Mock(status_code=200)
|
||||
mock_send.return_value = mock_response
|
||||
call_command('send_booking_reminders')
|
||||
|
||||
assert len(mailoutbox) == 2
|
||||
assert 'Take your' not in mailoutbox[1].body
|
||||
|
||||
body = json.loads(mock_send.call_args[0][0].body.decode())
|
||||
assert 'Take your' not in body['message']
|
||||
|
||||
|
||||
@override_settings(TIME_ZONE='UTC')
|
||||
def test_agenda_reminders_meetings(mailoutbox, freezer):
|
||||
freezer.move_to('2020-01-01 11:00')
|
||||
|
|
Loading…
Reference in New Issue