2012-12-14 14:21:53 +01:00
|
|
|
import json
|
2014-08-16 09:25:18 +02:00
|
|
|
from io import BytesIO
|
2012-02-21 13:17:42 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
from django.conf import settings
|
2012-03-16 14:31:59 +01:00
|
|
|
from django.contrib import messages
|
2012-02-20 17:46:45 +01:00
|
|
|
from django.contrib.auth.decorators import login_required
|
2012-07-01 15:35:05 +02:00
|
|
|
from django.core.context_processors import csrf
|
2013-09-25 12:53:44 +02:00
|
|
|
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
2012-07-01 15:35:05 +02:00
|
|
|
from django.core.urlresolvers import reverse
|
2015-02-17 20:07:44 +01:00
|
|
|
from django.http import HttpResponse, HttpResponseRedirect
|
2013-09-25 12:53:44 +02:00
|
|
|
from django.utils.decorators import method_decorator
|
2015-02-17 20:07:44 +01:00
|
|
|
from django.utils.translation import ugettext as _
|
|
|
|
from django.utils.translation import ugettext_lazy
|
2013-09-25 12:53:44 +02:00
|
|
|
from django.views import generic as django_views
|
2015-02-17 20:07:44 +01:00
|
|
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
2013-09-25 12:53:44 +02:00
|
|
|
from reportlab.lib.units import cm
|
|
|
|
from reportlab.platypus import SimpleDocTemplate, Spacer
|
2015-02-12 22:42:54 +01:00
|
|
|
from rest_framework.response import Response
|
2015-02-17 20:07:44 +01:00
|
|
|
from rest_framework.views import APIView as _APIView
|
2012-02-20 17:46:45 +01:00
|
|
|
|
2013-10-24 18:48:16 +02:00
|
|
|
from .exceptions import OpenSlidesError
|
2014-03-27 20:30:15 +01:00
|
|
|
from .forms import CSVImportForm
|
2013-09-25 12:53:44 +02:00
|
|
|
from .pdf import firstPage, laterPages
|
|
|
|
from .signals import template_manipulation
|
|
|
|
from .utils import html_strong
|
2012-04-15 11:24:40 +02:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
View = django_views.View
|
2012-04-13 11:35:53 +02:00
|
|
|
|
|
|
|
|
2015-02-17 20:07:44 +01:00
|
|
|
class LoginMixin:
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Mixin for Views, that only can be viseted from users how are logedin.
|
|
|
|
"""
|
|
|
|
|
2012-02-20 17:46:45 +01:00
|
|
|
@method_decorator(login_required)
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Check if the user is loged in.
|
|
|
|
"""
|
2015-01-22 18:29:12 +01:00
|
|
|
return super().dispatch(request, *args, **kwargs)
|
2012-02-20 17:46:45 +01:00
|
|
|
|
|
|
|
|
2015-02-17 20:07:44 +01:00
|
|
|
class PermissionMixin:
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
2014-05-15 20:07:09 +02:00
|
|
|
Mixin for views, that only can be visited from users with special
|
|
|
|
permissions.
|
2013-09-25 12:53:44 +02:00
|
|
|
|
2014-05-15 20:07:09 +02:00
|
|
|
Set the attribute 'required_permission' to the required permission
|
|
|
|
string or override the method 'check_permission'.
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
2014-05-15 20:07:09 +02:00
|
|
|
required_permission = None
|
2012-02-20 17:46:45 +01:00
|
|
|
|
2014-05-15 20:07:09 +02:00
|
|
|
def check_permission(self, request, *args, **kwargs):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Checks if the user has the required permission.
|
|
|
|
"""
|
2014-05-15 20:07:09 +02:00
|
|
|
if self.required_permission is None:
|
2012-04-15 11:24:40 +02:00
|
|
|
return True
|
2012-02-20 17:46:45 +01:00
|
|
|
else:
|
2014-05-15 20:07:09 +02:00
|
|
|
return request.user.has_perm(self.required_permission)
|
2012-02-20 17:46:45 +01:00
|
|
|
|
2012-04-15 11:24:40 +02:00
|
|
|
def dispatch(self, request, *args, **kwargs):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Check if the user has the permission.
|
|
|
|
|
|
|
|
If the user is not logged in, redirect the user to the login page.
|
|
|
|
"""
|
2014-05-15 20:07:09 +02:00
|
|
|
if not self.check_permission(request, *args, **kwargs):
|
2012-03-18 14:33:53 +01:00
|
|
|
if not request.user.is_authenticated():
|
|
|
|
path = request.get_full_path()
|
2012-11-24 14:01:21 +01:00
|
|
|
return HttpResponseRedirect(
|
|
|
|
"%s?next=%s" % (settings.LOGIN_URL, path))
|
2012-02-20 17:46:45 +01:00
|
|
|
else:
|
2013-09-25 12:53:44 +02:00
|
|
|
raise PermissionDenied
|
2015-01-22 18:29:12 +01:00
|
|
|
return super().dispatch(request, *args, **kwargs)
|
2012-02-20 17:46:45 +01:00
|
|
|
|
|
|
|
|
2015-02-17 20:07:44 +01:00
|
|
|
class AjaxMixin:
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Mixin to response to an ajax request with an json object.
|
|
|
|
"""
|
|
|
|
|
2015-02-12 22:42:54 +01:00
|
|
|
def get_ajax_context(self, **context):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Returns a dictonary with the context for the ajax response.
|
|
|
|
"""
|
2015-02-12 22:42:54 +01:00
|
|
|
return context
|
2012-07-01 15:35:05 +02:00
|
|
|
|
|
|
|
def ajax_get(self, request, *args, **kwargs):
|
2015-02-12 22:42:54 +01:00
|
|
|
"""
|
|
|
|
Deprecated. Use ajax_response instead.
|
|
|
|
"""
|
|
|
|
return self.ajax_response()
|
|
|
|
|
|
|
|
def ajax_response(self):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Returns the HttpResponse.
|
|
|
|
"""
|
|
|
|
return HttpResponse(json.dumps(self.get_ajax_context()))
|
2012-07-01 15:35:05 +02:00
|
|
|
|
|
|
|
|
2015-02-17 20:07:44 +01:00
|
|
|
class ExtraContextMixin:
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Mixin to send the signal 'template_manipulation' to add extra content to the
|
|
|
|
context of the view.
|
|
|
|
|
|
|
|
For example this is used to add the main menu of openslides.
|
|
|
|
"""
|
|
|
|
|
2013-01-06 12:07:37 +01:00
|
|
|
def get_context_data(self, **kwargs):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Sends the signal.
|
|
|
|
"""
|
2013-01-06 12:07:37 +01:00
|
|
|
context = super(ExtraContextMixin, self).get_context_data(**kwargs)
|
|
|
|
template_manipulation.send(
|
2015-01-22 18:29:12 +01:00
|
|
|
sender=type(self), request=self.request, context=context)
|
2013-01-06 12:07:37 +01:00
|
|
|
return context
|
|
|
|
|
|
|
|
|
2015-02-17 20:07:44 +01:00
|
|
|
class UrlMixin:
|
2013-05-11 00:59:07 +02:00
|
|
|
url_name_args = None
|
2013-01-26 16:19:53 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def get_url(self, url_name=None, url=None, args=None, use_absolute_url_link=None):
|
|
|
|
"""
|
|
|
|
Returns an url.
|
2013-01-26 16:19:53 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
Tries
|
|
|
|
1. to use the reverse for the attribute 'url_name',
|
|
|
|
2. to use the attribute 'url' or
|
|
|
|
3. to use self.object.get_absolute_url().
|
2013-01-26 16:33:55 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
Uses the attribute 'use_absolute_url_link' as argument for
|
|
|
|
get_absolute_url in the third step. If the attribute is 'None' then
|
|
|
|
the default value of get_absolute_url is used.
|
|
|
|
|
|
|
|
Raises ImproperlyConfigured if no url can be found.
|
|
|
|
"""
|
|
|
|
if url_name:
|
|
|
|
value = reverse(url_name, args=args or [])
|
|
|
|
elif url:
|
|
|
|
value = url
|
2013-01-06 12:07:37 +01:00
|
|
|
else:
|
2015-01-22 18:29:12 +01:00
|
|
|
if use_absolute_url_link is None:
|
|
|
|
get_absolute_url_args = []
|
|
|
|
else:
|
|
|
|
get_absolute_url_args = [use_absolute_url_link]
|
|
|
|
|
2013-01-06 12:07:37 +01:00
|
|
|
try:
|
2015-01-22 18:29:12 +01:00
|
|
|
value = self.object.get_absolute_url(*get_absolute_url_args)
|
2013-01-06 12:07:37 +01:00
|
|
|
except AttributeError:
|
|
|
|
raise ImproperlyConfigured(
|
2014-01-11 17:57:24 +01:00
|
|
|
'No url to redirect to. See openslides.utils.views.UrlMixin '
|
|
|
|
'for more details.')
|
2013-09-25 12:53:44 +02:00
|
|
|
return value
|
2013-02-01 12:51:01 +01:00
|
|
|
|
|
|
|
def get_url_name_args(self):
|
2013-05-11 00:59:07 +02:00
|
|
|
"""
|
2013-09-25 12:53:44 +02:00
|
|
|
Returns the arguments for the url name.
|
|
|
|
|
|
|
|
Default is an empty list or [self.object.pk] if this exist.
|
2013-05-11 00:59:07 +02:00
|
|
|
"""
|
|
|
|
if self.url_name_args is not None:
|
2013-09-25 12:53:44 +02:00
|
|
|
value = self.url_name_args
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
pk = self.object.pk
|
|
|
|
except AttributeError:
|
|
|
|
value = []
|
|
|
|
else:
|
2015-01-22 18:29:12 +01:00
|
|
|
value = [pk] if pk else []
|
2013-09-25 12:53:44 +02:00
|
|
|
return value
|
2013-01-06 12:07:37 +01:00
|
|
|
|
|
|
|
|
2014-12-22 18:09:05 +01:00
|
|
|
class SingleObjectMixin(django_views.detail.SingleObjectMixin):
|
|
|
|
"""
|
|
|
|
Mixin for single objects from the database.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def dispatch(self, *args, **kwargs):
|
|
|
|
if not hasattr(self, 'object'):
|
|
|
|
# Save the object not only in the cache but in the public
|
|
|
|
# attribute self.object because Django expects this later.
|
|
|
|
# Because get_object() has an internal cache this line is not a
|
|
|
|
# performance problem.
|
|
|
|
self.object = self.get_object()
|
2015-01-02 22:03:34 +01:00
|
|
|
return super().dispatch(*args, **kwargs)
|
2014-12-22 18:09:05 +01:00
|
|
|
|
|
|
|
def get_object(self, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Returns the single object from database or cache.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
obj = self._object
|
|
|
|
except AttributeError:
|
2015-01-02 22:03:34 +01:00
|
|
|
obj = super().get_object(*args, **kwargs)
|
2014-12-22 18:09:05 +01:00
|
|
|
self._object = obj
|
|
|
|
return obj
|
|
|
|
|
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
class FormMixin(UrlMixin):
|
2013-05-11 00:59:07 +02:00
|
|
|
"""
|
2013-09-25 12:53:44 +02:00
|
|
|
Mixin for views with forms.
|
2013-05-11 00:59:07 +02:00
|
|
|
"""
|
2013-03-18 12:34:47 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
use_apply = True
|
|
|
|
success_url_name = None
|
|
|
|
success_url = None
|
|
|
|
success_message = None
|
|
|
|
apply_url_name = None
|
|
|
|
apply_url = None
|
|
|
|
error_message = ugettext_lazy('Please check the form for errors.')
|
2013-02-03 13:23:55 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def get_apply_url(self):
|
2013-05-11 00:59:07 +02:00
|
|
|
"""
|
2013-09-25 12:53:44 +02:00
|
|
|
Returns the url when the user clicks on 'apply'.
|
2013-05-11 00:59:07 +02:00
|
|
|
"""
|
2013-09-25 12:53:44 +02:00
|
|
|
return self.get_url(self.apply_url_name, self.apply_url,
|
|
|
|
args=self.get_url_name_args(),
|
|
|
|
use_absolute_url_link='update')
|
2012-08-10 21:00:13 +02:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def get_success_url(self):
|
2013-03-18 12:34:47 +01:00
|
|
|
"""
|
2013-09-25 12:53:44 +02:00
|
|
|
Returns the url when the user submits a form.
|
|
|
|
|
|
|
|
Redirects to get_apply_url if self.use_apply is True
|
2013-03-18 12:34:47 +01:00
|
|
|
"""
|
2013-09-25 12:53:44 +02:00
|
|
|
if self.use_apply and 'apply' in self.request.POST:
|
|
|
|
value = self.get_apply_url()
|
|
|
|
else:
|
|
|
|
value = self.get_url(self.success_url_name, self.success_url,
|
|
|
|
args=self.get_url_name_args())
|
|
|
|
return value
|
2012-08-10 21:00:13 +02:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def form_valid(self, form):
|
|
|
|
value = super(FormMixin, self).form_valid(form)
|
|
|
|
messages.success(self.request, self.get_success_message())
|
|
|
|
return value
|
2012-08-10 21:00:13 +02:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def form_invalid(self, form):
|
|
|
|
value = super(FormMixin, self).form_invalid(form)
|
|
|
|
messages.error(self.request, self.get_error_message())
|
|
|
|
return value
|
2012-08-10 21:00:13 +02:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def get_success_message(self):
|
|
|
|
return self.success_message
|
2012-08-10 21:00:13 +02:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def get_error_message(self):
|
|
|
|
return self.error_message
|
2012-08-10 21:00:13 +02:00
|
|
|
|
2012-10-28 19:59:41 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
class ModelFormMixin(FormMixin):
|
|
|
|
"""
|
|
|
|
Mixin for FormViews.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
|
"""
|
|
|
|
Called if the form is valid.
|
|
|
|
|
|
|
|
1. saves the form into the model,
|
|
|
|
2. calls 'self.manipulate_object,
|
|
|
|
3. saves the object in the database,
|
|
|
|
4. calls self.post_save.
|
|
|
|
"""
|
|
|
|
self.object = form.save(commit=False)
|
|
|
|
self.manipulate_object(form)
|
|
|
|
self.object.save()
|
|
|
|
self.post_save(form)
|
|
|
|
messages.success(self.request, self.get_success_message())
|
|
|
|
return HttpResponseRedirect(self.get_success_url())
|
2012-10-28 19:59:41 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def manipulate_object(self, form):
|
|
|
|
"""
|
|
|
|
Called before the object is saved into the database.
|
|
|
|
"""
|
2012-10-28 19:59:41 +01:00
|
|
|
pass
|
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def post_save(self, form):
|
|
|
|
"""
|
|
|
|
Called after the object is saved into the database.
|
|
|
|
"""
|
|
|
|
form.save_m2m()
|
2012-08-10 21:00:13 +02:00
|
|
|
|
|
|
|
|
2015-02-17 20:07:44 +01:00
|
|
|
class CSRFMixin:
|
|
|
|
"""
|
|
|
|
Adds the csrf cookie to the response.
|
|
|
|
"""
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def as_view(cls, *args, **kwargs):
|
|
|
|
view = super().as_view(*args, **kwargs)
|
|
|
|
return ensure_csrf_cookie(view)
|
|
|
|
|
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
class TemplateView(PermissionMixin, ExtraContextMixin, django_views.TemplateView):
|
|
|
|
"""
|
|
|
|
View to return with an template.
|
|
|
|
"""
|
2013-01-06 12:07:37 +01:00
|
|
|
pass
|
2012-02-20 17:46:45 +01:00
|
|
|
|
2012-04-11 10:58:59 +02:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
class ListView(PermissionMixin, ExtraContextMixin, django_views.ListView):
|
|
|
|
"""
|
|
|
|
View to show a list of model objects.
|
|
|
|
"""
|
2013-01-06 12:07:37 +01:00
|
|
|
pass
|
2012-04-11 10:58:59 +02:00
|
|
|
|
2013-02-02 10:52:13 +01:00
|
|
|
|
2012-07-01 15:35:05 +02:00
|
|
|
class AjaxView(PermissionMixin, AjaxMixin, View):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
View for ajax requests.
|
2015-02-12 22:42:54 +01:00
|
|
|
|
|
|
|
Deprecated. Use APIView instead.
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
2012-04-12 14:20:05 +02:00
|
|
|
def get(self, request, *args, **kwargs):
|
2014-12-26 13:45:13 +01:00
|
|
|
# TODO: Raise an error, if the request is not an ajax-request
|
2015-02-12 22:42:54 +01:00
|
|
|
return self.ajax_response()
|
2012-04-12 14:20:05 +02:00
|
|
|
|
2014-12-26 13:45:13 +01:00
|
|
|
def post(self, *args, **kwargs):
|
2015-02-12 22:42:54 +01:00
|
|
|
return self.ajax_response()
|
2014-12-26 13:45:13 +01:00
|
|
|
|
2012-04-12 14:20:05 +02:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
class RedirectView(PermissionMixin, AjaxMixin, UrlMixin, django_views.RedirectView):
|
2013-05-11 00:59:07 +02:00
|
|
|
"""
|
|
|
|
View to redirect to another url.
|
|
|
|
|
|
|
|
The initial value of url_name_args is None, but the default given by
|
|
|
|
the used get_url_name_args method is [self.object.pk] if it exist, else
|
|
|
|
an empty list. Set url_name_args to an empty list, if you use an url
|
|
|
|
name which does not need any arguments.
|
|
|
|
"""
|
2012-02-20 17:46:45 +01:00
|
|
|
permanent = False
|
|
|
|
allow_ajax = False
|
2013-02-01 12:51:01 +01:00
|
|
|
url_name = None
|
2012-02-20 17:46:45 +01:00
|
|
|
|
|
|
|
def pre_redirect(self, request, *args, **kwargs):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Called before the redirect.
|
|
|
|
"""
|
|
|
|
# TODO: Also call this method on post-request.
|
|
|
|
# Add pre_get_redirect for get requests.
|
2012-02-20 17:46:45 +01:00
|
|
|
pass
|
|
|
|
|
|
|
|
def pre_post_redirect(self, request, *args, **kwargs):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Called before the redirect, if it is a post request.
|
|
|
|
"""
|
2012-02-20 17:46:45 +01:00
|
|
|
pass
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
if request.method == 'GET':
|
2012-04-19 12:46:04 +02:00
|
|
|
self.pre_redirect(request, *args, **kwargs)
|
2012-02-20 17:46:45 +01:00
|
|
|
elif request.method == 'POST':
|
|
|
|
self.pre_post_redirect(request, *args, **kwargs)
|
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
if request.is_ajax() and self.allow_ajax:
|
2012-07-01 15:35:05 +02:00
|
|
|
return self.ajax_get(request, *args, **kwargs)
|
2012-02-20 17:46:45 +01:00
|
|
|
return super(RedirectView, self).get(request, *args, **kwargs)
|
|
|
|
|
|
|
|
def get_redirect_url(self, **kwargs):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Returns the url to which the redirect should go.
|
|
|
|
"""
|
|
|
|
return self.get_url(self.url_name, self.url,
|
|
|
|
args=self.get_url_name_args())
|
|
|
|
|
|
|
|
|
|
|
|
class QuestionView(RedirectView):
|
|
|
|
"""
|
|
|
|
Mixin for questions to the requesting user.
|
|
|
|
|
|
|
|
The BaseView has to be a RedirectView.
|
|
|
|
"""
|
|
|
|
|
|
|
|
question_message = ugettext_lazy('Are you sure?')
|
|
|
|
final_message = ugettext_lazy('Thank you for your answer.')
|
|
|
|
answer_options = [('yes', ugettext_lazy("Yes")), ('no', ugettext_lazy("No"))]
|
|
|
|
question_url_name = None
|
|
|
|
question_url = None
|
|
|
|
|
|
|
|
def get_redirect_url(self, **kwargs):
|
|
|
|
"""
|
|
|
|
Returns the url to which the view should redirect.
|
|
|
|
"""
|
|
|
|
if self.request.method == 'GET':
|
|
|
|
url = self.get_url(self.question_url_name, self.question_url,
|
|
|
|
args=self.get_url_name_args())
|
2013-02-01 12:51:01 +01:00
|
|
|
else:
|
2013-09-25 12:53:44 +02:00
|
|
|
url = super(QuestionView, self).get_redirect_url()
|
|
|
|
return url
|
2012-02-20 17:46:45 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def pre_redirect(self, request, *args, **kwargs):
|
2013-05-11 00:59:07 +02:00
|
|
|
"""
|
2013-09-25 12:53:44 +02:00
|
|
|
Prints the question in a GET request.
|
2013-05-11 00:59:07 +02:00
|
|
|
"""
|
2013-09-25 12:53:44 +02:00
|
|
|
self.confirm_form()
|
2013-02-01 17:20:11 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def get_question_message(self):
|
|
|
|
"""
|
|
|
|
Returns the question.
|
|
|
|
"""
|
2014-08-16 09:25:18 +02:00
|
|
|
return str(self.question_message)
|
2012-02-20 17:46:45 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def get_answer_options(self):
|
|
|
|
"""
|
|
|
|
Returns the possible answers.
|
2012-03-16 14:31:59 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
This is a list of tubles. The first element of the tuble is the key,
|
|
|
|
the second element is shown to the user.
|
|
|
|
"""
|
|
|
|
return self.answer_options
|
2012-03-16 14:31:59 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def confirm_form(self):
|
|
|
|
"""
|
|
|
|
Returns the form to show in the message.
|
|
|
|
"""
|
|
|
|
option_fields = "\n".join([
|
|
|
|
'<button type="submit" class="btn btn-mini" name="%s">%s</button>'
|
2014-08-16 09:25:18 +02:00
|
|
|
% (option[0], str(option[1]))
|
2013-09-25 12:53:44 +02:00
|
|
|
for option in self.get_answer_options()])
|
|
|
|
messages.warning(
|
|
|
|
self.request,
|
2013-11-16 08:36:08 +01:00
|
|
|
'%(message)s<form action="%(url)s" method="post">'
|
|
|
|
'<input type="hidden" value="%(csrf)s" name="csrfmiddlewaretoken">'
|
|
|
|
'%(option_fields)s</form>' % {
|
|
|
|
'message': self.get_question_message(),
|
|
|
|
'url': self.request.path,
|
|
|
|
'csrf': csrf(self.request)['csrf_token'],
|
|
|
|
'option_fields': option_fields})
|
2013-01-06 12:07:37 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def pre_post_redirect(self, request, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Calls the method for the answer the user clicked.
|
2012-02-20 17:46:45 +01:00
|
|
|
|
2013-10-24 18:48:16 +02:00
|
|
|
The method name is on_clicked_ANSWER where ANSWER is the key from
|
|
|
|
the clicked answer. See get_answer_options. Prints an error
|
|
|
|
message, if no valid answer was given. If this method is not
|
|
|
|
defined, nothing happens, else it is called and the success message
|
|
|
|
is printed to the user.
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
2013-10-24 18:48:16 +02:00
|
|
|
try:
|
|
|
|
answer = self.get_answer()
|
|
|
|
except OpenSlidesError as error:
|
|
|
|
messages.error(self.request, error)
|
2013-09-25 12:53:44 +02:00
|
|
|
else:
|
2013-10-24 18:48:16 +02:00
|
|
|
method_name = 'on_clicked_%s' % answer
|
|
|
|
method = getattr(self, method_name, None)
|
|
|
|
if method is None:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
method()
|
|
|
|
self.create_final_message()
|
2012-03-16 14:31:59 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def get_answer(self):
|
|
|
|
"""
|
|
|
|
Returns the key of the clicked answer.
|
2013-09-07 22:57:29 +02:00
|
|
|
|
2013-10-24 18:48:16 +02:00
|
|
|
Raises OpenSlidesError if the answer is not one of get_answer_options.
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
for option_key, option_name in self.get_answer_options():
|
|
|
|
if option_key in self.request.POST:
|
2013-10-24 18:48:16 +02:00
|
|
|
answer = option_key
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
raise OpenSlidesError(ugettext_lazy('You did not send a valid answer.'))
|
|
|
|
return answer
|
2012-04-27 21:40:42 +02:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def get_final_message(self):
|
|
|
|
"""
|
|
|
|
Returns the message to show after the action.
|
|
|
|
|
|
|
|
Uses the attribute 'final_messsage' as default
|
|
|
|
"""
|
|
|
|
return self.final_message
|
|
|
|
|
|
|
|
def create_final_message(self):
|
|
|
|
"""
|
|
|
|
Creates a message.
|
|
|
|
"""
|
|
|
|
messages.success(self.request, self.get_final_message())
|
|
|
|
|
|
|
|
|
|
|
|
class FormView(PermissionMixin, ExtraContextMixin, FormMixin,
|
|
|
|
django_views.FormView):
|
|
|
|
"""
|
|
|
|
View for forms.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class UpdateView(PermissionMixin, ExtraContextMixin,
|
2014-12-22 18:09:05 +01:00
|
|
|
ModelFormMixin, SingleObjectMixin, django_views.UpdateView):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
View to update an model object.
|
|
|
|
"""
|
2013-09-07 22:57:29 +02:00
|
|
|
|
2012-07-07 14:01:40 +02:00
|
|
|
def get_success_message(self):
|
2013-09-07 22:57:29 +02:00
|
|
|
if self.success_message is None:
|
2014-12-22 18:09:05 +01:00
|
|
|
message = _('%s was successfully modified.') % html_strong(self.get_object())
|
2013-09-07 22:57:29 +02:00
|
|
|
else:
|
|
|
|
message = self.success_message
|
|
|
|
return message
|
2012-07-07 14:01:40 +02:00
|
|
|
|
2012-02-20 17:46:45 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
class CreateView(PermissionMixin, ExtraContextMixin,
|
|
|
|
ModelFormMixin, django_views.CreateView):
|
|
|
|
"""
|
|
|
|
View to create a model object.
|
2014-12-22 18:09:05 +01:00
|
|
|
|
|
|
|
Note: This class has a django method get_object() which is different form
|
|
|
|
the method in openslides.utils.views.SingleObjectMixin. The result
|
|
|
|
is not cached.
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
2013-09-07 22:57:29 +02:00
|
|
|
|
2012-07-07 14:01:40 +02:00
|
|
|
def get_success_message(self):
|
2013-09-07 22:57:29 +02:00
|
|
|
if self.success_message is None:
|
|
|
|
message = _('%s was successfully created.') % html_strong(self.object)
|
|
|
|
else:
|
|
|
|
message = self.success_message
|
|
|
|
return message
|
2012-07-07 14:01:40 +02:00
|
|
|
|
2012-02-20 17:46:45 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
class DeleteView(SingleObjectMixin, QuestionView):
|
|
|
|
"""
|
|
|
|
View to delete an model object.
|
|
|
|
"""
|
|
|
|
success_url = None
|
2013-02-01 13:24:30 +01:00
|
|
|
success_url_name = None
|
|
|
|
|
|
|
|
def get_redirect_url(self, **kwargs):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
Returns the url on which the delete dialog is shown and the url after
|
2013-10-24 18:48:16 +02:00
|
|
|
the deleting.
|
2013-09-25 12:53:44 +02:00
|
|
|
|
2013-10-24 18:48:16 +02:00
|
|
|
On GET-requests and on aborted or failed POST-requests, redirects to the detail
|
2013-09-25 12:53:44 +02:00
|
|
|
view as default. The attributes question_url_name or question_url can
|
|
|
|
define other urls.
|
|
|
|
"""
|
2013-10-24 18:48:16 +02:00
|
|
|
if self.request.method == 'POST':
|
|
|
|
try:
|
|
|
|
answer = self.get_answer()
|
|
|
|
except OpenSlidesError:
|
|
|
|
answer = 'no'
|
|
|
|
if answer == 'no':
|
|
|
|
url = self.get_url(self.question_url_name, self.question_url,
|
|
|
|
args=self.get_url_name_args())
|
|
|
|
else:
|
|
|
|
url = self.get_url(self.success_url_name, self.success_url,
|
|
|
|
args=self.get_url_name_args())
|
2013-02-01 13:24:30 +01:00
|
|
|
else:
|
2013-10-24 18:48:16 +02:00
|
|
|
url = self.get_url(self.question_url_name, self.question_url,
|
2013-09-25 12:53:44 +02:00
|
|
|
args=self.get_url_name_args())
|
|
|
|
return url
|
2013-02-01 13:24:30 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def get_question_message(self):
|
|
|
|
"""
|
|
|
|
Returns the question for the delete dialog.
|
|
|
|
"""
|
2014-12-22 18:09:05 +01:00
|
|
|
return _('Do you really want to delete %s?') % html_strong(self.get_object())
|
2012-10-28 19:59:41 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def on_clicked_yes(self):
|
|
|
|
"""
|
|
|
|
Deletes the object.
|
|
|
|
"""
|
2014-12-22 18:09:05 +01:00
|
|
|
self.get_object().delete()
|
2012-10-28 19:59:41 +01:00
|
|
|
|
2013-09-25 12:53:44 +02:00
|
|
|
def get_final_message(self):
|
|
|
|
"""
|
|
|
|
Prints the success message to the user.
|
|
|
|
"""
|
2014-12-22 18:09:05 +01:00
|
|
|
return _('%s was successfully deleted.') % html_strong(self.get_object())
|
2012-04-12 20:11:05 +02:00
|
|
|
|
2012-02-20 17:46:45 +01:00
|
|
|
|
2014-12-22 18:09:05 +01:00
|
|
|
class DetailView(PermissionMixin, ExtraContextMixin, SingleObjectMixin, django_views.DetailView):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
View to show an model object.
|
|
|
|
"""
|
|
|
|
pass
|
2012-04-15 10:02:53 +02:00
|
|
|
|
2012-04-15 09:55:21 +02:00
|
|
|
|
2012-03-18 14:33:53 +01:00
|
|
|
class PDFView(PermissionMixin, View):
|
2013-09-25 12:53:44 +02:00
|
|
|
"""
|
|
|
|
View to generate an PDF.
|
|
|
|
"""
|
|
|
|
|
2014-10-12 11:11:54 +02:00
|
|
|
filename = ugettext_lazy('undefined-filename')
|
2012-04-13 23:52:34 +02:00
|
|
|
top_space = 3
|
2012-04-14 18:13:55 +02:00
|
|
|
document_title = None
|
2012-04-13 23:52:34 +02:00
|
|
|
|
|
|
|
def get_top_space(self):
|
|
|
|
return self.top_space
|
2012-02-21 13:17:42 +01:00
|
|
|
|
2012-04-14 10:54:22 +02:00
|
|
|
def get_document_title(self):
|
2012-07-23 12:35:15 +02:00
|
|
|
if self.document_title:
|
2014-08-16 09:25:18 +02:00
|
|
|
return str(self.document_title)
|
2012-07-23 12:35:15 +02:00
|
|
|
else:
|
2012-07-23 22:59:31 +02:00
|
|
|
return ''
|
2012-04-14 10:54:22 +02:00
|
|
|
|
2012-04-29 18:54:42 +02:00
|
|
|
def get_filename(self):
|
2012-04-18 20:57:44 +02:00
|
|
|
return self.filename
|
|
|
|
|
2012-04-29 18:54:42 +02:00
|
|
|
def get_template(self, buffer):
|
|
|
|
return SimpleDocTemplate(buffer)
|
|
|
|
|
|
|
|
def build_document(self, pdf_document, story):
|
2012-11-24 14:01:21 +01:00
|
|
|
pdf_document.build(
|
|
|
|
story, onFirstPage=firstPage, onLaterPages=laterPages)
|
2012-04-29 18:54:42 +02:00
|
|
|
|
2012-02-21 13:17:42 +01:00
|
|
|
def render_to_response(self, filename):
|
2014-05-17 13:42:18 +02:00
|
|
|
response = HttpResponse(content_type='application/pdf')
|
2014-08-16 09:25:18 +02:00
|
|
|
filename = 'filename=%s.pdf;' % self.get_filename()
|
2012-02-21 13:17:42 +01:00
|
|
|
response['Content-Disposition'] = filename.encode('utf-8')
|
|
|
|
|
2014-08-16 09:25:18 +02:00
|
|
|
buffer = BytesIO()
|
2012-04-29 18:54:42 +02:00
|
|
|
pdf_document = self.get_template(buffer)
|
2012-04-14 10:54:22 +02:00
|
|
|
pdf_document.title = self.get_document_title()
|
2012-11-24 14:01:21 +01:00
|
|
|
story = [Spacer(1, self.get_top_space() * cm)]
|
2012-02-21 13:17:42 +01:00
|
|
|
|
|
|
|
self.append_to_pdf(story)
|
|
|
|
|
2012-04-29 18:54:42 +02:00
|
|
|
self.build_document(pdf_document, story)
|
2012-02-21 13:17:42 +01:00
|
|
|
|
|
|
|
pdf = buffer.getvalue()
|
|
|
|
buffer.close()
|
|
|
|
response.write(pdf)
|
|
|
|
return response
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
return self.render_to_response(self.get_filename())
|
2014-03-27 20:30:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
class CSVImportView(FormView):
|
|
|
|
"""
|
|
|
|
View for a csv import of some data.
|
|
|
|
|
|
|
|
The attribute import_function might to be a staticmethod.
|
|
|
|
"""
|
|
|
|
form_class = CSVImportForm
|
|
|
|
import_function = None
|
|
|
|
|
|
|
|
def get_import_function(self):
|
|
|
|
"""
|
|
|
|
Override this to return a specific function to import data from
|
|
|
|
a given csv file using some extra kwargs. This function has to
|
|
|
|
return a three-tuple of strings which are the messages for the
|
|
|
|
user.
|
|
|
|
|
|
|
|
Example function:
|
|
|
|
|
|
|
|
def my_import(csvfile, **kwargs):
|
|
|
|
# Parse file and import data
|
|
|
|
return success_message, warning_message, error_message
|
|
|
|
"""
|
|
|
|
if self.import_function is None:
|
|
|
|
raise NotImplementedError('A CSVImportView must provide an import_function '
|
|
|
|
'attribute or override a get_import_function method.')
|
|
|
|
return self.import_function
|
|
|
|
|
|
|
|
def form_valid(self, form):
|
|
|
|
success, warning, error = self.get_import_function()(**form.cleaned_data)
|
|
|
|
messages.success(self.request, success)
|
|
|
|
messages.warning(self.request, warning)
|
|
|
|
messages.error(self.request, error)
|
|
|
|
return super(CSVImportView, self).form_valid(form)
|
2015-02-12 22:42:54 +01:00
|
|
|
|
|
|
|
|
|
|
|
class APIView(_APIView):
|
|
|
|
"""
|
|
|
|
The Django Rest framework APIView with improvements for OpenSlides.
|
|
|
|
"""
|
|
|
|
|
|
|
|
http_method_names = []
|
|
|
|
"""
|
|
|
|
The allowed actions have to be explicitly defined.
|
|
|
|
|
|
|
|
Django allowes the following:
|
|
|
|
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
|
|
|
|
"""
|
|
|
|
|
|
|
|
def get_context_data(self, **context):
|
|
|
|
"""
|
|
|
|
Returns the context for the response.
|
|
|
|
"""
|
|
|
|
return context
|
|
|
|
|
|
|
|
def method_call(self, request, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Http method that returns the response object with the context data.
|
|
|
|
"""
|
|
|
|
return Response(self.get_context_data())
|
|
|
|
|
|
|
|
# Add the http-methods and delete the method "method_call"
|
|
|
|
get = post = put = patch = delete = head = options = trace = method_call
|
|
|
|
del method_call
|