manager: display link to booking form on events (#45417)

This commit is contained in:
Lauréline Guérin 2020-09-25 11:00:31 +02:00
parent 66b71a6d84
commit 0a7bdd9882
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
7 changed files with 134 additions and 17 deletions

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import chrono.agendas.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('agendas', '0063_auto_20200928_1445'),
]
operations = [
migrations.AddField(
model_name='agenda',
name='booking_form_url',
field=models.CharField(
blank=True,
max_length=200,
validators=[chrono.agendas.models.django_template_validator],
verbose_name='Booking form URL',
),
),
]

View File

@ -35,6 +35,7 @@ from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models, transaction
from django.db.models import Count, Q, Case, When
from django.template import engines, Context, Template, TemplateSyntaxError, VariableDoesNotExist
from django.urls import reverse
from django.utils import functional
from django.utils.dates import WEEKDAYS
@ -115,6 +116,13 @@ def validate_not_digit(value):
raise ValidationError(_('This value cannot be a number.'))
def django_template_validator(value):
try:
engines['django'].from_string(value)
except TemplateSyntaxError as e:
raise ValidationError(_('syntax error: %s') % e)
class ICSError(Exception):
pass
@ -179,6 +187,9 @@ class Agenda(models.Model):
'Category', verbose_name=_('Category'), blank=True, null=True, on_delete=models.SET_NULL
)
default_view = models.CharField(_('Default view'), max_length=20, choices=AGENDA_VIEWS, default='month')
booking_form_url = models.CharField(
_('Booking form URL'), max_length=200, blank=True, validators=[django_template_validator]
)
class Meta:
ordering = ['label']
@ -297,6 +308,8 @@ class Agenda(models.Model):
if hasattr(self, 'reminder_settings'):
agenda['reminder_settings'] = self.reminder_settings.export_json()
if self.kind == 'events':
agenda['default_view'] = self.default_view
agenda['booking_form_url'] = self.booking_form_url
agenda['events'] = [x.export_json() for x in self.event_set.all()]
if hasattr(self, 'notifications_settings'):
agenda['notifications_settings'] = self.notifications_settings.export_json()
@ -502,6 +515,15 @@ class Agenda(models.Model):
entries = entries.filter(start_datetime__lt=max_start)
return entries
def get_booking_form_url(self):
if not self.booking_form_url:
return
template_vars = Context(settings.TEMPLATE_VARS)
try:
return Template(self.booking_form_url).render(template_vars)
except (VariableDoesNotExist, TemplateSyntaxError):
return
class VirtualMember(models.Model):
'''Trough model to link virtual agendas to their real agendas.

View File

@ -76,6 +76,7 @@ class AgendaEditForm(AgendaAddForm):
'maximal_booking_delay',
'anonymize_delay',
'default_view',
'booking_form_url',
]
def __init__(self, *args, **kwargs):
@ -85,6 +86,7 @@ class AgendaEditForm(AgendaAddForm):
self.fields['maximal_booking_delay'].required = True
if kwargs['instance'].kind != 'events':
del self.fields['default_view']
del self.fields['booking_form_url']
class ResourceAddForm(forms.ModelForm):

View File

@ -44,8 +44,8 @@
</a>
{% if settings_view %}
<a rel="popup" class="delete" href="{% url 'chrono-manager-event-delete' pk=agenda.pk event_pk=event.pk %}?next=settings">{% trans "remove" %}</a>
{% elif not event.cancellation_status %}
<a rel="popup" class="link-action-text cancel" href="{% url 'chrono-manager-event-cancel' pk=agenda.pk event_pk=event.pk %}?next={{ request.path }}">{% trans "Cancel" %}</a>
{% elif agenda.booking_form_url %}
<a class="link-action-text" href="{{ agenda.get_booking_form_url }}?agenda={{ agenda.slug }}&event={{ event.slug }}">{% trans "Booking form" %}</a>
{% endif %}
<span class="occupation-bar"></span>
</li>

View File

@ -35,6 +35,9 @@
{% endif %}
<a href="{% url 'chrono-manager-event-edit' pk=agenda.id event_pk=object.id %}">{% trans "Options" %}</a>
{% endif %}
{% if object.agenda.booking_form_url %}
<a href="{{ object.agenda.get_booking_form_url }}?agenda={{ object.agenda.slug }}&event={{ event.slug }}">{% trans "Booking form" %}</a>
{% endif %}
</span>
{% endblock %}

View File

@ -134,6 +134,29 @@ def test_import_export(app):
shutil.rmtree(tempdir)
def test_import_export_events_agenda_options(app):
agenda = Agenda.objects.create(
label='Foo Bar',
kind='events',
default_view='open_events',
booking_form_url='{{ eservices_url }}backoffice/submission/inscription-aux-activites/',
)
output = get_output_of_command('export_site')
assert len(json.loads(output)['agendas']) == 1
import_site(data={}, clean=True)
with tempfile.NamedTemporaryFile() as f:
f.write(force_bytes(output))
f.flush()
call_command('import_site', f.name)
assert Agenda.objects.count() == 1
agenda = Agenda.objects.first()
assert agenda.default_view == 'open_events'
assert agenda.booking_form_url == '{{ eservices_url }}backoffice/submission/inscription-aux-activites/'
def test_import_export_event_details(app):
agenda = Agenda.objects.create(label='Foo Bar', kind='events')
Event.objects.create(

View File

@ -892,6 +892,7 @@ def test_options_agenda(app, admin_user):
resp.form['label'] = 'Foo baz'
resp.form['anonymize_delay'] = 365
assert 'default_view' in resp.context['form'].fields
assert 'booking_form_url' in resp.context['form'].fields
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda_events.pk)
resp = resp.follow()
@ -903,8 +904,10 @@ def test_options_agenda(app, admin_user):
resp = app.get('/manage/agendas/%s/edit' % agenda_meetings.pk)
assert 'default_view' not in resp.context['form'].fields
assert 'booking_form_url' not in resp.context['form'].fields
resp = app.get('/manage/agendas/%s/edit' % agenda_virtual.pk)
assert 'default_view' not in resp.context['form'].fields
assert 'booking_form_url' not in resp.context['form'].fields
def test_options_agenda_cant_unset_delays(app, admin_user):
@ -4056,11 +4059,10 @@ def test_event_cancellation(app, admin_user):
event = Event.objects.create(
label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda
)
day = event.start_datetime
login(app)
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
assert '0/10 bookings' in resp.text
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert 'Bookings (0/10)' in resp.text
resp = resp.click('Cancel', href='/cancel')
assert 'related bookings' not in resp.text
@ -4068,15 +4070,15 @@ def test_event_cancellation(app, admin_user):
Booking.objects.create(event=event)
Booking.objects.create(event=event)
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
assert '2/10 bookings' in resp.text
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert 'Bookings (2/10)' in resp.text
resp = resp.click('Cancel', href='/cancel')
resp = resp.click('Cancel', href='manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk))
assert '2 related bookings will also be cancelled.' in resp.text
resp = resp.form.submit().follow()
assert 'Cancelled' in resp.text
assert '0/10 bookings' in resp.text
assert 'Bookings (0/10)' in resp.text
assert Booking.objects.filter(event=event, cancellation_datetime__isnull=False).count() == 2
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert '/manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk) not in resp.text
@ -4100,8 +4102,7 @@ def test_event_cancellation_error_report(app, admin_user):
resp = resp.click('Cancellation error reports')
assert 'No error report' in resp.text
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
resp = resp.click('Cancel', href='/cancel')
resp = app.get('/manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk))
resp = resp.form.submit().follow()
assert 'Cancellation in progress' in resp.text
@ -4144,15 +4145,56 @@ def test_event_cancellation_forbidden(app, admin_user):
event = Event.objects.create(
label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda
)
booking = Booking.objects.create(event=event)
booking2 = Booking.objects.create(event=event, backoffice_url='http://example.org/backoffice/xx/')
Booking.objects.create(event=event)
Booking.objects.create(event=event, backoffice_url='http://example.org/backoffice/xx/')
login(app)
resp = app.get('/manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk))
assert 'event has bookings with no callback url configured' in resp.text
assert 'Proceed with cancellation' not in resp.text
def test_event_booking_form_url(settings, app, admin_user):
settings.TEMPLATE_VARS = {'eservices_url': 'http://demarches/'}
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda
)
day = event.start_datetime
login(app)
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
resp = resp.click('Cancel', href='/cancel')
assert 'event has bookings with no callback url configured' in resp.text
assert 'Proceed with cancellation' not in resp.text
assert agenda.get_booking_form_url() is None
resp = app.get('/manage/agendas/%d/%d/%d/' % (agenda.pk, day.year, day.month))
assert 'Booking form' not in resp.text
resp = app.get('/manage/agendas/%d/events/open/' % agenda.pk)
assert 'Booking form' not in resp.text
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert 'Booking form' not in resp.text
agenda.booking_form_url = '{{ eservices_url }}backoffice/submission/inscription-aux-activites/'
agenda.save()
assert (
agenda.get_booking_form_url() == 'http://demarches/backoffice/submission/inscription-aux-activites/'
)
resp = app.get('/manage/agendas/%d/%d/%d/' % (agenda.pk, day.year, day.month))
assert (
'<a class="link-action-text" href="http://demarches/backoffice/submission/inscription-aux-activites/?agenda=%s&event=%s">Booking form</a>'
% (agenda.slug, event.slug)
in resp.text
)
resp = app.get('/manage/agendas/%d/events/open/' % agenda.pk)
assert (
'<a class="link-action-text" href="http://demarches/backoffice/submission/inscription-aux-activites/?agenda=%s&event=%s">Booking form</a>'
% (agenda.slug, event.slug)
in resp.text
)
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
assert (
'<a href="http://demarches/backoffice/submission/inscription-aux-activites/?agenda=%s&event=%s">Booking form</a>'
% (agenda.slug, event.slug)
in resp.text
)
def test_agenda_notifications(app, admin_user, managers_group):