0001-manager-user_block-used-also-in-event-detail-page-63.patch
chrono/agendas/migrations/0086_booking_user_block_template.py | ||
---|---|---|
15 | 15 |
name='booking_user_block_template', |
16 | 16 |
field=models.TextField( |
17 | 17 |
blank=True, |
18 |
help_text='Displayed for each booking in event check page', |
|
19 | 18 |
verbose_name='User block template', |
20 | 19 |
validators=[chrono.agendas.models.django_template_validator], |
21 | 20 |
), |
chrono/agendas/models.py | ||
---|---|---|
45 | 45 |
from django.utils.encoding import force_text |
46 | 46 |
from django.utils.formats import date_format |
47 | 47 |
from django.utils.functional import cached_property |
48 |
from django.utils.html import escape |
|
48 | 49 |
from django.utils.module_loading import import_string |
49 | 50 |
from django.utils.safestring import mark_safe |
50 | 51 |
from django.utils.text import slugify |
... | ... | |
226 | 227 |
) |
227 | 228 |
booking_user_block_template = models.TextField( |
228 | 229 |
_('User block template'), |
229 |
help_text=_('Displayed for each booking in event check page'), |
|
230 | 230 |
blank=True, |
231 | 231 |
validators=[django_template_validator], |
232 | 232 |
) |
... | ... | |
2030 | 2030 |
self.event.set_is_checked() |
2031 | 2031 | |
2032 | 2032 |
def get_user_block(self): |
2033 |
template_vars = Context(settings.TEMPLATE_VARS) |
|
2033 |
template_vars = Context(settings.TEMPLATE_VARS, autoescape=False)
|
|
2034 | 2034 |
template_vars.update( |
2035 | 2035 |
{ |
2036 | 2036 |
'booking': self, |
2037 | 2037 |
} |
2038 | 2038 |
) |
2039 | 2039 |
try: |
2040 |
return Template(self.event.agenda.get_booking_user_block_template()).render(template_vars)
|
|
2040 |
return escape(Template(self.event.agenda.get_booking_user_block_template()).render(template_vars))
|
|
2041 | 2041 |
except (VariableDoesNotExist, TemplateSyntaxError): |
2042 | 2042 |
return |
2043 | 2043 | |
... | ... | |
3181 | 3181 |
return _('Subscription') |
3182 | 3182 | |
3183 | 3183 |
def get_user_block(self): |
3184 |
template_vars = Context(settings.TEMPLATE_VARS) |
|
3184 |
template_vars = Context(settings.TEMPLATE_VARS, autoescape=False)
|
|
3185 | 3185 |
template_vars.update( |
3186 | 3186 |
{ |
3187 | 3187 |
'booking': self, |
3188 | 3188 |
} |
3189 | 3189 |
) |
3190 | 3190 |
try: |
3191 |
return Template(self.agenda.get_booking_user_block_template()).render(template_vars)
|
|
3191 |
return escape(Template(self.agenda.get_booking_user_block_template()).render(template_vars))
|
|
3192 | 3192 |
except (VariableDoesNotExist, TemplateSyntaxError): |
3193 | 3193 |
return |
3194 | 3194 |
chrono/manager/forms.py | ||
---|---|---|
133 | 133 |
'anonymize_delay', |
134 | 134 |
'default_view', |
135 | 135 |
'booking_form_url', |
136 |
'event_display_template', |
|
137 | 136 |
'events_type', |
138 | 137 |
] |
139 | 138 | |
... | ... | |
141 | 140 |
super().__init__(*args, **kwargs) |
142 | 141 |
if kwargs['instance'].kind != 'events': |
143 | 142 |
del self.fields['booking_form_url'] |
144 |
del self.fields['event_display_template'] |
|
145 | 143 |
del self.fields['events_type'] |
146 | 144 |
self.fields['default_view'].choices = [ |
147 | 145 |
(k, v) for k, v in self.fields['default_view'].choices if k != 'open_events' |
... | ... | |
1305 | 1303 |
fields = [] |
1306 | 1304 | |
1307 | 1305 | |
1306 |
class AgendaDisplaySettingsForm(forms.ModelForm): |
|
1307 |
class Meta: |
|
1308 |
model = Agenda |
|
1309 |
fields = [ |
|
1310 |
'event_display_template', |
|
1311 |
'booking_user_block_template', |
|
1312 |
] |
|
1313 |
widgets = {'booking_user_block_template': forms.Textarea(attrs={'rows': 3})} |
|
1314 | ||
1315 |
def __init__(self, *args, **kwargs): |
|
1316 |
super().__init__(*args, **kwargs) |
|
1317 |
self.fields['booking_user_block_template'].help_text = ( |
|
1318 |
_('Displayed for each booking in event page and check page'), |
|
1319 |
) |
|
1320 | ||
1321 | ||
1308 | 1322 |
class AgendaBookingCheckSettingsForm(forms.ModelForm): |
1309 | 1323 |
class Meta: |
1310 | 1324 |
model = Agenda |
1311 | 1325 |
fields = [ |
1312 | 1326 |
'check_type_group', |
1313 | 1327 |
'booking_check_filters', |
1314 |
'booking_user_block_template', |
|
1315 | 1328 |
'mark_event_checked_auto', |
1316 | 1329 |
'disable_check_update', |
1317 | 1330 |
] |
1318 |
widgets = {'booking_user_block_template': forms.Textarea(attrs={'rows': 3})} |
|
1319 | 1331 | |
1320 | 1332 |
def __init__(self, *args, **kwargs): |
1321 | 1333 |
super().__init__(*args, **kwargs) |
chrono/manager/templates/chrono/manager_event_detail_fragment.html | ||
---|---|---|
29 | 29 |
<ul class="objects-list single-links"> |
30 | 30 |
{% for booking in booked %} |
31 | 31 |
<li> |
32 |
<a {% if booking.get_backoffice_url %}href="{{ booking.get_backoffice_url }}"{% endif %}>{{ booking.events_display }}</a>
|
|
32 |
<a {% if booking.get_backoffice_url %}href="{{ booking.get_backoffice_url }}"{% endif %}>{{ booking.get_user_block }}, {{ booking.creation_datetime|date:"DATETIME_FORMAT" }}</a>
|
|
33 | 33 |
{% if not booking.primary_booking %} |
34 | 34 |
<a rel="popup" class="delete" href="{% url 'chrono-manager-booking-cancel' pk=agenda.id booking_pk=booking.id %}?next={{ request.path }}">{% trans "Cancel" %}</a> |
35 | 35 |
{% else %} |
... | ... | |
53 | 53 |
<div> |
54 | 54 |
<ul class="objects-list single-links"> |
55 | 55 |
{% for booking in waiting %} |
56 |
<li><a {% if booking.get_backoffice_url %}href="{{ booking.get_backoffice_url }}"{% endif %}>{{ booking.events_display }}</a></li>
|
|
56 |
<li><a {% if booking.get_backoffice_url %}href="{{ booking.get_backoffice_url }}"{% endif %}>{{ booking.get_user_block }}, {{ booking.creation_datetime|date:"DATETIME_FORMAT" }}</a></li>
|
|
57 | 57 |
{% endfor %} |
58 | 58 |
</ul> |
59 | 59 |
</div> |
chrono/manager/templates/chrono/manager_events_agenda_settings.html | ||
---|---|---|
33 | 33 |
</div> |
34 | 34 |
</div> |
35 | 35 | |
36 |
<div class="section"> |
|
37 |
<h3>{% trans "Display options" %} |
|
38 |
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-display-settings' pk=object.pk %}">{% trans 'Configure' %}</a> |
|
39 |
</h3> |
|
40 |
<div> |
|
41 |
<ul> |
|
42 |
<li> |
|
43 |
{% if agenda.event_display_template %} |
|
44 |
{% trans "Event display template:" %} |
|
45 |
<pre>{{ agenda.event_display_template }}</pre> |
|
46 |
{% else %} |
|
47 |
{% trans "No event display template configured for this agenda." %} |
|
48 |
{% endif %} |
|
49 |
</li> |
|
50 |
<li> |
|
51 |
{% trans "Booking display template:" %} |
|
52 |
<pre>{{ agenda.get_booking_user_block_template }}</pre> |
|
53 |
</li> |
|
54 |
</ul> |
|
55 |
</div> |
|
56 |
</div> |
|
57 | ||
36 | 58 |
<div class="section"> |
37 | 59 |
<h3>{% trans "Booking check options" %} |
38 | 60 |
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-booking-check-settings' pk=object.pk %}">{% trans 'Configure' %}</a> |
... | ... | |
82 | 104 |
{% endif %} |
83 | 105 |
{% endwith %} |
84 | 106 | |
85 |
<li> |
|
86 |
{% trans "User block template:" %} |
|
87 |
<pre>{{ agenda.get_booking_user_block_template }}</pre> |
|
88 |
</li> |
|
89 | ||
90 | 107 |
<li>{% trans "Automatically mark event as checked when all bookings have been checked:" %} {{ agenda.mark_event_checked_auto|yesno }}</li> |
91 | 108 |
<li>{% trans "Prevent the check of bookings when event was marked as checked:" %} {{ agenda.disable_check_update|yesno }}</li> |
92 | 109 |
</ul> |
chrono/manager/urls.py | ||
---|---|---|
166 | 166 |
name='chrono-manager-agenda-booking-delays', |
167 | 167 |
), |
168 | 168 |
url(r'^agendas/(?P<pk>\d+)/roles$', views.agenda_roles, name='chrono-manager-agenda-roles'), |
169 |
url( |
|
170 |
r'^agendas/(?P<pk>\d+)/display-options$', |
|
171 |
views.agenda_display_settings, |
|
172 |
name='chrono-manager-agenda-display-settings', |
|
173 |
), |
|
169 | 174 |
url( |
170 | 175 |
r'^agendas/(?P<pk>\d+)/check-options$', |
171 | 176 |
views.agenda_booking_check_settings, |
chrono/manager/views.py | ||
---|---|---|
94 | 94 |
AgendaAddForm, |
95 | 95 |
AgendaBookingCheckSettingsForm, |
96 | 96 |
AgendaBookingDelaysForm, |
97 |
AgendaDisplaySettingsForm, |
|
97 | 98 |
AgendaDuplicateForm, |
98 | 99 |
AgendaEditForm, |
99 | 100 |
AgendaNotificationsForm, |
... | ... | |
1241 | 1242 |
agenda_roles = AgendaRolesView.as_view() |
1242 | 1243 | |
1243 | 1244 | |
1245 |
class AgendaDisplaySettingsView(AgendaEditView): |
|
1246 |
form_class = AgendaDisplaySettingsForm |
|
1247 |
title = _("Configure display options") |
|
1248 | ||
1249 |
def set_agenda(self, **kwargs): |
|
1250 |
self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='events') |
|
1251 | ||
1252 |
def get_initial(self): |
|
1253 |
return {'booking_user_block_template': self.agenda.get_booking_user_block_template()} |
|
1254 | ||
1255 | ||
1256 |
agenda_display_settings = AgendaDisplaySettingsView.as_view() |
|
1257 | ||
1258 | ||
1244 | 1259 |
class AgendaBookingCheckSettingsView(AgendaEditView): |
1245 | 1260 |
form_class = AgendaBookingCheckSettingsForm |
1246 | 1261 |
title = _("Configure booking check options") |
tests/manager/test_all.py | ||
---|---|---|
387 | 387 |
assert resp.context['form'].initial['default_view'] == 'month' |
388 | 388 |
assert 'open_events' in [k for k, v in resp.context['form'].fields['default_view'].choices] |
389 | 389 |
assert 'booking_form_url' in resp.context['form'].fields |
390 |
assert 'event_display_template' in resp.context['form'].fields |
|
391 | 390 |
resp = resp.form.submit() |
392 | 391 |
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda_events.pk) |
393 | 392 |
resp = resp.follow() |
... | ... | |
401 | 400 |
assert 'default_view' in resp.context['form'].fields |
402 | 401 |
assert 'open_events' not in [k for k, v in resp.context['form'].fields['default_view'].choices] |
403 | 402 |
assert 'booking_form_url' not in resp.context['form'].fields |
404 |
assert 'event_display_template' not in resp.context['form'].fields |
|
405 | 403 | |
406 | 404 |
resp.form['default_view'] = 'month' |
407 | 405 |
resp.form.submit() |
... | ... | |
416 | 414 |
assert 'default_view' in resp.context['form'].fields |
417 | 415 |
assert 'open_events' not in [k for k, v in resp.context['form'].fields['default_view'].choices] |
418 | 416 |
assert 'booking_form_url' not in resp.context['form'].fields |
419 |
assert 'event_display_template' not in resp.context['form'].fields |
|
420 | 417 | |
421 | 418 | |
422 | 419 |
def test_options_events_agenda_events_type(app, admin_user): |
... | ... | |
529 | 526 |
assert agenda.maximal_booking_delay is None |
530 | 527 | |
531 | 528 | |
532 |
def test_options_agenda_booking_check_options(app, admin_user):
|
|
529 |
def test_options_agenda_booking_display_options(app, admin_user):
|
|
533 | 530 |
agenda = Agenda.objects.create(label='Foo bar', kind='events') |
534 | 531 | |
532 |
app = login(app) |
|
533 | ||
535 | 534 |
# check user template |
536 | 535 |
assert agenda.booking_user_block_template == '' |
537 | 536 |
assert ( |
... | ... | |
539 | 538 |
== '{{ booking.user_name|default:booking.label|default:"Anonymous" }}' |
540 | 539 |
) |
541 | 540 | |
542 |
app = login(app) |
|
543 |
url = '/manage/agendas/%s/check-options' % agenda.pk |
|
541 |
url = '/manage/agendas/%s/display-options' % agenda.pk |
|
544 | 542 |
resp = app.get(url) |
545 | 543 |
resp.form['booking_user_block_template'] = '{{ booking.user_name }} Foo Bar' |
546 | 544 |
resp = resp.form.submit() |
... | ... | |
558 | 556 |
== '{{ booking.user_name|default:booking.label|default:"Anonymous" }}' |
559 | 557 |
) |
560 | 558 | |
559 |
resp = app.get(url) |
|
560 |
valid_template = '{{ event.label|default:event.slug }} - {{ event.remaining_places|add:"5" }} / {{ event.start_datetime|date }} - {{ event.agenda.name }}' |
|
561 |
resp.form['event_display_template'] = valid_template |
|
562 |
resp = resp.form.submit().follow() |
|
563 | ||
564 |
agenda.refresh_from_db() |
|
565 |
assert agenda.event_display_template == valid_template |
|
566 | ||
567 |
invalid_templates = [ |
|
568 |
'{{ syntax error }}', |
|
569 |
'{{ event.label|invalidfilter }}', |
|
570 |
'{{ event.label|default:notexist }}', |
|
571 |
] |
|
572 |
for template in invalid_templates: |
|
573 |
resp = app.get(url) |
|
574 |
resp.form['event_display_template'] = template |
|
575 |
resp = resp.form.submit() |
|
576 |
assert 'syntax error' in resp.text |
|
577 | ||
578 |
# check kind |
|
579 |
agenda.kind = 'meetings' |
|
580 |
agenda.save() |
|
581 |
app.get(url, status=404) |
|
582 |
agenda.kind = 'virtual' |
|
583 |
agenda.save() |
|
584 |
app.get(url, status=404) |
|
585 | ||
586 | ||
587 |
def test_options_agenda_booking_check_options(app, admin_user): |
|
588 |
agenda = Agenda.objects.create(label='Foo bar', kind='events') |
|
589 | ||
590 |
app = login(app) |
|
591 | ||
561 | 592 |
# check filters |
562 | 593 |
assert agenda.booking_check_filters == '' |
563 | 594 |
assert agenda.get_booking_check_filters() == [] |
564 | 595 | |
596 |
url = '/manage/agendas/%s/check-options' % agenda.pk |
|
565 | 597 |
resp = app.get(url) |
566 | 598 |
resp.form['booking_check_filters'] = 'foo,bar,baz' |
567 | 599 |
resp = resp.form.submit() |
... | ... | |
594 | 626 |
app.get(url, status=404) |
595 | 627 | |
596 | 628 | |
597 |
def test_options_agenda_event_display_template(app, admin_user): |
|
598 |
agenda = Agenda.objects.create(label='Foo bar', kind='events') |
|
599 |
Desk.objects.create(agenda=agenda, slug='_exceptions_holder') |
|
600 |
app = login(app) |
|
601 |
resp = app.get('/manage/agendas/%s/edit' % agenda.pk) |
|
602 |
valid_template = '{{ event.label|default:event.slug }} - {{ event.remaining_places|add:"5" }} / {{ event.start_datetime|date }} - {{ event.agenda.name }}' |
|
603 |
resp.form['event_display_template'] = valid_template |
|
604 |
resp = resp.form.submit().follow() |
|
605 | ||
606 |
agenda.refresh_from_db() |
|
607 |
assert agenda.event_display_template == valid_template |
|
608 | ||
609 |
invalid_templates = [ |
|
610 |
'{{ syntax error }}', |
|
611 |
'{{ event.label|invalidfilter }}', |
|
612 |
'{{ event.label|default:notexist }}', |
|
613 |
] |
|
614 |
for template in invalid_templates: |
|
615 |
resp = app.get('/manage/agendas/%s/edit' % agenda.pk) |
|
616 |
resp.form['event_display_template'] = template |
|
617 |
resp = resp.form.submit() |
|
618 |
assert 'syntax error' in resp.text |
|
619 | ||
620 | ||
621 | 629 |
def test_options_agenda_as_manager(app, manager_user): |
622 | 630 |
agenda = Agenda(label='Foo bar') |
623 | 631 |
agenda.view_role = manager_user.groups.all()[0] |
tests/manager/test_event.py | ||
---|---|---|
1112 | 1112 |
app.get('/manage/agendas/%s/import-events' % agenda.id, status=404) |
1113 | 1113 | |
1114 | 1114 | |
1115 |
@pytest.mark.freeze_time('2022-05-24') |
|
1116 |
def test_event_detail(app, admin_user): |
|
1117 |
agenda = Agenda.objects.create(label='Events', kind='events') |
|
1118 |
event = Event.objects.create( |
|
1119 |
label='xyz', |
|
1120 |
start_datetime=now() + datetime.timedelta(days=1), |
|
1121 |
places=10, |
|
1122 |
waiting_list_places=2, |
|
1123 |
agenda=agenda, |
|
1124 |
) |
|
1125 |
Booking.objects.create(event=event, user_last_name="User's 1") |
|
1126 |
Booking.objects.create(event=event, user_last_name='User 2', in_waiting_list=True) |
|
1127 | ||
1128 |
login(app) |
|
1129 |
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk)) |
|
1130 |
assert 'Bookings (1/10)' in resp.text |
|
1131 |
assert 'User's 1, May 24, 2022, 2 a.m.' in resp.text |
|
1132 |
assert 'Waiting List (1/2): 1 remaining place' in resp.text |
|
1133 |
assert 'User 2, May 24, 2022, 2 a.m.' in resp.text |
|
1134 | ||
1135 |
agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar' |
|
1136 |
agenda.save() |
|
1137 |
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk)) |
|
1138 |
assert 'Bookings (1/10)' in resp.text |
|
1139 |
assert '<b>User's 1</b> Foo Bar, May 24, 2022, 2 a.m.' in resp.text |
|
1140 |
assert 'Waiting List (1/2): 1 remaining place' in resp.text |
|
1141 |
assert '<b>User 2</b> Foo Bar, May 24, 2022, 2 a.m.' in resp.text |
|
1142 | ||
1143 | ||
1115 | 1144 |
def test_event_cancellation(app, admin_user): |
1116 | 1145 |
agenda = Agenda.objects.create(label='Events', kind='events') |
1117 | 1146 |
event = Event.objects.create( |
... | ... | |
1125 | 1154 |
resp = resp.click('Cancel', href='/cancel') |
1126 | 1155 |
assert 'related bookings' not in resp.text |
1127 | 1156 | |
1128 |
Booking.objects.create(event=event) |
|
1129 |
Booking.objects.create(event=event) |
|
1157 |
Booking.objects.create(event=event, user_last_name='User 1')
|
|
1158 |
Booking.objects.create(event=event, user_last_name='User 2')
|
|
1130 | 1159 | |
1131 | 1160 |
resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk)) |
1132 | 1161 |
assert 'Bookings (2/10)' in resp.text |
... | ... | |
1366 | 1395 |
event=event, user_external_id='user:1', user_first_name='User', user_last_name='42' |
1367 | 1396 |
) |
1368 | 1397 |
Booking.objects.create( |
1369 |
event=event, user_external_id='user:2', user_first_name='User', user_last_name='01'
|
|
1398 |
event=event, user_external_id='user:2', user_first_name="User's", user_last_name='01'
|
|
1370 | 1399 |
) |
1371 | 1400 |
Booking.objects.create( |
1372 | 1401 |
event=event, user_external_id='user:3', user_first_name='User', user_last_name='17' |
... | ... | |
1424 | 1453 |
resp = resp.click('Check') |
1425 | 1454 |
assert ( |
1426 | 1455 |
resp.text.index('Bookings (6/10)') |
1427 |
< resp.text.index('User 01')
|
|
1456 |
< resp.text.index("User's 01")
|
|
1428 | 1457 |
< resp.text.index('User 05') |
1429 | 1458 |
< resp.text.index('User 17') |
1430 | 1459 |
< resp.text.index('User 35') |
... | ... | |
1486 | 1515 |
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk)) |
1487 | 1516 |
assert ( |
1488 | 1517 |
resp.text.index('Bookings (6/10)') |
1489 |
< resp.text.index('User 01')
|
|
1518 |
< resp.text.index("User's 01")
|
|
1490 | 1519 |
< resp.text.index('User 05') |
1491 | 1520 |
< resp.text.index('User 12 Cancelled') |
1492 | 1521 |
< resp.text.index('Subscription 14') |
... | ... | |
1504 | 1533 |
assert 'Subscription too soon' not in resp |
1505 | 1534 |
assert 'Subscription too late' not in resp |
1506 | 1535 | |
1507 |
agenda.booking_user_block_template = '{{ booking.user_name }} Foo Bar'
|
|
1536 |
agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
|
|
1508 | 1537 |
agenda.save() |
1509 | 1538 |
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk)) |
1510 |
assert 'User 01 Foo Bar' in resp
|
|
1511 |
assert 'Subscription 14 Foo Bar' in resp
|
|
1512 |
assert 'User Waiting Foo Bar' in resp
|
|
1539 |
assert '<b>User's 01</b> Foo Bar' in resp
|
|
1540 |
assert '<b>Subscription 14</b> Foo Bar' in resp
|
|
1541 |
assert '<b>User Waiting</b> Foo Bar' in resp
|
|
1513 | 1542 | |
1514 | 1543 |
# cancelled booking |
1515 | 1544 |
token = resp.context['csrf_token'] |
1516 |
- |