commit
840ac34c80
@ -184,16 +184,12 @@ class SetClosed(RedirectView, SingleObjectMixin):
|
||||
model = Item
|
||||
|
||||
def get_ajax_context(self, **kwargs):
|
||||
context = super(SetClosed, self).get_ajax_context(**kwargs)
|
||||
closed = kwargs['closed']
|
||||
closed = self.kwargs['closed']
|
||||
if closed:
|
||||
link = reverse('item_open', args=[self.object.id])
|
||||
link = reverse('item_open', args=[self.object.pk])
|
||||
else:
|
||||
link = reverse('item_close', args=[self.object.id])
|
||||
context.update({
|
||||
'closed': kwargs['closed'],
|
||||
'link': link})
|
||||
return context
|
||||
link = reverse('item_close', args=[self.object.pk])
|
||||
return super(SetClosed, self).get_ajax_context(closed=closed, link=link)
|
||||
|
||||
def pre_redirect(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
@ -214,6 +210,7 @@ class ItemUpdate(UpdateView):
|
||||
model = Item
|
||||
context_object_name = 'item'
|
||||
success_url_name = 'item_overview'
|
||||
url_name_args = []
|
||||
|
||||
def get_form_class(self):
|
||||
if self.object.content_object:
|
||||
@ -233,6 +230,7 @@ class ItemCreate(CreateView):
|
||||
context_object_name = 'item'
|
||||
form_class = ItemForm
|
||||
success_url_name = 'item_overview'
|
||||
url_name_args = []
|
||||
|
||||
|
||||
class ItemDelete(DeleteView):
|
||||
|
@ -25,11 +25,10 @@ from django.utils.translation import ungettext, ugettext as _
|
||||
|
||||
from openslides.utils.pdf import stylesheet
|
||||
from openslides.utils.template import Tab
|
||||
from openslides.utils.utils import gen_confirm_form
|
||||
|
||||
from openslides.utils.views import (
|
||||
CreateView, DeleteView, RedirectView, UpdateView, ListView, PDFView,
|
||||
DetailView, View, PermissionMixin, SingleObjectMixin, QuestionMixin)
|
||||
DetailView, View, PermissionMixin, SingleObjectMixin, QuestionView)
|
||||
from openslides.utils.person import get_person
|
||||
from openslides.utils.utils import html_strong
|
||||
from openslides.config.api import config
|
||||
@ -150,8 +149,6 @@ class AssignmentRunView(SingleObjectMixin, PermissionMixin, View):
|
||||
class AssignmentRunDeleteView(SingleObjectMixin, RedirectView):
|
||||
model = Assignment
|
||||
url_name = 'assignment_detail'
|
||||
success_message = _("You have withdrawn your candidature successfully. "
|
||||
"You can not be nominated by other participants anymore.")
|
||||
|
||||
def pre_redirect(self, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
@ -162,54 +159,49 @@ class AssignmentRunDeleteView(SingleObjectMixin, RedirectView):
|
||||
except Exception, e:
|
||||
messages.error(self.request, e)
|
||||
else:
|
||||
messages.success(self.request, self.success_message)
|
||||
messages.success(self.request, _(
|
||||
'You have withdrawn your candidature successfully. '
|
||||
'You can not be nominated by other participants anymore.'))
|
||||
else:
|
||||
messages.error(self.request, _('The candidate list is already closed.'))
|
||||
|
||||
|
||||
class AssignmentRunOtherDeleteView(SingleObjectMixin, QuestionMixin,
|
||||
RedirectView):
|
||||
class AssignmentRunOtherDeleteView(SingleObjectMixin, QuestionView):
|
||||
model = Assignment
|
||||
permission_required = 'assignment.can_manage_assignment'
|
||||
question_url_name = 'assignment_detail'
|
||||
success_url_name = 'assignment_detail'
|
||||
success_message = ''
|
||||
|
||||
def pre_redirect(self, *args, **kwargs):
|
||||
self._get_person_information(*args, **kwargs)
|
||||
def get_question_message(self):
|
||||
self._get_person_information()
|
||||
if not self.is_blocked:
|
||||
message = _("Do you really want to withdraw %s from the election?") % html_strong(self.person)
|
||||
question = _("Do you really want to withdraw %s from the election?") % html_strong(self.person)
|
||||
else:
|
||||
message = _("Do you really want to unblock %s for the election?") % html_strong(self.person)
|
||||
gen_confirm_form(self.request, message, reverse('assignment_delother',
|
||||
args=[self.object.pk, kwargs['user_id']]))
|
||||
question = _("Do you really want to unblock %s for the election?") % html_strong(self.person)
|
||||
return question
|
||||
|
||||
def pre_post_redirect(self, *args, **kwargs):
|
||||
self._get_person_information(*args, **kwargs)
|
||||
if self.get_answer() == 'yes':
|
||||
self.case_yes()
|
||||
|
||||
def get_answer(self):
|
||||
if 'submit' in self.request.POST:
|
||||
return 'yes'
|
||||
|
||||
def case_yes(self):
|
||||
def on_clicked_yes(self):
|
||||
self._get_person_information()
|
||||
try:
|
||||
self.object.delrun(self.person, blocked=False)
|
||||
except Exception, e:
|
||||
messages.error(self.request, e)
|
||||
self.error = e
|
||||
else:
|
||||
messages.success(self.request, self.get_success_message())
|
||||
self.error = False
|
||||
|
||||
def get_success_message(self):
|
||||
success_message = _("Candidate %s was withdrawn successfully.") % html_strong(self.person)
|
||||
def create_final_message(self):
|
||||
if self.error:
|
||||
messages.error(self.request, self.error)
|
||||
else:
|
||||
messages.success(self.request, self.get_final_message())
|
||||
|
||||
def get_final_message(self):
|
||||
message = _("Candidate %s was withdrawn successfully.") % html_strong(self.person)
|
||||
if self.is_blocked:
|
||||
success_message = _("%s was unblocked successfully.") % html_strong(self.person)
|
||||
return success_message
|
||||
message = _("%s was unblocked successfully.") % html_strong(self.person)
|
||||
return message
|
||||
|
||||
def _get_person_information(self, *args, **kwargs):
|
||||
def _get_person_information(self):
|
||||
self.object = self.get_object()
|
||||
self.person = get_person(kwargs.get('user_id'))
|
||||
self.person = get_person(self.kwargs.get('user_id'))
|
||||
self.is_blocked = self.object.is_blocked(self.person)
|
||||
|
||||
|
||||
@ -310,7 +302,7 @@ class AssignmentPollDeleteView(DeleteView):
|
||||
def get_redirect_url(self, **kwargs):
|
||||
return reverse('assignment_detail', args=[self.assignment.id])
|
||||
|
||||
def get_success_message(self):
|
||||
def get_final_message(self):
|
||||
return _('Ballot was successfully deleted.')
|
||||
|
||||
|
||||
|
@ -69,11 +69,9 @@ STATICFILES_DIRS = (
|
||||
fs2unicode(os.path.join(SITE_ROOT, 'static')),
|
||||
)
|
||||
|
||||
#XXX: Note this setting (as well as our workaround finder)
|
||||
# can be removed again once django-bug-#18404 has been resolved
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'openslides.utils.staticfiles.AppDirectoriesFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
)
|
||||
|
||||
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
|
||||
|
@ -40,6 +40,7 @@ class MediafileCreateView(CreateView):
|
||||
model = Mediafile
|
||||
permission_required = 'mediafile.can_upload'
|
||||
success_url_name = 'mediafile_list'
|
||||
url_name_args = []
|
||||
|
||||
def get_form(self, form_class):
|
||||
form_kwargs = self.get_form_kwargs()
|
||||
@ -71,6 +72,7 @@ class MediafileUpdateView(UpdateView):
|
||||
permission_required = 'mediafile.can_manage'
|
||||
form_class = MediafileUpdateForm
|
||||
success_url_name = 'mediafile_list'
|
||||
url_name_args = []
|
||||
|
||||
def get_form_kwargs(self, *args, **kwargs):
|
||||
form_kwargs = super(MediafileUpdateView, self).get_form_kwargs(*args, **kwargs)
|
||||
@ -86,10 +88,10 @@ class MediafileDeleteView(DeleteView):
|
||||
permission_required = 'mediafile.can_manage'
|
||||
success_url_name = 'mediafile_list'
|
||||
|
||||
def case_yes(self, *args, **kwargs):
|
||||
def on_clicked_yes(self, *args, **kwargs):
|
||||
"""Deletes the file in the filesystem, if user clicks "Yes"."""
|
||||
self.object.mediafile.delete()
|
||||
return super(MediafileDeleteView, self).case_yes(*args, **kwargs)
|
||||
return super(MediafileDeleteView, self).on_clicked_yes(*args, **kwargs)
|
||||
|
||||
|
||||
def register_tab(request):
|
||||
|
@ -11,7 +11,6 @@
|
||||
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
from reportlab.platypus import SimpleDocTemplate
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib import messages
|
||||
@ -20,26 +19,26 @@ from django.db.models import Model
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from django.utils.text import slugify
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy, ugettext_noop
|
||||
from reportlab.platypus import SimpleDocTemplate
|
||||
|
||||
from openslides.agenda.views import (
|
||||
CreateRelatedAgendaItemView as _CreateRelatedAgendaItemView)
|
||||
from openslides.config.api import config
|
||||
from openslides.poll.views import PollFormView
|
||||
from openslides.projector.api import get_active_slide, update_projector
|
||||
from openslides.projector.projector import Widget
|
||||
from openslides.utils.pdf import stylesheet
|
||||
from openslides.utils.template import Tab
|
||||
from openslides.utils.utils import html_strong, htmldiff
|
||||
from openslides.utils.views import (
|
||||
TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView,
|
||||
DetailView, ListView, FormView, QuestionMixin, SingleObjectMixin)
|
||||
DetailView, ListView, FormView, QuestionView, SingleObjectMixin)
|
||||
from openslides.utils.template import Tab
|
||||
from openslides.utils.utils import html_strong, htmldiff
|
||||
from openslides.poll.views import PollFormView
|
||||
from openslides.projector.api import get_active_slide
|
||||
from openslides.projector.projector import Widget
|
||||
from openslides.config.api import config
|
||||
from openslides.agenda.views import CreateRelatedAgendaItemView as _CreateRelatedAgendaItemView
|
||||
|
||||
from .csv_import import import_motions
|
||||
from .models import (Motion, MotionSubmitter, MotionSupporter, MotionPoll,
|
||||
MotionVersion, State, WorkflowError, Category)
|
||||
from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin,
|
||||
MotionDisableVersioningMixin, MotionCategoryMixin,
|
||||
MotionIdentifierMixin, MotionWorkflowMixin, MotionImportForm)
|
||||
from .models import (Motion, MotionSubmitter, MotionSupporter, MotionPoll,
|
||||
MotionVersion, State, WorkflowError, Category)
|
||||
from .pdf import motions_to_pdf, motion_to_pdf, motion_poll_to_pdf
|
||||
|
||||
|
||||
@ -297,7 +296,7 @@ class MotionDeleteView(DeleteView):
|
||||
"""
|
||||
return self.get_object().get_allowed_actions(request.user)['delete']
|
||||
|
||||
def get_success_message(self):
|
||||
def get_final_message(self):
|
||||
return _('%s was successfully deleted.') % _('Motion')
|
||||
|
||||
motion_delete = MotionDeleteView.as_view()
|
||||
@ -326,21 +325,20 @@ class VersionDeleteView(DeleteView):
|
||||
raise Http404('You can not delete the active version of a motion.')
|
||||
return version
|
||||
|
||||
def get_success_url_name_args(self):
|
||||
def get_url_name_args(self):
|
||||
return (self.object.motion_id, )
|
||||
|
||||
version_delete = VersionDeleteView.as_view()
|
||||
|
||||
|
||||
class VersionPermitView(SingleObjectMixin, QuestionMixin, RedirectView):
|
||||
class VersionPermitView(SingleObjectMixin, QuestionView):
|
||||
"""
|
||||
View to permit a version of a motion.
|
||||
"""
|
||||
model = Motion
|
||||
question_url_name = 'motion_version_detail'
|
||||
success_url_name = 'motion_version_detail'
|
||||
success_message = ugettext_lazy('Version successfully permitted.')
|
||||
final_message = ugettext_lazy('Version successfully permitted.')
|
||||
permission_required = 'motion.can_manage_motion'
|
||||
question_url_name = 'motion_version_detail'
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
"""
|
||||
@ -360,13 +358,13 @@ class VersionPermitView(SingleObjectMixin, QuestionMixin, RedirectView):
|
||||
"""
|
||||
return [self.object.pk, self.version.version_number]
|
||||
|
||||
def get_question(self):
|
||||
def get_question_message(self):
|
||||
"""
|
||||
Return a string, shown to the user as question to permit the version.
|
||||
"""
|
||||
return _('Are you sure you want permit version %s?') % self.version.version_number
|
||||
|
||||
def case_yes(self):
|
||||
def on_clicked_yes(self):
|
||||
"""
|
||||
Activate the version, if the user chooses 'yes'.
|
||||
"""
|
||||
@ -418,7 +416,7 @@ class VersionDiffView(DetailView):
|
||||
version_diff = VersionDiffView.as_view()
|
||||
|
||||
|
||||
class SupportView(SingleObjectMixin, QuestionMixin, RedirectView):
|
||||
class SupportView(SingleObjectMixin, QuestionView):
|
||||
"""
|
||||
View to support or unsupport a motion.
|
||||
|
||||
@ -452,7 +450,7 @@ class SupportView(SingleObjectMixin, QuestionMixin, RedirectView):
|
||||
else:
|
||||
return True
|
||||
|
||||
def get_question(self):
|
||||
def get_question_message(self):
|
||||
"""
|
||||
Return the question string.
|
||||
"""
|
||||
@ -461,7 +459,7 @@ class SupportView(SingleObjectMixin, QuestionMixin, RedirectView):
|
||||
else:
|
||||
return _('Do you really want to unsupport this motion?')
|
||||
|
||||
def case_yes(self):
|
||||
def on_clicked_yes(self):
|
||||
"""
|
||||
Append or remove the request.user from the motion.
|
||||
|
||||
@ -477,7 +475,7 @@ class SupportView(SingleObjectMixin, QuestionMixin, RedirectView):
|
||||
self.object.unsupport(person=user)
|
||||
self.object.write_log([ugettext_noop('Motion unsupported')], user)
|
||||
|
||||
def get_success_message(self):
|
||||
def get_final_message(self):
|
||||
"""
|
||||
Return the success message.
|
||||
"""
|
||||
@ -486,12 +484,6 @@ class SupportView(SingleObjectMixin, QuestionMixin, RedirectView):
|
||||
else:
|
||||
return _("You have unsupported this motion successfully.")
|
||||
|
||||
def get_redirect_url(self, **kwargs):
|
||||
"""
|
||||
Return the url, the view should redirect to.
|
||||
"""
|
||||
return self.object.get_absolute_url()
|
||||
|
||||
motion_support = SupportView.as_view(support=True)
|
||||
motion_unsupport = SupportView.as_view(support=False)
|
||||
|
||||
@ -558,6 +550,7 @@ class PollUpdateView(PollMixin, PollFormView):
|
||||
"""
|
||||
View to update a MotionPoll.
|
||||
"""
|
||||
|
||||
poll_class = MotionPoll
|
||||
"""
|
||||
Poll Class to use for this view.
|
||||
@ -595,11 +588,11 @@ class PollDeleteView(PollMixin, DeleteView):
|
||||
|
||||
model = MotionPoll
|
||||
|
||||
def case_yes(self):
|
||||
def on_clicked_yes(self):
|
||||
"""
|
||||
Write a log message, if the form is valid.
|
||||
"""
|
||||
super(PollDeleteView, self).case_yes()
|
||||
super(PollDeleteView, self).on_clicked_yes()
|
||||
self.object.motion.write_log([ugettext_noop('Poll deleted')], self.request.user)
|
||||
|
||||
def get_redirect_url(self, **kwargs):
|
||||
|
@ -138,11 +138,11 @@ class Group(SlideMixin, PersonMixin, Person, DjangoGroup):
|
||||
Return the URL to the user group.
|
||||
"""
|
||||
if link == 'detail' or link == 'view':
|
||||
return reverse('user_group_view', args=[str(self.id)])
|
||||
return reverse('user_group_view', args=[str(self.pk)])
|
||||
if link == 'update' or link == 'edit':
|
||||
return reverse('user_group_edit', args=[str(self.id)])
|
||||
return reverse('user_group_edit', args=[str(self.pk)])
|
||||
if link == 'delete':
|
||||
return reverse('user_group_delete', args=[str(self.id)])
|
||||
return reverse('user_group_delete', args=[str(self.pk)])
|
||||
return super(Group, self).get_absolute_url(link)
|
||||
|
||||
|
||||
|
@ -40,7 +40,7 @@ from openslides.utils.utils import (
|
||||
template, delete_default_permissions, html_strong)
|
||||
from openslides.utils.views import (
|
||||
FormView, PDFView, CreateView, UpdateView, DeleteView, PermissionMixin,
|
||||
RedirectView, SingleObjectMixin, ListView, QuestionMixin, DetailView)
|
||||
RedirectView, SingleObjectMixin, ListView, QuestionView, DetailView)
|
||||
from openslides.config.api import config
|
||||
from openslides.projector.projector import Widget
|
||||
|
||||
@ -155,6 +155,7 @@ class UserDeleteView(DeleteView):
|
||||
permission_required = 'participant.can_manage_participant'
|
||||
model = User
|
||||
success_url_name = 'user_overview'
|
||||
url_name_args = []
|
||||
|
||||
def pre_redirect(self, request, *args, **kwargs):
|
||||
if self.object == self.request.user:
|
||||
@ -335,14 +336,14 @@ class UserImportView(FormView):
|
||||
return super(UserImportView, self).form_valid(form)
|
||||
|
||||
|
||||
class ResetPasswordView(SingleObjectMixin, QuestionMixin, RedirectView):
|
||||
class ResetPasswordView(SingleObjectMixin, QuestionView):
|
||||
"""
|
||||
Set the Passwort for a user to his default password.
|
||||
"""
|
||||
permission_required = 'participant.can_manage_participant'
|
||||
model = User
|
||||
allow_ajax = True
|
||||
question = ugettext_lazy('Do you really want to reset the password?')
|
||||
question_message = ugettext_lazy('Do you really want to reset the password?')
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
@ -351,10 +352,10 @@ class ResetPasswordView(SingleObjectMixin, QuestionMixin, RedirectView):
|
||||
def get_redirect_url(self, **kwargs):
|
||||
return reverse('user_edit', args=[self.object.id])
|
||||
|
||||
def case_yes(self):
|
||||
def on_clicked_yes(self):
|
||||
self.object.reset_password()
|
||||
|
||||
def get_success_message(self):
|
||||
def get_final_message(self):
|
||||
return _('The Password for %s was successfully reset.') % html_strong(self.object)
|
||||
|
||||
|
||||
@ -432,6 +433,7 @@ class GroupDeleteView(DeleteView):
|
||||
permission_required = 'participant.can_manage_participant'
|
||||
model = Group
|
||||
success_url_name = 'user_group_overview'
|
||||
url_name_args = []
|
||||
|
||||
def pre_redirect(self, request, *args, **kwargs):
|
||||
if not self.is_protected_from_deleting():
|
||||
@ -466,14 +468,17 @@ def login(request):
|
||||
try:
|
||||
admin = User.objects.get(pk=1)
|
||||
if admin.check_password(admin.default_password):
|
||||
user_data = {
|
||||
'user': html_strong(admin.username),
|
||||
'password': html_strong(admin.default_password)}
|
||||
|
||||
extra_content['first_time_message'] = _(
|
||||
"Installation was successfully! Use %(user)s "
|
||||
"(password: %(password)s) for first login.<br>"
|
||||
"<strong>Important:</strong> Please change the password after "
|
||||
"first login! Otherwise this message still appears for "
|
||||
"everyone and could be a security risk.") % {
|
||||
'user': html_strong(admin.username),
|
||||
'password': html_strong(admin.default_password)}
|
||||
"everyone and could be a security risk.") % user_data
|
||||
|
||||
extra_content['next'] = reverse('password_change')
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
|
@ -14,7 +14,7 @@ from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
||||
|
||||
from openslides.utils.modelfields import MinMaxIntegerField
|
||||
from openslides.utils.models import MinMaxIntegerField
|
||||
|
||||
|
||||
class BaseOption(models.Model):
|
||||
|
@ -14,10 +14,10 @@ from django.http import HttpResponseRedirect
|
||||
from django.forms.models import modelform_factory
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from openslides.utils.views import TemplateView, UrlMixin
|
||||
from openslides.utils.views import TemplateView, FormMixin
|
||||
|
||||
|
||||
class PollFormView(UrlMixin, TemplateView):
|
||||
class PollFormView(FormMixin, TemplateView):
|
||||
poll_class = None
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
@ -23,7 +23,7 @@ from django.shortcuts import redirect
|
||||
from django.template import RequestContext
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from openslides.utils.template import render_block_to_string, Tab
|
||||
from openslides.utils.template import Tab
|
||||
from openslides.utils.views import (
|
||||
TemplateView, RedirectView, CreateView, UpdateView, DeleteView, AjaxMixin)
|
||||
from openslides.config.api import config
|
||||
|
@ -1,5 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ error }}</h1>
|
||||
<h1>{% trans 'Permission Denied' %}</h1>
|
||||
{% trans 'Sorry, you have no rights to see this page.' %}
|
||||
{% endblock %}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
Modelfields for OpenSlides
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
@ -14,6 +14,10 @@ from django.db import models
|
||||
|
||||
|
||||
class MinMaxIntegerField(models.IntegerField):
|
||||
"""
|
||||
IntegerField with options to set a min- and a max-value.
|
||||
"""
|
||||
|
||||
def __init__(self, min_value=None, max_value=None, *args, **kwargs):
|
||||
self.min_value, self.max_value = min_value, max_value
|
||||
super(MinMaxIntegerField, self).__init__(*args, **kwargs)
|
0
openslides/utils/pdf.py
Executable file → Normal file
0
openslides/utils/pdf.py
Executable file → Normal file
@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
openslides.utils.staticfiles
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
StaticFiels fix for the django bug #18404.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
from django.utils.importlib import import_module
|
||||
from django.contrib.staticfiles.finders import (
|
||||
AppDirectoriesFinder as _AppDirectoriesFinder)
|
||||
|
||||
|
||||
# This is basically a copy of
|
||||
# django.contrib.staticfiles.storage.AppStaticStorage
|
||||
# with the fix for django bug #18404 applied
|
||||
# see https://code.djangoproject.com/ticket/18404 for details
|
||||
class AppStaticStorage(FileSystemStorage):
|
||||
"""
|
||||
A file system storage backend that takes an app module and works
|
||||
for the ``static`` directory of it.
|
||||
"""
|
||||
prefix = None
|
||||
source_dir = 'static'
|
||||
|
||||
def __init__(self, app, *args, **kwargs):
|
||||
"""
|
||||
Returns a static file storage if available in the given app.
|
||||
"""
|
||||
# app is the actual app module
|
||||
mod = import_module(app)
|
||||
mod_path = os.path.dirname(mod.__file__)
|
||||
location = os.path.join(mod_path, self.source_dir)
|
||||
fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
|
||||
location = location.decode(fs_encoding)
|
||||
super(AppStaticStorage, self).__init__(location, *args, **kwargs)
|
||||
|
||||
|
||||
class AppDirectoriesFinder(_AppDirectoriesFinder):
|
||||
storage_class = AppStaticStorage
|
@ -10,11 +10,11 @@
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from django.template import loader, Context
|
||||
from django.template.loader_tags import BlockNode, ExtendsNode
|
||||
|
||||
|
||||
class Tab(object):
|
||||
"""
|
||||
Entry for the main menu of OpenSlides.
|
||||
"""
|
||||
def __init__(self, title='', app='', stylefile='', url='', permission=True, selected=False):
|
||||
self.title = title
|
||||
self.app = app
|
||||
@ -22,62 +22,3 @@ class Tab(object):
|
||||
self.url = url
|
||||
self.permission = permission
|
||||
self.selected = selected
|
||||
|
||||
|
||||
## All following function are only needed to render a block from a template
|
||||
## and could be removed, if the template worked with an include-statement instead.
|
||||
## Its only used for ajax-request from the projector.
|
||||
|
||||
def get_template(template):
|
||||
if isinstance(template, (tuple, list)):
|
||||
return loader.select_template(template)
|
||||
return loader.get_template(template)
|
||||
|
||||
|
||||
class BlockNotFound(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def render_template_block(template, block, context):
|
||||
"""
|
||||
Renders a single block from a template. This template should have previously
|
||||
been rendered.
|
||||
"""
|
||||
return render_template_block_nodelist(template.nodelist, block, context)
|
||||
|
||||
|
||||
def render_template_block_nodelist(nodelist, block, context):
|
||||
for node in nodelist:
|
||||
if isinstance(node, BlockNode) and node.name == block:
|
||||
return node.render(context)
|
||||
for key in ('nodelist', 'nodelist_true', 'nodelist_false'):
|
||||
if hasattr(node, key):
|
||||
try:
|
||||
return render_template_block_nodelist(
|
||||
getattr(node, key), block, context)
|
||||
except:
|
||||
pass
|
||||
for node in nodelist:
|
||||
if isinstance(node, ExtendsNode):
|
||||
try:
|
||||
return render_template_block(
|
||||
node.get_parent(context), block, context)
|
||||
except BlockNotFound:
|
||||
pass
|
||||
raise BlockNotFound
|
||||
|
||||
|
||||
def render_block_to_string(template_name, block, dictionary=None,
|
||||
context_instance=None):
|
||||
"""
|
||||
Loads the given template_name and renders the given block with the given
|
||||
dictionary as context. Returns a string.
|
||||
"""
|
||||
dictionary = dictionary or {}
|
||||
t = get_template(template_name)
|
||||
if context_instance:
|
||||
context_instance.update(dictionary)
|
||||
else:
|
||||
context_instance = Context(dictionary)
|
||||
t.render(context_instance)
|
||||
return render_template_block(t, block, context_instance)
|
||||
|
@ -11,54 +11,12 @@
|
||||
"""
|
||||
|
||||
import difflib
|
||||
import json
|
||||
import sys
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.core.context_processors import csrf
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponse, HttpResponseForbidden
|
||||
from django.shortcuts import render_to_response, redirect
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from openslides.utils.signals import template_manipulation
|
||||
|
||||
|
||||
def gen_confirm_form(request, message, url):
|
||||
"""
|
||||
Generate a message-form.
|
||||
|
||||
Deprecated. Use Class base Views instead.
|
||||
"""
|
||||
messages.warning(
|
||||
request,
|
||||
"""
|
||||
%s
|
||||
<form action="%s" method="post">
|
||||
<input type="hidden" value="%s" name="csrfmiddlewaretoken">
|
||||
<button type="submit" name="submit" class="btn btn-mini">%s</button>
|
||||
<button name="cancel" class="btn btn-mini">%s</button>
|
||||
</form>
|
||||
"""
|
||||
% (message, url, csrf(request)['csrf_token'], _("Yes"), _("No")))
|
||||
|
||||
|
||||
def del_confirm_form(request, object, name=None, delete_link=None):
|
||||
"""
|
||||
Creates a question to delete an object.
|
||||
|
||||
Deprecated. Use Class base Views instead.
|
||||
"""
|
||||
if name is None:
|
||||
name = object
|
||||
if delete_link is None:
|
||||
delete_link = object.get_absolute_url('delete')
|
||||
gen_confirm_form(
|
||||
request, _('Do you really want to delete %s?')
|
||||
% html_strong(name), delete_link)
|
||||
from .signals import template_manipulation
|
||||
|
||||
|
||||
def template(template_name):
|
||||
@ -67,6 +25,8 @@ def template(template_name):
|
||||
|
||||
Deprecated. Use class based views instead.
|
||||
"""
|
||||
# TODO: Write the login page an the usersettings page with class based views
|
||||
# Remove this function afterwards
|
||||
def renderer(func):
|
||||
def wrapper(request, *args, **kwargs):
|
||||
output = func(request, *args, **kwargs)
|
||||
@ -85,36 +45,12 @@ def template(template_name):
|
||||
return renderer
|
||||
|
||||
|
||||
def permission_required(perm, login_url=None):
|
||||
"""
|
||||
Decorator for views that checks whether a user has a particular permission
|
||||
enabled, redirecting to the log-in page if necessary.
|
||||
|
||||
Deprecated.
|
||||
"""
|
||||
def renderer(func):
|
||||
def wrapper(request, *args, **kw):
|
||||
if request.user.has_perm(perm):
|
||||
return func(request, *args, **kw)
|
||||
if request.user.is_authenticated():
|
||||
return render_to_forbidden(request)
|
||||
return redirect(reverse('user_login'))
|
||||
return wrapper
|
||||
return renderer
|
||||
|
||||
|
||||
def render_to_forbidden(request,
|
||||
error=ugettext_lazy("Sorry, you have no rights to see this page.")):
|
||||
# TODO: Integrate this function into the PermissionMixin once the
|
||||
# above function is deleted.
|
||||
return HttpResponseForbidden(render_to_string(
|
||||
'403.html', {'error': error}, context_instance=RequestContext(request)))
|
||||
|
||||
|
||||
def delete_default_permissions(**kwargs):
|
||||
"""
|
||||
Deletes the permissions, django creates by default for the admin.
|
||||
"""
|
||||
# TODO: Create an participant app which does not create the permissions.
|
||||
# Delete this function afterwards
|
||||
for p in Permission.objects.all():
|
||||
if (p.codename.startswith('add') or
|
||||
p.codename.startswith('delete') or
|
||||
@ -122,29 +58,26 @@ def delete_default_permissions(**kwargs):
|
||||
p.delete()
|
||||
|
||||
|
||||
def ajax_request(data):
|
||||
"""
|
||||
generates a HTTPResponse-Object with json-Data for a
|
||||
ajax response.
|
||||
|
||||
Deprecated.
|
||||
"""
|
||||
return HttpResponse(json.dumps(data))
|
||||
|
||||
|
||||
def html_strong(string):
|
||||
"""
|
||||
Returns the text wrapped in an HTML-Strong element.
|
||||
"""
|
||||
return u"<strong>%s</strong>" % string
|
||||
|
||||
|
||||
def htmldiff(text1, text2):
|
||||
"""Return string of html diff between two strings (text1 and text2)"""
|
||||
|
||||
"""
|
||||
Return string of html diff between two strings (text1 and text2)
|
||||
"""
|
||||
diff = difflib.HtmlDiff(wrapcolumn=60)
|
||||
return diff.make_table(text1.splitlines(), text2.splitlines())
|
||||
|
||||
|
||||
def int_or_none(object):
|
||||
def int_or_none(var):
|
||||
"""
|
||||
Trys to convert 'var' into an integer. Returns None if an TypeError occures.
|
||||
"""
|
||||
try:
|
||||
return int(object)
|
||||
except TypeError:
|
||||
return int(var)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
Views for OpenSlides.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
@ -15,257 +15,280 @@ from cStringIO import StringIO
|
||||
from reportlab.platypus import SimpleDocTemplate, Spacer
|
||||
from reportlab.lib.units import cm
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.context_processors import csrf
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.dispatch import receiver
|
||||
from django.http import HttpResponseServerError, HttpResponse, HttpResponseRedirect
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.utils.importlib import import_module
|
||||
from django.http import (HttpResponse, HttpResponseRedirect,
|
||||
HttpResponseServerError)
|
||||
from django.template import RequestContext
|
||||
from django.template.loader import render_to_string
|
||||
from django.views.generic import (
|
||||
TemplateView as _TemplateView,
|
||||
RedirectView as _RedirectView,
|
||||
UpdateView as _UpdateView,
|
||||
CreateView as _CreateView,
|
||||
View as _View,
|
||||
FormView as _FormView,
|
||||
ListView as _ListView,
|
||||
DetailView as _DetailView,
|
||||
)
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.importlib import import_module
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.views import generic as django_views
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.views.generic.list import TemplateResponseMixin
|
||||
from reportlab.lib.units import cm
|
||||
from reportlab.platypus import SimpleDocTemplate, Spacer
|
||||
|
||||
from openslides.utils.utils import render_to_forbidden, html_strong
|
||||
from openslides.utils.signals import template_manipulation
|
||||
from openslides.utils.pdf import firstPage, laterPages
|
||||
|
||||
from .pdf import firstPage, laterPages
|
||||
from .signals import template_manipulation
|
||||
from .utils import html_strong
|
||||
|
||||
NO_PERMISSION_REQUIRED = 'No permission required'
|
||||
|
||||
View = _View
|
||||
|
||||
|
||||
class SetCookieMixin(object):
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
response = TemplateResponseMixin.render_to_response(
|
||||
self, context, **response_kwargs)
|
||||
if 'cookie' in context:
|
||||
response.set_cookie(context['cookie'][0], context['cookie'][1])
|
||||
return response
|
||||
View = django_views.View
|
||||
|
||||
|
||||
class LoginMixin(object):
|
||||
"""
|
||||
Mixin for Views, that only can be viseted from users how are logedin.
|
||||
"""
|
||||
|
||||
@method_decorator(login_required)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
"""
|
||||
Check if the user is loged in.
|
||||
"""
|
||||
return super(LoginMixin, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class PermissionMixin(object):
|
||||
"""
|
||||
Mixin for views, that only can be visited from users with special rights.
|
||||
|
||||
Set the attribute 'permission_required' to the required permission string.
|
||||
"""
|
||||
permission_required = NO_PERMISSION_REQUIRED
|
||||
|
||||
def has_permission(self, request, *args, **kwargs):
|
||||
"""
|
||||
Checks if the user has the required permission.
|
||||
"""
|
||||
if self.permission_required == NO_PERMISSION_REQUIRED:
|
||||
return True
|
||||
else:
|
||||
return request.user.has_perm(self.permission_required)
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
"""
|
||||
Check if the user has the permission.
|
||||
|
||||
If the user is not logged in, redirect the user to the login page.
|
||||
"""
|
||||
if not self.has_permission(request, *args, **kwargs):
|
||||
if not request.user.is_authenticated():
|
||||
path = request.get_full_path()
|
||||
return HttpResponseRedirect(
|
||||
"%s?next=%s" % (settings.LOGIN_URL, path))
|
||||
else:
|
||||
return render_to_forbidden(request)
|
||||
raise PermissionDenied
|
||||
return super(PermissionMixin, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class AjaxMixin(object):
|
||||
"""
|
||||
Mixin to response to an ajax request with an json object.
|
||||
"""
|
||||
|
||||
def get_ajax_context(self, **kwargs):
|
||||
return {}
|
||||
"""
|
||||
Returns a dictonary with the context for the ajax response.
|
||||
"""
|
||||
return kwargs
|
||||
|
||||
def ajax_get(self, request, *args, **kwargs):
|
||||
return HttpResponse(json.dumps(self.get_ajax_context(**kwargs)))
|
||||
"""
|
||||
Returns the HttpResponse.
|
||||
"""
|
||||
return HttpResponse(json.dumps(self.get_ajax_context()))
|
||||
|
||||
|
||||
class ExtraContextMixin(object):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
Sends the signal.
|
||||
"""
|
||||
context = super(ExtraContextMixin, self).get_context_data(**kwargs)
|
||||
context.setdefault('extra_javascript', [])
|
||||
template_manipulation.send(
|
||||
sender=self.__class__, request=self.request, context=context)
|
||||
return context
|
||||
|
||||
|
||||
class UrlMixin(object):
|
||||
"""
|
||||
Mixin to provide the use of url names in views with success url and
|
||||
apply url.
|
||||
|
||||
The initial value of url_name_args is None, but the default given by
|
||||
the used get_url_name_args method is [], because in the future, the
|
||||
method might return another default value. Compare this with the QuestionMixin.
|
||||
"""
|
||||
apply_url_name = None
|
||||
success_url_name = None
|
||||
url_name_args = None
|
||||
|
||||
def get_url(self, url_name=None, url=None, args=None, use_absolute_url_link=None):
|
||||
"""
|
||||
Returns an url.
|
||||
|
||||
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().
|
||||
|
||||
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
|
||||
else:
|
||||
try:
|
||||
if use_absolute_url_link is None:
|
||||
value = self.object.get_absolute_url()
|
||||
else:
|
||||
value = self.object.get_absolute_url(use_absolute_url_link)
|
||||
except AttributeError:
|
||||
raise ImproperlyConfigured(
|
||||
'No url to redirect to. See openslides.utils.views.UrlMixin for more details.')
|
||||
return value
|
||||
|
||||
def get_url_name_args(self):
|
||||
"""
|
||||
Returns the arguments for the url name.
|
||||
|
||||
Default is an empty list or [self.object.pk] if this exist.
|
||||
"""
|
||||
if self.url_name_args is not None:
|
||||
value = self.url_name_args
|
||||
else:
|
||||
try:
|
||||
pk = self.object.pk
|
||||
except AttributeError:
|
||||
value = []
|
||||
else:
|
||||
if pk:
|
||||
value = [pk]
|
||||
else:
|
||||
value = []
|
||||
return value
|
||||
|
||||
|
||||
class FormMixin(UrlMixin):
|
||||
"""
|
||||
Mixin for views with forms.
|
||||
"""
|
||||
|
||||
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.')
|
||||
|
||||
def get_apply_url(self):
|
||||
if self.apply_url_name:
|
||||
return reverse(self.apply_url_name, args=self.get_url_name_args())
|
||||
elif self.apply_url:
|
||||
return self.apply_url
|
||||
else:
|
||||
try:
|
||||
return self.object.get_absolute_url('edit')
|
||||
except AttributeError:
|
||||
raise ImproperlyConfigured(
|
||||
"No URL to redirect to. Provide an apply_url_name.")
|
||||
"""
|
||||
Returns the url when the user clicks on 'apply'.
|
||||
"""
|
||||
return self.get_url(self.apply_url_name, self.apply_url,
|
||||
args=self.get_url_name_args(),
|
||||
use_absolute_url_link='update')
|
||||
|
||||
def get_success_url(self):
|
||||
if 'apply' in self.request.POST:
|
||||
return self.get_apply_url()
|
||||
"""
|
||||
Returns the url when the user submits a form.
|
||||
|
||||
if self.success_url_name:
|
||||
return reverse(self.success_url_name, args=self.get_url_name_args())
|
||||
elif self.success_url:
|
||||
return self.success_url
|
||||
Redirects to get_apply_url if self.use_apply is True
|
||||
"""
|
||||
if self.use_apply and 'apply' in self.request.POST:
|
||||
value = self.get_apply_url()
|
||||
else:
|
||||
try:
|
||||
return self.object.get_absolute_url()
|
||||
except AttributeError:
|
||||
raise ImproperlyConfigured(
|
||||
"No URL to redirect to. Either provide an URL or define "
|
||||
"a get_absolute_url method of the model.")
|
||||
value = self.get_url(self.success_url_name, self.success_url,
|
||||
args=self.get_url_name_args())
|
||||
return value
|
||||
|
||||
def get_url_name_args(self):
|
||||
"""
|
||||
Returns the arguments for the url name. Default is an empty list.
|
||||
"""
|
||||
if self.url_name_args is not None:
|
||||
return self.url_name_args
|
||||
return []
|
||||
def form_valid(self, form):
|
||||
value = super(FormMixin, self).form_valid(form)
|
||||
messages.success(self.request, self.get_success_message())
|
||||
return value
|
||||
|
||||
|
||||
class QuestionMixin(object):
|
||||
"""
|
||||
Mixin for questions to the requesting user.
|
||||
|
||||
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.
|
||||
"""
|
||||
question = ugettext_lazy('Are you sure?')
|
||||
success_message = ugettext_lazy('Thank you for your answer')
|
||||
answer_options = [('yes', ugettext_lazy("Yes")), ('no', ugettext_lazy("No"))]
|
||||
question_url_name = None
|
||||
success_url_name = None
|
||||
url_name_args = None
|
||||
|
||||
def get_redirect_url(self, **kwargs):
|
||||
# TODO: raise error when question_url_name/success_url_name is not present
|
||||
if self.request.method == 'GET':
|
||||
return reverse(self.question_url_name, args=self.get_question_url_name_args())
|
||||
else:
|
||||
return reverse(self.success_url_name, args=self.get_success_url_name_args())
|
||||
|
||||
def get_question_url_name_args(self):
|
||||
return self.get_url_name_args()
|
||||
|
||||
def get_success_url_name_args(self):
|
||||
return self.get_url_name_args()
|
||||
|
||||
def get_url_name_args(self):
|
||||
"""
|
||||
Returns the arguments for the url name. Default is an empty list
|
||||
or [self.object.pk] if this exist.
|
||||
"""
|
||||
if self.url_name_args is not None:
|
||||
return self.url_name_args
|
||||
try:
|
||||
return [self.object.pk]
|
||||
except AttributeError:
|
||||
return []
|
||||
|
||||
def pre_redirect(self, request, *args, **kwargs):
|
||||
"""
|
||||
Prints the question in a GET request.
|
||||
"""
|
||||
self.confirm_form()
|
||||
|
||||
def get_question(self):
|
||||
return unicode(self.question)
|
||||
|
||||
def get_answer_options(self):
|
||||
return self.answer_options
|
||||
|
||||
def get_answer_url(self):
|
||||
try:
|
||||
return self.answer_url
|
||||
except AttributeError:
|
||||
return self.request.path
|
||||
|
||||
def confirm_form(self):
|
||||
option_fields = "\n".join([
|
||||
'<button type="submit" class="btn btn-mini" name="%s">%s</button>' % (option[0], unicode(option[1]))
|
||||
for option in self.get_answer_options()])
|
||||
messages.warning(
|
||||
self.request,
|
||||
"""
|
||||
%(message)s
|
||||
<form action="%(url)s" method="post">
|
||||
<input type="hidden" value="%(csrf)s" name="csrfmiddlewaretoken">
|
||||
%(option_fields)s
|
||||
</form>
|
||||
""" % {'message': self.get_question(),
|
||||
'url': self.get_answer_url(),
|
||||
'csrf': csrf(self.request)['csrf_token'],
|
||||
'option_fields': option_fields})
|
||||
|
||||
def pre_post_redirect(self, request, *args, **kwargs):
|
||||
# Reacts on the response of the user in a POST-request.
|
||||
# TODO: call the methodes for all possible answers.
|
||||
if self.get_answer() == 'yes':
|
||||
self.case_yes()
|
||||
messages.success(request, self.get_success_message())
|
||||
|
||||
def get_answer(self):
|
||||
for option in self.get_answer_options():
|
||||
if option[0] in self.request.POST:
|
||||
return option[0]
|
||||
return None
|
||||
|
||||
def case_yes(self):
|
||||
# TODO: raise a warning
|
||||
pass
|
||||
def form_invalid(self, form):
|
||||
value = super(FormMixin, self).form_invalid(form)
|
||||
messages.error(self.request, self.get_error_message())
|
||||
return value
|
||||
|
||||
def get_success_message(self):
|
||||
return self.success_message
|
||||
|
||||
def get_error_message(self):
|
||||
return self.error_message
|
||||
|
||||
class TemplateView(PermissionMixin, ExtraContextMixin, _TemplateView):
|
||||
|
||||
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())
|
||||
|
||||
def manipulate_object(self, form):
|
||||
"""
|
||||
Called before the object is saved into the database.
|
||||
"""
|
||||
pass
|
||||
|
||||
def post_save(self, form):
|
||||
"""
|
||||
Called after the object is saved into the database.
|
||||
"""
|
||||
form.save_m2m()
|
||||
|
||||
|
||||
class TemplateView(PermissionMixin, ExtraContextMixin, django_views.TemplateView):
|
||||
"""
|
||||
View to return with an template.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ListView(PermissionMixin, SetCookieMixin, ExtraContextMixin, _ListView):
|
||||
class ListView(PermissionMixin, ExtraContextMixin, django_views.ListView):
|
||||
"""
|
||||
View to show a list of model objects.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class AjaxView(PermissionMixin, AjaxMixin, View):
|
||||
"""
|
||||
View for ajax requests.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
return self.ajax_get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class RedirectView(PermissionMixin, AjaxMixin, _RedirectView):
|
||||
class RedirectView(PermissionMixin, AjaxMixin, UrlMixin, django_views.RedirectView):
|
||||
"""
|
||||
View to redirect to another url.
|
||||
|
||||
@ -277,12 +300,19 @@ class RedirectView(PermissionMixin, AjaxMixin, _RedirectView):
|
||||
permanent = False
|
||||
allow_ajax = False
|
||||
url_name = None
|
||||
url_name_args = None
|
||||
|
||||
def pre_redirect(self, request, *args, **kwargs):
|
||||
"""
|
||||
Called before the redirect.
|
||||
"""
|
||||
# TODO: Also call this method on post-request.
|
||||
# Add pre_get_redirect for get requests.
|
||||
pass
|
||||
|
||||
def pre_post_redirect(self, request, *args, **kwargs):
|
||||
"""
|
||||
Called before the redirect, if it is a post request.
|
||||
"""
|
||||
pass
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
@ -291,62 +321,143 @@ class RedirectView(PermissionMixin, AjaxMixin, _RedirectView):
|
||||
elif request.method == 'POST':
|
||||
self.pre_post_redirect(request, *args, **kwargs)
|
||||
|
||||
if self.request.is_ajax() and self.allow_ajax:
|
||||
if request.is_ajax() and self.allow_ajax:
|
||||
return self.ajax_get(request, *args, **kwargs)
|
||||
return super(RedirectView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_redirect_url(self, **kwargs):
|
||||
if self.url_name is not None:
|
||||
return reverse(self.url_name, args=self.get_url_name_args())
|
||||
"""
|
||||
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())
|
||||
else:
|
||||
return super(RedirectView, self).get_redirect_url(**kwargs)
|
||||
url = super(QuestionView, self).get_redirect_url()
|
||||
return url
|
||||
|
||||
def get_url_name_args(self):
|
||||
def pre_redirect(self, request, *args, **kwargs):
|
||||
"""
|
||||
Returns the arguments for the url name. Default is an empty list
|
||||
or [self.object.pk] if this exist.
|
||||
Prints the question in a GET request.
|
||||
"""
|
||||
if self.url_name_args is not None:
|
||||
return self.url_name_args
|
||||
try:
|
||||
return [self.object.pk]
|
||||
except AttributeError:
|
||||
return []
|
||||
self.confirm_form()
|
||||
|
||||
def get_question_message(self):
|
||||
"""
|
||||
Returns the question.
|
||||
"""
|
||||
return self.question_message
|
||||
|
||||
def get_answer_options(self):
|
||||
"""
|
||||
Returns the possible answers.
|
||||
|
||||
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
|
||||
|
||||
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>'
|
||||
% (option[0], unicode(option[1]))
|
||||
for option in self.get_answer_options()])
|
||||
messages.warning(
|
||||
self.request,
|
||||
"""
|
||||
%(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})
|
||||
|
||||
def pre_post_redirect(self, request, *args, **kwargs):
|
||||
"""
|
||||
Calls the method for the answer the user clicked.
|
||||
|
||||
The method name is on_clicked_ANSWER where ANSWER is the key from the
|
||||
clicked answer. See get_answer_options. If this method is not defined,
|
||||
raises a NotImplementedError.
|
||||
|
||||
If the method returns True, then the success message is printed to the
|
||||
user.
|
||||
"""
|
||||
method_name = 'on_clicked_%s' % self.get_answer()
|
||||
method = getattr(self, method_name, None)
|
||||
if method is None:
|
||||
pass
|
||||
else:
|
||||
method()
|
||||
self.create_final_message()
|
||||
|
||||
def get_answer(self):
|
||||
"""
|
||||
Returns the key of the clicked answer.
|
||||
|
||||
Raises ImproperlyConfigured, if the answer is not one of
|
||||
get_answer_options.
|
||||
"""
|
||||
for option_key, option_name in self.get_answer_options():
|
||||
if option_key in self.request.POST:
|
||||
return option_key
|
||||
raise ImproperlyConfigured('%s is not a valid answer' % self.request.POST)
|
||||
|
||||
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, UrlMixin, _FormView):
|
||||
def form_invalid(self, form):
|
||||
messages.error(self.request, _('Please check the form for errors.'))
|
||||
return super(FormView, self).form_invalid(form)
|
||||
class FormView(PermissionMixin, ExtraContextMixin, FormMixin,
|
||||
django_views.FormView):
|
||||
"""
|
||||
View for forms.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ModelFormMixin(object):
|
||||
def form_valid(self, form):
|
||||
self.object = form.save(commit=False)
|
||||
self.manipulate_object(form)
|
||||
self.object.save()
|
||||
self.post_save(form)
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
def manipulate_object(self, form):
|
||||
pass
|
||||
|
||||
def post_save(self, form):
|
||||
form.save_m2m()
|
||||
|
||||
|
||||
class UpdateView(PermissionMixin, UrlMixin, ExtraContextMixin,
|
||||
ModelFormMixin, _UpdateView):
|
||||
success_message = None
|
||||
|
||||
def form_invalid(self, form):
|
||||
messages.error(self.request, _('Please check the form for errors.'))
|
||||
return super(UpdateView, self).form_invalid(form)
|
||||
|
||||
def form_valid(self, form):
|
||||
value = super(UpdateView, self).form_valid(form)
|
||||
messages.success(self.request, self.get_success_message())
|
||||
return value
|
||||
class UpdateView(PermissionMixin, ExtraContextMixin,
|
||||
ModelFormMixin, django_views.UpdateView):
|
||||
"""
|
||||
View to update an model object.
|
||||
"""
|
||||
|
||||
def get_success_message(self):
|
||||
if self.success_message is None:
|
||||
@ -356,18 +467,11 @@ class UpdateView(PermissionMixin, UrlMixin, ExtraContextMixin,
|
||||
return message
|
||||
|
||||
|
||||
class CreateView(PermissionMixin, UrlMixin, ExtraContextMixin,
|
||||
ModelFormMixin, _CreateView):
|
||||
success_message = None
|
||||
|
||||
def form_invalid(self, form):
|
||||
messages.error(self.request, _('Please check the form for errors.'))
|
||||
return super(CreateView, self).form_invalid(form)
|
||||
|
||||
def form_valid(self, form):
|
||||
value = super(CreateView, self).form_valid(form)
|
||||
messages.success(self.request, self.get_success_message())
|
||||
return value
|
||||
class CreateView(PermissionMixin, ExtraContextMixin,
|
||||
ModelFormMixin, django_views.CreateView):
|
||||
"""
|
||||
View to create a model object.
|
||||
"""
|
||||
|
||||
def get_success_message(self):
|
||||
if self.success_message is None:
|
||||
@ -377,8 +481,12 @@ class CreateView(PermissionMixin, UrlMixin, ExtraContextMixin,
|
||||
return message
|
||||
|
||||
|
||||
class DeleteView(SingleObjectMixin, QuestionMixin, RedirectView):
|
||||
question_url_name = None
|
||||
class DeleteView(SingleObjectMixin, QuestionView):
|
||||
"""
|
||||
View to delete an model object.
|
||||
"""
|
||||
|
||||
success_url = None
|
||||
success_url_name = None
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
@ -386,32 +494,53 @@ class DeleteView(SingleObjectMixin, QuestionMixin, RedirectView):
|
||||
return super(DeleteView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_redirect_url(self, **kwargs):
|
||||
if self.question_url_name is None and (self.request.method == 'GET' or
|
||||
self.get_answer() == 'no'):
|
||||
return self.object.get_absolute_url()
|
||||
else:
|
||||
return super(DeleteView, self).get_redirect_url(**kwargs)
|
||||
"""
|
||||
Returns the url on which the delete dialog is shown and the url after
|
||||
the deleten.
|
||||
|
||||
def get_question(self):
|
||||
On GET-requests and on aborted POST-requests, redirect to the detail
|
||||
view as default. The attributes question_url_name or question_url can
|
||||
define other urls.
|
||||
"""
|
||||
if self.request.method == 'GET' or self.get_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())
|
||||
return url
|
||||
|
||||
def get_question_message(self):
|
||||
"""
|
||||
Returns the question for the delete dialog.
|
||||
"""
|
||||
return _('Do you really want to delete %s?') % html_strong(self.object)
|
||||
|
||||
def case_yes(self):
|
||||
def on_clicked_yes(self):
|
||||
"""
|
||||
Deletes the object.
|
||||
"""
|
||||
self.object.delete()
|
||||
|
||||
def get_success_message(self):
|
||||
def get_final_message(self):
|
||||
"""
|
||||
Prints the success message to the user.
|
||||
"""
|
||||
return _('%s was successfully deleted.') % html_strong(self.object)
|
||||
|
||||
def get_url_name_args(self):
|
||||
return []
|
||||
|
||||
|
||||
class DetailView(PermissionMixin, ExtraContextMixin, _DetailView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
return super(DetailView, self).get(request, *args, **kwargs)
|
||||
class DetailView(PermissionMixin, ExtraContextMixin, django_views.DetailView):
|
||||
"""
|
||||
View to show an model object.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class PDFView(PermissionMixin, View):
|
||||
"""
|
||||
View to generate an PDF.
|
||||
"""
|
||||
|
||||
filename = _('undefined-filename')
|
||||
top_space = 3
|
||||
document_title = None
|
||||
@ -480,6 +609,8 @@ def send_register_tab(sender, request, context, **kwargs):
|
||||
extra_stylefiles = context['extra_stylefiles']
|
||||
else:
|
||||
extra_stylefiles = []
|
||||
context.setdefault('extra_javascript', [])
|
||||
|
||||
# TODO: Do not go over the filesystem by any request
|
||||
for app in settings.INSTALLED_APPS:
|
||||
try:
|
||||
|
@ -370,14 +370,14 @@ class TestMotionDeleteView(MotionViewTestCase):
|
||||
self.assertRedirects(response, '/motion/2/')
|
||||
|
||||
def test_admin(self):
|
||||
response = self.admin_client.post('/motion/2/del/', {})
|
||||
response = self.admin_client.post('/motion/2/del/', {'yes': 'yes'})
|
||||
self.assertRedirects(response, '/motion/')
|
||||
|
||||
def test_delegate(self):
|
||||
response = self.delegate_client.post('/motion/2/del/', {})
|
||||
response = self.delegate_client.post('/motion/2/del/', {'yes': 'yes'})
|
||||
self.assertEqual(response.status_code, 403)
|
||||
motion = Motion.objects.get(pk=2).add_submitter(self.delegate)
|
||||
response = self.delegate_client.post('/motion/2/del/', {})
|
||||
response = self.delegate_client.post('/motion/2/del/', {'yes': 'yes'})
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
|
||||
@ -394,7 +394,7 @@ class TestVersionPermitView(MotionViewTestCase):
|
||||
def test_post(self):
|
||||
new_version = self.motion1.get_last_version()
|
||||
response = self.admin_client.post('/motion/1/version/2/permit/', {'yes': 1})
|
||||
self.assertRedirects(response, '/motion/1/version/2/')
|
||||
self.assertRedirects(response, '/motion/1/')
|
||||
self.assertEqual(self.motion1.get_active_version(), new_version)
|
||||
|
||||
def test_activate_old_version(self):
|
||||
|
25
tests/utils/test_utils.py
Normal file
25
tests/utils/test_utils.py
Normal file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for openslides utils.utils
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
TODO: Move this test to the correct place when the projector app is cleaned up.
|
||||
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from openslides.utils.test import TestCase
|
||||
from openslides.utils.utils import html_strong, int_or_none
|
||||
|
||||
|
||||
class Test_functions(TestCase):
|
||||
def test_string(self):
|
||||
self.assertEqual(html_strong('some text'), '<strong>some text</strong>')
|
||||
|
||||
def test_int_or_none(self):
|
||||
self.assertEqual(int_or_none('5'), 5)
|
||||
self.assertEqual(int_or_none(5), 5)
|
||||
self.assertIsNone(int_or_none('text'))
|
||||
self.assertIsNone(int_or_none(None))
|
210
tests/utils/test_views.py
Normal file
210
tests/utils/test_views.py
Normal file
@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for openslides utils.utils
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
TODO: Move this test to the correct place when the projector app is cleaned up.
|
||||
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from django.contrib.messages.storage import default_storage
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.urlresolvers import clear_url_caches
|
||||
from django.dispatch import receiver
|
||||
from django.http.response import HttpResponseNotAllowed
|
||||
from django.test import RequestFactory
|
||||
from django.test.client import Client
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from openslides.participant.models import User
|
||||
from openslides.utils import views
|
||||
from openslides.utils.signals import template_manipulation
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
from . import views as test_views
|
||||
|
||||
|
||||
@override_settings(ROOT_URLCONF='tests.utils.urls')
|
||||
class ViewTestCase(TestCase):
|
||||
rf = RequestFactory()
|
||||
|
||||
def setUp(self):
|
||||
# Clear the cache for the urlresolver, so the overriden settings works.
|
||||
clear_url_caches()
|
||||
|
||||
|
||||
class LoginMixinTest(ViewTestCase):
|
||||
def test_dispatch(self):
|
||||
view = test_views.LoginMixinView.as_view()
|
||||
client = Client()
|
||||
response = client.get('/login_mixin/')
|
||||
self.assertEqual(response['Location'], 'http://testserver/login/?next=/login_mixin/')
|
||||
|
||||
client.login(username='admin', password='admin')
|
||||
response = client.get('/login_mixin/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, 'Well done.')
|
||||
|
||||
|
||||
class PermissionMixinTest(ViewTestCase):
|
||||
def test_dispatch(self):
|
||||
client = Client()
|
||||
|
||||
# View without permission_required
|
||||
response = client.get('/permission_mixin1/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, 'Well done.')
|
||||
|
||||
# View with permission_required without login
|
||||
response = client.get('/permission_mixin2/')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response['Location'], 'http://testserver/login/?next=/permission_mixin2/')
|
||||
|
||||
# View with permission_required, with login, without permission
|
||||
client.login(username='admin', password='admin')
|
||||
response = client.get('/permission_mixin2/')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
# View with permission_required, with login, with permission
|
||||
response = client.get('/permission_mixin3/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class AjaxMixinTest(ViewTestCase):
|
||||
def test_ajax_get(self):
|
||||
view = test_views.AjaxMixinView()
|
||||
ajax_get = view.ajax_get
|
||||
response = ajax_get(self.rf.get('/', {}))
|
||||
self.assertEqual(response.content, '{"new_context": "newer_context"}')
|
||||
|
||||
def test_get_ajax_context(self):
|
||||
get_ajax_context = test_views.AjaxMixinView().get_ajax_context
|
||||
self.assertEqual(get_ajax_context()['new_context'], 'newer_context')
|
||||
self.assertEqual(
|
||||
get_ajax_context(test_content='some_content')['test_content'],
|
||||
'some_content')
|
||||
|
||||
|
||||
class ExtraContextMixinTest(ViewTestCase):
|
||||
"""
|
||||
Tests the ExtraContextMixin by testen the TemplateView
|
||||
"""
|
||||
def test_get_context_data(self):
|
||||
view = views.TemplateView()
|
||||
get_context_data = view.get_context_data
|
||||
view.request = self.rf.get('/', {})
|
||||
|
||||
context = get_context_data()
|
||||
self.assertIn('tabs', context)
|
||||
|
||||
context = get_context_data(some_context='context')
|
||||
self.assertIn('tabs', context)
|
||||
self.assertIn('some_context', context)
|
||||
|
||||
template_manipulation.connect(set_context, dispatch_uid='set_context_test')
|
||||
context = get_context_data()
|
||||
self.assertIn('tabs', context)
|
||||
self.assertIn('new_context', context)
|
||||
template_manipulation.disconnect(set_context, dispatch_uid='set_context_test')
|
||||
|
||||
|
||||
class UrlMixinTest(ViewTestCase):
|
||||
def test_get_url(self):
|
||||
get_url = test_views.UrlMixinView().get_url
|
||||
|
||||
# url_name has the higher priority
|
||||
self.assertEqual(get_url('test_url_mixin', 'view_url'),
|
||||
'/url_mixin/')
|
||||
|
||||
# If the url_name is none, return the second argument
|
||||
self.assertEqual(get_url(None, 'view_url'),
|
||||
'view_url')
|
||||
|
||||
# Test argument in url
|
||||
self.assertEqual(get_url('test_url_mixin_args', None,
|
||||
args=[1]),
|
||||
'/url_mixin_args/1/')
|
||||
|
||||
# No Url given.
|
||||
self.assertRaisesMessage(
|
||||
ImproperlyConfigured,
|
||||
'No url to redirect to. See openslides.utils.views.UrlMixin for more details.',
|
||||
get_url, None, None)
|
||||
|
||||
def test_get_url_with_object(self):
|
||||
get_url = test_views.UrlMixinViewWithObject().get_url
|
||||
|
||||
self.assertEqual(get_url(None, None),
|
||||
'default_url')
|
||||
self.assertEqual(get_url(None, None, use_absolute_url_link='detail'),
|
||||
'detail_url')
|
||||
|
||||
def test_get_url_name_args(self):
|
||||
view = test_views.UrlMixinViewWithObject()
|
||||
get_url_name_args = view.get_url_name_args
|
||||
view.url_name_args = [1]
|
||||
|
||||
self.assertEqual(get_url_name_args(), [1])
|
||||
|
||||
view.url_name_args = None
|
||||
self.assertEqual(get_url_name_args(), [])
|
||||
|
||||
view.object.pk = 5
|
||||
self.assertEqual(get_url_name_args(), [5])
|
||||
|
||||
|
||||
## class QuestionMixinTest(ViewTestCase):
|
||||
## def test_get_redirect_url(self):
|
||||
## view = views.QuestionMixin()
|
||||
## get_redirect_url = view.get_redirect_url
|
||||
##
|
||||
## view.request = self.rf.get('/')
|
||||
## view.question_url = 'redirect_to_get_url'
|
||||
## self.assertEqual(get_redirect_url(), 'redirect_to_get_url')
|
||||
##
|
||||
## view.request = self.rf.post('/')
|
||||
## view.success_url = 'redirect_to_post_url'
|
||||
## self.assertEqual(get_redirect_url(), 'redirect_to_post_url')
|
||||
##
|
||||
## def test_get_question(self):
|
||||
## view = views.QuestionMixin()
|
||||
## get_redirect_url = view.get_question
|
||||
##
|
||||
## self.assertEqual(get_redirect_url(), 'Are you sure?')
|
||||
##
|
||||
## view.question = 'new_question'
|
||||
## self.assertEqual(get_redirect_url(), 'new_question')
|
||||
##
|
||||
## def test_get_answer_options(self):
|
||||
## view = views.QuestionMixin()
|
||||
## get_answer_options = view.get_answer_options
|
||||
##
|
||||
## self.assertIn('yes', dict(get_answer_options()))
|
||||
## self.assertIn('no', dict(get_answer_options()))
|
||||
##
|
||||
## view.answer_options = [('new_answer', 'Answer')]
|
||||
## self.assertNotIn('yes', dict(get_answer_options()))
|
||||
## self.assertIn('new_answer', dict(get_answer_options()))
|
||||
##
|
||||
## def test_confirm_form(self):
|
||||
## view = views.QuestionMixin()
|
||||
## confirm_form = view.confirm_form
|
||||
## view.request = self.rf.get('/')
|
||||
## view.request._messages = default_storage(view.request)
|
||||
##
|
||||
## confirm_form()
|
||||
## message = "".join(view.request._messages._queued_messages[0].message.split())
|
||||
## self.assertEqual(
|
||||
## message, 'Areyousure?<formaction="/"method="post">'
|
||||
## '<inputtype="hidden"value="NOTPROVIDED"name="csrfmiddlewaretoken">' '<buttontype="submit"class="btnbtn-mini"name="yes">Yes</button>'
|
||||
## '<buttontype="submit"class="btnbtn-mini"name="no">No</button></form>')
|
||||
|
||||
|
||||
def set_context(sender, request, context, **kwargs):
|
||||
"""
|
||||
receiver for testing the ExtraContextMixin
|
||||
"""
|
||||
context.update({'new_context': 'some new context'})
|
29
tests/utils/urls.py
Normal file
29
tests/utils/urls.py
Normal file
@ -0,0 +1,29 @@
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
from openslides.urls import urlpatterns
|
||||
|
||||
from . import views
|
||||
|
||||
|
||||
urlpatterns += patterns(
|
||||
'',
|
||||
url(r'^url_mixin/$',
|
||||
views.UrlMixinView.as_view(),
|
||||
name='test_url_mixin'),
|
||||
|
||||
url(r'^url_mixin_args/(?P<arg>\d+)/$',
|
||||
views.UrlMixinView.as_view(),
|
||||
name='test_url_mixin_args'),
|
||||
|
||||
url(r'^login_mixin/$',
|
||||
views.LoginMixinView.as_view()),
|
||||
|
||||
url(r'^permission_mixin1/$',
|
||||
views.PermissionMixinView.as_view()),
|
||||
|
||||
url(r'^permission_mixin2/$',
|
||||
views.PermissionMixinView.as_view(permission_required='permission_string')),
|
||||
|
||||
url(r'^permission_mixin3/$',
|
||||
views.PermissionMixinView.as_view(permission_required='agenda.can_see_agenda')),
|
||||
)
|
47
tests/utils/views.py
Normal file
47
tests/utils/views.py
Normal file
@ -0,0 +1,47 @@
|
||||
from django.http import HttpResponse
|
||||
|
||||
from openslides.utils import views
|
||||
|
||||
|
||||
class GetAbsoluteUrl(object):
|
||||
"""
|
||||
Generates objects with a get_absolute_url method.
|
||||
|
||||
Helper Class for the tests.
|
||||
"""
|
||||
def get_absolute_url(self, link='default'):
|
||||
if link == 'detail':
|
||||
url = 'detail_url'
|
||||
elif link == 'update':
|
||||
url = 'update_url'
|
||||
elif link == 'default':
|
||||
url = 'default_url'
|
||||
return url
|
||||
|
||||
|
||||
class LoginMixinView(views.LoginMixin, views.View):
|
||||
def get(self, *args, **kwargs):
|
||||
response = HttpResponse('Well done.')
|
||||
response.status_code = 200
|
||||
return response
|
||||
|
||||
|
||||
class PermissionMixinView(views.PermissionMixin, views.View):
|
||||
def get(self, *args, **kwargs):
|
||||
response = HttpResponse('Well done.')
|
||||
response.status_code = 200
|
||||
return response
|
||||
|
||||
|
||||
class AjaxMixinView(views.AjaxMixin, views.View):
|
||||
def get_ajax_context(self, **kwargs):
|
||||
return super(AjaxMixinView, self).get_ajax_context(
|
||||
new_context='newer_context', **kwargs)
|
||||
|
||||
|
||||
class UrlMixinView(views.UrlMixin, views.View):
|
||||
pass
|
||||
|
||||
|
||||
class UrlMixinViewWithObject(views.UrlMixin, views.View):
|
||||
object = GetAbsoluteUrl()
|
Loading…
Reference in New Issue
Block a user