From 0231fe37f5830ef20903c39d4c7d2539a6d04f80 Mon Sep 17 00:00:00 2001
From: Oskar Hahn
Date: Sun, 6 Jan 2013 12:07:37 +0100
Subject: [PATCH] Start to rewrite the motion app
---
openslides/motion/forms.py | 51 +-
openslides/motion/models.py | 705 ++++---------
.../motion/templates/motion/base_motion.html | 66 --
.../motion/templates/motion/config.html | 22 -
openslides/motion/templates/motion/edit.html | 40 -
.../motion/templates/motion/import.html | 36 -
.../templates/motion/motion_detail.html | 12 +
.../motion/templates/motion/motion_form.html | 26 +
.../motion/templates/motion/motion_list.html | 16 +
.../motion/templates/motion/overview.html | 88 --
.../motion/templates/motion/poll_view.html | 64 --
openslides/motion/templates/motion/view.html | 303 ------
.../motion/templates/motion/widget.html | 34 -
openslides/motion/urls.py | 125 +--
openslides/motion/views.py | 957 +-----------------
openslides/participant/views.py | 40 +-
openslides/utils/views.py | 115 +--
17 files changed, 368 insertions(+), 2332 deletions(-)
delete mode 100644 openslides/motion/templates/motion/base_motion.html
delete mode 100644 openslides/motion/templates/motion/config.html
delete mode 100644 openslides/motion/templates/motion/edit.html
delete mode 100644 openslides/motion/templates/motion/import.html
create mode 100644 openslides/motion/templates/motion/motion_detail.html
create mode 100644 openslides/motion/templates/motion/motion_form.html
create mode 100644 openslides/motion/templates/motion/motion_list.html
delete mode 100644 openslides/motion/templates/motion/overview.html
delete mode 100644 openslides/motion/templates/motion/poll_view.html
delete mode 100644 openslides/motion/templates/motion/view.html
delete mode 100644 openslides/motion/templates/motion/widget.html
diff --git a/openslides/motion/forms.py b/openslides/motion/forms.py
index 6fbfc9cff..9f35e4bab 100644
--- a/openslides/motion/forms.py
+++ b/openslides/motion/forms.py
@@ -11,50 +11,39 @@
"""
from django import forms
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import ugettext as _
from openslides.utils.forms import CssClassMixin
from openslides.utils.person import PersonFormField, MultiplePersonFormField
-from openslides.motion.models import Motion
+from .models import Motion
-class MotionForm(forms.Form, CssClassMixin):
+class BaseMotionForm(forms.ModelForm, CssClassMixin):
+ class Meta:
+ model = Motion
+ fields = ()
+
+ def __init__(self, *args, **kwargs):
+ motion = kwargs.get('instance', None)
+ if motion is not None:
+ initial = kwargs.setdefault('initial', {})
+ initial['title'] = motion.title
+ initial['text'] = motion.text
+ initial['reason'] = motion.reason
+ super(BaseMotionForm, self).__init__(*args, **kwargs)
+
title = forms.CharField(widget=forms.TextInput(), label=_("Title"))
text = forms.CharField(widget=forms.Textarea(), label=_("Text"))
reason = forms.CharField(
widget=forms.Textarea(), required=False, label=_("Reason"))
-class MotionFormTrivialChanges(MotionForm):
- trivial_change = forms.BooleanField(
- required=False, label=_("Trivial change"),
- help_text=_("Trivial changes don't create a new version."))
+class MotionCreateForm(BaseMotionForm):
+ pass
-class MotionManagerForm(forms.ModelForm, CssClassMixin):
- submitter = PersonFormField(label=_("Submitter"))
-
- class Meta:
- model = Motion
- exclude = ('number', 'status', 'permitted', 'log', 'supporter')
-
-
-class MotionManagerFormSupporter(MotionManagerForm):
- # TODO: Do not show the submitter in the user-list
- supporter = MultiplePersonFormField(required=False, label=_("Supporters"))
-
-
-class MotionImportForm(forms.Form, CssClassMixin):
- csvfile = forms.FileField(
- widget=forms.FileInput(attrs={'size': '50'}),
- label=_("CSV File"),
- )
- import_permitted = forms.BooleanField(
- required=False,
- label=_("Import motions with status \"authorized\""),
- help_text=_('Set the initial status for each motion to '
- '"authorized"'),
- )
+class MotionUpdateForm(BaseMotionForm):
+ pass
class ConfigForm(forms.Form, CssClassMixin):
diff --git a/openslides/motion/models.py b/openslides/motion/models.py
index 7127e521c..780e848bc 100644
--- a/openslides/motion/models.py
+++ b/openslides/motion/models.py
@@ -31,576 +31,217 @@ from openslides.projector.models import SlideMixin
from openslides.agenda.models import Item
-class MotionSupporter(models.Model):
- motion = models.ForeignKey("Motion")
- person = PersonField()
+RELATION = (
+ (1, _('Submitter')),
+ (2, _('Supporter')))
-class Motion(models.Model, SlideMixin):
- prefix = "motion"
- STATUS = (
- ('pub', _('Published')),
- ('per', _('Permitted')),
- ('acc', _('Accepted')),
- ('rej', _('Rejected')),
- ('wit', _('Withdrawed')),
- ('adj', _('Adjourned')),
- ('noc', _('Not Concerned')),
- ('com', _('Commited a bill')),
- ('nop', _('Rejected (not authorized)')),
- ('rev', _('Needs Review')), # Where is this status used?
- #additional actions:
- # edit
- # delete
- # setnumber
- # support
- # unsupport
- # createitem
- # activateitem
- # genpoll
- )
-
- submitter = PersonField(verbose_name=_("Submitter"))
- number = models.PositiveSmallIntegerField(blank=True, null=True,
- unique=True)
- status = models.CharField(max_length=3, choices=STATUS, default='pub')
- permitted = models.ForeignKey('AVersion', related_name='permitted',
- null=True, blank=True)
- log = models.TextField(blank=True, null=True)
-
- @property
- def last_version(self):
- """
- Return last version of the motion.
- """
- try:
- return AVersion.objects.filter(motion=self).order_by('id') \
- .reverse()[0]
- except IndexError:
- return None
-
- @property
- def public_version(self):
- """
- Return permitted, if the motion was permitted, else last_version
- """
- if self.permitted is not None:
- return self.permitted
- else:
- return self.last_version
-
- def accept_version(self, version, user=None):
- """
- accept a Version
- """
- self.permitted = version
- self.save(nonewversion=True)
- version.rejected = False
- version.save()
- self.writelog(_("Version %d authorized") % version.aid, user)
-
- def reject_version(self, version, user=None):
- if version.id > self.permitted.id:
- version.rejected = True
- version.save()
- self.writelog(pgettext(
- "Rejected means not authorized", "Version %d rejected")
- % version.aid, user)
- return True
- return False
-
- @property
- def versions(self):
- """
- Return a list of all versions of the motion.
- """
- return AVersion.objects.filter(motion=self)
-
- @property
- def creation_time(self):
- """
- Return the time of the creation of the motion.
- """
- try:
- return self.versions[0].time
- except IndexError:
- return None
-
- @property
- def notes(self):
- """
- Return some information of the motion.
- """
- note = []
- if self.status == "pub" and not self.enough_supporters:
- note.append(ugettext("Searching for supporters."))
- if self.status == "pub" and self.permitted is None:
- note.append(ugettext("Not yet authorized."))
- elif self.unpermitted_changes and self.permitted:
- note.append(ugettext("Not yet authorized changes."))
- return note
-
- @property
- def unpermitted_changes(self):
- """
- Return True if the motion has unpermitted changes.
-
- The motion has unpermitted changes, if the permitted-version
- is not the lastone and the lastone is not rejected.
- TODO: rename the property in unchecked__changes
- """
- if (self.last_version != self.permitted and
- not self.last_version.rejected):
- return True
- else:
- return False
-
- @property
- def supporters(self):
- return sorted([object.person for object in self.motionsupporter_set.all()],
- key=lambda person: person.sort_name)
-
- def is_supporter(self, person):
- try:
- return self.motionsupporter_set.filter(person=person).exists()
- except AttributeError:
- return False
-
- @property
- def enough_supporters(self):
- """
- Return True, if the motion has enough supporters
- """
- min_supporters = int(config['motion_min_supporters'])
- if self.status == "pub":
- return self.count_supporters() >= min_supporters
- else:
- return True
-
- def count_supporters(self):
- return self.motionsupporter_set.count()
-
- @property
- def missing_supporters(self):
- """
- Return number of missing supporters
- """
- min_supporters = int(config['motion_min_supporters'])
- delta = min_supporters - self.count_supporters()
- if delta > 0:
- return delta
- else:
- return 0
-
- def save(self, user=None, nonewversion=False, trivial_change=False):
- """
- Save the Motion, and create a new AVersion if necessary
- """
- super(Motion, self).save()
- if nonewversion:
- return
- last_version = self.last_version
- fields = ["text", "title", "reason"]
- if last_version is not None:
- changed_fields = [
- f for f in fields
- if getattr(last_version, f) != getattr(self, f)]
- if not changed_fields:
- return # No changes
- try:
- if trivial_change and last_version is not None:
- last_version.text = self.text
- last_version.title = self.title
- last_version.reason = self.reason
- last_version.save()
-
- meta = AVersion._meta
- field_names = [
- unicode(meta.get_field(f).verbose_name)
- for f in changed_fields]
-
- self.writelog(
- _("Trivial changes to version %(version)d; "
- "changed fields: %(changed_fields)s")
- % dict(version=last_version.aid,
- changed_fields=", ".join(field_names)))
- return # Done
-
- version = AVersion(
- title=getattr(self, 'title', ''),
- text=getattr(self, 'text', ''),
- reason=getattr(self, 'reason', ''),
- motion=self)
- version.save()
- self.writelog(_("Version %s created") % version.aid, user)
- is_manager = user.has_perm('motion.can_manage_motion')
- except AttributeError:
- is_manager = False
-
- supporters = self.motionsupporter_set.all()
- if (self.status == "pub" and
- supporters and not is_manager):
- supporters.delete()
- self.writelog(_("Supporters removed"), user)
-
- def reset(self, user):
- """
- Reset the motion.
- """
- self.status = "pub"
- self.permitted = None
- self.save()
- self.writelog(_("Status reseted to: %s") % (self.get_status_display()), user)
-
- def support(self, person):
- """
- Add a Supporter to the list of supporters of the motion.
- """
- if person == self.submitter:
- # TODO: Use own Exception
- raise NameError('Supporter can not be the submitter of a '
- 'motion.')
- if not self.is_supporter(person):
- MotionSupporter(motion=self, person=person).save()
- self.writelog(_("Supporter: +%s") % (person))
- # TODO: Raise a precise exception for the view in else-clause
-
- def unsupport(self, person):
- """
- remove a supporter from the list of supporters of the motion
- """
- try:
- self.motionsupporter_set.get(person=person).delete()
- except MotionSupporter.DoesNotExist:
- # TODO: Don't do nothing but raise a precise exception for the view
- pass
- else:
- self.writelog(_("Supporter: -%s") % (person))
-
- def set_number(self, number=None, user=None):
- """
- Set a number for ths motion.
- """
- if self.number is not None:
- # TODO: Use own Exception
- raise NameError('This motion has already a number.')
- if number is None:
- try:
- number = Motion.objects.aggregate(Max('number'))['number__max'] + 1
- except TypeError:
- number = 1
- self.number = number
- self.save()
- self.writelog(_("Number set: %s") % (self.number), user)
- return self.number
-
- def permit(self, user=None):
- """
- Change the status of this motion to permit.
- """
- self.set_status(user, "per")
- aversion = self.last_version
- if self.number is None:
- self.set_number()
- self.permitted = aversion
- self.save()
- self.writelog(_("Version %s authorized") % (aversion.aid), user)
- return self.permitted
-
- def notpermit(self, user=None):
- """
- Change the status of this motion to 'not permitted (rejected)'.
- """
- self.set_status(user, "nop")
- #TODO: reject last version
- if self.number is None:
- self.set_number()
- self.save()
- self.writelog(_("Version %s not authorized") % (self.last_version.aid), user)
-
- def set_status(self, user, status, force=False):
- """
- Set the status of the motion.
- """
- error = True
- for a, b in Motion.STATUS:
- if status == a:
- error = False
+class RelatedPersonsManager(models.Manager):
+ def __init__(self, relation, *args, **kwargs):
+ super(RelatedPersonsManager, self).__init__(*args, **kwargs)
+ for key, value in RELATION:
+ if key == relation:
+ self.relation = key
break
- if error:
- # TODO: Use the Right Error
- raise NameError(_('%s is not a valid status.') % status)
- if self.status == status:
- # TODO: Use the Right Error
- raise NameError(_('The motion status is already \'%s.\'')
- % self.status)
+ else:
+ raise ValueError('Unknown relation with id %d' % relation)
- actions = []
- actions = self.get_allowed_actions(user)
- if status not in actions and not force:
- #TODO: Use the Right Error
- raise NameError(_(
- 'The motion status is: \'%(currentstatus)s\'. '
- 'You can not set the status to \'%(newstatus)s\'.') % {
- 'currentstatus': self.status,
- 'newstatus': status})
+ def get_query_set(self):
+ return (super(RelatedPersonsManager, self).get_query_set()
+ .filter(relation=self.relation))
- oldstatus = self.get_status_display()
- self.status = status
- self.save()
- self.writelog(_("Status modified") + ": %s -> %s"
- % (oldstatus, self.get_status_display()), user)
- def get_allowed_actions(self, user):
- """
- Return a list of all the allowed status.
- """
- actions = []
+class MotionRelatedPersons(models.Model):
+ submitter = RelatedPersonsManager(relation=1)
+ supporter = RelatedPersonsManager(relation=2)
+ objects = models.Manager()
- # check if user allowed to withdraw an motion
- if ((self.status == "pub"
- and self.number
- and user == self.submitter)
- or (self.status == "pub"
- and self.number
- and user.has_perm("motion.can_manage_motion"))
- or (self.status == "per"
- and user == self.submitter)
- or (self.status == "per"
- and user.has_perm("motion.can_manage_motion"))):
- actions.append("wit")
- #Check if the user can review the motion
- if (self.status == "rev"
- and (self.submitter == user
- or user.has_perm("motion.can_manage_motion"))):
- actions.append("pub")
+ person = PersonField()
+ relation = models.IntegerField(default=1, choices=RELATION)
+ motion = models.ForeignKey('Motion', related_name="persons")
- # Check if the user can support and unspoort the motion
- if (self.status == "pub"
- and user != self.submitter
- and not self.is_supporter(user)):
- actions.append("support")
- if self.status == "pub" and self.is_supporter(user):
- actions.append("unsupport")
+class Motion(SlideMixin, models.Model):
+ prefix = "motion" # Rename this in the slide-system
- #Check if the user can edit the motion
- if (user == self.submitter \
- and (self.status in ('pub', 'per'))) \
- or user.has_perm("motion.can_manage_motion"):
- actions.append("edit")
+ # TODO: Use this attribute for the default_version, if the permission system
+ # is deactivated. Maybe it has to be renamed.
+ permitted_version = models.ForeignKey(
+ 'MotionVersion', null=True, blank=True, related_name="permitted")
+ # TODO: Define status
+ status = models.CharField(max_length=3)
+ # Log (Translatable)
+ identifier = models.CharField(max_length=255, null=True, blank=True,
+ unique=True)
+ category = models.ForeignKey('Category', null=True, blank=True)
+ # TODO proposal
+ # Maybe rename to master_copy
+ master = models.ForeignKey('self', null=True, blank=True)
- # Check if the user can delete the motion (admin, manager, owner)
- # reworked as requiered in #100
- if (user.has_perm("motion.can_delete_all_motions") or
- (user.has_perm("motion.can_manage_motion") and
- self.number is None) or
- (self.submitter == user and self.number is None)):
- actions.append("delete")
+ class Meta:
+ permissions = (
+ ('can_see_motion', ugettext_noop('Can see motions')),
+ ('can_create_motion', ugettext_noop('Can create motions')),
+ ('can_support_motion', ugettext_noop('Can support motions')),
+ ('can_manage_motion', ugettext_noop('Can manage motions')),
+ )
+ # TODO: order per default by category and identifier
+ # ordering = ('number',)
- #For the rest, all actions need the manage permission
- if not user.has_perm("motion.can_manage_motion"):
- return actions
+ def __unicode__(self):
+ return self.get_title()
- if self.status == "pub":
- actions.append("nop")
- actions.append("per")
- if self.number == None:
- actions.append("setnumber")
-
- if self.status == "per":
- actions.append("acc")
- actions.append("rej")
- actions.append("adj")
- actions.append("noc")
- actions.append("com")
- actions.append("genpoll")
- if self.unpermitted_changes:
- actions.append("permitversion")
- actions.append("rejectversion")
-
- return actions
-
- def delete(self, force=False):
- """
- Delete the motion. It is not possible, if the motion has
- allready a number
- """
- if self.number and not force:
- raise NameError('The motion has already a number. '
- 'You can not delete it.')
-
- for item in Item.objects.filter(related_sid=self.sid):
- item.delete()
- super(Motion, self).delete()
-
- def writelog(self, text, user=None):
- if not self.log:
- self.log = ""
- self.log += u"%s | %s" % (datetime.now().strftime("%d.%m.%Y %H:%M:%S"), _propper_unicode(text))
- if user is not None:
- if isinstance(user, User):
- self.log += u" (%s %s)" % (_("by"), _propper_unicode(user.username))
- else:
- self.log += u" (%s %s)" % (_("by"), _propper_unicode(str(user)))
- self.log += "\n"
- self.save()
-
- def get_agenda_title(self):
- return self.public_version.title
-
- def get_agenda_title_supplement(self):
- number = self.number or '[%s]' % ugettext('no number')
- return '(%s %s)' % (ugettext('motion'), number)
-
- def __getattr__(self, name):
- """
- if name is title, text, reason or time,
- Return this attribute from the newest version of the motion
- """
- if name in ('title', 'text', 'reason', 'time', 'aid'):
+ # TODO: Use transaction
+ def save(self, *args, **kwargs):
+ super(Motion, self).save(*args, **kwargs)
+ new_data = False
+ for attr in ['_title', '_text', '_reason']:
+ if hasattr(self, attr):
+ new_data = True
+ break
+ need_new_version = True # TODO: Do we need a new version (look in config)
+ if hasattr(self, '_version') or (new_data and need_new_version):
+ version = self.new_version
+ del self._new_version
+ version.motion = self # Test if this line is realy neccessary.
+ elif new_data and not need_new_version:
+ # TODO: choose an explicit version
+ version = self.last_version
+ else:
+ # We do not need to save the motion version
+ return
+ for attr in ['title', 'text', 'reason']:
+ _attr = '_%s' % attr
try:
- if name == 'aid':
- return self.last_version.aid
- return self.last_version.__dict__[name]
- except TypeError:
- raise AttributeError(name)
+ setattr(version, attr, getattr(self, _attr))
except AttributeError:
- raise AttributeError(name)
- raise AttributeError(name)
+ setattr(version, attr, getattr(self.last_version, attr))
+ version.save()
- def gen_poll(self, user=None):
- """
- Generates a poll object for the motion
- """
- poll = MotionPoll(motion=self)
- poll.save()
- poll.set_options()
- self.writelog(_("Poll created"), user)
- return poll
-
- @property
- def polls(self):
- return self.motionpoll_set.all()
-
- @property
- def results(self):
- return self.get_poll_results()
-
- def get_poll_results(self):
- """
- Return a list of voting results
- """
- results = []
- for poll in self.polls:
- for option in poll.get_options():
- if option.get_votes().exists():
- results.append((
- option['Yes'], option['No'],
- option['Abstain'], poll.print_votesinvalid(),
- poll.print_votescast()))
- return results
-
- def slide(self):
- """
- return the slide dict
- """
- data = super(Motion, self).slide()
- data['motion'] = self
- data['title'] = self.title
- data['template'] = 'projector/Motion.html'
- return data
-
- def get_absolute_url(self, link='view'):
- if link == 'view':
- return reverse('motion_view', args=[str(self.id)])
+ def get_absolute_url(self, link='detail'):
+ if link == 'view' or link == 'detail':
+ return reverse('motion_detail', args=[str(self.id)])
if link == 'edit':
return reverse('motion_edit', args=[str(self.id)])
if link == 'delete':
return reverse('motion_delete', args=[str(self.id)])
- def __unicode__(self):
+ def get_title(self):
try:
- return self.last_version.title
+ return self._title
except AttributeError:
- return "no title jet"
+ return self.default_version.title
- class Meta:
- permissions = (
- ('can_see_motion', ugettext_noop("Can see motions")),
- ('can_create_motion', ugettext_noop("Can create motions")),
- ('can_support_motion', ugettext_noop("Can support motions")),
- ('can_manage_motion', ugettext_noop("Can manage motions")),
- )
- ordering = ('number',)
+ def set_title(self, title):
+ self._title = title
+ title = property(get_title, set_title)
-class AVersion(models.Model):
- title = models.CharField(max_length=100, verbose_name=_("Title"))
- text = models.TextField(verbose_name=_("Text"))
- reason = models.TextField(null=True, blank=True, verbose_name=_("Reason"))
- rejected = models.BooleanField() # = Not Permitted
- time = models.DateTimeField(auto_now=True)
- motion = models.ForeignKey(Motion)
+ def get_text(self):
+ try:
+ return self._text
+ except AttributeError:
+ return self.default_version.text
- def __unicode__(self):
- return "%s %s" % (self.id, self.title)
+ def set_text(self, text):
+ self._text = text
+
+ text = property(get_text, set_text)
+
+ def get_reason(self):
+ try:
+ return self._reason
+ except AttributeError:
+ return self.default_version.reason
+
+ def set_reason(self, reason):
+ self._reason = reason
+
+ reason = property(get_reason, set_reason)
@property
- def aid(self):
+ def new_version(self):
try:
- return self._aid
+ return self._new_version
except AttributeError:
- self._aid = AVersion.objects \
- .filter(motion=self.motion) \
- .filter(id__lte=self.id).count()
- return self._aid
+ self._new_version = MotionVersion(motion=self)
+ return self._new_version
-register_slidemodel(Motion)
+ @property
+ def submitter(self):
+ return MotionRelatedPersons.submitter.filter(motion=self)
+
+ @property
+ def supporter(self):
+ return MotionRelatedPersons.supporter.filter(motion=self)
+
+ def get_version(self, version_id):
+ # TODO: Check case, if version_id is not one of this motion
+ return self.versions.get(pk=version_id)
-class MotionVote(BaseVote):
- option = models.ForeignKey('MotionOption')
+ def get_default_version(self):
+ try:
+ return self._default_version
+ except AttributeError:
+ # TODO: choose right version via config
+ return self.last_version
+
+ def set_default_version(self, version):
+ if version is None:
+ try:
+ del self._default_version
+ except AttributeError:
+ pass
+ else:
+ if type(version) is int:
+ version = self.versions.all()[version]
+ elif type(version) is not MotionVersion:
+ raise ValueError('The argument \'version\' has to be int or '
+ 'MotionVersion, not %s' % type(version))
+ self._default_version = version
+
+ default_version = property(get_default_version, set_default_version)
+
+ @property
+ def last_version(self):
+ # TODO: Fix the case, that the motion has no Version
+ try:
+ return self.versions.order_by('id').reverse()[0]
+ except IndexError:
+ return self.new_version
-class MotionOption(BaseOption):
- poll = models.ForeignKey('MotionPoll')
- vote_class = MotionVote
+class MotionVersion(models.Model):
+ title = models.CharField(max_length=255, verbose_name=_("Title"))
+ text = models.TextField(verbose_name=_("Text"))
+ reason = models.TextField(null=True, blank=True, verbose_name=_("Reason"))
+ rejected = models.BooleanField(default=False)
+ creation_time = models.DateTimeField(auto_now=True)
+ motion = models.ForeignKey(Motion, related_name='versions')
+ identifier = models.CharField(max_length=255, verbose_name=_("Version identifier"))
+ note = models.TextField(null=True, blank=True)
+
+ def __unicode__(self):
+ return "%s Version %s" % (self.motion, self.get_version_number())
+
+ def get_version_number(self):
+ if self.pk is None:
+ return 'new'
+ return (MotionVersion.objects.filter(motion=self.motion)
+ .filter(id__lte=self.pk).count())
-class MotionPoll(BasePoll, CountInvalid, CountVotesCast):
- option_class = MotionOption
- vote_values = [
- ugettext_noop('Yes'), ugettext_noop('No'), ugettext_noop('Abstain')]
+class Category(models.Model):
+ name = models.CharField(max_length=255, verbose_name=_("Category name"))
+ prefix = models.CharField(max_length=32, verbose_name=_("Category prefix"))
- motion = models.ForeignKey(Motion)
-
- def get_motion(self):
- return self.motion
-
- def set_options(self):
- #TODO: maybe it is possible with .create() to call this without poll=self
- self.get_option_class()(poll=self).save()
-
- def append_pollform_fields(self, fields):
- CountInvalid.append_pollform_fields(self, fields)
- CountVotesCast.append_pollform_fields(self, fields)
-
- def get_absolute_url(self):
- return reverse('motion_poll_view', args=[self.id])
-
- def get_ballot(self):
- return self.motion.motionpoll_set.filter(id__lte=self.id).count()
+ def __unicode__(self):
+ return self.name
-@receiver(default_config_value, dispatch_uid="motion_default_config")
-def default_config(sender, key, **kwargs):
- return {
- 'motion_min_supporters': 0,
- 'motion_preamble': _('The assembly may decide,'),
- 'motion_pdf_ballot_papers_selection': 'CUSTOM_NUMBER',
- 'motion_pdf_ballot_papers_number': '8',
- 'motion_pdf_title': _('Motions'),
- 'motion_pdf_preamble': '',
- 'motion_allow_trivial_change': False,
- }.get(key)
+class Comment(models.Model):
+ motion_version = models.ForeignKey(MotionVersion)
+ text = models.TextField()
+ author = PersonField()
+ creation_time = models.DateTimeField(auto_now=True)
diff --git a/openslides/motion/templates/motion/base_motion.html b/openslides/motion/templates/motion/base_motion.html
deleted file mode 100644
index 0dc5846a6..000000000
--- a/openslides/motion/templates/motion/base_motion.html
+++ /dev/null
@@ -1,66 +0,0 @@
-{% extends "base.html" %}
-
-{% load tags %}
-{% load i18n %}
-{% load staticfiles %}
-
-{% block submenu %}
- {% url 'motion_overview' as url_motionoverview %}
- {% trans "Motions" %}
-
-
- {# second submenu #}
- {% if motion %}
-
- {% trans "Motion No." %}
- {% if motion.number != None %}
- {{ motion.number }}
- {% else %}
- [-]
- {% endif %}
-
-
- {% endif %}
-{% endblock %}
diff --git a/openslides/motion/templates/motion/config.html b/openslides/motion/templates/motion/config.html
deleted file mode 100644
index 42e064444..000000000
--- a/openslides/motion/templates/motion/config.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% extends "config/base_config.html" %}
-
-{% load i18n %}
-
-{% block title %}{{ block.super }} – {% trans "Motion settings" %}{% endblock %}
-
-{% block content %}
- {% trans "Motion settings" %}
-
-{% endblock %}
diff --git a/openslides/motion/templates/motion/edit.html b/openslides/motion/templates/motion/edit.html
deleted file mode 100644
index 7c5f09ed4..000000000
--- a/openslides/motion/templates/motion/edit.html
+++ /dev/null
@@ -1,40 +0,0 @@
-{% extends "motion/base_motion.html" %}
-
-{% load i18n %}
-
-{% block title %}
- {{ block.super }} –
- {% if motion %}
- {% trans "Edit motion" %}
- {% else %}
- {% trans "New motion" %}
- {% endif %}
-{% endblock %}
-
-{% block content %}
- {% if motion %}
- {% trans "Edit motion" %}
- {% else %}
- {% trans "New motion" %}
- {% endif %}
-
-
-
-{% endblock %}
diff --git a/openslides/motion/templates/motion/import.html b/openslides/motion/templates/motion/import.html
deleted file mode 100644
index 8a0d8eab1..000000000
--- a/openslides/motion/templates/motion/import.html
+++ /dev/null
@@ -1,36 +0,0 @@
-{% extends "motion/base_motion.html" %}
-
-{% load i18n %}
-
-{% block title %}{{ block.super }} – {% trans "Import motions" %} {% endblock %}
-
-{% block content %}
- {% trans "Import motions" %}
- {% trans 'Select a CSV file to import motions!' %}
-
- {% trans 'Required comma separated values' %}:
- ({% trans 'number, title, text, reason, first_name, last_name, is_group' %})
-
- {% trans 'number
, reason
and is_group
are optional and may be empty' %}.
-
- {% trans 'Required CSV file encoding: UTF-8 (Unicode).' %}
-
-
- {% trans 'A CSV example file is available in OpenSlides Wiki.' %}
-
-
-
-{% endblock %}
diff --git a/openslides/motion/templates/motion/motion_detail.html b/openslides/motion/templates/motion/motion_detail.html
new file mode 100644
index 000000000..9cd880936
--- /dev/null
+++ b/openslides/motion/templates/motion/motion_detail.html
@@ -0,0 +1,12 @@
+{% extends "base.html" %}
+
+{% load tags %}
+{% load i18n %}
+{% load staticfiles %}
+
+{% block title %}{{ block.super }} – {% trans "Motion" %} "{{ version.title }}"{% endblock %}
+
+{% block content %}
+Titel: {{ object.title }}
+Text: {{ object.text }}
+{% endblock %}
diff --git a/openslides/motion/templates/motion/motion_form.html b/openslides/motion/templates/motion/motion_form.html
new file mode 100644
index 000000000..ddffb8254
--- /dev/null
+++ b/openslides/motion/templates/motion/motion_form.html
@@ -0,0 +1,26 @@
+{% extends "base.html" %}
+
+{% load tags %}
+{% load i18n %}
+{% load staticfiles %}
+
+{% block title %}{{ block.super }} – {% trans "Motion Form" %}{% endblock %}
+
+{% block content %}
+ {% trans "Motions Forms" %}
+
+{% endblock %}
diff --git a/openslides/motion/templates/motion/motion_list.html b/openslides/motion/templates/motion/motion_list.html
new file mode 100644
index 000000000..32118ff73
--- /dev/null
+++ b/openslides/motion/templates/motion/motion_list.html
@@ -0,0 +1,16 @@
+{% extends "base.html" %}
+
+{% load tags %}
+{% load i18n %}
+{% load staticfiles %}
+
+{% block title %}{{ block.super }} – {% trans "Motions" %}{% endblock %}
+
+{% block content %}
+ {% trans "Motions" %}
+
+ {% for motion in motion_list %}
+ - {{ motion }}
+ {% endfor %}
+
+{% endblock %}
diff --git a/openslides/motion/templates/motion/overview.html b/openslides/motion/templates/motion/overview.html
deleted file mode 100644
index e11c6f2b5..000000000
--- a/openslides/motion/templates/motion/overview.html
+++ /dev/null
@@ -1,88 +0,0 @@
-{% extends "motion/base_motion.html" %}
-
-{% load tags %}
-{% load i18n %}
-{% load staticfiles %}
-
-{% block title %}{{ block.super }} – {% trans "Motions" %}{% endblock %}
-
-{% block content %}
- {% trans "Motions" %}
-
-
- {{ motions|length }}
- {% blocktrans count counter=motions|length context "number of motions"%}motion{% plural %}motions{% endblocktrans %}
-
-
- {% trans "Number" %} |
- {% trans "Motion title" %} |
- {% if min_supporters > 0 %}
- {% trans "Number of supporters" %} |
- {% endif %}
- {% trans "Status" %} |
- {% trans "Submitter" %} |
- {% trans "Creation Time" %} |
- {% trans "Actions" %} |
-
- {% for app_info in motions %}
- {% with motion=app_info.motion useractions=app_info.actions %}
-
- {% if motion.number %}{{ motion.number }}{% else %}-{% endif %} |
- {{ motion.public_version.title }} |
- {% if min_supporters > 0 %}
- {{ motion.count_supporters }} |
- {% endif %}
- {% if motion.status != "pub" %}
- {{ motion.get_status_display }}
- {% endif %}
- {% for note in motion.notes %}
- {{ note }}
- {% if not forloop.last %} {%endif%}
- {% endfor %}
- |
- {{ motion.submitter }} |
- {{ motion.creation_time }} |
-
-
- {% if perms.projector.can_manage_projector %}
-
-
-
- {% endif %}
- {% if perms.motion.can_manage_motion %}
-
- {% if "delete" in useractions %}
-
- {% endif %}
- {% endif %}
-
-
- |
-
- {% endwith %}
- {% empty %}
-
- {% trans "No motions available." %} |
-
- {% endfor %}
-
-{% endblock %}
diff --git a/openslides/motion/templates/motion/poll_view.html b/openslides/motion/templates/motion/poll_view.html
deleted file mode 100644
index 9894cd51e..000000000
--- a/openslides/motion/templates/motion/poll_view.html
+++ /dev/null
@@ -1,64 +0,0 @@
-{% extends 'motion/base_motion.html' %}
-
-{% load i18n %}
-{% load staticfiles %}
-
-{% block title %}
- {{ block.super }} – {% trans "Motion" %} "{{ motion.public_version.title }}"
- – {{ ballot }}. {% trans "Vote" %}
-{% endblock %}
-
-{% block content %}
- {{ motion.public_version.title }} ({% trans "Motion" %}
- {{ motion.number }}) – {{ ballot }}. {% trans "Vote" %}
- {% trans "Special values" %}: -1 = {% trans 'majority' %}; -2 = {% trans 'undocumented' %}
-
-{% endblock %}
diff --git a/openslides/motion/templates/motion/view.html b/openslides/motion/templates/motion/view.html
deleted file mode 100644
index 3d2ad91b1..000000000
--- a/openslides/motion/templates/motion/view.html
+++ /dev/null
@@ -1,303 +0,0 @@
-{% extends "motion/base_motion.html" %}
-
-{% load tags %}
-{% load i18n %}
-{% load staticfiles %}
-
-{% block title %}{{ block.super }} – {% trans "Motion" %} "{{ version.title }}"{% endblock %}
-
-
-{% block submenu %}
- {{ block.super }}
-
-{% endblock %}
-
-{% block content %}
-
-
-
-
-
- {{ version.title }}
- ({% trans "Motion" %}
- {% if motion.number != None %}
- {{ motion.number }})
- {% else %}
- [{% trans "no number" %}])
- {% endif %}
-
-
- {% trans "Version" %} {{ version.aid }}
-
- {% if motion.public_version != motion.last_version %}
- ⋅
- {% if version == motion.public_version %}
- {% trans "This is not the newest version." %}
{% trans "Go to version" %} {{ motion.last_version.aid }}.
- {% else %}
- {% trans "This is not the authorized version." %}
{% trans "Go to version" %} {{ motion.public_version.aid }}.
- {% endif %}
- {% endif %}
-
-
{% trans "Motion" %}:
-
- {{ version.text|linebreaks }}
-
-
{% trans "Reason" %}:
-
- {% if version.reason %}
- {{ version.reason|linebreaks }}
- {% else %}
- –
- {% endif %}
-
-
- {% if motion.versions|length > 1 %}
-
{% trans "Version History" %}:
-
-
-
- |
- {% trans "Version" %} |
- {% trans "Time" %} |
- {% trans "Title" %} |
- {% trans "Text" %} |
-
- {% trans "Reason" %} |
-
- {% for revision in motion.versions %}
-
-
- {% if motion.status != "pub" %}
- {% if revision == motion.permitted %}
-
- {% else %}
- {% if perms.motion.can_manage_motion %}
-
- {% endif %}
- {% if not revision.rejected and revision.id > motion.permitted.id and perms.motion.can_manage_motion %}
-
- {% endif %}
- {% endif %}
- {% if revision.rejected %}
-
- {% endif %}
- {% endif %}
- |
- {{ revision.aid }} |
- {{ revision.time }} |
-
- {% ifchanged %}
- {{ revision.title }}
- {% else %}
- [{% trans "unchanged" %}]
- {% endifchanged %}
- |
-
- {% ifchanged %}
- {{ revision.text|linebreaks }}
- {% else %}
- [{% trans "unchanged" %}]
- {% endifchanged %}
- |
-
- {% ifchanged %}
- {{ revision.reason|linebreaks }}
- {% else %}
- [{% trans "unchanged" %}]
- {% endifchanged %}
- |
-
- {% endfor %}
-
- {% endif %}
-
- {% if perms.motion.can_manage_motion %}
-
{% trans "Log" %}:
- {{ motion.log|linebreaks }}
- {% endif %}
-
-{% endblock %}
diff --git a/openslides/motion/templates/motion/widget.html b/openslides/motion/templates/motion/widget.html
deleted file mode 100644
index c0796de89..000000000
--- a/openslides/motion/templates/motion/widget.html
+++ /dev/null
@@ -1,34 +0,0 @@
-{% load staticfiles %}
-{% load i18n %}
-{% load tags %}
-
-
-{% for motion in motions %}
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ motion.public_version.title }}
-
- ({% trans "motion" %}
- {% if motion.number %}
- {{ motion.number }})
- {% else %}
- [{% trans "no number" %}])
- {% endif %}
-
-{% empty %}
- - {% trans 'No motion available.' %}
-{% endfor %}
-
-
diff --git a/openslides/motion/urls.py b/openslides/motion/urls.py
index aff36a491..3020f074e 100644
--- a/openslides/motion/urls.py
+++ b/openslides/motion/urls.py
@@ -12,130 +12,29 @@
from django.conf.urls import url, patterns
-from openslides.motion.views import (MotionDelete, ViewPoll,
- MotionPDF, MotionPollPDF, CreateAgendaItem, SupportView)
-
urlpatterns = patterns('openslides.motion.views',
url(r'^$',
- 'overview',
- name='motion_overview',
+ 'motion_list',
+ name='motion_list',
),
- url(r'^(?P\d+)/$',
- 'view',
- name='motion_view',
- ),
-
- url(r'^(?P\d+)/agenda/$',
- CreateAgendaItem.as_view(),
- name='motion_create_agenda',
- ),
-
- url(r'^(?P\d+)/newest/$',
- 'view',
- {'newest': True},
- name='motion_view_newest',
- ),
-
- url(r'^new/$',
- 'edit',
+ url(r'^create/$',
+ 'motion_create',
name='motion_new',
),
- url(r'^import/$',
- 'motion_import',
- name='motion_import',
+ url(r'^(?P\d+)/$',
+ 'motion_detail',
+ name='motion_detail',
),
- url(r'^(?P\d+)/edit/$',
- 'edit',
+ url(r'^(?P\d+)/edit/$',
+ 'motion_edit',
name='motion_edit',
),
- url(r'^(?P\d+)/del/$',
- MotionDelete.as_view(),
- name='motion_delete',
- ),
-
- url(r'^del/$',
- MotionDelete.as_view(),
- { 'motion_id' : None , 'motion_ids' : None },
- name='motion_delete',
- ),
-
- url(r'^(?P\d+)/setnumber/$',
- 'set_number',
- name='motion_set_number',
- ),
-
- url(r'^(?P\d+)/setstatus/(?P[a-z]{3})/$',
- 'set_status',
- name='motion_set_status',
- ),
-
- url(r'^(?P\d+)/permit/$',
- 'permit',
- name='motion_permit',
- ),
-
- url(r'^version/(?P\d+)/permit/$',
- 'permit_version',
- name='motion_version_permit',
- ),
-
- url(r'^version/(?P\d+)/reject/$',
- 'reject_version',
- name='motion_version_reject',
- ),
-
- url(r'^(?P\d+)/notpermit/$',
- 'notpermit',
- name='motion_notpermit',
- ),
-
- url(r'^(?P\d+)/reset/$',
- 'reset',
- name='motion_reset',
- ),
-
- url(r'^(?P\d+)/support/$',
- SupportView.as_view(support=True),
- name='motion_support',
- ),
-
- url(r'^(?P\d+)/unsupport/$',
- SupportView.as_view(support=False),
- name='motion_unsupport',
- ),
-
- url(r'^(?P\d+)/gen_poll/$',
- 'gen_poll',
- name='motion_gen_poll',
- ),
-
- url(r'^print/$',
- MotionPDF.as_view(),
- {'motion_id': None},
- name='print_motions',
- ),
-
- url(r'^(?P\d+)/print/$',
- MotionPDF.as_view(),
- name='print_motion',
- ),
-
- url(r'^poll/(?P\d+)/print/$',
- MotionPollPDF.as_view(),
- name='print_motion_poll',
- ),
-
- url(r'^poll/(?P\d+)/$',
- ViewPoll.as_view(),
- name='motion_poll_view',
- ),
-
- url(r'^poll/(?P\d+)/del/$',
- 'delete_poll',
- name='motion_poll_delete',
+ url(r'^(?P\d+)/version/(?P\d+)/$',
+ 'motion_detail',
+ name='motion_version_detail',
),
)
diff --git a/openslides/motion/views.py b/openslides/motion/views.py
index deff7eaef..49f4a7ffc 100644
--- a/openslides/motion/views.py
+++ b/openslides/motion/views.py
@@ -2,950 +2,75 @@
# -*- coding: utf-8 -*-
"""
openslides.motion.views
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~~~~~~~~~~~~~~~~~~~~~~~
Views for the motion app.
- :copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
+ :copyright: 2011, 2012 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
+from reportlab.platypus import Paragraph
-import csv
-import os
-
-from urlparse import parse_qs
-
-from reportlab.lib import colors
-from reportlab.lib.units import cm
-from reportlab.platypus import (
- SimpleDocTemplate, PageBreak, Paragraph, Spacer, Table, TableStyle)
-
-from django.conf import settings
-from django.contrib import messages
-from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
+from django.contrib import messages
from django.db import transaction
-from django.shortcuts import redirect
-from django.utils.translation import ugettext as _, ungettext
+from django.db.models import Model
+from django.utils.translation import ugettext as _, ugettext_lazy
+from django.views.generic.detail import SingleObjectMixin
-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
-from openslides.config.models import config
-from openslides.projector.projector import Widget
-from openslides.poll.views import PollFormView
-from openslides.participant.api import gen_username, gen_password
-from openslides.participant.models import User, Group
-from openslides.agenda.models import Item
-from openslides.motion.models import Motion, AVersion, MotionPoll
-from openslides.motion.forms import (
- MotionForm, MotionFormTrivialChanges, MotionManagerForm,
- MotionManagerFormSupporter, MotionImportForm, ConfigForm)
+ TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView,
+ DetailView, ListView)
+from openslides.utils.template import Tab
+from openslides.utils.utils import html_strong
+from openslides.projector.api import get_active_slide
+from openslides.projector.projector import Widget, SLIDE
+from .models import Motion
+from .forms import MotionCreateForm, MotionUpdateForm
-@permission_required('motion.can_see_motion')
-@template('motion/overview.html')
-def overview(request):
- """
- View all motions
- """
- try:
- sortfilter = parse_qs(request.COOKIES['votecollector_sortfilter'])
- for value in sortfilter:
- sortfilter[value] = sortfilter[value][0]
- except KeyError:
- 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]
-
- query = Motion.objects.all()
- 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_'):
- # limit result to last version of an motion
- query = query.filter(aversion__id__in=[x.last_version.id for x in Motion.objects.all()])
-
- if 'reverse' in sortfilter:
- query = query.reverse()
-
- # todo: rewrite this with a .filter()
- if 'needsup' in sortfilter:
- motions = []
- for motion in query.all():
- if not motion.enough_supporters:
- motions.append(motion)
- else:
- motions = query
-
- 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..
- for (i, motion) in enumerate(motions):
- try:
- motions[i] = {
- 'actions': motion.get_allowed_actions(request.user),
- 'motion': motion
- }
- except:
- # todo: except what?
- motions[i] = {
- 'actions': [],
- 'motion': motion
- }
-
- return {
- 'motions': motions,
- 'min_supporters': int(config['motion_min_supporters']),
- }
+from django.views.generic.edit import ModelFormMixin
-@permission_required('motion.can_see_motion')
-@template('motion/view.html')
-def view(request, motion_id, newest=False):
- """
- View one motion.
- """
- motion = Motion.objects.get(pk=motion_id)
- if newest:
- version = motion.last_version
- else:
- version = motion.public_version
- revisions = motion.versions
- actions = motion.get_allowed_actions(user=request.user)
-
- return {
- 'motion': motion,
- 'revisions': revisions,
- 'actions': actions,
- 'min_supporters': int(config['motion_min_supporters']),
- 'version': version,
- #'results': motion.results
- }
-
-
-@login_required
-@template('motion/edit.html')
-def edit(request, motion_id=None):
- """
- View a form to edit or create a motion.
- """
- if request.user.has_perm('motion.can_manage_motion'):
- is_manager = True
- else:
- is_manager = False
-
- if not is_manager \
- and not request.user.has_perm('motion.can_create_motion'):
- messages.error(request, _("You have not the necessary rights to create or edit motions."))
- 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."))
- return redirect(reverse('motion_view', args=[motion.id]))
- actions = motion.get_allowed_actions(user=request.user)
- else:
- motion = None
- actions = None
-
- formclass = MotionFormTrivialChanges \
- if config['motion_allow_trivial_change'] and motion_id \
- else MotionForm
-
- managerformclass = MotionManagerFormSupporter \
- if config['motion_min_supporters'] \
- else MotionManagerForm
-
- if request.method == 'POST':
- dataform = formclass(request.POST, prefix="data")
- valid = dataform.is_valid()
-
- if is_manager:
- managerform = managerformclass(request.POST,
- instance=motion,
- prefix="manager")
- valid = valid and managerform.is_valid()
- else:
- managerform = None
-
- if valid:
- if is_manager:
- 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']
-
- try:
- trivial_change = config['motion_allow_trivial_change'] \
- and dataform.cleaned_data['trivial_change']
- except KeyError:
- trivial_change = False
- 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:
- old_supporters = set(motion.supporters)
- # add new supporters
- for supporter in new_supporters.difference(old_supporters):
- motion.support(supporter)
- # remove old supporters
- for supporter in old_supporters.difference(new_supporters):
- motion.unsupport(supporter)
-
- if motion_id is None:
- messages.success(request, _('New motion was successfully created.'))
- else:
- messages.success(request, _('Motion was successfully modified.'))
-
- if not 'apply' in request.POST:
- 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.'))
- else:
- if motion_id is None:
- initial = {'text': config['motion_preamble']}
- else:
- 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 not be removed automatically because you can manage motions. Please check if the supports are valid after your changing!"))
- else:
- messages.warning(request, _("Attention: Do you really want to edit this motion? All %s supporters will be removed! Try to convince the supporters again.") % motion.count_supporters() )
- initial = {'title': motion.title,
- 'text': motion.text,
- 'reason': motion.reason}
-
- dataform = formclass(initial=initial, prefix="data")
- if is_manager:
- if motion_id is None:
- initial = {'submitter': request.user.person_id}
- else:
- initial = {'submitter': motion.submitter.person_id,
- 'supporter': [supporter.person_id for supporter in motion.supporters]}
- managerform = managerformclass(initial=initial,
- instance=motion, prefix="manager")
- else:
- managerform = None
- return {
- 'form': dataform,
- 'managerform': managerform,
- 'motion': motion,
- 'actions': actions,
- }
-
-
-@permission_required('motion.can_manage_motion')
-@template('motion/view.html')
-def set_number(request, motion_id):
- """
- set a number for an motion.
- """
- try:
- Motion.objects.get(pk=motion_id).set_number(user=request.user)
- messages.success(request, _("Motion number was successfully set."))
- except Motion.DoesNotExist:
- pass
- except NameError:
- pass
- return redirect(reverse('motion_view', args=[motion_id]))
-
-
-@permission_required('motion.can_manage_motion')
-@template('motion/view.html')
-def permit(request, motion_id):
- """
- permit an motion.
- """
- try:
- Motion.objects.get(pk=motion_id).permit(user=request.user)
- messages.success(request, _("Motion was successfully authorized."))
- except Motion.DoesNotExist:
- pass
- except NameError, e:
- messages.error(request, e)
- return redirect(reverse('motion_view', args=[motion_id]))
-
-@permission_required('motion.can_manage_motion')
-@template('motion/view.html')
-def notpermit(request, motion_id):
- """
- reject (not permit) an motion.
- """
- try:
- Motion.objects.get(pk=motion_id).notpermit(user=request.user)
- messages.success(request, _("Motion was successfully rejected."))
- except Motion.DoesNotExist:
- pass
- except NameError, e:
- messages.error(request, e)
- return redirect(reverse('motion_view', args=[motion_id]))
-
-@template('motion/view.html')
-def set_status(request, motion_id=None, status=None):
- """
- set a status of an motion.
- """
- try:
- if status is not None:
- motion = Motion.objects.get(pk=motion_id)
- motion.set_status(user=request.user, status=status)
- messages.success(request, _("Motion status was set to: %s.") % motion.get_status_display())
- except Motion.DoesNotExist:
- pass
- except NameError, e:
- messages.error(request, e)
- return redirect(reverse('motion_view', args=[motion_id]))
-
-
-@permission_required('motion.can_manage_motion')
-@template('motion/view.html')
-def reset(request, motion_id):
- """
- reset an motion.
- """
- try:
- Motion.objects.get(pk=motion_id).reset(user=request.user)
- messages.success(request, _("Motion status was reset.") )
- except Motion.DoesNotExist:
- pass
- return redirect(reverse('motion_view', args=[motion_id]))
-
-
-class SupportView(SingleObjectMixin, QuestionMixin, RedirectView):
- """
- Classed based view to support or unsupport a motion. Use
- support=True or support=False in urls.py
- """
- permission_required = 'motion.can_support_motion'
+class MotionListView(ListView):
+ permission_required = 'motion.can_see_motion'
model = Motion
- pk_url_kwarg = 'motion_id'
- support = True
- def get(self, request, *args, **kwargs):
- self.object = self.get_object()
- return super(SupportView, self).get(request, *args, **kwargs)
-
- def check_allowed_actions(self, request):
- """
- Checks whether request.user can support or unsupport the motion.
- Returns True or False.
- """
- 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.'))
- return False
- elif not self.support and not 'unsupport' in allowed_actions:
- messages.error(request, _('You can not unsupport this motion.'))
- return False
- else:
- return True
-
- def pre_redirect(self, request, *args, **kwargs):
- if self.check_allowed_actions(request):
- super(SupportView, self).pre_redirect(request, *args, **kwargs)
-
- 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:
- self.object.support(person=self.request.user)
- else:
- self.object.unsupport(person=self.request.user)
-
- def get_success_message(self):
- if self.support:
- return _("You have supported this motion successfully.")
- else:
- return _("You have unsupported this motion successfully.")
-
- def get_redirect_url(self, **kwargs):
- return reverse('motion_view', args=[kwargs[self.pk_url_kwarg]])
+motion_list = MotionListView.as_view()
-@permission_required('motion.can_manage_motion')
-@template('motion/view.html')
-def gen_poll(request, motion_id):
- """
- gen a poll for this motion.
- """
- try:
- poll = Motion.objects.get(pk=motion_id).gen_poll(user=request.user)
- messages.success(request, _("New vote was successfully created.") )
- except Motion.DoesNotExist:
- pass # TODO: do not call poll after this excaption
- return redirect(reverse('motion_poll_view', args=[poll.id]))
-
-
-@permission_required('motion.can_manage_motion')
-def delete_poll(request, poll_id):
- """
- delete a poll from this motion
- """
- poll = MotionPoll.objects.get(pk=poll_id)
- motion = poll.motion
- count = motion.polls.filter(id__lte=poll_id).count()
- if request.method == 'POST':
- poll.delete()
- motion.writelog(_("Poll deleted"), request.user)
- messages.success(request, _('Poll was successfully deleted.'))
- else:
- 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]))
-
-
-class MotionDelete(DeleteView):
- """
- Delete one or more Motions.
- """
+class MotionDetailView(DetailView):
+ permission_required = 'motion.can_see_motion'
model = Motion
- url = 'motion_overview'
-
- def has_permission(self, request, *args, **kwargs):
- self.kwargs = kwargs
- return self.get_object().get_allowed_actions(request.user)
+ template_name = 'motion/motion_detail.html'
def get_object(self):
- self.motions = []
+ object = super(MotionDetailView, self).get_object()
+ version_id = self.kwargs.get('version_id', None)
+ if version_id is not None:
+ object.default_version = int(version_id) -1
+ return object
- if self.kwargs.get('motion_id', None):
- try:
- return Motion.objects.get(id=int(self.kwargs['motion_id']))
- except Motion.DoesNotExist:
- return None
+motion_detail = MotionDetailView.as_view()
- if self.kwargs.get('motion_ids', []):
- for appid in self.kwargs['motion_ids']:
- try:
- self.motions.append(Motion.objects.get(id=int(appid)))
- except Motion.DoesNotExist:
- pass
- if self.motions:
- return self.motions[0]
- return None
+class MotionMixin(object):
+ def manipulate_object(self, form):
+ for attr in ['title', 'text', 'reason']:
+ setattr(self.object, attr, form.cleaned_data[attr])
- def pre_post_redirect(self, request, *args, **kwargs):
- self.object = self.get_object()
- 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 %s.") % motion)
- continue
+class MotionCreateView(MotionMixin, CreateView):
+ permission_required = 'motion.can_create_motion'
+ model = Motion
+ form_class = MotionCreateForm
- title = motion.title
- motion.delete(force=True)
- messages.success(request, _("Motion %s was successfully deleted.") % title)
+motion_create = MotionCreateView.as_view()
- elif self.object:
- if not 'delete' in self.object.get_allowed_actions(user=request.user):
- messages.error(request, _("You can not delete motion %s.") % self.object)
- elif self.get_answer() == 'yes':
- title = self.object.title
- self.object.delete(force=True)
- messages.success(request, _("Motion %s was successfully deleted.") % title)
- else:
- messages.error(request, _("Invalid request"))
+class MotionUpdateView(MotionMixin, UpdateView):
+ model = Motion
+ form_class = MotionUpdateForm
-class ViewPoll(PollFormView):
- permission_required = 'motion.can_manage_motion'
- poll_class = MotionPoll
- template_name = 'motion/poll_view.html'
-
- def get_context_data(self, **kwargs):
- context = super(ViewPoll, self).get_context_data(**kwargs)
- self.motion = self.poll.get_motion()
- context['motion'] = self.motion
- context['ballot'] = self.poll.get_ballot()
- context['actions'] = self.motion.get_allowed_actions(user=self.request.user)
- 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)
- motion = instance.motion
- motion.writelog(_("Poll was updated"), user)
- return instance
-
- return ViewPollFormClass
-
- def get_success_url(self):
- if not 'apply' in self.request.POST:
- return reverse('motion_view', args=[self.poll.motion.id])
- return ''
-
-
-@permission_required('motion.can_manage_motion')
-def permit_version(request, aversion_id):
- aversion = AVersion.objects.get(pk=aversion_id)
- motion = aversion.motion
- if request.method == 'POST':
- motion.accept_version(aversion, user=request.user)
- messages.success(request, _("Version %s accepted.") % (aversion.aid))
- else:
- gen_confirm_form(request, _('Do you really want to authorize version %s?') % aversion.aid, reverse('motion_version_permit', args=[aversion.id]))
- return redirect(reverse('motion_view', args=[motion.id]))
-
-
-@permission_required('motion.can_manage_motion')
-def reject_version(request, aversion_id):
- aversion = AVersion.objects.get(pk=aversion_id)
- motion = aversion.motion
- if request.method == 'POST':
- if motion.reject_version(aversion, user=request.user):
- messages.success(request, _("Version %s rejected.") % (aversion.aid))
- else:
- messages.error(request, _("ERROR by rejecting the version.") )
- else:
- gen_confirm_form(request, _('Do you really want to reject version %s?') % aversion.aid, reverse('motion_version_reject', args=[aversion.id]))
- return redirect(reverse('motion_view', args=[motion.id]))
-
-
-@permission_required('motion.can_manage_motion')
-@template('motion/import.html')
-def motion_import(request):
- if request.method == 'POST':
- 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
- motions_generated = 0
- motions_modified = 0
- groups_assigned = 0
- groups_generated = 0
- with transaction.commit_on_success():
- dialect = csv.Sniffer().sniff(request.FILES['csvfile'].readline())
- 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
- except ValueError:
- messages.error(request, _('Ignoring malformed line %d in import file.') % (lno + 1))
- continue
- 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:
- group = Group()
- group.group_as_person = True
- group.description = _('Created by motion import.')
- group.name = last_name
- group.save()
- groups_generated += 1
-
- user = get_person(group.person_id)
- 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
- # create / modify the motion
- motion = None
- if number:
- try:
- motion = Motion.objects.get(number=number)
- motions_modified += 1
- except Motion.DoesNotExist:
- motion = None
- if motion is None:
- motion = Motion(submitter=user)
- if number:
- motion.number = number
- motions_generated += 1
-
- motion.title = form.cleaned_data['title']
- motion.text = form.cleaned_data['text']
- motion.reason = form.cleaned_data['reason']
- if import_permitted:
- motion.status = 'per'
-
- motion.save(user, trivial_change=True)
-
- if motions_generated:
- messages.success(request, ungettext('%d motion was successfully imported.',
- '%d motions were successfully imported.', motions_generated) % motions_generated)
- if motions_modified:
- messages.success(request, ungettext('%d motion was successfully modified.',
- '%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_generated:
- messages.success(request, ungettext('%d new group was added.', '%d new groups were added.', groups_generated) % groups_generated)
-
- if groups_assigned:
- messages.success(request, ungettext('%d group assigned to motions.', '%d groups assigned to motions.', groups_assigned) % groups_assigned)
- return redirect(reverse('motion_overview'))
-
- except csv.Error:
- messages.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."))
- form = MotionImportForm()
- return {
- 'form': form,
- }
-
-
-class CreateAgendaItem(RedirectView):
- permission_required = 'agenda.can_manage_agenda'
-
- def pre_redirect(self, request, *args, **kwargs):
- 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')
-
-
-class MotionPDF(PDFView):
- permission_required = 'motion.can_see_motion'
- top_space = 0
-
- def get_filename(self):
- motion_id = self.kwargs['motion_id']
- if motion_id is None:
- filename = _("Motions")
- else:
- motion = Motion.objects.get(id=motion_id)
- if motion.number:
- number = motion.number
- else:
- number = ""
- filename = u'%s%s' % (_("Motion"), str(number))
- return filename
-
- def append_to_pdf(self, story):
- 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']))
- preamble = config["motion_pdf_preamble"]
- if preamble:
- story.append(Paragraph("%s" % preamble.replace('\r\n','
'), stylesheet['Paragraph']))
- story.append(Spacer(0,0.75*cm))
- motions = Motion.objects.all()
- if not motions: # No motions existing
- story.append(Paragraph(_("No motions available."), stylesheet['Heading3']))
- 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:
- story.append(Paragraph(_("Motion No.")+" : %s" % (motion.title), stylesheet['Heading3']))
- # Motions details (each motion on single page)
- for motion in motions:
- story.append(PageBreak())
- story = self.get_motion(motion, story)
- else: # print selected motion
- motion = Motion.objects.get(id=motion_id)
- story = self.get_motion(motion, story)
-
- def get_motion(self, motion, story):
- # Preparing Table
- data = []
-
- # 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 = []
- cell1a.append(Spacer(0, 0.2 * cm))
- cell1a.append(Paragraph("%s:" % _("Submitter"), stylesheet['Heading4']))
- cell1b = []
- cell1b.append(Spacer(0, 0.2 * cm))
- cell1b.append(Paragraph("%s" % motion.submitter, stylesheet['Normal']))
- data.append([cell1a, cell1b])
-
- if motion.status == "pub":
- # Cell for the signature
- cell2a = []
- cell2b = []
- cell2a.append(Paragraph("%s:" % _("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
- if config['motion_min_supporters']:
- cell3a = []
- cell3b = []
- cell3a.append(Paragraph("%s:" % _("Supporters"), stylesheet['Heading4']))
- for supporter in motion.supporters:
- cell3b.append(Paragraph(". %s" % supporter, stylesheet['Signaturefield']))
- if motion.status == "pub":
- for x in range(motion.missing_supporters):
- cell3b.append(Paragraph(". __________________________________________",stylesheet['Signaturefield']))
- cell3b.append(Spacer(0, 0.2 * cm))
- data.append([cell3a, cell3b])
-
- # status
- cell4a = []
- cell4b = []
- note = " ".join(motion.notes)
- cell4a.append(Paragraph("%s:" % _("Status"), stylesheet['Heading4']))
- if note != "":
- if motion.status == "pub":
- cell4b.append(Paragraph(note, stylesheet['Normal']))
- else:
- cell4b.append(Paragraph("%s | %s" % (motion.get_status_display(), note), stylesheet['Normal']))
- else:
- cell4b.append(Paragraph("%s" % motion.get_status_display(), stylesheet['Normal']))
- data.append([cell4a, cell4b])
-
- # Version number (aid)
- if motion.public_version.aid > 1:
- cell5a = []
- cell5b = []
- cell5a.append(Paragraph("%s:" % _("Version"), stylesheet['Heading4']))
- cell5b.append(Paragraph("%s" % motion.public_version.aid, stylesheet['Normal']))
- data.append([cell5a, cell5b])
-
- # voting results
- poll_results = motion.get_poll_results()
- if poll_results:
- cell6a = []
- cell6a.append(Paragraph("%s:" % _("Vote results"), stylesheet['Heading4']))
- cell6b = []
- ballotcounter = 0
- for result in poll_results:
- ballotcounter += 1
- if len(poll_results) > 1:
- cell6b.append(Paragraph("%s. %s" % (ballotcounter, _("Vote")), stylesheet['Bold']))
- cell6b.append(Paragraph("%s: %s
%s: %s
%s: %s
%s: %s
%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)
- story.append(Spacer(0, 1 * cm))
-
- # title
- story.append(Paragraph(motion.public_version.title, stylesheet['Heading3']))
- # text
- story.append(Paragraph("%s" % motion.public_version.text.replace('\r\n','
'), stylesheet['Paragraph']))
- # reason
- if motion.public_version.reason:
- story.append(Paragraph(_("Reason")+":", stylesheet['Heading3']))
- story.append(Paragraph("%s" % motion.public_version.reason.replace('\r\n','
'), stylesheet['Paragraph']))
- return story
-
-
-class MotionPollPDF(PDFView):
- permission_required = 'motion.can_manage_motion'
- top_space = 0
-
- def get(self, request, *args, **kwargs):
- self.poll = MotionPoll.objects.get(id=self.kwargs['poll_id'])
- return super(MotionPollPDF, self).get(request, *args, **kwargs)
-
- def get_filename(self):
- 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 = " " % imgpath
- cell = []
- cell.append(Spacer(0,0.8*cm))
- 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
- 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)
-
-
-class Config(FormView):
- permission_required = 'config.can_manage_config'
- form_class = ConfigForm
- template_name = 'motion/config.html'
-
- def get_initial(self):
- return {
- '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'],
- }
-
- def form_valid(self, form):
- 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.'))
- return super(Config, self).form_valid(form)
-
-
-def register_tab(request):
- selected = True if request.path.startswith('/motion/') else False
- return Tab(
- 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'),
- selected=selected,
- )
-
-
-def get_widgets(request):
- return [
- Widget(
- name='motions',
- display_name=_('Motions'),
- template='motion/widget.html',
- context={'motions': Motion.objects.all()},
- permission_required='projector.can_manage_projector')]
+motion_edit = MotionUpdateView.as_view()
diff --git a/openslides/participant/views.py b/openslides/participant/views.py
index ded8a55c7..e39b5d9e6 100644
--- a/openslides/participant/views.py
+++ b/openslides/participant/views.py
@@ -554,30 +554,30 @@ def get_widgets(request):
group_widget and a personal_info_widget.
"""
return [
- get_personal_info_widget(request),
+ #get_personal_info_widget(request),
get_user_widget(request),
get_group_widget(request)]
-def get_personal_info_widget(request):
- """
- Provides a widget for personal info. It shows your submitted motions
- and where you are supporter or candidate.
- """
- personal_info_context = {
- 'submitted_motions': Motion.objects.filter(submitter=request.user),
- 'config_motion_min_supporters': config['motion_min_supporters'],
- 'supported_motions': Motion.objects.filter(motionsupporter=request.user),
- 'assignments': Assignment.objects.filter(
- assignmentcandidate__person=request.user,
- assignmentcandidate__blocked=False)}
- return Widget(
- name='personal_info',
- display_name=_('My motions and elections'),
- template='participant/personal_info_widget.html',
- context=personal_info_context,
- permission_required=None,
- default_column=1)
+## def get_personal_info_widget(request):
+ ## """
+ ## Provides a widget for personal info. It shows your submitted motions
+ ## and where you are supporter or candidate.
+ ## """
+ ## personal_info_context = {
+ ## 'submitted_motions': Motion.objects.filter(submitter=request.user),
+ ## 'config_motion_min_supporters': config['motion_min_supporters'],
+ ## 'supported_motions': Motion.objects.filter(motionsupporter=request.user),
+ ## 'assignments': Assignment.objects.filter(
+ ## assignmentcandidate__person=request.user,
+ ## assignmentcandidate__blocked=False)}
+ ## return Widget(
+ ## name='personal_info',
+ ## display_name=_('My motions and elections'),
+ ## template='participant/personal_info_widget.html',
+ ## context=personal_info_context,
+ ## permission_required=None,
+ ## default_column=1)
def get_user_widget(request):
diff --git a/openslides/utils/views.py b/openslides/utils/views.py
index 33bdafc32..c13087592 100644
--- a/openslides/utils/views.py
+++ b/openslides/utils/views.py
@@ -41,6 +41,7 @@ from django.views.generic import (
View as _View,
FormView as _FormView,
ListView as _ListView,
+ DetailView as _DetailView,
)
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.list import TemplateResponseMixin
@@ -98,6 +99,31 @@ class AjaxMixin(object):
return HttpResponse(json.dumps(self.get_ajax_context(**kwargs)))
+class ExtraContextMixin(object):
+ def get_context_data(self, **kwargs):
+ context = super(ExtraContextMixin, self).get_context_data(**kwargs)
+ template_manipulation.send(
+ sender=self.__class__, request=self.request, context=context)
+ return context
+
+
+class SuccessUrlMixin(object):
+ def get_success_url(self):
+ messages.success(self.request, self.get_success_message())
+ if 'apply' in self.request.POST:
+ return reverse(self.get_apply_url(), args=[self.object.id])
+ if self.success_url:
+ url = reverse(success_url)
+ else:
+ try:
+ url = self.object.get_absolute_url()
+ except AttributeError:
+ raise ImproperlyConfigured(
+ "No URL to redirect to. Either provide a url or define"
+ " a get_absolute_url method on the Model.")
+ return url
+
+
class QuestionMixin(object):
question = ugettext_lazy('Are you sure?')
success_message = ugettext_lazy('Thank you for your answer')
@@ -157,21 +183,12 @@ class QuestionMixin(object):
return self.success_message
-class TemplateView(PermissionMixin, _TemplateView):
- def get_context_data(self, **kwargs):
- context = super(TemplateView, self).get_context_data(**kwargs)
- template_manipulation.send(
- sender=self.__class__, request=self.request, context=context)
- return context
+class TemplateView(PermissionMixin, ExtraContextMixin, _TemplateView):
+ pass
-class ListView(PermissionMixin, SetCookieMixin, _ListView):
- def get_context_data(self, **kwargs):
- context = super(ListView, self).get_context_data(**kwargs)
- template_manipulation.send(
- sender=self.__class__, request=self.request, context=context)
- return context
-
+class ListView(PermissionMixin, SetCookieMixin, ExtraContextMixin, _ListView):
+ pass
class AjaxView(PermissionMixin, AjaxMixin, View):
def get(self, request, *args, **kwargs):
@@ -202,36 +219,26 @@ class RedirectView(PermissionMixin, AjaxMixin, _RedirectView):
return reverse(super(RedirectView, self).get_redirect_url(**kwargs))
-class FormView(PermissionMixin, _FormView):
- def get_success_url(self):
- if not self.success_url:
- return ''
- return reverse(super(FormView, self).get_success_url())
-
- def get_context_data(self, **kwargs):
- context = super(FormView, self).get_context_data(**kwargs)
- template_manipulation.send(
- sender=self.__class__, request=self.request, context=context)
- return context
-
+class FormView(PermissionMixin, ExtraContextMixin, SuccessUrlMixin, _FormView):
def form_invalid(self, form):
messages.error(self.request, _('Please check the form for errors.'))
return super(FormView, self).form_invalid(form)
-class UpdateView(PermissionMixin, _UpdateView):
- def get_success_url(self):
- messages.success(self.request, self.get_success_message())
- if 'apply' in self.request.POST:
- return ''
- return reverse(super(UpdateView, self).get_success_url())
+class ModelFormMixin(object):
+ def form_valid(self, form):
+ self.object = form.save(commit=False)
+ self.manipulate_object(form)
+ self.object.save()
+ form.save_m2m()
+ return HttpResponseRedirect(self.get_success_url())
- def get_context_data(self, **kwargs):
- context = super(UpdateView, self).get_context_data(**kwargs)
- template_manipulation.send(
- sender=self.__class__, request=self.request, context=context)
- return context
+ def manipulate_object(self, form):
+ pass
+
+class UpdateView(PermissionMixin, SuccessUrlMixin, ExtraContextMixin,
+ ModelFormMixin, _UpdateView):
def form_invalid(self, form):
messages.error(self.request, _('Please check the form for errors.'))
return super(UpdateView, self).form_invalid(form)
@@ -240,46 +247,25 @@ class UpdateView(PermissionMixin, _UpdateView):
return _('%s was successfully modified.') % html_strong(self.object)
-class CreateView(PermissionMixin, _CreateView):
+class CreateView(PermissionMixin, SuccessUrlMixin, ExtraContextMixin,
+ ModelFormMixin, _CreateView):
apply_url = None
-
- def get_success_url(self):
- messages.success(self.request, self.get_success_message())
- if 'apply' in self.request.POST:
- return reverse(self.get_apply_url(), args=[self.object.id])
- return reverse(super(CreateView, self).get_success_url())
-
- def get_context_data(self, **kwargs):
- context = super(CreateView, self).get_context_data(**kwargs)
- template_manipulation.send(
- sender=self.__class__, request=self.request, context=context)
- return context
+ success_url = None
def get_apply_url(self):
- if self apply_url:
+ if self.apply_url:
return self.apply_url
else:
raise ImproperlyConfigured(
"No URL to redirect to. Provide a apply_url.")
-
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):
- self.object = form.save(commit=False)
- self.manipulate_object(form)
- self.object.save()
- form.save_m2m()
- return HttpResponseRedirect(self.get_success_url())
-
def get_success_message(self):
return _('%s was successfully created.') % html_strong(self.object)
- def manipulate_object(self, form):
- pass
-
class DeleteView(SingleObjectMixin, QuestionMixin, RedirectView):
def get(self, request, *args, **kwargs):
@@ -296,16 +282,11 @@ class DeleteView(SingleObjectMixin, QuestionMixin, RedirectView):
return _('%s was successfully deleted.') % html_strong(self.object)
-class DetailView(TemplateView, SingleObjectMixin):
+class DetailView(PermissionMixin, ExtraContextMixin, _DetailView):
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super(DetailView, self).get(request, *args, **kwargs)
- def get_context_data(self, **kwargs):
- context = super(DetailView, self).get_context_data(**kwargs)
- context.update(SingleObjectMixin.get_context_data(self, **kwargs))
- return context
-
class PDFView(PermissionMixin, View):
filename = _('undefined-filename')