From 8c52ecd6693eb0e935465d53f3411ac320f7954f Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Mon, 20 Feb 2012 17:46:45 +0100 Subject: [PATCH] generic views for the Agenda-App --- openslides/agenda/api.py | 21 ---- openslides/agenda/forms.py | 2 +- openslides/agenda/urls.py | 66 ++++++---- openslides/agenda/views.py | 245 +++++++++++++++++++------------------ openslides/utils/views.py | 100 ++++++++++++++- 5 files changed, 271 insertions(+), 163 deletions(-) diff --git a/openslides/agenda/api.py b/openslides/agenda/api.py index 38022c9ea..757ea2db6 100644 --- a/openslides/agenda/api.py +++ b/openslides/agenda/api.py @@ -10,12 +10,7 @@ :license: GNU GPL, see LICENSE for more details. """ -from django.utils.translation import ugettext as _ -from django.contrib import messages -from django.core.context_processors import csrf - from system import config -from projector.api import get_active_slide def is_summary(): @@ -25,19 +20,3 @@ def is_summary(): if config['agenda_summary']: return True return False - - -def gen_confirm_form_for_items(request, message, url, singleitem=None): - if singleitem: - messages.warning(request, '%s
' % (message, url, csrf(request)['csrf_token'], _("Yes"), _("No"))) - else: - messages.warning(request, '%s
' % (message, url, csrf(request)['csrf_token'], _("Yes"), _("Yes, with all child items."), _("No"))) - - -def del_confirm_form_for_items(request, object, name=None): - if name is None: - name = object - if object.children: - gen_confirm_form_for_items(request, _('Do you really want to delete %s?') % name, object.get_absolute_url('delete'), False) - else: - gen_confirm_form_for_items(request, _('Do you really want to delete %s?') % name, object.get_absolute_url('delete'), True) diff --git a/openslides/agenda/forms.py b/openslides/agenda/forms.py index 1b52720f8..50a00c2e1 100644 --- a/openslides/agenda/forms.py +++ b/openslides/agenda/forms.py @@ -18,7 +18,7 @@ from mptt.forms import TreeNodeChoiceField from agenda.models import Item -class ItemFormText(ModelForm): +class ItemForm(ModelForm): error_css_class = 'error' required_css_class = 'required' diff --git a/openslides/agenda/urls.py b/openslides/agenda/urls.py index 4275e96b8..17a1ed365 100644 --- a/openslides/agenda/urls.py +++ b/openslides/agenda/urls.py @@ -11,36 +11,60 @@ """ from django.conf.urls.defaults import * +from agenda.views import Overview, View, SetActive, SetClosed, ItemUpdate, ItemCreate, ItemDelete urlpatterns = patterns('agenda.views', - url(r'^/$', 'overview', - name='item_overview'), + url(r'^$', + Overview.as_view(), + name='item_overview', + ), - url(r'^(?P\d+)/$', 'view', - name='item_view'), + url(r'^(?P\d+)/$', + View.as_view(), + name='item_view', + ), - url(r'^(?P\d+)/activate/$', 'set_active', - name='item_activate'), + url(r'^(?P\d+)/activate/$', + SetActive.as_view(), + {'summary': False}, + name='item_activate', + ), - url(r'^(?P\d+)/activate/summary/$', 'set_active', - {'summary': True},\ - name='item_activate_summary'), + url(r'^(?P\d+)/activate/summary/$', + SetActive.as_view(), + {'summary': True}, + name='item_activate_summary', + ), - url(r'^(?P\d+)/close/$', 'set_closed', {'closed': True}, - name='item_close'), + url(r'^(?P\d+)/close/$', + SetClosed.as_view(), + {'closed': True}, + name='item_close', + ), - url(r'^(?P\d+)/open/$', 'set_closed', {'closed': False}, - name='item_open'), + url(r'^(?P\d+)/open/$', + SetClosed.as_view(), + {'closed': False}, + name='item_open', + ), - url(r'^(?P\d+)/edit/$', 'edit', - name='item_edit'), + url(r'^(?P\d+)/edit/$', + ItemUpdate.as_view(), + name='item_edit', + ), - url(r'^new/$', 'edit', - name='item_new'), + url(r'^new/$', + ItemCreate.as_view(), + name='item_new', + ), - url(r'^(?P\d+)/del/$', 'delete', - name='item_delete'), + url(r'^(?P\d+)/del/$', + ItemDelete.as_view(), + name='item_delete', + ), - url(r'^print/$', 'print_agenda', - name='print_agenda'), + url(r'^print/$', + 'print_agenda', + name='print_agenda', + ), ) diff --git a/openslides/agenda/views.py b/openslides/agenda/views.py index 3e67ea4ae..94c3b431a 100644 --- a/openslides/agenda/views.py +++ b/openslides/agenda/views.py @@ -9,44 +9,54 @@ :copyright: 2011 by the OpenSlides team, see AUTHORS. :license: GNU GPL, see LICENSE for more details. """ -from django.shortcuts import redirect from django.core.urlresolvers import reverse from django.contrib import messages from django.utils.translation import ugettext as _ +from django.core.context_processors import csrf + +from utils.pdf import print_agenda +from utils.views import TemplateView, RedirectView, UpdateView, CreateView, DeleteView from system import config from projector.api import get_active_slide, set_active_slide from agenda.models import Item -from agenda.api import is_summary, del_confirm_form_for_items -from agenda.forms import ItemOrderForm, ItemFormText - -from utils.utils import template, permission_required, \ - del_confirm_form, ajax_request -from utils.pdf import print_agenda +from agenda.api import is_summary +from agenda.forms import ItemOrderForm, ItemForm -@permission_required('agenda.can_see_projector') -@template('projector/AgendaText.html') -def view(request, item_id): - """ - Shows the Slide. - """ - item = Item.objects.get(pk=item_id) - return { - 'item': item, - 'ajax': 'off', - } +class View(TemplateView): + permission_required = 'agenda.can_see_projector' + template_name = 'projector/AgendaText.html' + + def get_context_data(self, **kwargs): + context = super(View, self).get_context_data(**kwargs) + context.update({ + 'item': Item.objects.get(pk=kwargs['item_id']), + 'ajax': 'off', + }) + return context -@permission_required('agenda.can_see_agenda') -@template('agenda/overview.html') -def overview(request): - """ - Shows an overview of all items. - """ - if request.method == 'POST': +class Overview(TemplateView): + permission_required = 'agenda.can_see_agenda' + template_name = 'agenda/overview.html' + + def get_context_data(self, **kwargs): + context = super(TemplateView, self).get_context_data(**kwargs) + context.update({ + 'items': Item.objects.all(), + 'overview': get_active_slide(only_sid=True) == 'agenda_show', + 'summary': is_summary(), + 'countdown_visible': config['countdown_visible'], + 'countdown_time': config['agenda_countdown_time'], + }) + return context + + def post(self, request, *args, **kwargs): + context = self.get_context_data(**kwargs) + #todo: check for any erros in the forms befor saving the data for item in Item.objects.all(): form = ItemOrderForm(request.POST, prefix="i%d" % item.id) if form.is_valid(): @@ -57,124 +67,123 @@ def overview(request): item.parent = None item.weight = form.cleaned_data['weight'] item.save() - - items = Item.objects.all() - - if get_active_slide(only_sid=True) == 'agenda_show': - overview = True - else: - overview = False - return { - 'items': items, - 'overview': overview, - 'summary': is_summary(), - 'countdown_visible': config['countdown_visible'], - 'countdown_time': config['agenda_countdown_time'], - } + return self.render_to_response(context) -@permission_required('agenda.can_manage_agenda') -def set_active(request, item_id, summary=False): +class SetActive(RedirectView): """ Set an Item as the active one. """ - if item_id == "0": - set_active_slide("agenda_show") - else: - try: - item = Item.objects.get(pk=item_id) - item.set_active(summary) - except Item.DoesNotExist: - messages.error(request, _('Item ID %d does not exist.') % int(item_id)) - config["bigger"] = 100 - config["up"] = 0 - if request.is_ajax(): - return ajax_request({'active': item_id, 'summary': summary}) + url = 'item_overview' + allow_ajax = True + permission_required = 'agenda.can_manage_agenda' - return redirect(reverse('item_overview')) + def get_ajax_context(self, **kwargs): + context = super(SetActive, self).get_ajax_context(**kwargs) + context.update({ + 'active': kwargs['item_id'], + 'summary': is_summary(), + }) + return context + + def pre_redirect(self, request, *args, **kwargs): + item_id = kwargs['item_id'] + summary = kwargs['summary'] + if item_id == "0": + set_active_slide("agenda_show") + else: + try: + item = Item.objects.get(pk=item_id) + item.set_active(summary) + except Item.DoesNotExist: + messages.error(request, _('Item ID %d does not exist.') % int(item_id)) + config["bigger"] = 100 + config["up"] = 0 + return super(SetActive, self).pre_redirect(request, *args, **kwargs) -@permission_required('agenda.can_manage_agenda') -def set_closed(request, item_id, closed=True): +class SetClosed(RedirectView): """ Close or open an Item. """ - try: - item = Item.objects.get(pk=item_id) - item.set_closed(closed) - except Item.DoesNotExist: - messages.error(request, _('Item ID %d does not exist.') % int(item_id)) + permission_required = 'agenda.can_manage_agenda' + allow_ajax = True + url = 'item_overview' - if request.is_ajax(): + def get_ajax_context(self, **kwargs): + context = super(SetClosed, self).get_ajax_context(**kwargs) + closed = kwargs['closed'] if closed: - link = reverse('item_open', args=[item.id]) + link = reverse('item_open', args=[self.item.id]) else: - link = reverse('item_close', args=[item.id]) + link = reverse('item_close', args=[self.item.id]) + context.update({ + 'closed': kwargs['closed'], + 'link': link, + }) + return context - return ajax_request({'closed': closed, - 'link': link}) - return redirect(reverse('item_overview')) - - -@permission_required('agenda.can_manage_agenda') -@template('agenda/edit.html') -def edit(request, item_id=None): - """ - Show a form to edit an existing Item, or create a new one. - """ - if item_id is not None: + def pre_redirect(self, request, *args, **kwargs): + item_id = kwargs['item_id'] + closed = kwargs['closed'] try: item = Item.objects.get(pk=item_id) + item.set_closed(closed) except Item.DoesNotExist: messages.error(request, _('Item ID %d does not exist.') % int(item_id)) - return redirect(reverse('item_overview')) - else: - item = None - - if request.method == 'POST': - form = ItemFormText(request.POST, instance=item) - - if form.is_valid(): - item = form.save() - if item_id is None: - messages.success(request, _('New item was successfully created.')) - else: - messages.success(request, _('Item was successfully modified.')) - if not 'apply' in request.POST: - return redirect(reverse('item_overview')) - if item_id is None: - return redirect(reverse('item_edit', args=[item.id])) - else: - messages.error(request, _('Please check the form for errors.')) - else: - form = ItemFormText(instance=item) - return { - 'form': form, - 'item': item, - } + self.item = item + return super(SetClosed, self).pre_redirect(request, *args, **kwargs) -@permission_required('agenda.can_manage_agenda') -def delete(request, item_id): +class ItemUpdate(UpdateView): + permission_required = 'agenda.can_manage_agenda' + template_name = 'agenda/edit.html' + model = Item + context_object_name = 'item' + form_class = ItemForm + success_url = 'item_overview' + + +class ItemCreate(CreateView): + permission_required = 'agenda.can_manage_agenda' + template_name = 'agenda/edit.html' + model = Item + context_object_name = 'item' + form_class = ItemForm + success_url = 'item_overview' + + +class ItemDelete(DeleteView): """ Delete an Item. """ - try: - item = Item.objects.get(pk=item_id) - except Item.DoesNotExist: - messages.error(request, _('Item ID %d does not exist.') % int(item_id)) - return redirect(reverse('item_overview')) + permission_required = 'agenda.can_manage_agenda' + model = Item + url = 'item_overview' + + def pre_post_redirect(self, request, *args, **kwargs): + self.object = self.get_object() - if request.method == 'POST': if 'all' in request.POST: - item.delete() - messages.success(request, _("Item %s and his children were successfully deleted.") % item) + self.object.delete() + messages.success(request, _("Item %s and his children were successfully deleted.") % self.object) else: - for child in item.children: - child.parent = item.parent + for child in self.object.children: + child.parent = self.object.parent child.save() - item.delete() - messages.success(request, _("Item %s was successfully deleted.") % item) - else: - del_confirm_form_for_items(request, item) - return redirect(reverse('item_overview')) + self.object.delete() + messages.success(request, _("Item %s was successfully deleted.") % self.object) + + def gen_confirm_form(self, request, message, url, singleitem=None): + if singleitem: + messages.warning(request, '%s
' % (message, url, csrf(request)['csrf_token'], _("Yes"), _("No"))) + else: + messages.warning(request, '%s
' % (message, url, csrf(request)['csrf_token'], _("Yes"), _("Yes, with all child items."), _("No"))) + + def confirm_form(self, request, object, name=None): + if name is None: + name = object + if object.children: + self.gen_confirm_form(request, _('Do you really want to delete %s?') % name, object.get_absolute_url('delete'), False) + else: + self.gen_confirm_form(request, _('Do you really want to delete %s?') % name, object.get_absolute_url('delete'), True) diff --git a/openslides/utils/views.py b/openslides/utils/views.py index 0d17cfa94..58068daaa 100644 --- a/openslides/utils/views.py +++ b/openslides/utils/views.py @@ -1,7 +1,103 @@ +try: + import json +except ImportError: + import simplejson as json + from django.conf import settings -from django.http import HttpResponseServerError -from django.template import Context, loader, RequestContext +from django.http import HttpResponseServerError, HttpResponse +from django.core.urlresolvers import reverse +from django.template import loader, RequestContext from django.template.loader import render_to_string +from django.contrib.auth.decorators import login_required +from django.utils.decorators import method_decorator +from django.views.generic import (TemplateView as _TemplateView, + RedirectView as _RedirectView, + UpdateView as _UpdateView, + CreateView as _CreateView,) +from django.views.generic.detail import SingleObjectMixin + +from utils import render_to_forbitten + +FREE_TO_GO = 'free to go' + + +class LoginMixin(object): + @method_decorator(login_required) + def dispatch(self, request, *args, **kwargs): + return super(LoginMixin, self).dispatch(request, *args, **kwargs) + + +class PermissionMixin(object): + permission_required = FREE_TO_GO + + def dispatch(self, request, *args, **kwargs): + if self.permission_required == FREE_TO_GO: + has_permission = True + else: + has_permission = request.user.has_perm(self.permission_required) + + if has_permission: + if request.user.is_authenticated(): + path = urlquote(request.get_full_path()) + return HttpResponseRedirect("%s?next=%s" % (settings.LOGIN_URL, path)) + else: + return render_to_forbitten(request) + return super(LoginMixin, self).dispatch(request, *args, **kwargs) + + +class TemplateView(_TemplateView, PermissionMixin): + pass + + +class RedirectView(_RedirectView, PermissionMixin): + permanent = False + allow_ajax = False + + def pre_redirect(self, request, *args, **kwargs): + pass + + def pre_post_redirect(self, request, *args, **kwargs): + pass + + def get(self, request, *args, **kwargs): + if request.method == 'GET': + self.pre_redirect( request, *args, **kwargs) + elif request.method == 'POST': + self.pre_post_redirect(request, *args, **kwargs) + + if self.request.is_ajax() and self.allow_ajax: + return HttpResponse(json.dumps(self.get_ajax_context(**kwargs))) + return super(RedirectView, self).get(request, *args, **kwargs) + + def get_redirect_url(self, **kwargs): + return reverse(super(RedirectView, self).get_redirect_url(**kwargs)) + + def get_ajax_context(self, **kwargs): + return {} + + +class UpdateView(_UpdateView, PermissionMixin): + def get_success_url(self): + if 'apply' in self.request.POST: + return '' + return reverse(super(UpdateView, self).get_success_url()) + + +class CreateView(_CreateView, PermissionMixin): + def get_success_url(self): + if 'apply' in self.request.POST: + return reverse('item_edit', args=[self.object.id]) + return reverse(super(CreateView, self).get_success_url()) + + +class DeleteView(RedirectView, SingleObjectMixin): + def pre_redirect(self, request, *args, **kwargs): + self.object = self.get_object() + self.confirm_form(request, self.object) + + def pre_post_redirect(self, request, *args, **kwargs): + pass + def server_error(request, template_name='500.html'): """