425 lines
14 KiB
Python
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
|