manager: use user_block also in meetings agenda views (#63915)

This commit is contained in:
Lauréline Guérin 2022-05-24 14:38:29 +02:00
parent 50963bb083
commit efad2bdc29
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
10 changed files with 140 additions and 31 deletions

View File

@ -776,10 +776,17 @@ class Agenda(models.Model):
return [x.strip() for x in self.booking_check_filters.split(',')]
def get_booking_user_block_template(self):
return (
self.booking_user_block_template
or '{{ booking.user_name|default:booking.label|default:"%s" }}' % _('Anonymous')
)
if self.kind == 'events':
default = '{{ booking.user_name|default:booking.label|default:"%s" }}' % _('Anonymous')
else:
default = """{%% if booking.label and booking.user_name %%}
{{ booking.label }} - {{ booking.user_name }}
{%% else %%}
{{ booking.user_name|default:booking.label|default:"%s" }}
{%% endif %%}""" % _(
'booked'
)
return self.booking_user_block_template or default
def get_recurrence_exceptions(self, min_start, max_start):
return TimePeriodException.objects.filter(
@ -2075,14 +2082,6 @@ class Booking(models.Model):
name = self.user_name or self.label or _('Anonymous')
return '%s, %s' % (name, date_format(localtime(self.creation_datetime), 'DATETIME_FORMAT'))
def meetings_display(self):
if self.label and self.user_name:
return '%s - %s' % (self.label, self.user_name)
elif self.label or self.user_name:
return self.label or self.user_name
else:
return ugettext('booked')
def get_form_url(self):
return translate_from_publik_url(self.form_url)

View File

@ -1297,9 +1297,15 @@ class AgendaDisplaySettingsForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['booking_user_block_template'].help_text = (
_('Displayed for each booking in event page and check page'),
)
if kwargs['instance'].kind == 'events':
self.fields['booking_user_block_template'].help_text = (
_('Displayed for each booking in event page and check page'),
)
else:
self.fields['booking_user_block_template'].help_text = (
_('Displayed for each booking in agenda view pages'),
)
del self.fields['event_display_template']
class AgendaBookingCheckSettingsForm(forms.ModelForm):

View File

@ -46,7 +46,7 @@
<div class="booking{% if booking.color %} booking-color-{{ booking.color.index }}{% endif %}"
style="height: {{ booking.css_height }}%; min-height: {{ booking.css_height }}%; top: {{ booking.css_top }}%;"
><span class="start-time">{{booking.event.start_datetime|date:"TIME_FORMAT"}}</span>
<a {% if booking.get_backoffice_url %}href="{{booking.get_backoffice_url}}"{% endif %}>{{ booking.meetings_display }}</a>
<a {% if booking.get_backoffice_url %}href="{{booking.get_backoffice_url}}"{% endif %}>{{ booking.get_user_block }}</a>
<a rel="popup" class="cancel" href="{% url 'chrono-manager-booking-cancel' pk=booking.event.agenda_id booking_pk=booking.pk %}?next={{ request.path }}">{% trans "Cancel" %}</a>
{% if booking.color %}<span class="booking-color-label booking-bg-color-{{ booking.color.index }}">{{ booking.color }}</span>{% endif %}
</div>

View File

@ -36,7 +36,7 @@
{% for slot in day.infos.booked_slots %}
<div class="booking{% if slot.booking.color %} booking-color-{{ slot.booking.color.index }}{% endif %}" style="left:{{ slot.css_left|stringformat:".1f" }}%;height:{{ slot.css_height|stringformat:".1f" }}%;min-height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%;">
<span class="start-time">{{slot.booking.event.start_datetime|date:"TIME_FORMAT"}}</span>
<a {% if slot.booking.get_backoffice_url %}href="{{slot.booking.get_backoffice_url}}"{% endif %}>{{ slot.booking.meetings_display }}</a>
<a {% if slot.booking.get_backoffice_url %}href="{{slot.booking.get_backoffice_url}}"{% endif %}>{{ slot.booking.get_user_block }}</a>
<a rel="popup" class="cancel" href="{% url 'chrono-manager-booking-cancel' pk=slot.booking.event.agenda_id booking_pk=slot.booking.id %}?next={{ request.path }}">{% trans "Cancel" %}</a>
{% if not single_desk %}<span class="desk">{{ slot.desk }}</span>{% endif %}
{% if slot.booking.color %}<span class="booking-color-label booking-bg-color-{{ slot.booking.color.index }}">{{ slot.booking.color }}</span>{% endif %}

View File

@ -153,4 +153,18 @@
{% endif %}
{% endwith %}
<div class="section">
<h3>{% trans "Display options" %}
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-display-settings' pk=object.pk %}">{% trans 'Configure' %}</a>
</h3>
<div>
<ul>
<li>
{% trans "Booking display template:" %}
<pre>{{ agenda.get_booking_user_block_template }}</pre>
</li>
</ul>
</div>
</div>
{% endblock %}

View File

@ -40,7 +40,7 @@
<div class="booking"
style="height: {{ booking.css_height }}%; min-height: {{ booking.css_height }}%; top: {{ booking.css_top }}%;"
><span class="start-time">{{ booking.event.start_datetime|date:"TIME_FORMAT" }}</span>
<a {% if booking.get_backoffice_url %}href="{{ booking.get_backoffice_url }}"{% endif %}>{{ booking.meetings_display }}</a>
<a {% if booking.get_backoffice_url %}href="{{ booking.get_backoffice_url }}"{% endif %}>{{ booking.get_user_block }}</a>
</div>
{% endfor %}
</td>

View File

@ -49,7 +49,7 @@
{% for slot in day.infos.booked_slots %}
<div class="booking" style="height:{{ slot.css_height|stringformat:".1f" }}%;min-height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%">
<span class="start-time">{{ slot.booking.event.start_datetime|date:"TIME_FORMAT" }}</span>
<a {% if slot.booking.get_backoffice_url %}href="{{ slot.booking.get_backoffice_url }}"{% endif %}>{{ booking.meetings_display }}</a>
<a {% if slot.booking.get_backoffice_url %}href="{{ slot.booking.get_backoffice_url }}"{% endif %}>{{ slot.booking.get_user_block }}</a>
</div>
{% endfor %}
{% endif %}

View File

@ -283,7 +283,9 @@ class ResourceDayView(DateMixin, DayArchiveView):
def get_queryset(self):
queryset = (
self.resource.event_set.all().select_related('meeting_type').prefetch_related('booking_set')
self.resource.event_set.all()
.select_related('meeting_type', 'agenda')
.prefetch_related('booking_set')
)
return queryset
@ -398,7 +400,9 @@ class ResourceMonthView(DateMixin, MonthArchiveView):
def get_queryset(self):
queryset = (
self.resource.event_set.all().select_related('meeting_type').prefetch_related('booking_set')
self.resource.event_set.all()
.select_related('meeting_type', 'agenda')
.prefetch_related('booking_set')
)
return queryset
@ -1200,7 +1204,7 @@ class AgendaDisplaySettingsView(AgendaEditView):
title = _("Configure display options")
def set_agenda(self, **kwargs):
self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='events')
self.agenda = get_object_or_404(Agenda.objects.exclude(kind='virtual'), pk=kwargs.get('pk'))
def get_initial(self):
return {'booking_user_block_template': self.agenda.get_booking_user_block_template()}
@ -1372,7 +1376,7 @@ class AgendaDateView(DateMixin, ViewableAgendaMixin):
else:
queryset = (
Event.objects.filter(agenda__virtual_agendas=self.agenda)
.select_related('meeting_type')
.select_related('meeting_type', 'agenda')
.prefetch_related('booking_set')
)
return queryset

View File

@ -524,10 +524,14 @@ def test_options_agenda_booking_display_options(app, admin_user):
resp = resp.form.submit()
assert 'syntax error' in resp.text
# check kind
# and for meetings agenda
agenda.kind = 'meetings'
agenda.save()
app.get(url, status=404)
resp = app.get(url)
assert 'event_display_template' not in resp.form.fields
assert 'booking_user_block_template' in resp.form.fields
# check kind
agenda.kind = 'virtual'
agenda.save()
app.get(url, status=404)
@ -807,16 +811,28 @@ def test_agenda_day_view(app, admin_user, manager_user, api_user):
booking_url = resp.json['data'][0]['api']['fillslot_url']
booking_url2 = resp.json['data'][2]['api']['fillslot_url']
resp = app.post(booking_url)
resp = app.post_json(booking_url2, params={'label': 'foo', 'user': 'bar', 'url': 'http://baz/'})
resp = app.post_json(
booking_url2, params={'label': 'foo', 'user_last_name': "bar's", 'url': 'http://baz/'}
)
app.reset()
login(app)
date = Booking.objects.all()[0].event.start_datetime
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
assert resp.text.count('div class="booking') == 2
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"
assert 'foo - bar&#39;s' in resp
assert 'hourspan-2' in resp.text # table CSS class
assert 'height: 50%; top: 0%;' in resp.text # booking cells
agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
agenda.save()
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Foo Bar"
assert '&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar' in resp
# create a shorter meeting type, this will change the table CSS class
# (and visually this will give more room for events)
meetingtype = MeetingType(agenda=agenda, label='Baz', duration=15)
@ -1354,15 +1370,27 @@ def test_agenda_month_view(app, admin_user, manager_user, api_user):
booking_url = resp.json['data'][0]['api']['fillslot_url']
booking_url2 = resp.json['data'][2]['api']['fillslot_url']
booking = app.post(booking_url)
booking_2 = app.post_json(booking_url2, params={'label': 'foo book', 'user': 'bar', 'url': 'http://baz/'})
booking_2 = app.post_json(
booking_url2, params={'label': 'foo book', 'user_last_name': "bar's", 'url': 'http://baz/'}
)
app.reset()
login(app)
date = Booking.objects.all()[0].event.start_datetime
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month))
assert resp.text.count('<div class="booking" style="left:1.0%;height:33.0%;') == 2 # booking cells
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo book - bar's"
assert 'foo book - bar&#39;s' in resp
assert len(resp.pyquery.find('span.desk')) == 0
agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
agenda.save()
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month))
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Foo Bar"
assert '&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar' in resp
desk = Desk.objects.create(agenda=agenda, label='Desk B')
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month))
assert len(resp.pyquery.find('span.desk')) == 2
@ -1853,14 +1881,30 @@ def test_virtual_agenda_day_view(app, admin_user, manager_user):
meeting_type=meetingtype2,
start_datetime=now().replace(hour=hour, minute=minute),
)
Booking.objects.create(event=event)
Booking.objects.create(event=event, label='foo', user_last_name="bar's")
date = Booking.objects.all()[0].event.start_datetime
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
assert resp.text.count('div class="booking') == 4
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"
assert resp.pyquery.find('div.booking a').not_('.cancel')[2].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[3].text.strip() == "foo - bar's"
assert 'foo - bar&#39;s' in resp
assert 'hourspan-2' in resp.text # table CSS class
assert 'height: 50%; top: 0%;' in resp.text # booking cells
real_agenda_1.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
real_agenda_1.save()
real_agenda_2.booking_user_block_template = '<b>{{ booking.user_name }}</b> Bar Foo'
real_agenda_2.save()
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Bar Foo"
assert resp.pyquery.find('div.booking a').not_('.cancel')[2].text.strip() == '<b></b> Foo Bar'
assert resp.pyquery.find('div.booking a').not_('.cancel')[3].text.strip() == "<b>bar's</b> Bar Foo"
assert '&lt;b&gt;bar&#39;s&lt;/b&gt; Bar Foo' in resp
# create a shorter meeting type, this will change the table CSS class
# (and visually this will give more room for events)
MeetingType.objects.create(agenda=real_agenda_1, label='Baz', duration=15)
@ -1986,7 +2030,7 @@ def test_virtual_agenda_month_view(app, admin_user):
meeting_type=meetingtype2,
start_datetime=now().replace(hour=hour, minute=minute),
)
Booking.objects.create(event=event)
Booking.objects.create(event=event, label='foo', user_last_name="bar's")
date = Booking.objects.all()[0].event.start_datetime
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month))
@ -1994,6 +2038,22 @@ def test_virtual_agenda_month_view(app, admin_user):
assert (
resp.text.count('<div class="booking" style="left:50.0%;height:50.0%;min-height:50.0%;') == 2
) # booking cells
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"
assert resp.pyquery.find('div.booking a').not_('.cancel')[2].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[3].text.strip() == "foo - bar's"
assert 'foo - bar&#39;s' in resp
real_agenda_1.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
real_agenda_1.save()
real_agenda_2.booking_user_block_template = '<b>{{ booking.user_name }}</b> Bar Foo'
real_agenda_2.save()
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month))
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Bar Foo"
assert resp.pyquery.find('div.booking a').not_('.cancel')[2].text.strip() == '<b></b> Foo Bar'
assert resp.pyquery.find('div.booking a').not_('.cancel')[3].text.strip() == "<b>bar's</b> Bar Foo"
assert '&lt;b&gt;bar&#39;s&lt;/b&gt; Bar Foo' in resp
# cancel a booking
booking = Booking.objects.first()

View File

@ -102,16 +102,29 @@ def test_resource_day_view(app, admin_user):
start_datetime=now().replace(hour=hour, minute=minute),
)
event.resources.add(resource)
Booking.objects.create(event=event)
if hour == 10:
Booking.objects.create(event=event)
else:
Booking.objects.create(event=event, label='foo', user_last_name="bar's")
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert len(ctx.captured_queries) == 7
assert resp.text.count('div class="booking') == 2
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"
assert 'foo - bar&#39;s' in resp
assert 'hourspan-2' in resp.text # table CSS class
assert 'height: 50%; top: 0%;' in resp.text # booking cells
assert 'height: 50%; top: 50%;' in resp.text # booking cells
agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
agenda.save()
resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Foo Bar"
assert '&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar' in resp
# create a shorter meeting type, this will change the table CSS class
# (and visually this will give more room for events)
meetingtype = MeetingType.objects.create(agenda=agenda, label='Baz', duration=15)
@ -240,13 +253,26 @@ def test_resource_month_view(app, admin_user):
start_datetime=now().replace(hour=hour, minute=minute),
)
event.resources.add(resource)
Booking.objects.create(event=event)
if hour == 10:
Booking.objects.create(event=event)
else:
Booking.objects.create(event=event, label='foo', user_last_name="bar's")
today = datetime.date.today()
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, today.year, today.month))
assert len(ctx.captured_queries) == 8
assert resp.text.count('<div class="booking" style="height:33.0%;') == 2 # booking cells
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"
assert 'foo - bar&#39;s' in resp
agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
agenda.save()
resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, today.year, today.month))
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Foo Bar"
assert '&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar' in resp
# cancel booking
booking = Booking.objects.first()