0001-manager-configuer-user-block-on-check-page-53237.patch
chrono/agendas/migrations/0080_booking_user_block_template.py | ||
---|---|---|
1 |
from django.db import migrations, models |
|
2 | ||
3 |
import chrono.agendas.models |
|
4 | ||
5 | ||
6 |
class Migration(migrations.Migration): |
|
7 | ||
8 |
dependencies = [ |
|
9 |
('agendas', '0079_text_to_jsonb'), |
|
10 |
] |
|
11 | ||
12 |
operations = [ |
|
13 |
migrations.AddField( |
|
14 |
model_name='agenda', |
|
15 |
name='booking_user_block_template', |
|
16 |
field=models.TextField( |
|
17 |
blank=True, |
|
18 |
help_text='Displayed for each booking in event check page', |
|
19 |
verbose_name='User block template', |
|
20 |
validators=[chrono.agendas.models.django_template_validator], |
|
21 |
), |
|
22 |
), |
|
23 |
] |
chrono/agendas/models.py | ||
---|---|---|
203 | 203 |
_('Booking form URL'), max_length=200, blank=True, validators=[django_template_validator] |
204 | 204 |
) |
205 | 205 |
desk_simple_management = models.BooleanField(default=False) |
206 |
booking_user_block_template = models.TextField( |
|
207 |
_('User block template'), |
|
208 |
help_text=_('Displayed for each booking in event check page'), |
|
209 |
blank=True, |
|
210 |
validators=[django_template_validator], |
|
211 |
) |
|
206 | 212 | |
207 | 213 |
class Meta: |
208 | 214 |
ordering = ['label'] |
... | ... | |
663 | 669 |
except (VariableDoesNotExist, TemplateSyntaxError): |
664 | 670 |
return |
665 | 671 | |
672 |
def get_booking_user_block_template(self): |
|
673 |
return ( |
|
674 |
self.booking_user_block_template |
|
675 |
or '{{ booking.user_name|default:booking.label|default:"%s" }}' % _('Unknown') |
|
676 |
) |
|
677 | ||
666 | 678 |
def prefetch_desks_and_exceptions(self, with_sources=False): |
667 | 679 |
if self.kind == 'meetings': |
668 | 680 |
desks = self.desk_set.all() |
... | ... | |
1547 | 1559 |
self.secondary_booking_set.update(in_waiting_list=True) |
1548 | 1560 |
self.save() |
1549 | 1561 | |
1562 |
def get_user_block(self): |
|
1563 |
template_vars = Context(settings.TEMPLATE_VARS) |
|
1564 |
template_vars.update( |
|
1565 |
{ |
|
1566 |
'booking': self, |
|
1567 |
} |
|
1568 |
) |
|
1569 |
try: |
|
1570 |
return Template(self.event.agenda.get_booking_user_block_template()).render(template_vars) |
|
1571 |
except (VariableDoesNotExist, TemplateSyntaxError): |
|
1572 |
return |
|
1573 | ||
1550 | 1574 |
@classmethod |
1551 | 1575 |
def anonymize_bookings(cls, bookings_queryset): |
1552 | 1576 |
bookings_queryset.update( |
chrono/manager/forms.py | ||
---|---|---|
27 | 27 |
from django.forms import ValidationError |
28 | 28 |
from django.utils.encoding import force_text |
29 | 29 |
from django.utils.six import StringIO |
30 |
from django.utils.timezone import make_aware, make_naive, now
|
|
30 |
from django.utils.timezone import make_aware, now |
|
31 | 31 |
from django.utils.translation import ugettext_lazy as _ |
32 | 32 | |
33 | 33 |
from chrono.agendas.models import ( |
34 | 34 |
WEEKDAYS_LIST, |
35 |
AbsenceReason, |
|
35 |
AbsenceReasonGroup,
|
|
36 | 36 |
Agenda, |
37 | 37 |
AgendaNotificationsSettings, |
38 | 38 |
AgendaReminderSettings, |
... | ... | |
729 | 729 |
fields = [] |
730 | 730 | |
731 | 731 | |
732 |
class AgendaBookingCheckSettingsForm(forms.ModelForm): |
|
733 |
class Meta: |
|
734 |
model = Agenda |
|
735 |
fields = ['absence_reasons_group', 'booking_user_block_template'] |
|
736 |
widgets = {'booking_user_block_template': forms.Textarea(attrs={'rows': 3})} |
|
737 | ||
738 |
def __init__(self, *args, **kwargs): |
|
739 |
super().__init__(*args, **kwargs) |
|
740 |
if not AbsenceReasonGroup.objects.exists(): |
|
741 |
del self.fields['absence_reasons_group'] |
|
742 | ||
743 | ||
732 | 744 |
class AgendaNotificationsForm(forms.ModelForm): |
733 | 745 |
class Meta: |
734 | 746 |
model = AgendaNotificationsSettings |
chrono/manager/templates/chrono/manager_event_check.html | ||
---|---|---|
31 | 31 |
<h3> |
32 | 32 |
{% blocktrans with places=object.waiting_list_places booked_places=waiting|length %}Waiting List ({{ booked_places }}/{{ places }}){% endblocktrans %} |
33 | 33 |
</h3> |
34 |
<div> |
|
35 |
<ul class="objects-list single-links"> |
|
36 |
{% for booking in waiting %} |
|
37 |
<li><a>{{ booking.user_name|default:booking.label|default:_('Unknown') }}</a></li> |
|
38 |
{% endfor %} |
|
39 |
</ul> |
|
40 |
</div> |
|
34 |
<div> |
|
35 |
<table class="main check-bookings"> |
|
36 |
<tbody> |
|
37 |
{% for booking in waiting %} |
|
38 |
<tr><td class="booking-username">{{ booking.get_user_block }}</td></tr> |
|
39 |
{% endfor %} |
|
40 |
</tbody> |
|
41 |
</table> |
|
42 |
</div> |
|
41 | 43 |
</div> |
42 | 44 |
{% endif %} |
43 | 45 |
{% endblock %} |
chrono/manager/templates/chrono/manager_event_check_booking_fragment.html | ||
---|---|---|
1 | 1 |
{% load i18n %} |
2 | 2 | |
3 |
<td class="booking-username">{{ booking.user_name|default:booking.label|default:_('Unknown') }}</td>
|
|
3 |
<td class="booking-username">{{ booking.get_user_block }}</td>
|
|
4 | 4 |
<td class="booking-status"> |
5 | 5 |
{{ booking.user_was_present|yesno:_('Present,Absent,-') }} |
6 | 6 |
{% if booking.user_was_present is False and booking.user_absence_reason %} |
chrono/manager/templates/chrono/manager_events_agenda_settings.html | ||
---|---|---|
30 | 30 |
</div> |
31 | 31 |
</div> |
32 | 32 | |
33 |
{% if has_absence_reasons %} |
|
34 | 33 |
<div class="section"> |
35 |
<h3>{% trans "Absence reasons" %}
|
|
36 |
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-absence-reasons' pk=object.id %}">{% trans 'Configure' %}</a>
|
|
34 |
<h3>{% trans "Booking check options" %}
|
|
35 |
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-booking-check-settings' pk=object.pk %}">{% trans 'Configure' %}</a>
|
|
37 | 36 |
</h3> |
38 | 37 |
<div> |
39 |
{% if agenda.absence_reasons_group %} |
|
40 |
<p>{% trans "Absence reasons group:" %} {{ agenda.absence_reasons_group }}</p> |
|
41 |
<ul> |
|
42 |
{% for reason in agenda.absence_reasons_group.absence_reasons.all %} |
|
43 |
<li>{{ reason }}</li> |
|
44 |
{% endfor %} |
|
45 |
</ul> |
|
46 |
{% else %} |
|
47 |
<p>{% trans "No absence reasons configured for this agenda." %}</p> |
|
48 |
{% endif %} |
|
38 |
{% if has_absence_reasons %} |
|
39 |
{% if agenda.absence_reasons_group %} |
|
40 |
<p>{% trans "Absence reasons group:" %} {{ agenda.absence_reasons_group }}</p> |
|
41 |
<ul> |
|
42 |
{% for reason in agenda.absence_reasons_group.absence_reasons.all %} |
|
43 |
<li>{{ reason }}</li> |
|
44 |
{% endfor %} |
|
45 |
</ul> |
|
46 |
{% else %} |
|
47 |
<p>{% trans "No absence reasons configured for this agenda." %}</p> |
|
48 |
{% endif %} |
|
49 |
{% endif %} |
|
50 | ||
51 |
<p>{% trans "User block template" %}:</p> |
|
52 |
<pre>{{ agenda.get_booking_user_block_template }}</pre> |
|
49 | 53 |
</div> |
50 | 54 |
</div> |
51 |
{% endif %} |
|
52 | 55 | |
53 | 56 |
<div class="section"> |
54 | 57 |
<h3>{% trans "Notifications" %} |
chrono/manager/urls.py | ||
---|---|---|
148 | 148 |
), |
149 | 149 |
url(r'^agendas/(?P<pk>\d+)/roles$', views.agenda_roles, name='chrono-manager-agenda-roles'), |
150 | 150 |
url( |
151 |
r'^agendas/(?P<pk>\d+)/absence-reasons$',
|
|
152 |
views.agenda_absence_reasons,
|
|
153 |
name='chrono-manager-agenda-absence-reasons',
|
|
151 |
r'^agendas/(?P<pk>\d+)/check-options$',
|
|
152 |
views.agenda_booking_check_settings,
|
|
153 |
name='chrono-manager-agenda-booking-check-settings',
|
|
154 | 154 |
), |
155 | 155 |
url(r'^agendas/(?P<pk>\d+)/delete$', views.agenda_delete, name='chrono-manager-agenda-delete'), |
156 | 156 |
url(r'^agendas/(?P<pk>\d+)/export$', views.agenda_export, name='chrono-manager-agenda-export'), |
chrono/manager/views.py | ||
---|---|---|
77 | 77 | |
78 | 78 |
from .forms import ( |
79 | 79 |
AgendaAddForm, |
80 |
AgendaBookingCheckSettingsForm, |
|
80 | 81 |
AgendaBookingDelaysForm, |
81 | 82 |
AgendaDuplicateForm, |
82 | 83 |
AgendaEditForm, |
... | ... | |
912 | 913 |
agenda_roles = AgendaRolesView.as_view() |
913 | 914 | |
914 | 915 | |
915 |
class AgendaAbsenceReasonsView(AgendaEditView): |
|
916 |
form_class = None |
|
917 |
fields = ['absence_reasons_group'] |
|
918 |
title = _('Configure absence reasons') |
|
916 |
class AgendaBookingCheckSettingsView(AgendaEditView): |
|
917 |
form_class = AgendaBookingCheckSettingsForm |
|
918 |
title = _("Configure booking check options") |
|
919 | 919 | |
920 | 920 |
def set_agenda(self, **kwargs): |
921 | 921 |
self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='events') |
922 | 922 | |
923 |
def get_initial(self): |
|
924 |
return {'booking_user_block_template': self.agenda.get_booking_user_block_template()} |
|
923 | 925 | |
924 |
agenda_absence_reasons = AgendaAbsenceReasonsView.as_view() |
|
926 | ||
927 |
agenda_booking_check_settings = AgendaBookingCheckSettingsView.as_view() |
|
925 | 928 | |
926 | 929 | |
927 | 930 |
class AgendaDeleteView(DeleteView): |
tests/manager/test_absence_reason.py | ||
---|---|---|
198 | 198 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
199 | 199 |
assert 'has_absence_reasons' in resp.context |
200 | 200 |
assert resp.context['has_absence_reasons'] is False |
201 |
assert 'Absence reasons' not in resp
|
|
201 |
assert 'No absence reasons configured for this agenda.' not in resp
|
|
202 | 202 |
assert '/manage/agendas/%s/absence-reasons' % agenda.pk not in resp |
203 |
resp = resp.click(href='/manage/agendas/%s/check-options' % agenda.pk) |
|
204 |
assert 'absence_reasons_group' not in resp.context['form'].fields |
|
203 | 205 | |
204 | 206 |
group = AbsenceReasonGroup.objects.create(label='Foo bar') |
205 | 207 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
206 | 208 |
assert 'has_absence_reasons' in resp.context |
207 | 209 |
assert resp.context['has_absence_reasons'] is True |
208 |
assert 'Absence reasons' in resp |
|
209 | 210 |
assert 'No absence reasons configured for this agenda.' in resp |
210 |
resp = resp.click(href='/manage/agendas/%s/absence-reasons' % agenda.pk)
|
|
211 |
resp = resp.click(href='/manage/agendas/%s/check-options' % agenda.pk)
|
|
211 | 212 |
resp.form['absence_reasons_group'] = group.pk |
212 | 213 |
resp = resp.form.submit().follow() |
213 | 214 |
assert 'Absence reasons group: Foo bar' in resp |
tests/manager/test_all.py | ||
---|---|---|
480 | 480 |
assert agenda.minimal_booking_delay == 1 |
481 | 481 | |
482 | 482 | |
483 |
def test_options_virtuale_agenda_can_unset_delays(app, admin_user):
|
|
483 |
def test_options_virtual_agenda_can_unset_delays(app, admin_user): |
|
484 | 484 |
agenda = Agenda.objects.create(label=u'Foo bar', kind='virtual', maximal_booking_delay=2) |
485 | 485 |
assert agenda.maximal_booking_delay == 2 |
486 | 486 |
app = login(app) |
... | ... | |
492 | 492 |
assert agenda.maximal_booking_delay is None |
493 | 493 | |
494 | 494 | |
495 |
def test_options_agenda_booking_user_block_template(app, admin_user): |
|
496 |
agenda = Agenda.objects.create(label='Foo bar', kind='events') |
|
497 |
assert agenda.booking_user_block_template == '' |
|
498 |
assert ( |
|
499 |
agenda.get_booking_user_block_template() |
|
500 |
== '{{ booking.user_name|default:booking.label|default:"Unknown" }}' |
|
501 |
) |
|
502 | ||
503 |
app = login(app) |
|
504 |
url = '/manage/agendas/%s/check-options' % agenda.pk |
|
505 |
resp = app.get(url) |
|
506 |
resp.form['booking_user_block_template'] = '{{ booking.user_name }} Foo Bar' |
|
507 |
resp = resp.form.submit() |
|
508 |
agenda.refresh_from_db() |
|
509 |
assert agenda.booking_user_block_template == '{{ booking.user_name }} Foo Bar' |
|
510 |
assert agenda.get_booking_user_block_template() == '{{ booking.user_name }} Foo Bar' |
|
511 | ||
512 |
resp = app.get(url) |
|
513 |
resp.form['booking_user_block_template'] = '' |
|
514 |
resp = resp.form.submit() |
|
515 |
agenda.refresh_from_db() |
|
516 |
assert agenda.booking_user_block_template == '' |
|
517 |
assert ( |
|
518 |
agenda.get_booking_user_block_template() |
|
519 |
== '{{ booking.user_name|default:booking.label|default:"Unknown" }}' |
|
520 |
) |
|
521 | ||
522 |
# check kind |
|
523 |
agenda.kind = 'meetings' |
|
524 |
agenda.save() |
|
525 |
app.get(url, status=404) |
|
526 |
agenda.kind = 'virtual' |
|
527 |
agenda.save() |
|
528 |
app.get(url, status=404) |
|
529 | ||
530 | ||
495 | 531 |
def test_options_agenda_as_manager(app, manager_user): |
496 | 532 |
agenda = Agenda(label=u'Foo bar') |
497 | 533 |
agenda.view_role = manager_user.groups.all()[0] |
tests/manager/test_event.py | ||
---|---|---|
1102 | 1102 |
) # user ordering is not optimal ... |
1103 | 1103 |
assert 'User Cancelled' not in resp |
1104 | 1104 | |
1105 |
agenda.booking_user_block_template = '{{ booking.user_name }} Foo Bar' |
|
1106 |
agenda.save() |
|
1107 |
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk)) |
|
1108 |
assert 'User 1 Foo Bar' in resp |
|
1109 |
assert 'User Waiting Foo Bar' in resp |
|
1110 | ||
1105 | 1111 |
# cancelled booking |
1106 | 1112 |
token = resp.context['csrf_token'] |
1107 | 1113 |
app.post( |
1108 |
- |