From ecb270a627e56ebbb1ece8eef892e8ee72bb6fae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Sun, 22 Dec 2019 14:27:13 +0100
Subject: [PATCH 1/4] manager: add view of events agenda for restricted users
 (#20279)

---
 .../chrono/manager_agenda_month_view.html     |  52 +------
 .../manager_events_agenda_month_view.html     |  53 +++++++
 .../manager_meetings_agenda_month_view.html   |  56 +++++++
 chrono/manager/views.py                       | 144 +++++++++---------
 tests/test_manager.py                         |  55 ++++++-
 5 files changed, 230 insertions(+), 130 deletions(-)
 create mode 100644 chrono/manager/templates/chrono/manager_events_agenda_month_view.html
 create mode 100644 chrono/manager/templates/chrono/manager_meetings_agenda_month_view.html

diff --git a/chrono/manager/templates/chrono/manager_agenda_month_view.html b/chrono/manager/templates/chrono/manager_agenda_month_view.html
index 976aafc..8adcf60 100644
--- a/chrono/manager/templates/chrono/manager_agenda_month_view.html
+++ b/chrono/manager/templates/chrono/manager_agenda_month_view.html
@@ -26,56 +26,6 @@
   <a href="{% url 'chrono-manager-agenda-settings' pk=agenda.id %}">{% trans 'Settings' %}</a>
 {% endif %}
 <a href="" onclick="window.print()">{% trans 'Print' %}</a>
-<a href="{% url 'chrono-manager-agenda-day-view' pk=agenda.id year=view.date|date:"Y" month=view.date|date:"m" day=view.date|date:"d" %}">{% trans 'Day view' %}</a>
-{% endblock %}
+{% block extra-actions %}{% endblock %}
 </span>
-
-{% block content %}
-{% for week_days in view.get_timetable_infos %}
-{% if forloop.first %}
-<table class="agenda-table month-view">
-  <tbody>
-{% endif %}
-  <tr>
-    <th></th>
-    {% for day in week_days.days %}
-    <th class="weekday {% if day.today %}today{% endif %}">{% if not day.other_month %}<a href="{% url 'chrono-manager-agenda-day-view' pk=agenda.id year=day.date|date:"Y" month=day.date|date:"m" day=day.date|date:"d" %}">{{ day.date|date:"l j" }}</a>{% endif %}</th>
-    {% endfor %}
-  </tr>
-  {% for hour in week_days.periods %}
-  <tr class="{% cycle 'odd' 'even' %}">
-    <th class="hour">{{ hour|date:"TIME_FORMAT" }}</th>
-    {% for day in week_days.days %}
-    <td class="{% if day.other_month %}other-month{% endif %} {% if day.today %}today{% endif %}">
-      {% if forloop.parentloop.first %}
-      {% for slot in day.infos.opening_hours %}
-      <div class="opening-hours" style="height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%;left:{{ slot.css_left|stringformat:".1f" }}%;"></div>
-      {% endfor %}
-      {% for slot in day.infos.booked_slots %}
-      <div class="booking" style="left:{{ slot.css_left|stringformat:".1f" }}%;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.backoffice_url %}href="{{slot.booking.backoffice_url}}"{% endif %}
-           >{% if slot.booking.label or slot.booking.user_name %}
-          {{slot.booking.label}}{% if slot.booking.label and slot.booking.user_name %} - {% endif %} {{slot.booking.user_name}}
-          {% else %}{% trans "booked" %}{% endif %}</a>
-        <span class="desk">{{ slot.desk }}</span>
-        </div>
-      {% endfor %}
-      {% endif %}
-    </td>
-    {% endfor %}
-  </tr>
-  {% endfor %}
-  {% resetcycle %}
-{% if forloop.last %}
-  </tbody>
-</table>
-{% endif %}
-
-{% empty %}
-<div class="closed-for-the-day">
-  <p>{% trans "No opening hours this month." %}</p>
-</div>
-{% endfor %}
-
 {% endblock %}
diff --git a/chrono/manager/templates/chrono/manager_events_agenda_month_view.html b/chrono/manager/templates/chrono/manager_events_agenda_month_view.html
new file mode 100644
index 0000000..08a2a53
--- /dev/null
+++ b/chrono/manager/templates/chrono/manager_events_agenda_month_view.html
@@ -0,0 +1,53 @@
+{% extends "chrono/manager_agenda_month_view.html" %}
+{% load i18n %}
+
+{% block content %}
+<div class="section">
+<h3>{% trans "Events" %}</h3>
+<div>
+{% if object_list %}
+  <ul class="objects-list single-links">
+  {% for event in object_list %}
+    <li class="{% if event.booked_places > event.places %}overbooking{% endif %}
+               {% if event.full %}full{% endif %}
+               {% if not event.in_bookable_period %}not-{% endif %}bookable"
+        {% if event.places %}
+          data-total="{{event.places}}" data-booked="{{event.booked_places}}"
+        {% elif event.waiting_list_places %}
+          data-total="{{event.waiting_list_places}}" data-booked="{{event.waiting_list}}"
+        {% endif %}
+        ><a>
+        {% if event.label %}{{event.label}} / {% endif %}
+        {{ event.start_datetime }}
+        {% if event.full %}/ <span class="full">{% trans "full" %}</span>{% endif %}
+        (
+        {% if event.places %}
+        {% blocktrans with places=event.places booked_places=event.booked_places %}{{ places }} places, {{ booked_places }} booked places{% endblocktrans %}
+        {% endif %}
+        {% if event.places and event.waiting_list_places %} / {% endif %}
+        {% if event.waiting_list_places %}
+        {% blocktrans with places=event.waiting_list_places waiting_places=event.waiting_list %}
+        {{waiting_places}} on {{ places }} in waiting list
+        {% endblocktrans %}
+        {% endif %}
+        )
+        {% if not event.in_bookable_period %}
+        ({% trans "out of bookable period" %})
+        {% endif %}
+            </a>
+        <span class="occupation-bar"></span>
+    </li>
+    {% endfor %}
+  </ul>
+  {% include "gadjo/pagination.html" %}
+{% else %}
+<div class="big-msg-info">
+  {% blocktrans %}
+  This month doesn't have any event configured.
+  {% endblocktrans %}
+</div>
+{% endif %}
+</div>
+</div>
+
+{% endblock %}
diff --git a/chrono/manager/templates/chrono/manager_meetings_agenda_month_view.html b/chrono/manager/templates/chrono/manager_meetings_agenda_month_view.html
new file mode 100644
index 0000000..7f0b963
--- /dev/null
+++ b/chrono/manager/templates/chrono/manager_meetings_agenda_month_view.html
@@ -0,0 +1,56 @@
+{% extends "chrono/manager_agenda_month_view.html" %}
+{% load i18n %}
+
+{% block extra-actions %}
+<a href="{% url 'chrono-manager-agenda-day-view' pk=agenda.id year=view.date|date:"Y" month=view.date|date:"m" day=view.date|date:"d" %}">{% trans 'Day view' %}</a>
+{% endblock %}
+
+{% block content %}
+{% for week_days in view.get_timetable_infos %}
+{% if forloop.first %}
+<table class="agenda-table month-view">
+  <tbody>
+{% endif %}
+  <tr>
+    <th></th>
+    {% for day in week_days.days %}
+    <th class="weekday {% if day.today %}today{% endif %}">{% if not day.other_month %}<a href="{% url 'chrono-manager-agenda-day-view' pk=agenda.id year=day.date|date:"Y" month=day.date|date:"m" day=day.date|date:"d" %}">{{ day.date|date:"l j" }}</a>{% endif %}</th>
+    {% endfor %}
+  </tr>
+  {% for hour in week_days.periods %}
+  <tr class="{% cycle 'odd' 'even' %}">
+    <th class="hour">{{ hour|date:"TIME_FORMAT" }}</th>
+    {% for day in week_days.days %}
+    <td class="{% if day.other_month %}other-month{% endif %} {% if day.today %}today{% endif %}">
+      {% if forloop.parentloop.first %}
+      {% for slot in day.infos.opening_hours %}
+      <div class="opening-hours" style="height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%;left:{{ slot.css_left|stringformat:".1f" }}%;"></div>
+      {% endfor %}
+      {% for slot in day.infos.booked_slots %}
+      <div class="booking" style="left:{{ slot.css_left|stringformat:".1f" }}%;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.backoffice_url %}href="{{slot.booking.backoffice_url}}"{% endif %}
+           >{% if slot.booking.label or slot.booking.user_name %}
+          {{slot.booking.label}}{% if slot.booking.label and slot.booking.user_name %} - {% endif %} {{slot.booking.user_name}}
+          {% else %}{% trans "booked" %}{% endif %}</a>
+        <span class="desk">{{ slot.desk }}</span>
+        </div>
+      {% endfor %}
+      {% endif %}
+    </td>
+    {% endfor %}
+  </tr>
+  {% endfor %}
+  {% resetcycle %}
+{% if forloop.last %}
+  </tbody>
+</table>
+{% endif %}
+
+{% empty %}
+<div class="closed-for-the-day">
+  <p>{% trans "No opening hours this month." %}</p>
+</div>
+{% endfor %}
+
+{% endblock %}
diff --git a/chrono/manager/views.py b/chrono/manager/views.py
index 4ffdc76..5abc456 100644
--- a/chrono/manager/views.py
+++ b/chrono/manager/views.py
@@ -160,17 +160,42 @@ class AgendasImportView(FormView):
 agendas_import = AgendasImportView.as_view()
 
 
-class AgendaEditView(UpdateView):
+class ViewableAgendaMixin(object):
+    agenda = None
+
+    def dispatch(self, request, *args, **kwargs):
+        self.agenda = get_object_or_404(Agenda, id=kwargs.get('pk'))
+        if not self.check_permissions(request.user):
+            raise PermissionDenied()
+        return super(ViewableAgendaMixin, self).dispatch(request, *args, **kwargs)
+
+    def check_permissions(self, user):
+        return self.agenda.can_be_viewed(user)
+
+    def get_context_data(self, **kwargs):
+        context = super(ViewableAgendaMixin, self).get_context_data(**kwargs)
+        context['agenda'] = self.agenda
+        return context
+
+
+class ManagedAgendaMixin(ViewableAgendaMixin):
+    def check_permissions(self, user):
+        return self.agenda.can_be_managed(user)
+
+    def get_initial(self):
+        initial = super(ManagedAgendaMixin, self).get_initial()
+        initial['agenda'] = self.agenda.id
+        return initial
+
+    def get_success_url(self):
+        return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id})
+
+
+class AgendaEditView(ManagedAgendaMixin, UpdateView):
     template_name = 'chrono/manager_agenda_form.html'
     model = Agenda
     form_class = AgendaEditForm
 
-    def get_object(self, queryset=None):
-        obj = super(AgendaEditView, self).get_object(queryset=queryset)
-        if not obj.can_be_managed(self.request.user):
-            raise PermissionDenied()
-        return obj
-
     def get_success_url(self):
         return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.object.id})
 
@@ -208,35 +233,46 @@ class AgendaDeleteView(DeleteView):
 agenda_delete = AgendaDeleteView.as_view()
 
 
-class AgendaView(DetailView):
+class AgendaView(ViewableAgendaMixin, ListView):
     model = Agenda
 
     def get(self, request, *args, **kwargs):
-        try:
-            agenda = Agenda.objects.get(id=kwargs.get('pk'))
-        except Agenda.DoesNotExist:
-            raise Http404()
-        if not agenda.can_be_viewed(self.request.user):
-            raise PermissionDenied()
-
-        if agenda.kind == 'meetings':
+        today = datetime.date.today()
+        if self.agenda.kind == 'meetings':
             # redirect to today view
-            today = datetime.date.today()
             return HttpResponseRedirect(
                 reverse(
                     'chrono-manager-agenda-day-view',
-                    kwargs={'pk': agenda.id, 'year': today.year, 'month': today.month, 'day': today.day},
+                    kwargs={'pk': self.agenda.id, 'year': today.year, 'month': today.month, 'day': today.day},
                 )
             )
 
-        # redirect to settings
-        return HttpResponseRedirect(reverse('chrono-manager-agenda-settings', kwargs={'pk': agenda.id}))
+        if self.agenda.kind == 'events':
+            # redirect to monthly view, to first month where there are events,
+            # otherwise to latest month with events, otherwise to this month.
+            event = self.agenda.event_set.filter(
+                start_datetime__gte=datetime.date(today.year, today.month, 1)
+            ).first()
+            if not event:
+                event = self.agenda.event_set.filter(
+                    start_datetime__lte=datetime.date(today.year, today.month, 1)
+                ).last()
+            if event:
+                day = event.start_datetime
+            else:
+                day = today
+            return HttpResponseRedirect(
+                reverse(
+                    'chrono-manager-agenda-month-view',
+                    kwargs={'pk': self.agenda.id, 'year': day.year, 'month': day.month},
+                )
+            )
 
 
 agenda_view = AgendaView.as_view()
 
 
-class AgendaDateView(object):
+class AgendaDateView(ViewableAgendaMixin):
     model = Event
     month_format = '%m'
     date_field = 'start_datetime'
@@ -244,12 +280,6 @@ class AgendaDateView(object):
     allow_future = True
 
     def dispatch(self, request, *args, **kwargs):
-        self.agenda = get_object_or_404(Agenda, id=kwargs.get('pk'))
-        if self.agenda.kind != 'meetings':
-            raise Http404()
-        if not self.agenda.can_be_viewed(request.user):
-            raise PermissionDenied()
-
         # specify 6am time to get the expected timezone on daylight saving time
         # days.
         try:
@@ -267,7 +297,7 @@ class AgendaDateView(object):
             return HttpResponseRedirect(
                 reverse(
                     'chrono-manager-agenda-day-view',
-                    kwargs={'pk': self.agenda.id, 'year': date.year, 'month': date.month, 'day': date.day},
+                    kwargs={'pk': kwargs['pk'], 'year': date.year, 'month': date.month, 'day': date.day},
                 )
             )
         return super(AgendaDateView, self).dispatch(request, *args, **kwargs)
@@ -275,10 +305,11 @@ class AgendaDateView(object):
     def get_context_data(self, **kwargs):
         context = super(AgendaDateView, self).get_context_data(**kwargs)
         context['agenda'] = self.agenda
-        try:
-            context['hour_span'] = max(60 // self.agenda.get_base_meeting_duration(), 1)
-        except ValueError:  # no meeting types defined
-            context['hour_span'] = 1
+        if self.agenda.kind == 'meetings':
+            try:
+                context['hour_span'] = max(60 // self.agenda.get_base_meeting_duration(), 1)
+            except ValueError:  # no meeting types defined
+                context['hour_span'] = 1
         context['user_can_manage'] = self.agenda.can_be_managed(self.request.user)
         return context
 
@@ -301,6 +332,11 @@ class AgendaDateView(object):
 class AgendaDayView(AgendaDateView, DayArchiveView):
     template_name = 'chrono/manager_agenda_day_view.html'
 
+    def dispatch(self, request, *args, **kwargs):
+        # day view should only exist for meetings kind.
+        get_object_or_404(Agenda, id=kwargs.get('pk'), kind='meetings')
+        return super(AgendaDayView, self).dispatch(request, *args, **kwargs)
+
     def get_previous_day_url(self):
         previous_day = self.date.date() - datetime.timedelta(days=1)
         return reverse(
@@ -385,7 +421,8 @@ agenda_day_view = AgendaDayView.as_view()
 
 
 class AgendaMonthView(AgendaDateView, MonthArchiveView):
-    template_name = 'chrono/manager_agenda_month_view.html'
+    def get_template_names(self):
+        return ['chrono/manager_%s_agenda_month_view.html' % self.agenda.kind]
 
     def get_previous_month_url(self):
         previous_month = self.get_previous_month(self.date.date())
@@ -507,32 +544,6 @@ class AgendaMonthView(AgendaDateView, MonthArchiveView):
 agenda_monthly_view = AgendaMonthView.as_view()
 
 
-class ManagedAgendaMixin(object):
-    agenda = None
-
-    def dispatch(self, request, *args, **kwargs):
-        try:
-            self.agenda = Agenda.objects.get(id=kwargs.get('pk'))
-        except Agenda.DoesNotExist:
-            raise Http404()
-        if not self.agenda.can_be_managed(request.user):
-            raise PermissionDenied()
-        return super(ManagedAgendaMixin, self).dispatch(request, *args, **kwargs)
-
-    def get_context_data(self, **kwargs):
-        context = super(ManagedAgendaMixin, self).get_context_data(**kwargs)
-        context['agenda'] = self.agenda
-        return context
-
-    def get_initial(self):
-        initial = super(ManagedAgendaMixin, self).get_initial()
-        initial['agenda'] = self.agenda.id
-        return initial
-
-    def get_success_url(self):
-        return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id})
-
-
 class ManagedAgendaSubobjectMixin(object):
     agenda = None
 
@@ -600,22 +611,9 @@ class ManagedDeskSubobjectMixin(object):
 class AgendaSettings(ManagedAgendaMixin, DetailView):
     model = Agenda
 
-    def dispatch(self, request, *args, **kwargs):
-        try:
-            self.agenda = Agenda.objects.get(id=kwargs.get('pk'))
-        except Agenda.DoesNotExist:
-            raise Http404()
-        if not self.agenda.can_be_managed(request.user):
-            # "events" agendas settings page can be access by user with the
-            # view permission as there are no other "view" page for this type
-            # of agenda.
-            if self.agenda.kind != 'events' or not self.agenda.can_be_viewed(request.user):
-                raise PermissionDenied()
-        return super(DetailView, self).dispatch(request, *args, **kwargs)
-
     def get_context_data(self, **kwargs):
         context = super(AgendaSettings, self).get_context_data(**kwargs)
-        context['user_can_manage'] = self.get_object().can_be_managed(self.request.user)
+        context['user_can_manage'] = True
         return context
 
     def get_template_names(self):
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 4a32670..f2b703e 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -159,11 +159,8 @@ def test_view_agendas_as_manager(app, manager_user):
     # check user doesn't have access
     app.get('/manage/agendas/%s/' % agenda2.id, status=403)
 
-    # check view gives access to the settings page for "events" agenda
-    resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
-    # but there's no links to actions
-    assert not '>New Event<' in resp.text
-    assert not '>Options<' in resp.text
+    # check there's no access to the settings page for "events" agenda
+    resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=403)
     app.get('/manage/agendas/%s/add-event' % agenda.id, status=403)
     app.get('/manage/agendas/%s/edit' % agenda.id, status=403)
 
@@ -207,6 +204,7 @@ def test_options_agenda(app, admin_user):
     app = login(app)
     resp = app.get('/manage/', status=200)
     resp = resp.click('Foo bar').follow()
+    resp = resp.click('Settings')
     resp = resp.click('Options')
     assert resp.form['label'].value == 'Foo bar'
     resp.form['label'] = 'Foo baz'
@@ -225,7 +223,7 @@ def test_options_agenda_as_manager(app, manager_user):
     resp = app.get('/manage/', status=200)
     resp = resp.click('Foo bar')
     assert not 'Settings' in resp.text
-    resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)  # ok for "events" agendas
+    resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=403)
     resp = app.get('/manage/agendas/%s/edit' % agenda.id, status=403)
     agenda.kind = 'meetings'
     agenda.save()
@@ -241,6 +239,7 @@ def test_options_agenda_as_manager(app, manager_user):
 
     resp = app.get('/manage/', status=200)
     resp = resp.click('Foo bar').follow()
+    resp = resp.click('Settings')
     resp = resp.click('Options')
     assert resp.form['label'].value == 'Foo bar'
     resp.form['label'] = 'Foo baz'
@@ -257,6 +256,7 @@ def test_delete_agenda(app, admin_user):
     app = login(app)
     resp = app.get('/manage/', status=200)
     resp = resp.click('Foo bar').follow()
+    resp = resp.click('Settings')
     resp = resp.click('Delete')
     resp = resp.form.submit()
     assert resp.location.endswith('/manage/')
@@ -273,6 +273,7 @@ def test_delete_busy_agenda(app, admin_user):
     app = login(app)
     resp = app.get('/manage/', status=200)
     resp = resp.click('Foo bar').follow()
+    resp = resp.click('Settings')
     resp = resp.click('Delete')
     assert 'Are you sure you want to delete this?' in resp.text
 
@@ -280,6 +281,7 @@ def test_delete_busy_agenda(app, admin_user):
     booking.save()
     resp = app.get('/manage/', status=200)
     resp = resp.click('Foo bar').follow()
+    resp = resp.click('Settings')
     resp = resp.click('Delete')
     assert 'This cannot be removed' in resp.text
 
@@ -287,6 +289,7 @@ def test_delete_busy_agenda(app, admin_user):
     booking.save()
     resp = app.get('/manage/', status=200)
     resp = resp.click('Foo bar').follow()
+    resp = resp.click('Settings')
     resp = resp.click('Delete')
     assert 'Are you sure you want to delete this?' in resp.text
 
@@ -304,6 +307,7 @@ def test_delete_agenda_as_manager(app, manager_user):
     app = login(app, username='manager', password='manager')
     resp = app.get('/manage/', status=200)
     resp = resp.click('Foo bar').follow()
+    resp = resp.click('Settings')
     assert 'Options' in resp.text
     assert 'Delete' not in resp.text
     resp = app.get('/manage/agendas/%s/delete' % agenda.id, status=403)
@@ -391,6 +395,7 @@ def test_add_event_as_manager(app, manager_user):
     agenda.save()
 
     resp = app.get('/manage/agendas/%s/' % agenda.id).follow()
+    resp = resp.click('Settings')
     assert '<h2>Settings' in resp.text
     resp = resp.click('New Event')
     resp.form['start_datetime'] = '2016-02-15 17:00'
@@ -1683,6 +1688,44 @@ def test_agenda_invalid_day_view(app, admin_user, manager_user, api_user):
     assert resp.location.endswith('2018/11/30/')
 
 
+def test_agenda_events_month_view(app, admin_user, manager_user, api_user):
+    agenda = Agenda.objects.create(label='Events', kind='events')
+
+    login(app)
+    resp = app.get('/manage/agendas/%s/' % agenda.id, status=302)
+    resp = resp.follow()
+    assert "This month doesn't have any event configured." in resp.text
+    today = datetime.date.today()
+    assert resp.request.url.endswith('%s/%s/' % (today.year, today.month))
+
+    # add event in a future month
+    event = Event(label='xyz', start_datetime=now() + datetime.timedelta(days=40), places=10, agenda=agenda)
+    event.save()
+    resp = app.get('/manage/agendas/%s/' % agenda.id).follow()
+    assert 'xyz' in resp.text
+    day = event.start_datetime
+    assert resp.request.url.endswith('%s/%s/' % (day.year, day.month))
+
+    # current month still doesn't have events
+    resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month))
+    assert "This month doesn't have any event configured." in resp.text
+
+    # add event in the past
+    event2 = Event(label='zyx', start_datetime=now() - datetime.timedelta(days=40), places=10, agenda=agenda)
+    event2.save()
+    resp = app.get('/manage/agendas/%s/' % agenda.id).follow()
+    assert 'xyz' in resp.text
+    day = event.start_datetime  # still the future event
+    assert resp.request.url.endswith('%s/%s/' % (day.year, day.month))
+
+    # remove future event
+    event.delete()
+    resp = app.get('/manage/agendas/%s/' % agenda.id).follow()
+    assert 'zyx' in resp.text
+    day = event2.start_datetime  # now the past event
+    assert resp.request.url.endswith('%s/%s/' % (day.year, day.month))
+
+
 def test_agenda_month_view(app, admin_user, manager_user, api_user):
     agenda = Agenda.objects.create(label='Passeports', kind='meetings')
     desk = Desk.objects.create(agenda=agenda, label='Desk A')
-- 
2.24.1

