diff --git a/chrono/manager/templates/chrono/manager_agenda_month_view.html b/chrono/manager/templates/chrono/manager_agenda_month_view.html
index 976aafc5..8adcf605 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 @@
{% trans 'Settings' %}
{% endif %}
{% trans 'Print' %}
-{% trans 'Day view' %}
-{% endblock %}
+{% block extra-actions %}{% endblock %}
-
-{% block content %}
-{% for week_days in view.get_timetable_infos %}
-{% if forloop.first %}
-
-
-{% endif %}
-
- |
- {% for day in week_days.days %}
- {% if not day.other_month %}{{ day.date|date:"l j" }}{% endif %} |
- {% endfor %}
-
- {% for hour in week_days.periods %}
-
- {{ hour|date:"TIME_FORMAT" }} |
- {% for day in week_days.days %}
-
- {% if forloop.parentloop.first %}
- {% for slot in day.infos.opening_hours %}
-
- {% endfor %}
- {% for slot in day.infos.booked_slots %}
-
- {% endfor %}
- {% endif %}
- |
- {% endfor %}
-
- {% endfor %}
- {% resetcycle %}
-{% if forloop.last %}
-
-
-{% endif %}
-
-{% empty %}
-
-
{% trans "No opening hours this month." %}
-
-{% 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 00000000..08a2a533
--- /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 %}
+
+
{% trans "Events" %}
+
+{% if object_list %}
+
+ {% include "gadjo/pagination.html" %}
+{% else %}
+
+ {% blocktrans %}
+ This month doesn't have any event configured.
+ {% endblocktrans %}
+
+{% endif %}
+
+
+
+{% 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 00000000..7f0b9637
--- /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 %}
+{% trans 'Day view' %}
+{% endblock %}
+
+{% block content %}
+{% for week_days in view.get_timetable_infos %}
+{% if forloop.first %}
+
+
+{% endif %}
+
+ |
+ {% for day in week_days.days %}
+ {% if not day.other_month %}{{ day.date|date:"l j" }}{% endif %} |
+ {% endfor %}
+
+ {% for hour in week_days.periods %}
+
+ {{ hour|date:"TIME_FORMAT" }} |
+ {% for day in week_days.days %}
+
+ {% if forloop.parentloop.first %}
+ {% for slot in day.infos.opening_hours %}
+
+ {% endfor %}
+ {% for slot in day.infos.booked_slots %}
+
+ {% endfor %}
+ {% endif %}
+ |
+ {% endfor %}
+
+ {% endfor %}
+ {% resetcycle %}
+{% if forloop.last %}
+
+
+{% endif %}
+
+{% empty %}
+
+
{% trans "No opening hours this month." %}
+
+{% endfor %}
+
+{% endblock %}
diff --git a/chrono/manager/views.py b/chrono/manager/views.py
index 3a695914..030f99ee 100644
--- a/chrono/manager/views.py
+++ b/chrono/manager/views.py
@@ -39,6 +39,7 @@ from django.views.generic import (
TemplateView,
DayArchiveView,
MonthArchiveView,
+ View,
)
from chrono.agendas.models import (
@@ -160,17 +161,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 +234,44 @@ class AgendaDeleteView(DeleteView):
agenda_delete = AgendaDeleteView.as_view()
-class AgendaView(DetailView):
- model = Agenda
-
+class AgendaView(ViewableAgendaMixin, View):
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 +279,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 +296,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 +304,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 +331,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 +420,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())
@@ -518,32 +554,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
@@ -611,22 +621,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 68933802..271ffe11 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 'Settings' in resp.text
resp = resp.click('New Event')
resp.form['start_datetime'] = '2016-02-15 17:00'
@@ -1685,6 +1690,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')