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" %}

-
{% csrf_token %} - {{ form.as_p }} -

- - - - -

-
-{% 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 %} - -
{% csrf_token %} - {{ form.as_p }} - {{ managerform.as_p }} -

- - - - - -

- * {% trans "required" %} -
- -{% 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.' %} -

- -
{% csrf_token %} - {{ form.as_p }} -

- - - - -

- * {% trans "required" %} -
-{% 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" %}

+
{% csrf_token %} + {{ form.as_p }} + + + + + + * {% trans "required" %} +
+{% 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" %}

+ +{% 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" %}

-

- {% trans "Filter" %}: - {% if min_supporters > 0 %} - {% trans "Need supporters" %}   - {% endif %} - {% trans "Without number" %}   - {% trans "Status" %}: - -
-

- {{ motions|length }} - {% blocktrans count counter=motions|length context "number of motions"%}motion{% plural %}motions{% endblocktrans %} - - - - - {% if min_supporters > 0 %} - - {% endif %} - - - - - - {% for app_info in motions %} - {% with motion=app_info.motion useractions=app_info.actions %} - - - - {% if min_supporters > 0 %} - - {% endif %} - - - - - - {% endwith %} - {% empty %} - - - - {% endfor %} -
{% trans "Number" %}{% trans "Motion title" %}{% trans "Number of supporters" %}{% trans "Status" %}{% trans "Submitter" %}{% trans "Creation Time" %}{% trans "Actions" %}
{% if motion.number %}{{ motion.number }}{% else %}-{% endif %}{{ motion.public_version.title }}{{ motion.count_supporters }}{% 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 %} - - -
{% trans "No motions available." %}
-{% 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' %} -
{% csrf_token %} - {{ pre_form }} - - - - - - - - {% for value in forms.0 %} - - - - - {% endfor %} - - - - - - - - -
{% trans "Option" %}{% trans "Votes" %}
{{ value.label }}{{ value.errors }}{{ value }}
{% trans "Invalid votes" %}{{ pollform.votesinvalid.errors }}{{ pollform.votesinvalid }}
{% trans "Votes cast" %}{{ pollform.votescast.errors }}{{ pollform.votescast }}
- - {{ post_form }} - -

- - - -

- -

- - - - - -

-
-{% 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" %}:

- - - - - - - - - - - - {% for revision in motion.versions %} - - - - - - - - - {% endfor %} -
{% trans "Version" %}{% trans "Time" %}{% trans "Title" %}{% trans "Text" %}{% trans "Reason" %}
- {% 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 %} -
- {% 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 %} - - - 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')