OpenSlides/openslides/motion/views.py

961 lines
39 KiB
Python
Raw Normal View History

2011-07-31 10:46:29 +02:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
2012-10-24 11:04:23 +02:00
openslides.motion.views
2011-07-31 10:46:29 +02:00
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2012-10-24 11:04:23 +02:00
Views for the motion app.
2011-07-31 10:46:29 +02:00
2012-04-25 22:29:19 +02:00
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
2011-07-31 10:46:29 +02:00
:license: GNU GPL, see LICENSE for more details.
"""
# for python 2.5 support
from __future__ import with_statement
2011-07-31 10:46:29 +02:00
import csv
import os
2012-04-18 19:57:39 +02:00
try:
from urlparse import parse_qs
except ImportError: # python <= 2.5
from cgi import parse_qs
2012-06-11 13:43:48 +02:00
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.platypus import (SimpleDocTemplate, PageBreak, Paragraph, Spacer,
Table, TableStyle)
2012-06-11 13:43:48 +02:00
from django.conf import settings
2011-07-31 10:46:29 +02:00
from django.contrib import messages
from django.contrib.auth.decorators import login_required
2012-07-10 11:27:06 +02:00
from django.contrib.auth.models import User
from django.core.context_processors import csrf
from django.core.urlresolvers import reverse
from django.db import transaction
from django.shortcuts import redirect
from django.utils.translation import ugettext as _, ungettext
2012-06-11 13:43:48 +02:00
from openslides.utils import csv_ext
from openslides.utils.pdf import stylesheet
from openslides.utils.template import Tab
from openslides.utils.utils import (template, permission_required,
del_confirm_form, gen_confirm_form)
from openslides.utils.views import (PDFView, RedirectView, DeleteView,
FormView, SingleObjectMixin, QuestionMixin)
from openslides.utils.person import get_person
2012-06-11 13:43:48 +02:00
from openslides.config.models import config
from openslides.projector.projector import Widget
2012-06-11 13:43:48 +02:00
from openslides.poll.views import PollFormView
2012-06-11 13:43:48 +02:00
from openslides.participant.api import gen_username, gen_password
from openslides.participant.models import User, Group
2012-02-15 12:04:11 +01:00
from openslides.agenda.models import Item
2012-02-15 12:04:11 +01:00
2012-10-24 11:04:23 +02:00
from openslides.motion.models import Motion, AVersion, MotionPoll
from openslides.motion.forms import (MotionForm,
MotionFormTrivialChanges, MotionManagerForm,
MotionManagerFormSupporter, MotionImportForm, ConfigForm)
2012-02-15 12:04:11 +01:00
2012-04-15 13:10:24 +02:00
2012-10-24 11:04:23 +02:00
@permission_required('motion.can_see_motion')
@template('motion/overview.html')
2011-07-31 10:46:29 +02:00
def overview(request):
"""
2012-10-24 11:04:23 +02:00
View all motions
2011-07-31 10:46:29 +02:00
"""
try:
2012-04-18 19:57:39 +02:00
sortfilter = parse_qs(request.COOKIES['votecollector_sortfilter'])
for value in sortfilter:
sortfilter[value] = sortfilter[value][0]
2011-07-31 10:46:29 +02:00
except KeyError:
2012-04-18 19:57:39 +02:00
sortfilter = {}
for value in [u'sort', u'reverse', u'number', u'status', u'needsup', u'statusvalue']:
if value in request.REQUEST:
if request.REQUEST[value] == '0':
try:
del sortfilter[value]
except KeyError:
pass
else:
sortfilter[value] = request.REQUEST[value]
2012-10-24 11:04:23 +02:00
query = Motion.objects.all()
2012-04-18 19:57:39 +02:00
if 'number' in sortfilter:
query = query.filter(number=None)
if 'status' in sortfilter:
if 'statusvalue' in sortfilter and 'on' in sortfilter['status']:
query = query.filter(status__iexact=sortfilter['statusvalue'])
if 'sort' in sortfilter:
if sortfilter['sort'] == 'title':
sort = 'aversion__title'
elif sortfilter['sort'] == 'time':
sort = 'aversion__time'
else:
sort = sortfilter['sort']
query = query.order_by(sort)
if sort.startswith('aversion_'):
2012-10-24 11:04:23 +02:00
# limit result to last version of an motion
query = query.filter(aversion__id__in=[x.last_version.id for x in Motion.objects.all()])
2012-04-18 19:57:39 +02:00
if 'reverse' in sortfilter:
2011-07-31 10:46:29 +02:00
query = query.reverse()
2012-04-18 19:57:39 +02:00
# todo: rewrite this with a .filter()
if 'needsup' in sortfilter:
2012-10-24 11:04:23 +02:00
motions = []
for motion in query.all():
if not motion.enough_supporters:
motions.append(motion)
2011-07-31 10:46:29 +02:00
else:
2012-10-24 11:04:23 +02:00
motions = query
2012-04-18 19:57:39 +02:00
2012-10-24 11:04:23 +02:00
if type(motions) is not list:
motions = list(query.all())
# not the most efficient way to do this but 'get_allowed_actions'
# is not callable from within djangos templates..
2012-10-24 11:04:23 +02:00
for (i, motion) in enumerate(motions):
try:
2012-10-24 11:04:23 +02:00
motions[i] = {
'actions' : motion.get_allowed_actions(request.user),
'motion' : motion
}
except:
# todo: except what?
2012-10-24 11:04:23 +02:00
motions[i] = {
'actions' : [],
2012-10-24 11:04:23 +02:00
'motion' : motion
}
2011-07-31 10:46:29 +02:00
return {
2012-10-24 11:04:23 +02:00
'motions': motions,
'min_supporters': int(config['motion_min_supporters']),
2011-07-31 10:46:29 +02:00
}
2012-10-24 11:04:23 +02:00
@permission_required('motion.can_see_motion')
@template('motion/view.html')
def view(request, motion_id, newest=False):
2011-07-31 10:46:29 +02:00
"""
2012-10-24 11:04:23 +02:00
View one motion.
2011-07-31 10:46:29 +02:00
"""
2012-10-24 11:04:23 +02:00
motion = Motion.objects.get(pk=motion_id)
2011-09-06 10:41:06 +02:00
if newest:
2012-10-24 11:04:23 +02:00
version = motion.last_version
2011-09-06 10:41:06 +02:00
else:
2012-10-24 11:04:23 +02:00
version = motion.public_version
revisions = motion.versions
actions = motion.get_allowed_actions(user=request.user)
2011-07-31 10:46:29 +02:00
return {
2012-10-24 11:04:23 +02:00
'motion': motion,
2011-07-31 10:46:29 +02:00
'revisions': revisions,
'actions': actions,
2012-10-24 11:04:23 +02:00
'min_supporters': int(config['motion_min_supporters']),
'version': version,
2012-10-24 11:04:23 +02:00
#'results': motion.results
2011-07-31 10:46:29 +02:00
}
@login_required
2012-10-24 11:04:23 +02:00
@template('motion/edit.html')
def edit(request, motion_id=None):
2011-07-31 10:46:29 +02:00
"""
2012-10-24 11:04:23 +02:00
View a form to edit or create a motion.
2011-07-31 10:46:29 +02:00
"""
2012-10-24 11:04:23 +02:00
if request.user.has_perm('motion.can_manage_motion'):
2011-07-31 10:46:29 +02:00
is_manager = True
else:
is_manager = False
if not is_manager \
2012-10-24 11:04:23 +02:00
and not request.user.has_perm('motion.can_create_motion'):
messages.error(request, _("You have not the necessary rights to create or edit motions."))
2012-10-24 11:04:23 +02:00
return redirect(reverse('motion_overview'))
if motion_id is not None:
motion = Motion.objects.get(id=motion_id)
if not 'edit' in motion.get_allowed_actions(request.user):
messages.error(request, _("You can not edit this motion."))
2012-10-24 11:04:23 +02:00
return redirect(reverse('motion_view', args=[motion.id]))
actions = motion.get_allowed_actions(user=request.user)
2011-07-31 10:46:29 +02:00
else:
2012-10-24 11:04:23 +02:00
motion = None
actions = None
2011-07-31 10:46:29 +02:00
2012-10-24 11:04:23 +02:00
formclass = MotionFormTrivialChanges \
if config['motion_allow_trivial_change'] and motion_id \
else MotionForm
2012-10-24 11:04:23 +02:00
managerformclass = MotionManagerFormSupporter \
if config['motion_min_supporters'] \
else MotionManagerForm
2011-07-31 10:46:29 +02:00
if request.method == 'POST':
dataform = formclass(request.POST, prefix="data")
2011-07-31 10:46:29 +02:00
valid = dataform.is_valid()
if is_manager:
managerform = managerformclass(request.POST,
2012-10-24 11:04:23 +02:00
instance=motion,
2011-07-31 10:46:29 +02:00
prefix="manager")
valid = valid and managerform.is_valid()
else:
managerform = None
if valid:
del_supporters = True
if is_manager:
2012-10-24 11:04:23 +02:00
if motion: # Edit motion
original_supporters = list(motion.supporters)
else:
original_supporters = []
2012-10-24 11:04:23 +02:00
motion = managerform.save(commit=False)
elif motion_id is None:
motion = Motion(submitter=request.user)
motion.title = dataform.cleaned_data['title']
motion.text = dataform.cleaned_data['text']
motion.reason = dataform.cleaned_data['reason']
2012-06-30 14:50:14 +02:00
try:
2012-10-24 11:04:23 +02:00
trivial_change = config['motion_allow_trivial_change'] \
2012-06-30 14:50:14 +02:00
and dataform.cleaned_data['trivial_change']
except KeyError:
trivial_change = False
2012-10-24 11:04:23 +02:00
motion.save(request.user, trivial_change=trivial_change)
if is_manager:
try:
new_supporters = set(managerform.cleaned_data['supporter'])
except KeyError:
# The managerform has no field for the supporters
pass
else:
2012-10-24 11:04:23 +02:00
old_supporters = set(motion.supporters)
# add new supporters
for supporter in new_supporters.difference(old_supporters):
2012-10-24 11:04:23 +02:00
motion.support(supporter)
# remove old supporters
for supporter in old_supporters.difference(new_supporters):
2012-10-24 11:04:23 +02:00
motion.unsupport(supporter)
2012-10-24 11:04:23 +02:00
if motion_id is None:
messages.success(request, _('New motion was successfully created.'))
2011-07-31 10:46:29 +02:00
else:
messages.success(request, _('Motion was successfully modified.'))
2011-09-04 10:02:54 +02:00
if not 'apply' in request.POST:
2012-10-24 11:04:23 +02:00
return redirect(reverse('motion_view', args=[motion.id]))
if motion_id is None:
return redirect(reverse('motion_edit', args=[motion.id]))
else:
messages.error(request, _('Please check the form for errors.'))
2011-07-31 10:46:29 +02:00
else:
2012-10-24 11:04:23 +02:00
if motion_id is None:
initial = {'text': config['motion_preamble']}
2011-07-31 10:46:29 +02:00
else:
2012-10-24 11:04:23 +02:00
if motion.status == "pub" and motion.supporters:
if request.user.has_perm('motion.can_manage_motion'):
messages.warning(request, _("Attention: Do you really want to edit this motion? The supporters will <b>not</b> be removed automatically because you can manage motions. Please check if the supports are valid after your changing!"))
2011-07-31 10:46:29 +02:00
else:
2012-10-24 11:04:23 +02:00
messages.warning(request, _("Attention: Do you really want to edit this motion? All <b>%s</b> supporters will be removed! Try to convince the supporters again.") % motion.count_supporters() )
initial = {'title': motion.title,
'text': motion.text,
'reason': motion.reason}
2011-07-31 10:46:29 +02:00
dataform = formclass(initial=initial, prefix="data")
2011-07-31 10:46:29 +02:00
if is_manager:
2012-10-24 11:04:23 +02:00
if motion_id is None:
initial = {'submitter': request.user.person_id}
2011-07-31 10:46:29 +02:00
else:
2012-10-24 11:04:23 +02:00
initial = {'submitter': motion.submitter.person_id,
'supporter': [supporter.person_id for supporter in motion.supporters]}
2012-08-03 00:11:53 +02:00
managerform = managerformclass(initial=initial,
2012-10-24 11:04:23 +02:00
instance=motion, prefix="manager")
2011-07-31 10:46:29 +02:00
else:
managerform = None
return {
'form': dataform,
'managerform': managerform,
2012-10-24 11:04:23 +02:00
'motion': motion,
'actions': actions,
2011-07-31 10:46:29 +02:00
}
2012-06-30 14:50:14 +02:00
2012-10-24 11:04:23 +02:00
@permission_required('motion.can_manage_motion')
@template('motion/view.html')
def set_number(request, motion_id):
2011-07-31 10:46:29 +02:00
"""
2012-10-24 11:04:23 +02:00
set a number for an motion.
2011-07-31 10:46:29 +02:00
"""
try:
2012-10-24 11:04:23 +02:00
Motion.objects.get(pk=motion_id).set_number(user=request.user)
messages.success(request, _("Motion number was successfully set."))
2012-10-24 11:04:23 +02:00
except Motion.DoesNotExist:
2011-07-31 10:46:29 +02:00
pass
except NameError:
pass
2012-10-24 11:04:23 +02:00
return redirect(reverse('motion_view', args=[motion_id]))
2011-07-31 10:46:29 +02:00
2012-10-24 11:04:23 +02:00
@permission_required('motion.can_manage_motion')
@template('motion/view.html')
def permit(request, motion_id):
2011-07-31 10:46:29 +02:00
"""
2012-10-24 11:04:23 +02:00
permit an motion.
2011-07-31 10:46:29 +02:00
"""
try:
2012-10-24 11:04:23 +02:00
Motion.objects.get(pk=motion_id).permit(user=request.user)
messages.success(request, _("Motion was successfully authorized."))
2012-10-24 11:04:23 +02:00
except Motion.DoesNotExist:
2011-07-31 10:46:29 +02:00
pass
except NameError, e:
messages.error(request, e)
2012-10-24 11:04:23 +02:00
return redirect(reverse('motion_view', args=[motion_id]))
2011-07-31 10:46:29 +02:00
2012-10-24 11:04:23 +02:00
@permission_required('motion.can_manage_motion')
@template('motion/view.html')
def notpermit(request, motion_id):
2011-07-31 10:46:29 +02:00
"""
2012-10-24 11:04:23 +02:00
reject (not permit) an motion.
2011-07-31 10:46:29 +02:00
"""
try:
2012-10-24 11:04:23 +02:00
Motion.objects.get(pk=motion_id).notpermit(user=request.user)
messages.success(request, _("Motion was successfully rejected."))
2012-10-24 11:04:23 +02:00
except Motion.DoesNotExist:
2011-07-31 10:46:29 +02:00
pass
except NameError, e:
messages.error(request, e)
2012-10-24 11:04:23 +02:00
return redirect(reverse('motion_view', args=[motion_id]))
2011-07-31 10:46:29 +02:00
2012-10-24 11:04:23 +02:00
@template('motion/view.html')
def set_status(request, motion_id=None, status=None):
2011-07-31 10:46:29 +02:00
"""
2012-10-24 11:04:23 +02:00
set a status of an motion.
2011-07-31 10:46:29 +02:00
"""
try:
if status is not None:
2012-10-24 11:04:23 +02:00
motion = Motion.objects.get(pk=motion_id)
motion.set_status(user=request.user, status=status)
messages.success(request, _("Motion status was set to: <b>%s</b>.") % motion.get_status_display())
except Motion.DoesNotExist:
2011-07-31 10:46:29 +02:00
pass
except NameError, e:
messages.error(request, e)
2012-10-24 11:04:23 +02:00
return redirect(reverse('motion_view', args=[motion_id]))
2011-07-31 10:46:29 +02:00
2012-10-24 11:04:23 +02:00
@permission_required('motion.can_manage_motion')
@template('motion/view.html')
def reset(request, motion_id):
2011-07-31 10:46:29 +02:00
"""
2012-10-24 11:04:23 +02:00
reset an motion.
2011-07-31 10:46:29 +02:00
"""
try:
2012-10-24 11:04:23 +02:00
Motion.objects.get(pk=motion_id).reset(user=request.user)
messages.success(request, _("Motion status was reset.") )
2012-10-24 11:04:23 +02:00
except Motion.DoesNotExist:
2011-07-31 10:46:29 +02:00
pass
2012-10-24 11:04:23 +02:00
return redirect(reverse('motion_view', args=[motion_id]))
2011-07-31 10:46:29 +02:00
class SupportView(SingleObjectMixin, QuestionMixin, RedirectView):
2011-07-31 10:46:29 +02:00
"""
Classed based view to support or unsupport a motion. Use
support=True or support=False in urls.py
2011-07-31 10:46:29 +02:00
"""
permission_required = 'motion.can_support_motion'
model = Motion
pk_url_kwarg = 'motion_id'
support = True
2012-10-28 19:59:41 +01:00
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super(SupportView, self).get(request, *args, **kwargs)
2012-10-28 17:44:15 +01:00
def check_allowed_actions(self, request):
"""
Checks whether request.user can support or unsupport the motion.
Returns True or False.
"""
2012-10-28 19:59:41 +01:00
allowed_actions = self.object.get_allowed_actions(request.user)
if self.support and not 'support' in allowed_actions:
messages.error(request, _('You can not support this motion.'))
2012-10-28 17:44:15 +01:00
return False
elif not self.support and not 'unsupport' in allowed_actions:
messages.error(request, _('You can not unsupport this motion.'))
2012-10-28 17:44:15 +01:00
return False
else:
2012-10-28 17:44:15 +01:00
return True
def pre_redirect(self, request, *args, **kwargs):
if self.check_allowed_actions(request):
super(SupportView, self).pre_redirect(request, *args, **kwargs)
2012-10-28 19:59:41 +01:00
def get_question(self):
if self.support:
return _('Do you really want to support this motion?')
else:
return _('Do you really want to unsupport this motion?')
def case_yes(self):
if self.check_allowed_actions(self.request):
if self.support:
2012-10-28 19:59:41 +01:00
self.object.support(person=self.request.user)
else:
2012-10-28 19:59:41 +01:00
self.object.unsupport(person=self.request.user)
2011-07-31 10:46:29 +02:00
2012-10-28 19:59:41 +01:00
def get_success_message(self):
if self.support:
2012-10-28 19:59:41 +01:00
return _("You have supported this motion successfully.")
else:
2012-10-28 19:59:41 +01:00
return _("You have unsupported this motion successfully.")
def get_redirect_url(self, **kwargs):
return reverse('motion_view', args=[kwargs[self.pk_url_kwarg]])
2012-10-24 11:04:23 +02:00
@permission_required('motion.can_manage_motion')
@template('motion/view.html')
def gen_poll(request, motion_id):
2011-07-31 10:46:29 +02:00
"""
2012-10-24 11:04:23 +02:00
gen a poll for this motion.
2011-07-31 10:46:29 +02:00
"""
try:
2012-10-24 11:04:23 +02:00
poll = Motion.objects.get(pk=motion_id).gen_poll(user=request.user)
messages.success(request, _("New vote was successfully created.") )
2012-10-24 11:04:23 +02:00
except Motion.DoesNotExist:
2012-02-14 16:31:21 +01:00
pass # TODO: do not call poll after this excaption
2012-10-24 11:04:23 +02:00
return redirect(reverse('motion_poll_view', args=[poll.id]))
2011-07-31 10:46:29 +02:00
2012-10-24 11:04:23 +02:00
@permission_required('motion.can_manage_motion')
2011-07-31 10:46:29 +02:00
def delete_poll(request, poll_id):
"""
2012-10-24 11:04:23 +02:00
delete a poll from this motion
2011-07-31 10:46:29 +02:00
"""
2012-10-24 11:04:23 +02:00
poll = MotionPoll.objects.get(pk=poll_id)
motion = poll.motion
count = motion.polls.filter(id__lte=poll_id).count()
2011-07-31 10:46:29 +02:00
if request.method == 'POST':
poll.delete()
2012-10-24 11:04:23 +02:00
motion.writelog(_("Poll deleted"), request.user)
2011-07-31 10:46:29 +02:00
messages.success(request, _('Poll was successfully deleted.'))
else:
2012-10-24 11:04:23 +02:00
del_confirm_form(request, poll, name=_("the %s. poll") % count, delete_link=reverse('motion_poll_delete', args=[poll_id]))
return redirect(reverse('motion_view', args=[motion.id]))
2011-07-31 10:46:29 +02:00
2012-06-09 19:39:52 +02:00
2012-10-24 11:04:23 +02:00
class MotionDelete(DeleteView):
"""
2012-10-24 11:04:23 +02:00
Delete one or more Motions.
"""
2012-10-24 11:04:23 +02:00
model = Motion
url = 'motion_overview'
2012-10-24 12:45:40 +02:00
def has_permission(self, request, *args, **kwargs):
self.kwargs = kwargs
return self.get_object().get_allowed_actions(request.user)
def get_object(self):
2012-10-24 11:04:23 +02:00
self.motions = []
2012-10-24 11:04:23 +02:00
if self.kwargs.get('motion_id', None):
try:
2012-10-24 11:04:23 +02:00
return Motion.objects.get(id=int(self.kwargs['motion_id']))
except Motion.DoesNotExist:
return None
2012-10-24 11:04:23 +02:00
if self.kwargs.get('motion_ids', []):
for appid in self.kwargs['motion_ids']:
try:
2012-10-24 11:04:23 +02:00
self.motions.append(Motion.objects.get(id=int(appid)))
except Motion.DoesNotExist:
pass
2012-10-24 11:04:23 +02:00
if self.motions:
return self.motions[0]
return None
def pre_post_redirect(self, request, *args, **kwargs):
self.object = self.get_object()
2012-10-24 11:04:23 +02:00
if len(self.motions):
for motion in self.motions:
if not 'delete' in motion.get_allowed_actions(user=request.user):
messages.error(request, _("You can not delete motion <b>%s</b>.") % motion)
continue
2012-10-24 11:04:23 +02:00
title = motion.title
motion.delete(force=True)
messages.success(request, _("Motion <b>%s</b> was successfully deleted.") % title)
elif self.object:
if not 'delete' in self.object.get_allowed_actions(user=request.user):
messages.error(request, _("You can not delete motion <b>%s</b>.") % self.object)
elif self.get_answer() == 'yes':
title = self.object.title
self.object.delete(force=True)
messages.success(request, _("Motion <b>%s</b> was successfully deleted.") % title)
else:
messages.error(request, _("Invalid request"))
2012-02-14 16:31:21 +01:00
class ViewPoll(PollFormView):
2012-10-24 11:04:23 +02:00
permission_required = 'motion.can_manage_motion'
poll_class = MotionPoll
template_name = 'motion/poll_view.html'
2012-02-14 16:31:21 +01:00
def get_context_data(self, **kwargs):
context = super(ViewPoll, self).get_context_data(**kwargs)
2012-10-24 11:04:23 +02:00
self.motion = self.poll.get_motion()
context['motion'] = self.motion
context['ballot'] = self.poll.get_ballot()
2012-10-24 11:04:23 +02:00
context['actions'] = self.motion.get_allowed_actions(user=self.request.user)
2012-02-14 16:31:21 +01:00
return context
def get_modelform_class(self):
cls = super(ViewPoll, self).get_modelform_class()
user = self.request.user
class ViewPollFormClass(cls):
def save(self, commit = True):
instance = super(ViewPollFormClass, self).save(commit)
2012-10-24 11:04:23 +02:00
motion = instance.motion
motion.writelog(_("Poll was updated"), user)
return instance
return ViewPollFormClass
2012-02-14 16:31:21 +01:00
def get_success_url(self):
if not 'apply' in self.request.POST:
2012-10-24 11:04:23 +02:00
return reverse('motion_view', args=[self.poll.motion.id])
2012-02-14 16:31:21 +01:00
return ''
2012-10-24 11:04:23 +02:00
@permission_required('motion.can_manage_motion')
def permit_version(request, aversion_id):
aversion = AVersion.objects.get(pk=aversion_id)
2012-10-24 11:04:23 +02:00
motion = aversion.motion
2011-09-04 10:43:25 +02:00
if request.method == 'POST':
2012-10-24 11:04:23 +02:00
motion.accept_version(aversion, user=request.user)
2011-09-04 10:43:25 +02:00
messages.success(request, _("Version <b>%s</b> accepted.") % (aversion.aid))
else:
2012-10-24 11:04:23 +02:00
gen_confirm_form(request, _('Do you really want to authorize version <b>%s</b>?') % aversion.aid, reverse('motion_version_permit', args=[aversion.id]))
return redirect(reverse('motion_view', args=[motion.id]))
2012-10-24 11:04:23 +02:00
@permission_required('motion.can_manage_motion')
def reject_version(request, aversion_id):
aversion = AVersion.objects.get(pk=aversion_id)
2012-10-24 11:04:23 +02:00
motion = aversion.motion
if request.method == 'POST':
2012-10-24 11:04:23 +02:00
if motion.reject_version(aversion, user=request.user):
messages.success(request, _("Version <b>%s</b> rejected.") % (aversion.aid))
else:
messages.error(request, _("ERROR by rejecting the version.") )
else:
2012-10-24 11:04:23 +02:00
gen_confirm_form(request, _('Do you really want to reject version <b>%s</b>?') % aversion.aid, reverse('motion_version_reject', args=[aversion.id]))
return redirect(reverse('motion_view', args=[motion.id]))
2012-02-15 12:04:11 +01:00
2012-10-24 11:04:23 +02:00
@permission_required('motion.can_manage_motion')
@template('motion/import.html')
def motion_import(request):
if request.method == 'POST':
2012-10-24 11:04:23 +02:00
form = MotionImportForm(request.POST, request.FILES)
if form.is_valid():
import_permitted = form.cleaned_data['import_permitted']
try:
# check for valid encoding (will raise UnicodeDecodeError if not)
request.FILES['csvfile'].read().decode('utf-8')
request.FILES['csvfile'].seek(0)
users_generated = 0
2012-10-24 11:04:23 +02:00
motions_generated = 0
motions_modified = 0
groups_assigned = 0
with transaction.commit_on_success():
dialect = csv.Sniffer().sniff(request.FILES['csvfile'].readline())
2012-07-23 08:55:05 +02:00
dialect = csv_ext.patchup(dialect)
request.FILES['csvfile'].seek(0)
for (lno, line) in enumerate(csv.reader(request.FILES['csvfile'], dialect=dialect)):
# basic input verification
if lno < 1:
continue
try:
(number, title, text, reason, first_name, last_name, is_group) = line[:7]
if is_group.strip().lower() in ['y', 'j', 't', 'yes', 'ja', 'true', '1', 1]:
is_group = True
else:
is_group = False
print 'works for %d' % (lno + 1)
except ValueError:
print 'doesn\'t work for %d' % (lno + 1)
messages.error(request, _('Ignoring malformed line %d in import file.') % (lno + 1))
continue
2012-10-24 11:04:23 +02:00
form = MotionForm({'title': title, 'text': text, 'reason': reason})
if not form.is_valid():
messages.error(request, _('Ignoring malformed line %d in import file.') % (lno + 1))
continue
if number:
try:
number = abs(long(number))
if number < 1:
messages.error(request, _('Ignoring malformed line %d in import file.') % (lno + 1))
continue
except ValueError:
messages.error(request, _('Ignoring malformed line %d in import file.') % (lno + 1))
continue
if is_group:
# fetch existing groups or issue an error message
try:
user = Group.objects.get(name=last_name)
if user.group_as_person == False:
messages.error(request, _('Ignoring line %d because the assigned group may not act as a person.') % (lno + 1))
continue
else:
user = get_person(user.person_id)
groups_assigned += 1
except Group.DoesNotExist:
messages.error(request, _('Ignoring line %d because the assigned group does not exist.') % (lno + 1))
continue
else:
# fetch existing users or create new users as needed
try:
user = User.objects.get(first_name=first_name, last_name=last_name)
except User.DoesNotExist:
user = None
if user is None:
if not first_name or not last_name:
messages.error(request, _('Ignoring line %d because it contains an incomplete first / last name pair.') % (lno + 1))
continue
user = User()
user.last_name = last_name
user.first_name = first_name
user.username = gen_username(first_name, last_name)
user.structure_level = ''
user.committee = ''
user.gender = ''
user.type = ''
user.default_password = gen_password()
user.save()
user.reset_password()
users_generated += 1
2012-10-24 11:04:23 +02:00
# create / modify the motion
motion = None
if number:
try:
2012-10-24 11:04:23 +02:00
motion = Motion.objects.get(number=number)
motions_modified += 1
except Motion.DoesNotExist:
motion = None
if motion is None:
motion = Motion(submitter=user)
if number:
2012-10-24 11:04:23 +02:00
motion.number = number
motions_generated += 1
2012-10-24 11:04:23 +02:00
motion.title = form.cleaned_data['title']
motion.text = form.cleaned_data['text']
motion.reason = form.cleaned_data['reason']
if import_permitted:
2012-10-24 11:04:23 +02:00
motion.status = 'per'
2012-10-24 11:04:23 +02:00
motion.save(user, trivial_change=True)
2012-10-24 11:04:23 +02:00
if motions_generated:
messages.success(request, ungettext('%d motion was successfully imported.',
2012-10-24 11:04:23 +02:00
'%d motions were successfully imported.', motions_generated) % motions_generated)
if motions_modified:
messages.success(request, ungettext('%d motion was successfully modified.',
2012-10-24 11:04:23 +02:00
'%d motions were successfully modified.', motions_modified) % motions_modified)
if users_generated:
messages.success(request, ungettext('%d new user was added.', '%d new users were added.', users_generated) % users_generated)
if groups_assigned:
messages.success(request, ungettext('%d group assigned to motions.', '%d groups assigned to motions.', groups_assigned) % groups_assigned)
2012-10-24 11:04:23 +02:00
return redirect(reverse('motion_overview'))
except csv.Error:
message.error(request, _('Import aborted because of severe errors in the input file.'))
except UnicodeDecodeError:
messages.error(request, _('Import file has wrong character encoding, only UTF-8 is supported!'))
else:
messages.error(request, _('Please check the form for errors.'))
else:
messages.warning(request, _("Attention: Existing motions will be modified if you import new motions with the same number."))
messages.warning(request, _("Attention: Importing an motions without a number multiple times will create duplicates."))
2012-10-24 11:04:23 +02:00
form = MotionImportForm()
return {
'form': form,
}
class CreateAgendaItem(RedirectView):
permission_required = 'agenda.can_manage_agenda'
def pre_redirect(self, request, *args, **kwargs):
2012-10-24 11:04:23 +02:00
self.motion = Motion.objects.get(pk=kwargs['motion_id'])
self.item = Item(related_sid=self.motion.sid)
self.item.save()
def get_redirect_url(self, **kwargs):
return reverse('item_overview')
2012-10-24 11:04:23 +02:00
class MotionPDF(PDFView):
permission_required = 'motion.can_see_motion'
top_space = 0
2012-04-18 18:54:48 +02:00
def get_filename(self):
2012-10-24 11:04:23 +02:00
motion_id = self.kwargs['motion_id']
if motion_id is None:
filename = _("Motions")
2012-04-18 18:54:48 +02:00
else:
2012-10-24 11:04:23 +02:00
motion = Motion.objects.get(id=motion_id)
if motion.number:
number = motion.number
2012-04-18 18:54:48 +02:00
else:
number = ""
2012-10-24 11:04:23 +02:00
filename = u'%s%s' % (_("Motion"), str(number))
2012-04-18 18:54:48 +02:00
return filename
2012-04-15 12:11:03 +02:00
2012-04-18 18:54:48 +02:00
def append_to_pdf(self, story):
2012-10-24 11:04:23 +02:00
motion_id = self.kwargs['motion_id']
if motion_id is None: #print all motions
title = config["motion_pdf_title"]
story.append(Paragraph(title, stylesheet['Heading1']))
2012-10-24 11:04:23 +02:00
preamble = config["motion_pdf_preamble"]
if preamble:
story.append(Paragraph("%s" % preamble.replace('\r\n','<br/>'), stylesheet['Paragraph']))
story.append(Spacer(0,0.75*cm))
2012-10-24 11:04:23 +02:00
motions = Motion.objects.all()
if not motions: # No motions existing
story.append(Paragraph(_("No motions available."), stylesheet['Heading3']))
2012-10-24 11:04:23 +02:00
else: # Print all Motions
# List of motions
for motion in motions:
if motion.number:
story.append(Paragraph(_("Motion No.")+" %s: %s" % (motion.number, motion.title), stylesheet['Heading3']))
else:
2012-10-24 11:04:23 +02:00
story.append(Paragraph(_("Motion No.")+"&nbsp;&nbsp;&nbsp;: %s" % (motion.title), stylesheet['Heading3']))
# Motions details (each motion on single page)
for motion in motions:
story.append(PageBreak())
2012-10-24 11:04:23 +02:00
story = self.get_motion(motion, story)
else: # print selected motion
motion = Motion.objects.get(id=motion_id)
story = self.get_motion(motion, story)
2012-10-24 11:04:23 +02:00
def get_motion(self, motion, story):
2012-09-13 21:33:57 +02:00
# Preparing Table
data = []
2012-10-24 11:04:23 +02:00
# motion number
if motion.number:
story.append(Paragraph(_("Motion No.")+" %s" % motion.number, stylesheet['Heading1']))
else:
story.append(Paragraph(_("Motion No."), stylesheet['Heading1']))
# submitter
cell1a = []
2012-09-13 21:33:57 +02:00
cell1a.append(Spacer(0, 0.2 * cm))
cell1a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font>" % _("Submitter"), stylesheet['Heading4']))
cell1b = []
2012-09-13 21:33:57 +02:00
cell1b.append(Spacer(0, 0.2 * cm))
2012-10-24 11:04:23 +02:00
cell1b.append(Paragraph("%s" % motion.submitter, stylesheet['Normal']))
2012-09-13 21:33:57 +02:00
data.append([cell1a, cell1b])
2012-10-24 11:04:23 +02:00
if motion.status == "pub":
2012-09-13 21:33:57 +02:00
# Cell for the signature
cell2a = []
cell2b = []
cell2a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font>" % _("Signature"), stylesheet['Heading4']))
cell2b.append(Paragraph("__________________________________________", stylesheet['Signaturefield']))
cell2b.append(Spacer(0, 0.1 * cm))
cell2b.append(Spacer(0,0.2*cm))
data.append([cell2a, cell2b])
# supporters
2012-10-24 11:04:23 +02:00
if config['motion_min_supporters']:
2012-09-13 21:33:57 +02:00
cell3a = []
cell3b = []
cell3a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font><seqreset id='counter'>" % _("Supporters"), stylesheet['Heading4']))
2012-10-24 11:04:23 +02:00
for supporter in motion.supporters:
2012-09-13 21:33:57 +02:00
cell3b.append(Paragraph("<seq id='counter'/>.&nbsp; %s" % supporter, stylesheet['Signaturefield']))
2012-10-24 11:04:23 +02:00
if motion.status == "pub":
for x in range(motion.missing_supporters):
2012-09-13 21:33:57 +02:00
cell3b.append(Paragraph("<seq id='counter'/>.&nbsp; __________________________________________",stylesheet['Signaturefield']))
cell3b.append(Spacer(0, 0.2 * cm))
data.append([cell3a, cell3b])
# status
2012-09-13 21:33:57 +02:00
cell4a = []
cell4b = []
2012-10-24 11:04:23 +02:00
note = " ".join(motion.notes)
2012-09-13 21:33:57 +02:00
cell4a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font>" % _("Status"), stylesheet['Heading4']))
if note != "":
2012-10-24 11:04:23 +02:00
if motion.status == "pub":
2012-09-13 21:33:57 +02:00
cell4b.append(Paragraph(note, stylesheet['Normal']))
else:
2012-10-24 11:04:23 +02:00
cell4b.append(Paragraph("%s | %s" % (motion.get_status_display(), note), stylesheet['Normal']))
else:
2012-10-24 11:04:23 +02:00
cell4b.append(Paragraph("%s" % motion.get_status_display(), stylesheet['Normal']))
2012-09-13 21:33:57 +02:00
data.append([cell4a, cell4b])
# Version number (aid)
2012-10-24 11:04:23 +02:00
if motion.public_version.aid > 1:
2012-09-13 21:33:57 +02:00
cell5a = []
cell5b = []
cell5a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font>" % _("Version"), stylesheet['Heading4']))
2012-10-24 11:04:23 +02:00
cell5b.append(Paragraph("%s" % motion.public_version.aid, stylesheet['Normal']))
2012-09-13 21:33:57 +02:00
data.append([cell5a, cell5b])
# voting results
2012-10-24 11:04:23 +02:00
poll_results = motion.get_poll_results()
2012-04-15 12:11:03 +02:00
if poll_results:
2012-09-13 21:33:57 +02:00
cell6a = []
cell6a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font>" % _("Vote results"), stylesheet['Heading4']))
cell6b = []
ballotcounter = 0
2012-04-15 12:11:03 +02:00
for result in poll_results:
ballotcounter += 1
2012-04-15 12:11:03 +02:00
if len(poll_results) > 1:
2012-09-13 21:33:57 +02:00
cell6b.append(Paragraph("%s. %s" % (ballotcounter, _("Vote")), stylesheet['Bold']))
cell6b.append(Paragraph("%s: %s <br/> %s: %s <br/> %s: %s <br/> %s: %s <br/> %s: %s" % (_("Yes"), result[0], _("No"), result[1], _("Abstention"), result[2], _("Invalid"), result[3], _("Votes cast"), result[4]), stylesheet['Normal']))
cell6b.append(Spacer(0, 0.2*cm))
data.append([cell6a, cell6b])
# Creating Table
t = Table(data)
t._argW[0] = 4.5 * cm
t._argW[1] = 11 * cm
t.setStyle(TableStyle([('BOX', (0, 0), (-1, -1), 1, colors.black),
('VALIGN', (0,0), (-1,-1), 'TOP')]))
story.append(t)
2012-09-13 21:33:57 +02:00
story.append(Spacer(0, 1 * cm))
# title
2012-10-24 11:04:23 +02:00
story.append(Paragraph(motion.public_version.title, stylesheet['Heading3']))
# text
2012-10-24 11:04:23 +02:00
story.append(Paragraph("%s" % motion.public_version.text.replace('\r\n','<br/>'), stylesheet['Paragraph']))
# reason
2012-10-24 11:04:23 +02:00
if motion.public_version.reason:
story.append(Paragraph(_("Reason")+":", stylesheet['Heading3']))
2012-10-24 11:04:23 +02:00
story.append(Paragraph("%s" % motion.public_version.reason.replace('\r\n','<br/>'), stylesheet['Paragraph']))
return story
2012-10-24 11:04:23 +02:00
class MotionPollPDF(PDFView):
permission_required = 'motion.can_manage_motion'
top_space = 0
def get(self, request, *args, **kwargs):
2012-10-24 11:04:23 +02:00
self.poll = MotionPoll.objects.get(id=self.kwargs['poll_id'])
return super(MotionPollPDF, self).get(request, *args, **kwargs)
def get_filename(self):
2012-10-24 11:04:23 +02:00
filename = u'%s%s_%s' % (_("Motion"), str(self.poll.motion.number), _("Poll"))
return filename
def get_template(self, buffer):
return SimpleDocTemplate(buffer, topMargin=-6, bottomMargin=-6, leftMargin=0, rightMargin=0, showBoundary=False)
def build_document(self, pdf_document, story):
pdf_document.build(story)
def append_to_pdf(self, story):
imgpath = os.path.join(settings.SITE_ROOT, 'static/images/circle.png')
circle = "<img src='%s' width='15' height='15'/>&nbsp;&nbsp;" % imgpath
cell = []
cell.append(Spacer(0,0.8*cm))
2012-10-24 11:04:23 +02:00
cell.append(Paragraph(_("Motion No. %s") % self.poll.motion.number, stylesheet['Ballot_title']))
cell.append(Paragraph(self.poll.motion.title, stylesheet['Ballot_subtitle']))
cell.append(Paragraph(_("%d. Vote") % self.poll.get_ballot(), stylesheet['Ballot_description']))
cell.append(Spacer(0,0.5*cm))
cell.append(Paragraph(circle + unicode(_("Yes")), stylesheet['Ballot_option']))
cell.append(Paragraph(circle + unicode(_("No")), stylesheet['Ballot_option']))
cell.append(Paragraph(circle + unicode(_("Abstention")), stylesheet['Ballot_option']))
data= []
# get ballot papers config values
2012-10-24 11:04:23 +02:00
ballot_papers_selection = config["motion_pdf_ballot_papers_selection"]
ballot_papers_number = config["motion_pdf_ballot_papers_number"]
# set number of ballot papers
if ballot_papers_selection == "NUMBER_OF_DELEGATES":
number = User.objects.filter(type__iexact="delegate").count()
elif ballot_papers_selection == "NUMBER_OF_ALL_PARTICIPANTS":
number = int(User.objects.count())
else: # ballot_papers_selection == "CUSTOM_NUMBER"
number = int(ballot_papers_number)
number = max(1, number)
# print ballot papers
if number > 0:
for user in xrange(number / 2):
data.append([cell, cell])
rest = number % 2
if rest:
data.append([cell, ''])
t=Table(data, 10.5 * cm, 7.42 * cm)
t.setStyle(TableStyle([('GRID', (0, 0), (-1, -1), 0.25, colors.grey),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
]))
story.append(t)
2012-03-16 14:31:59 +01:00
class Config(FormView):
permission_required = 'config.can_manage_config'
2012-03-16 14:31:59 +01:00
form_class = ConfigForm
2012-10-24 11:04:23 +02:00
template_name = 'motion/config.html'
2012-03-16 14:31:59 +01:00
def get_initial(self):
return {
2012-10-24 11:04:23 +02:00
'motion_min_supporters': config['motion_min_supporters'],
'motion_preamble': config['motion_preamble'],
'motion_pdf_ballot_papers_selection': config['motion_pdf_ballot_papers_selection'],
'motion_pdf_ballot_papers_number': config['motion_pdf_ballot_papers_number'],
'motion_pdf_title': config['motion_pdf_title'],
'motion_pdf_preamble': config['motion_pdf_preamble'],
'motion_allow_trivial_change': config['motion_allow_trivial_change'],
2012-03-16 14:31:59 +01:00
}
def form_valid(self, form):
2012-10-24 11:04:23 +02:00
config['motion_min_supporters'] = form.cleaned_data['motion_min_supporters']
config['motion_preamble'] = form.cleaned_data['motion_preamble']
config['motion_pdf_ballot_papers_selection'] = form.cleaned_data['motion_pdf_ballot_papers_selection']
config['motion_pdf_ballot_papers_number'] = form.cleaned_data['motion_pdf_ballot_papers_number']
config['motion_pdf_title'] = form.cleaned_data['motion_pdf_title']
config['motion_pdf_preamble'] = form.cleaned_data['motion_pdf_preamble']
config['motion_allow_trivial_change'] = form.cleaned_data['motion_allow_trivial_change']
messages.success(self.request, _('Motion settings successfully saved.'))
2012-03-16 14:31:59 +01:00
return super(Config, self).form_valid(form)
2012-03-18 17:11:58 +01:00
def register_tab(request):
2012-10-24 11:04:23 +02:00
selected = True if request.path.startswith('/motion/') else False
2012-03-18 17:11:58 +01:00
return Tab(
2012-10-24 11:04:23 +02:00
title=_('Motions'),
url=reverse('motion_overview'),
permission=request.user.has_perm('motion.can_see_motion') or request.user.has_perm('motion.can_support_motion') or request.user.has_perm('motion.can_support_motion') or request.user.has_perm('motion.can_manage_motion'),
2012-03-18 17:11:58 +01:00
selected=selected,
)
2012-06-11 13:43:48 +02:00
def get_widgets(request):
return [
2012-08-15 11:56:43 +02:00
Widget(
2012-10-24 11:04:23 +02:00
name='motions',
2012-11-08 09:43:04 +01:00
display_name=_('Motions'),
2012-10-24 11:04:23 +02:00
template='motion/widget.html',
context={'motions': Motion.objects.all()},
permission_required='projector.can_manage_projector')]