chrono/chrono/manager/views.py

425 lines
14 KiB
Python

# chrono - agendas system
# Copyright (C) 2016 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse, reverse_lazy
from django.db.models import Q
from django.http import HttpResponse, Http404
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext
from django.utils.encoding import force_text
from django.views.generic import (DetailView, CreateView, UpdateView,
ListView, DeleteView, FormView, TemplateView)
from chrono.agendas.models import (Agenda, Event, MeetingType, TimePeriod,
Booking, Desk, TimePeriodException, ICSError)
from .forms import (EventForm, NewMeetingTypeForm, MeetingTypeForm,
TimePeriodForm, ImportEventsForm, NewDeskForm, DeskForm, TimePeriodExceptionForm,
ExceptionsImportForm)
class HomepageView(ListView):
template_name = 'chrono/manager_home.html'
model = Agenda
def get_queryset(self):
queryset = super(HomepageView, self).get_queryset()
if not self.request.user.is_staff:
group_ids = [x.id for x in self.request.user.groups.all()]
queryset = queryset.filter(Q(view_role_id__in=group_ids) | Q(edit_role_id__in=group_ids))
return queryset
homepage = HomepageView.as_view()
class AgendaAddView(CreateView):
template_name = 'chrono/manager_agenda_form.html'
model = Agenda
fields = ['label', 'kind', 'edit_role', 'view_role']
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
raise PermissionDenied()
return super(AgendaAddView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
model_form = super(AgendaAddView, self).form_valid(form)
if self.object.kind == 'meetings':
default_desk = Desk(agenda=self.object, label=_('Desk 1'))
default_desk.save()
return model_form
agenda_add = AgendaAddView.as_view()
class AgendaEditView(UpdateView):
template_name = 'chrono/manager_agenda_form.html'
model = Agenda
fields = ['label', 'slug', 'edit_role', 'view_role', 'minimal_booking_delay', 'maximal_booking_delay']
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
agenda_edit = AgendaEditView.as_view()
class AgendaDeleteView(DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = Agenda
success_url = reverse_lazy('chrono-manager-homepage')
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
raise PermissionDenied()
return super(AgendaDeleteView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(AgendaDeleteView, self).get_context_data(**kwargs)
context['cannot_delete'] = Booking.objects.filter(
event__agenda=self.get_object(),
event__start_datetime__gt=now(),
cancellation_datetime__isnull=True).exists()
return context
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data()
if context['cannot_delete']:
raise PermissionDenied()
return super(AgendaDeleteView, self).delete(request, *args, **kwargs)
agenda_delete = AgendaDeleteView.as_view()
class AgendaView(DetailView):
template_name = 'chrono/manager_agenda_view.html'
model = Agenda
def get_context_data(self, **kwargs):
context = super(AgendaView, self).get_context_data(**kwargs)
if not self.get_object().can_be_viewed(self.request.user):
raise PermissionDenied()
context['user_can_manage'] = self.get_object().can_be_managed(self.request.user)
return context
agenda_view = AgendaView.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 self.agenda.get_absolute_url()
class ManagedAgendaSubobjectMixin(object):
agenda = None
def dispatch(self, request, *args, **kwargs):
self.agenda = self.get_object().agenda
if not self.agenda.can_be_managed(request.user):
raise PermissionDenied()
return super(ManagedAgendaSubobjectMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(ManagedAgendaSubobjectMixin, self).get_context_data(**kwargs)
context['agenda'] = self.object.agenda
return context
def get_success_url(self):
return self.agenda.get_absolute_url()
class ManagedDeskMixin(object):
desk = None
def dispatch(self, request, *args, **kwargs):
try:
self.desk = Desk.objects.get(id=kwargs.get('pk'))
except Desk.DoesNotExist:
raise Http404()
if not self.desk.agenda.can_be_managed(request.user):
raise PermissionDenied()
return super(ManagedDeskMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(ManagedDeskMixin, self).get_context_data(**kwargs)
context['desk'] = self.desk
context['agenda'] = self.desk.agenda
return context
def get_initial(self):
initial = super(ManagedDeskMixin, self).get_initial()
initial['desk'] = self.desk
return initial
def get_success_url(self):
return self.desk.agenda.get_absolute_url()
class ManagedDeskSubobjectMixin(object):
desk = None
def dispatch(self, request, *args, **kwargs):
self.desk = self.get_object().desk
if not self.desk.agenda.can_be_managed(request.user):
raise PermissionDenied()
return super(ManagedDeskSubobjectMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(ManagedDeskSubobjectMixin, self).get_context_data(**kwargs)
context['desk'] = self.object.desk
context['agenda'] = self.object.desk.agenda
return context
def get_success_url(self):
return self.desk.agenda.get_absolute_url()
class AgendaAddEventView(ManagedAgendaMixin, CreateView):
template_name = 'chrono/manager_event_form.html'
model = Event
form_class = EventForm
agenda_add_event = AgendaAddEventView.as_view()
class AgendaImportEventsSampleView(TemplateView):
template_name = 'chrono/manager_sample_events.csv'
content_type = 'text/csv'
agenda_import_events_sample_csv = AgendaImportEventsSampleView.as_view()
class AgendaImportEventsView(ManagedAgendaMixin, FormView):
form_class = ImportEventsForm
template_name = 'chrono/manager_import_events.html'
agenda = None
def form_valid(self, form):
if form.events:
for event in form.events:
event.agenda_id = self.kwargs['pk']
event.save()
messages.info(self.request, _('%d events have been imported.') % len(form.events))
return super(AgendaImportEventsView, self).form_valid(form)
agenda_import_events = AgendaImportEventsView.as_view()
class EventEditView(ManagedAgendaSubobjectMixin, UpdateView):
template_name = 'chrono/manager_event_form.html'
model = Event
form_class = EventForm
event_edit = EventEditView.as_view()
class EventDeleteView(ManagedAgendaSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = Event
def get_context_data(self, **kwargs):
context = super(EventDeleteView, self).get_context_data(**kwargs)
context['cannot_delete'] = bool(
self.object.booking_set.filter(cancellation_datetime__isnull=True).exists() and
self.object.start_datetime > now())
return context
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data()
if context['cannot_delete']:
raise PermissionDenied()
return super(EventDeleteView, self).delete(request, *args, **kwargs)
event_delete = EventDeleteView.as_view()
class AgendaAddMeetingTypeView(ManagedAgendaMixin, CreateView):
template_name = 'chrono/manager_meeting_type_form.html'
model = Event
form_class = NewMeetingTypeForm
agenda_add_meeting_type = AgendaAddMeetingTypeView.as_view()
class MeetingTypeEditView(ManagedAgendaSubobjectMixin, UpdateView):
template_name = 'chrono/manager_meeting_type_form.html'
model = MeetingType
form_class = MeetingTypeForm
meeting_type_edit = MeetingTypeEditView.as_view()
class MeetingTypeDeleteView(ManagedAgendaSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = MeetingType
meeting_type_delete = MeetingTypeDeleteView.as_view()
class AgendaAddTimePeriodView(ManagedDeskMixin, CreateView):
template_name = 'chrono/manager_time_period_form.html'
model = TimePeriod
form_class = TimePeriodForm
agenda_add_time_period = AgendaAddTimePeriodView.as_view()
class TimePeriodEditView(ManagedDeskSubobjectMixin, UpdateView):
template_name = 'chrono/manager_time_period_form.html'
model = TimePeriod
form_class = TimePeriodForm
time_period_edit = TimePeriodEditView.as_view()
class TimePeriodDeleteView(ManagedDeskSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = TimePeriod
time_period_delete = TimePeriodDeleteView.as_view()
class AgendaAddDesk(ManagedAgendaMixin, CreateView):
template_name = 'chrono/manager_desk_form.html'
model = Desk
form_class = NewDeskForm
agenda_add_desk = AgendaAddDesk.as_view()
class DeskEditView(ManagedAgendaSubobjectMixin, UpdateView):
template_name = 'chrono/manager_desk_form.html'
model = Desk
form_class = DeskForm
desk_edit = DeskEditView.as_view()
class DeskDeleteView(ManagedAgendaSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = Desk
desk_delete = DeskDeleteView.as_view()
class AgendaAddTimePeriodExceptionView(ManagedDeskMixin, CreateView):
template_name = 'chrono/manager_time_period_exception_form.html'
model = TimePeriodException
form_class = TimePeriodExceptionForm
agenda_add_time_period_exception = AgendaAddTimePeriodExceptionView.as_view()
class TimePeriodExceptionEditView(ManagedDeskSubobjectMixin, UpdateView):
template_name = 'chrono/manager_time_period_exception_form.html'
model = TimePeriodException
form_class = TimePeriodExceptionForm
time_period_exception_edit = TimePeriodExceptionEditView.as_view()
class TimePeriodExceptionListView(ManagedDeskMixin, ListView):
template_name = 'chrono/manager_time_period_exception_list.html'
model = TimePeriodException
def get_queryset(self):
return self.model.objects.filter(desk=self.desk)
def get_context_data(self, **kwargs):
context = super(TimePeriodExceptionListView, self).get_context_data(**kwargs)
context['user_can_manage'] = self.desk.agenda.can_be_managed(self.request.user)
return context
time_period_exception_list = TimePeriodExceptionListView.as_view()
class TimePeriodExceptionDeleteView(ManagedDeskSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = TimePeriodException
time_period_exception_delete = TimePeriodExceptionDeleteView.as_view()
class DeskImportTimePeriodExceptionsView(ManagedAgendaSubobjectMixin, UpdateView):
model = Desk
form_class = ExceptionsImportForm
template_name = 'chrono/manager_import_exceptions.html'
def form_valid(self, form):
try:
exceptions = form.instance.create_timeperiod_exceptions_from_ics(form.cleaned_data['ics_file'])
except ICSError as e:
form.add_error(None, unicode(e))
return self.form_invalid(form)
message = ungettext('An exception has been imported.',
'%(count)d exceptions have been imported.', exceptions)
message = message % {'count': exceptions}
messages.info(self.request, message)
return super(DeskImportTimePeriodExceptionsView, self).form_valid(form)
desk_import_time_period_exceptions = DeskImportTimePeriodExceptionsView.as_view()
def menu_json(request):
response = HttpResponse(content_type='application/json')
label = _('Agendas')
json_str = json.dumps([{'label': force_text(label),
'slug': 'calendar',
'url': request.build_absolute_uri(reverse('chrono-manager-homepage'))
}])
for variable in ('jsonpCallback', 'callback'):
if variable in request.GET:
json_str = '%s(%s);' % (request.GET[variable], json_str)
break
response.write(json_str)
return response