Start to rewrite the motion app
This commit is contained in:
parent
ae8641bcad
commit
0231fe37f5
@ -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):
|
||||
|
@ -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)
|
||||
|
||||
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})
|
||||
|
||||
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 = []
|
||||
|
||||
# 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")
|
||||
|
||||
# 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")
|
||||
|
||||
#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")
|
||||
|
||||
# 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")
|
||||
|
||||
#For the rest, all actions need the manage permission
|
||||
if not user.has_perm("motion.can_manage_motion"):
|
||||
return actions
|
||||
|
||||
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()
|
||||
raise ValueError('Unknown relation with id %d' % relation)
|
||||
|
||||
def get_agenda_title(self):
|
||||
return self.public_version.title
|
||||
def get_query_set(self):
|
||||
return (super(RelatedPersonsManager, self).get_query_set()
|
||||
.filter(relation=self.relation))
|
||||
|
||||
def get_agenda_title_supplement(self):
|
||||
number = self.number or '<i>[%s]</i>' % 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'):
|
||||
class MotionRelatedPersons(models.Model):
|
||||
submitter = RelatedPersonsManager(relation=1)
|
||||
supporter = RelatedPersonsManager(relation=2)
|
||||
objects = models.Manager()
|
||||
|
||||
person = PersonField()
|
||||
relation = models.IntegerField(default=1, choices=RELATION)
|
||||
motion = models.ForeignKey('Motion', related_name="persons")
|
||||
|
||||
|
||||
class Motion(SlideMixin, models.Model):
|
||||
prefix = "motion" # Rename this in the slide-system
|
||||
|
||||
# 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)
|
||||
|
||||
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',)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.get_title()
|
||||
|
||||
# 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)
|
||||
|
@ -1,66 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load tags %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block submenu %}
|
||||
{% url 'motion_overview' as url_motionoverview %}
|
||||
<h4>{% trans "Motions" %}</h4>
|
||||
<ul>
|
||||
<li class="{% if request.path == url_motionoverview %}selected{% endif %}"><a href="{% url 'motion_overview' %}">{% trans "All motions" %}</a></li>
|
||||
{% if perms.motion.can_create_motion or perms.motion.can_manage_motion %}
|
||||
<li class="{% active request '/motion/new' %}"><a href="{% url 'motion_new' %}">{% trans "New motion" %}</a></li>
|
||||
{% endif %}
|
||||
{% if perms.motion.can_manage_motion %}
|
||||
<li class="{% active request '/motion/import' %}"><a href="{% url 'motion_import' %}">{% trans 'Import motions' %}</a></li>
|
||||
{% endif %}
|
||||
<li><a href="{% url 'print_motions' %}"><img src="{% static 'images/icons/pdf.png' %}"> {% trans 'All motions as PDF' %}</a></li>
|
||||
</ul>
|
||||
|
||||
{# second submenu #}
|
||||
{% if motion %}
|
||||
<br>
|
||||
<h3>{% trans "Motion No." %}
|
||||
{% if motion.number != None %}
|
||||
{{ motion.number }}
|
||||
{% else %}
|
||||
<i>[-]</i>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<ul>
|
||||
{# view motion #}
|
||||
{% url 'motion_view' motion.id as url_motionview %}
|
||||
<li class="{% if request.path == url_motionview %}selected{% endif %}"><a href="{% url 'motion_view' motion.id %}">{% trans 'View motion' %}</a></li>
|
||||
{# edit motion #}
|
||||
{% if "edit" in actions %}
|
||||
{% url 'motion_edit' motion.id as url_motionedit %}
|
||||
<li class="{% if request.path == url_motionedit %}selected{% endif %}"><a href="{% url 'motion_edit' motion.id %}"><img src="{% static 'images/icons/edit.png' %}"> {% trans 'Edit motion' %}</a></li>
|
||||
{% endif %}
|
||||
{# delete motion #}
|
||||
{% if "delete" in actions %}
|
||||
<li><a href="{% url 'motion_delete' motion.id %}"><img src="{% static 'images/icons/delete.png' %}"> {% trans 'Delete motion' %}</a></li>
|
||||
{% endif %}
|
||||
{# PDF #}
|
||||
<li><a href="{% url 'print_motion' motion.id %}"><img src="{% static 'images/icons/pdf.png' %}"> {% trans 'Motion as PDF' %}</a></li>
|
||||
{# activate and polls #}
|
||||
{% if perms.projector.can_manage_projector %}
|
||||
<li>
|
||||
<a class="activate_link {% if item.active %}active{% endif %}" href="{% url 'projector_activate_slide' motion.sid %}"><img src="{% static 'images/icons/projector.png' %}"> {% trans 'Show Motion' %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.motion.can_manage_motion %}
|
||||
{% for poll in motion.polls %}
|
||||
{% url 'motion_poll_view' poll.id as url_motionpollview %}
|
||||
<li class="{% if request.path == url_motionpollview %}selected{% endif %}"><a href="{% url 'motion_poll_view' poll.id %}"><img src="{% static 'images/icons/edit.png' %}"> {{ forloop.counter }}. {% trans "Vote" %}</a></li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{# Agenda Item #}
|
||||
{% if perms.agenda.can_manage_agenda %}
|
||||
<li>
|
||||
<a href="{% url 'motion_create_agenda' motion.id %}">{% trans 'New agenda item' %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -1,22 +0,0 @@
|
||||
{% extends "config/base_config.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{{ block.super }} – {% trans "Motion settings" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Motion settings" %}</h1>
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<button class="button" type="submit">
|
||||
<span class="icon ok">{% trans 'Save' %}</span>
|
||||
</button>
|
||||
<a href="{% url 'config_motion' %}">
|
||||
<button class="button" type="button" onclick="window.location='{% url 'config_motion' %}'">
|
||||
<span class="icon cancel">{% trans 'Cancel' %}</span>
|
||||
</button>
|
||||
</a>
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
@ -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 %}
|
||||
<h1>{% trans "Edit motion" %}</h1>
|
||||
{% else %}
|
||||
<h1>{% trans "New motion" %}</h1>
|
||||
{% endif %}
|
||||
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
{{ managerform.as_p }}
|
||||
<p>
|
||||
<button class="button" type="submit">
|
||||
<span class="icon ok">{% trans 'Save' %}</span>
|
||||
</button>
|
||||
<button class="button" type="submit" name="apply">
|
||||
<span class="icon apply">{% trans 'Apply' %}</span>
|
||||
</button>
|
||||
<a href="{% url 'motion_overview' %}">
|
||||
<button class="button" type="button" onclick="window.location='{% url 'motion_overview' %}'">
|
||||
<span class="icon cancel">{% trans 'Cancel' %}</span>
|
||||
</button>
|
||||
</a>
|
||||
</p>
|
||||
<small>* {% trans "required" %}</small>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
@ -1,36 +0,0 @@
|
||||
{% extends "motion/base_motion.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{{ block.super }} – {% trans "Import motions" %} {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Import motions" %}</h1>
|
||||
<p>{% trans 'Select a CSV file to import motions!' %}</p>
|
||||
|
||||
<p>{% trans 'Required comma separated values' %}:
|
||||
<code>({% trans 'number, title, text, reason, first_name, last_name, is_group' %})</code>
|
||||
<br>
|
||||
{% trans '<code>number</code>, <code>reason</code> and <code>is_group</code> are optional and may be empty' %}.
|
||||
<br>
|
||||
{% trans 'Required CSV file encoding: UTF-8 (Unicode).' %}
|
||||
</p>
|
||||
|
||||
<p><a href="http://dev.openslides.org/wiki/CSVImport" target="_blank">{% trans 'A CSV example file is available in OpenSlides Wiki.' %}</a>
|
||||
</p>
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<button class="button" type="submit">
|
||||
<span class="icon import">{% trans 'Import' %}</span>
|
||||
</button>
|
||||
<a href="{% url 'motion_overview' %}">
|
||||
<button class="button" type="button" onclick="window.location='{% url 'motion_overview' %}'">
|
||||
<span class="icon cancel">{% trans 'Cancel' %}</span>
|
||||
</button>
|
||||
</a>
|
||||
</p>
|
||||
<small>* {% trans "required" %}</small>
|
||||
</form>
|
||||
{% endblock %}
|
12
openslides/motion/templates/motion/motion_detail.html
Normal file
12
openslides/motion/templates/motion/motion_detail.html
Normal file
@ -0,0 +1,12 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load tags %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block title %}{{ block.super }} – {% trans "Motion" %} "{{ version.title }}"{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Titel: {{ object.title }} </p>
|
||||
<p>Text: {{ object.text }}</p>
|
||||
{% endblock %}
|
26
openslides/motion/templates/motion/motion_form.html
Normal file
26
openslides/motion/templates/motion/motion_form.html
Normal file
@ -0,0 +1,26 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load tags %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block title %}{{ block.super }} – {% trans "Motion Form" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Motions Forms" %}</h1>
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button class="button" type="submit">
|
||||
<span class="icon ok">{% trans 'Save' %}</span>
|
||||
</button>
|
||||
<button class="button" type="submit" name="apply">
|
||||
<span class="icon apply">{% trans 'Apply' %}</span>
|
||||
</button>
|
||||
<a href="{% url 'motion_list' %}">
|
||||
<button class="button" type="button" onclick="window.location='{% url 'motion_list' %}'">
|
||||
<span class="icon cancel">{% trans 'Cancel' %}</span>
|
||||
</button>
|
||||
</a>
|
||||
<small>* {% trans "required" %}</small>
|
||||
</form>
|
||||
{% endblock %}
|
16
openslides/motion/templates/motion/motion_list.html
Normal file
16
openslides/motion/templates/motion/motion_list.html
Normal file
@ -0,0 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load tags %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block title %}{{ block.super }} – {% trans "Motions" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Motions" %}</h1>
|
||||
<ul>
|
||||
{% for motion in motion_list %}
|
||||
<li>{{ motion }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
@ -1,88 +0,0 @@
|
||||
{% extends "motion/base_motion.html" %}
|
||||
|
||||
{% load tags %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block title %}{{ block.super }} – {% trans "Motions" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Motions" %}</h1>
|
||||
<p><form action="{{ request.url }}" name="filter" method="get">
|
||||
{% trans "Filter" %}:
|
||||
{% if min_supporters > 0 %}
|
||||
<input type="checkbox" name="needsup" onchange="document.forms['filter'].submit()"
|
||||
{% if 'on' in request.GET.needsup %}checked{% endif %}> {% trans "Need supporters" %}
|
||||
{% endif %}
|
||||
<input type="checkbox" name="number" onchange="document.forms['filter'].submit()"
|
||||
{% if 'on' in request.GET.number %}checked{% endif %}> {% trans "Without number" %}
|
||||
<input type="checkbox" name="status" onchange="document.forms['filter'].submit()"
|
||||
{% if 'on' in request.GET.status %}checked{% endif %}> {% trans "Status" %}:
|
||||
<select class="default-input" name="statusvalue" onchange="{% if 'on' in request.GET.status %}document.forms['filter'].submit(){% endif %}">
|
||||
<option value="pub" {% if 'pub' in request.GET.statusvalue %}selected{% endif %}>{% trans "Not yet authorized" %}</option>
|
||||
<option value="per" {% if 'on' in request.GET.status and 'per' in request.GET.statusvalue %}selected{% endif %}>{% trans "Authorized" %}</option>
|
||||
<option value="acc" {% if 'on' in request.GET.status and 'acc' in request.GET.statusvalue %}selected{% endif %}>{% trans "Accepted" %}</option>
|
||||
<option value="rej" {% if 'on' in request.GET.status and 'rej' in request.GET.statusvalue %}selected{% endif %}>{% trans "Rejected" %}</option>
|
||||
<option value="wit" {% if 'on' in request.GET.status and 'wit' in request.GET.statusvalue %}selected{% endif %}>{% trans "Withdrawen (by submitter)" %}</option>
|
||||
<option value="rev" {% if 'rev' in request.GET.statusvalue %}selected{% endif %}>{% trans "Needs Review" %}</option>
|
||||
</select>
|
||||
</form>
|
||||
</p>
|
||||
{{ motions|length }}
|
||||
{% blocktrans count counter=motions|length context "number of motions"%}motion{% plural %}motions{% endblocktrans %}
|
||||
<table>
|
||||
<tr>
|
||||
<th><a href="?sort=number{% if 'number' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{% trans "Number" %}</a></th>
|
||||
<th><a href="?sort=title{% if 'title' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{% trans "Motion title" %}</a></th>
|
||||
{% if min_supporters > 0 %}
|
||||
<th><a href="?sort=supporter{% if 'supporter' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{% trans "Number of supporters" %}</a></th>
|
||||
{% endif %}
|
||||
<th><a href="?sort=status{% if 'status' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{% trans "Status" %}</a></th>
|
||||
<th><a href="?sort=submitter{% if 'submitter' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{% trans "Submitter" %}</a></th>
|
||||
<th><a href="?sort=time{% if 'time' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{% trans "Creation Time" %}<a></th>
|
||||
<th style="width: 1px;">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
{% for app_info in motions %}
|
||||
{% with motion=app_info.motion useractions=app_info.actions %}
|
||||
<tr class="{% cycle '' 'odd' %}
|
||||
{% if motion.active %}activeline{% endif %}">
|
||||
<td>{% if motion.number %}{{ motion.number }}{% else %}-{% endif %}</td>
|
||||
<td><a href="{% url 'motion_view' motion.id %}">{{ motion.public_version.title }}</a></td>
|
||||
{% if min_supporters > 0 %}
|
||||
<td>{{ motion.count_supporters }}</td>
|
||||
{% endif %}
|
||||
<td>{% if motion.status != "pub" %}
|
||||
{{ motion.get_status_display }}<br>
|
||||
{% endif %}
|
||||
{% for note in motion.notes %}
|
||||
{{ note }}
|
||||
{% if not forloop.last %}<br>{%endif%}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>{{ motion.submitter }}</td>
|
||||
<td>{{ motion.creation_time }}</td>
|
||||
<td>
|
||||
<span style="width: 1px; white-space: nowrap;">
|
||||
{% if perms.projector.can_manage_projector %}
|
||||
<a class="activate_link {% if motion.active %}active{% endif %}" href="{% url 'projector_activate_slide' motion.sid %}" title="{% trans 'Activate motion' %}">
|
||||
<span></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.motion.can_manage_motion %}
|
||||
<a href="{% url 'motion_edit' motion.id %}"><img src="{% static 'images/icons/edit.png' %}" title="{% trans 'Edit motion' %}"></a>
|
||||
{% if "delete" in useractions %}
|
||||
<a href="{% url 'motion_delete' motion.id %}"><img src="{% static 'images/icons/delete.png' %}" title="{% trans 'Delete motion' %}"></a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<a href="{% url 'print_motion' motion.id %}" title="{% trans 'Motion as PDF' %}"><img src="{% static 'images/icons/pdf.png' %}"></a>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endwith %}
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="7"><i>{% trans "No motions available." %}</i></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
@ -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 %}
|
||||
<h1>{{ motion.public_version.title }} ({% trans "Motion" %}
|
||||
{{ motion.number }}) – {{ ballot }}. {% trans "Vote" %}</h1>
|
||||
<i class="helptext">{% trans "Special values" %}: -1 = {% trans 'majority' %}; -2 = {% trans 'undocumented' %}</i>
|
||||
<form action="" method="post" class="small-form">{% csrf_token %}
|
||||
{{ pre_form }}
|
||||
<span id="poll_id" style="display:none">{{ poll.id }}</span>
|
||||
|
||||
<table class="table" style="width: auto;">
|
||||
<tr>
|
||||
<th>{% trans "Option" %}</th>
|
||||
<th>{% trans "Votes" %}</th>
|
||||
</tr>
|
||||
{% for value in forms.0 %}
|
||||
<tr>
|
||||
<td>{{ value.label }}</td>
|
||||
<td>{{ value.errors }}{{ value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr class="total">
|
||||
<td>{% trans "Invalid votes" %}</td>
|
||||
<td>{{ pollform.votesinvalid.errors }}{{ pollform.votesinvalid }}</td>
|
||||
</tr>
|
||||
<tr class="total">
|
||||
<td>{% trans "Votes cast" %}</td>
|
||||
<td>{{ pollform.votescast.errors }}{{ pollform.votescast }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{ post_form }}
|
||||
|
||||
<p>
|
||||
<a href="{% url 'print_motion_poll' poll.id %}">
|
||||
<button class="button" type="button" onclick="window.location='{% url 'print_motion_poll' poll.id %}'">
|
||||
<span class="icon pdf">{% trans 'Ballot paper as PDF' %}</span>
|
||||
</button>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<button class="button" type="submit">
|
||||
<span class="icon ok">{% trans 'Save' %}</span>
|
||||
</button>
|
||||
<button class="button" type="submit" name="apply">
|
||||
<span class="icon apply">{% trans 'Apply' %}</span>
|
||||
</button>
|
||||
<a href="{% url 'motion_view' motion.id %}">
|
||||
<button class="button" type="button" onclick="window.location='{% url 'motion_view' motion.id %}'">
|
||||
<span class="icon cancel">{% trans 'Cancel' %}</span>
|
||||
</button>
|
||||
</a>
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
@ -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 %}
|
||||
<div id="sidebar">
|
||||
<div class="box">
|
||||
<h4>{% trans "Submitter" %}:</h4>
|
||||
{{ motion.submitter }}
|
||||
|
||||
{% if min_supporters > 0 %}
|
||||
<h4>{% trans "Supporters" %}: *</h4>
|
||||
{% if not motion.supporters %}
|
||||
-
|
||||
{% else %}
|
||||
<ol>
|
||||
{% for supporter in motion.supporters %}
|
||||
<li> {{ supporter }}</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<h4>{% trans "Status" %}:</h4>
|
||||
{% if motion.status != "pub" %}
|
||||
{% trans motion.get_status_display %}
|
||||
<br>
|
||||
{% endif %}
|
||||
{% for note in motion.notes %}
|
||||
{{ note }}
|
||||
{% if not forloop.last %}<br>{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<h4>{% trans "Vote results" %}:</h4>
|
||||
{% with motion.polls as polls %}
|
||||
{% if not polls.exists %}
|
||||
{% if perms.motion.can_manage_motion %}
|
||||
{% if "genpoll" in actions %}
|
||||
<a href="{% url 'motion_gen_poll' motion.id %}">
|
||||
<span class="button">
|
||||
<span class="icon statistics">{% trans 'New vote' %}</span>
|
||||
</span>
|
||||
</a>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<ul class="results">
|
||||
{% for poll in polls %}
|
||||
{% if perms.motion.can_manage_motion or poll.has_votes %}
|
||||
<li>
|
||||
{% if perms.motion.can_manage_motion %}
|
||||
<strong>{{ forloop.counter }}. {% trans "Vote" %} </strong>
|
||||
<a class="icon edit" href="{% url 'motion_poll_view' poll.id %}" title="{% trans 'Edit Vote' %}">
|
||||
<span></span>
|
||||
</a>
|
||||
<a class="icon delete" href="{% url 'motion_poll_delete' poll.id %}" title="{% trans 'Delete Vote' %}">
|
||||
<span></span>
|
||||
</a>
|
||||
{% elif poll.has_votes %}
|
||||
<strong>{{ forloop.counter }}. {% trans "Vote" %}:</strong>
|
||||
{% endif %}
|
||||
<br>
|
||||
{% if poll.has_votes %}
|
||||
{% with poll.get_options.0 as option %}
|
||||
<img src="{% static 'images/icons/voting-yes.png' %}" title="{% trans 'Yes' %}"> {{ option.Yes }}<br>
|
||||
<img src="{% static 'images/icons/voting-no.png' %}" title="{% trans 'No' %}"> {{ option.No }}<br>
|
||||
<img src="{% static 'images/icons/voting-abstention.png' %}" title="{% trans 'Abstention' %}"> {{ option.Abstain }}<br>
|
||||
<img src="{% static 'images/icons/voting-invalid.png' %}" title="{% trans 'Invalid' %}"> {{ poll.print_votesinvalid }}<br>
|
||||
<div style="border-top: 1px solid; padding-top: 5px; margin: 5px 0; width: 10em;">
|
||||
<img src="{% static 'images/icons/voting-total.png' %}" title="{% trans 'Votes cast' %}"> {{ poll.print_votescast }}
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% if perms.motion.can_manage_motion %}
|
||||
{% if forloop.last %}
|
||||
{% if "genpoll" in actions %}
|
||||
<a href="{% url 'motion_gen_poll' motion.id %}">
|
||||
<span class="button"><span class="icon statistics">{% trans 'New vote' %}</span></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if perms.motion.can_manage_motion %}
|
||||
<a href="{% url 'motion_poll_view' poll.id %}">
|
||||
<span class="button"><span class="icon statistics">{% trans 'Enter result' %}</span></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endwith %}
|
||||
|
||||
<h4>{% trans "Creation Time" %}:</h4>
|
||||
{{ motion.creation_time }}
|
||||
|
||||
<p></p>
|
||||
{% if "wit" in actions and user == motion.submitter.user %}
|
||||
<p></p>
|
||||
<a href="{% url 'motion_set_status' motion.id 'wit' %}">
|
||||
<span class="button"><span class="icon revert">{% trans 'Withdraw' %}</span></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.motion.can_support_motion and min_supporters > 0 %}
|
||||
{% if "unsupport" in actions %}
|
||||
<p></p>
|
||||
<a href="{% url 'motion_unsupport' motion.id %}">
|
||||
<span class="button"><span class="icon remove">{% trans 'Unsupport' %}</span></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if "support" in actions %}
|
||||
<p></p>
|
||||
<a href="{% url 'motion_support' motion.id %}">
|
||||
<span class="button"><span class="icon add">{% trans 'Support' %}</span></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if min_supporters > 0 %}
|
||||
<small>* {% trans "minimum required supporters" %}: {{ min_supporters }}</small>
|
||||
{% endif %}
|
||||
|
||||
<br><br>
|
||||
|
||||
{% if perms.motion.can_manage_motion %}
|
||||
<div class="box">
|
||||
<h4><b>{% trans "Manage motion" %}</b></h4>
|
||||
|
||||
{% if "pub" in actions or "per" in actions or "nop" in actions or "setnumber" in actions %}
|
||||
<h4>{% trans "Formal validation" %}:</h4>
|
||||
{% if "pub" in actions %}
|
||||
<a href="{% url 'motion_set_status' motion.id 'pub' %}"><span class="button"><span class="icon ok-blue">{% trans 'Publish' %}</span></span></a>
|
||||
{% endif %}
|
||||
{% if "per" in actions %}
|
||||
<a href="{% url 'motion_permit' motion.id %}"><span class="button"><span class="icon ok-blue">{% trans 'Permit' %}</span></span></a>
|
||||
{% endif %}
|
||||
{% if "nop" in actions %}
|
||||
<a href="{% url 'motion_notpermit' motion.id %}"><span class="button"><span class="icon reject">{% trans 'Not permit (reject)' %}</span></span></a>
|
||||
{% endif %}
|
||||
{% if "setnumber" in actions %}
|
||||
<a href="{% url 'motion_set_number' motion.id %}"><span class="button"><span class="icon number">{% trans 'Set number' %}</span></span></a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<h4></h4>
|
||||
{% if "acc" in actions or "rej" in actions %}
|
||||
<h4>{% trans "Result after vote" %}:</h4>
|
||||
{% if "acc" in actions %}
|
||||
<a href="{% url 'motion_set_status' motion.id 'acc' %}">
|
||||
<span class="button"><span class="icon done">{% trans 'Accepted' %}</span></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if "rej" in actions %}
|
||||
<a href="{% url 'motion_set_status' motion.id 'rej' %}">
|
||||
<span class="button"><span class="icon reject">{% trans 'Rejected' %}</span></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if "adj" in actions or "noc" in actions or "com" in actions or "wit" in actions %}
|
||||
<h4>{% trans 'Result after debate' %}:</h4>
|
||||
{% if "adj" in actions %}
|
||||
<a href="{% url 'motion_set_status' motion.id 'adj' %}"><span class="button">{% trans 'Adjourned' %}</span></a><br>
|
||||
{% endif %}
|
||||
{% if "noc" in actions %}
|
||||
<a href="{% url 'motion_set_status' motion.id 'noc' %}'><span class="button">{% trans 'Not Concerned' %}</span></a><br>
|
||||
{% endif %}
|
||||
{% if "com" in actions %}
|
||||
<a href="{% url 'motion_set_status' motion.id 'com' %}'><span class="button">{% trans 'Commited a bill' %}</span></a><br>
|
||||
{% endif %}
|
||||
{% if "wit" in actions %}
|
||||
<a href="{% url 'motion_set_status' motion.id 'wit' %}'><span class="button">{% trans 'Withdrawed by submitter' %}</span></a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<p></p>
|
||||
<hr>
|
||||
<h4>{% trans "For Administration only:" %}</h4>
|
||||
<a href="{% url 'motion_reset' motion.id %}">
|
||||
<span class="button"><span class="icon undo">{% trans 'Reset' %}</span></span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %} {# end perms.motion.can_support_motion #}
|
||||
</div> <!-- end sidebar -->
|
||||
|
||||
|
||||
<div id="main">
|
||||
<h1>
|
||||
{{ version.title }}
|
||||
({% trans "Motion" %}
|
||||
{% if motion.number != None %}
|
||||
{{ motion.number }})
|
||||
{% else %}
|
||||
<i>[{% trans "no number" %}]</i>)
|
||||
{% endif %}
|
||||
</h1>
|
||||
|
||||
{% trans "Version" %} {{ version.aid }}
|
||||
|
||||
{% if motion.public_version != motion.last_version %}
|
||||
⋅
|
||||
{% if version == motion.public_version %}
|
||||
{% trans "This is not the newest version." %} <a href="{% url 'motion_view_newest' motion.id %}">{% trans "Go to version" %} {{ motion.last_version.aid }}.</a>
|
||||
{% else %}
|
||||
{% trans "This is not the authorized version." %} <a href="{% url 'motion_view' motion.id %}">{% trans "Go to version" %} {{ motion.public_version.aid }}.</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<h2>{% trans "Motion" %}:</h2>
|
||||
|
||||
{{ version.text|linebreaks }}
|
||||
|
||||
<h2>{% trans "Reason" %}:</h2>
|
||||
|
||||
{% if version.reason %}
|
||||
{{ version.reason|linebreaks }}
|
||||
{% else %}
|
||||
–
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if motion.versions|length > 1 %}
|
||||
<h2>{% trans "Version History" %}:</h2>
|
||||
|
||||
<table class="table valigntop" style="width: auto;">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{% trans "Version" %}</th>
|
||||
<th>{% trans "Time" %}</th>
|
||||
<th>{% trans "Title" %}</th>
|
||||
<th>{% trans "Text" %}</th>
|
||||
|
||||
<th>{% trans "Reason" %}</th>
|
||||
</tr>
|
||||
{% for revision in motion.versions %}
|
||||
<tr class="{% cycle 'odd' '' %}">
|
||||
<td style="white-space:nowrap;">
|
||||
{% if motion.status != "pub" %}
|
||||
{% if revision == motion.permitted %}
|
||||
<img title="{% trans 'Version authorized' %}" src="{% static 'images/icons/accept.png' %}">
|
||||
{% else %}
|
||||
{% if perms.motion.can_manage_motion %}
|
||||
<a href="{% url 'motion_version_permit' revision.id %}"><img title="{% trans 'Permit Version' %}" src="{% static 'images/icons/accept-grey.png' %}"></a>
|
||||
{% endif %}
|
||||
{% if not revision.rejected and revision.id > motion.permitted.id and perms.motion.can_manage_motion %}
|
||||
<a href="{% url 'motion_version_reject' revision.id %}"><img title="{% trans 'Reject Version' %}" src="{% static 'images/icons/reject-grey.png' %}"></a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if revision.rejected %}
|
||||
<img title="{% trans 'Version rejected' %}" src="{% static 'images/icons/reject.png' %}">
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ revision.aid }}</td>
|
||||
<td><i>{{ revision.time }}</i></td>
|
||||
<td>
|
||||
{% ifchanged %}
|
||||
<b>{{ revision.title }}</b>
|
||||
{% else %}
|
||||
<i>[{% trans "unchanged" %}]</i>
|
||||
{% endifchanged %}
|
||||
</td>
|
||||
<td>
|
||||
{% ifchanged %}
|
||||
{{ revision.text|linebreaks }}
|
||||
{% else %}
|
||||
<i>[{% trans "unchanged" %}]</i>
|
||||
{% endifchanged %}
|
||||
</td>
|
||||
<td>
|
||||
{% ifchanged %}
|
||||
{{ revision.reason|linebreaks }}
|
||||
{% else %}
|
||||
<i>[{% trans "unchanged" %}]</i>
|
||||
{% endifchanged %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.motion.can_manage_motion %}
|
||||
<h2>{% trans "Log" %}:</h2>
|
||||
{{ motion.log|linebreaks }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,34 +0,0 @@
|
||||
{% load staticfiles %}
|
||||
{% load i18n %}
|
||||
{% load tags %}
|
||||
|
||||
<ul style="line-height: 180%">
|
||||
{% for motion in motions %}
|
||||
<li class="{% if motion.active %}activeline{% endif %}">
|
||||
<a href="{% url 'projector_activate_slide' motion.sid %}" class="activate_link {% if motion.active %}active{% endif %}">
|
||||
<div></div>
|
||||
</a>
|
||||
<a href="{% model_url motion 'delete' %}" title="{% trans 'Delete' %}" class="icon delete right">
|
||||
<span></span>
|
||||
</a>
|
||||
<a href="{% model_url motion 'edit' %}" title="{% trans 'Edit' %}" class="icon edit right">
|
||||
<span></span>
|
||||
</a>
|
||||
<a href="{% url 'projctor_preview_slide' motion.sid %}" title="{% trans 'Preview' %}" class="icon preview right">
|
||||
<span></span>
|
||||
</a>
|
||||
<a href="{% model_url motion 'view' %}">
|
||||
{{ motion.public_version.title }}
|
||||
</a>
|
||||
({% trans "motion" %}
|
||||
{% if motion.number %}
|
||||
{{ motion.number }})
|
||||
{% else %}
|
||||
<i>[{% trans "no number" %}]</i>)
|
||||
{% endif %}
|
||||
</li>
|
||||
{% empty %}
|
||||
<li>{% trans 'No motion available.' %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
@ -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<motion_id>\d+)/$',
|
||||
'view',
|
||||
name='motion_view',
|
||||
),
|
||||
|
||||
url(r'^(?P<motion_id>\d+)/agenda/$',
|
||||
CreateAgendaItem.as_view(),
|
||||
name='motion_create_agenda',
|
||||
),
|
||||
|
||||
url(r'^(?P<motion_id>\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<pk>\d+)/$',
|
||||
'motion_detail',
|
||||
name='motion_detail',
|
||||
),
|
||||
|
||||
url(r'^(?P<motion_id>\d+)/edit/$',
|
||||
'edit',
|
||||
url(r'^(?P<pk>\d+)/edit/$',
|
||||
'motion_edit',
|
||||
name='motion_edit',
|
||||
),
|
||||
|
||||
url(r'^(?P<motion_id>\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<motion_id>\d+)/setnumber/$',
|
||||
'set_number',
|
||||
name='motion_set_number',
|
||||
),
|
||||
|
||||
url(r'^(?P<motion_id>\d+)/setstatus/(?P<status>[a-z]{3})/$',
|
||||
'set_status',
|
||||
name='motion_set_status',
|
||||
),
|
||||
|
||||
url(r'^(?P<motion_id>\d+)/permit/$',
|
||||
'permit',
|
||||
name='motion_permit',
|
||||
),
|
||||
|
||||
url(r'^version/(?P<aversion_id>\d+)/permit/$',
|
||||
'permit_version',
|
||||
name='motion_version_permit',
|
||||
),
|
||||
|
||||
url(r'^version/(?P<aversion_id>\d+)/reject/$',
|
||||
'reject_version',
|
||||
name='motion_version_reject',
|
||||
),
|
||||
|
||||
url(r'^(?P<motion_id>\d+)/notpermit/$',
|
||||
'notpermit',
|
||||
name='motion_notpermit',
|
||||
),
|
||||
|
||||
url(r'^(?P<motion_id>\d+)/reset/$',
|
||||
'reset',
|
||||
name='motion_reset',
|
||||
),
|
||||
|
||||
url(r'^(?P<motion_id>\d+)/support/$',
|
||||
SupportView.as_view(support=True),
|
||||
name='motion_support',
|
||||
),
|
||||
|
||||
url(r'^(?P<motion_id>\d+)/unsupport/$',
|
||||
SupportView.as_view(support=False),
|
||||
name='motion_unsupport',
|
||||
),
|
||||
|
||||
url(r'^(?P<motion_id>\d+)/gen_poll/$',
|
||||
'gen_poll',
|
||||
name='motion_gen_poll',
|
||||
),
|
||||
|
||||
url(r'^print/$',
|
||||
MotionPDF.as_view(),
|
||||
{'motion_id': None},
|
||||
name='print_motions',
|
||||
),
|
||||
|
||||
url(r'^(?P<motion_id>\d+)/print/$',
|
||||
MotionPDF.as_view(),
|
||||
name='print_motion',
|
||||
),
|
||||
|
||||
url(r'^poll/(?P<poll_id>\d+)/print/$',
|
||||
MotionPollPDF.as_view(),
|
||||
name='print_motion_poll',
|
||||
),
|
||||
|
||||
url(r'^poll/(?P<poll_id>\d+)/$',
|
||||
ViewPoll.as_view(),
|
||||
name='motion_poll_view',
|
||||
),
|
||||
|
||||
url(r'^poll/(?P<poll_id>\d+)/del/$',
|
||||
'delete_poll',
|
||||
name='motion_poll_delete',
|
||||
url(r'^(?P<pk>\d+)/version/(?P<version_id>\d+)/$',
|
||||
'motion_detail',
|
||||
name='motion_version_detail',
|
||||
),
|
||||
)
|
||||
|
@ -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 <b>not</b> 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 <b>%s</b> supporters will be removed! Try to convince the supporters again.") % motion.count_supporters() )
|
||||
initial = {'title': motion.title,
|
||||
'text': motion.text,
|
||||
'reason': motion.reason}
|
||||
|
||||
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: <b>%s</b>.") % 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 <b>%s</b>.") % 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 <b>%s</b> 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 <b>%s</b>.") % self.object)
|
||||
elif self.get_answer() == 'yes':
|
||||
title = self.object.title
|
||||
self.object.delete(force=True)
|
||||
messages.success(request, _("Motion <b>%s</b> was successfully deleted.") % title)
|
||||
else:
|
||||
messages.error(request, _("Invalid request"))
|
||||
|
||||
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 <b>%s</b> accepted.") % (aversion.aid))
|
||||
else:
|
||||
gen_confirm_form(request, _('Do you really want to authorize version <b>%s</b>?') % aversion.aid, reverse('motion_version_permit', args=[aversion.id]))
|
||||
return redirect(reverse('motion_view', args=[motion.id]))
|
||||
|
||||
|
||||
@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 <b>%s</b> rejected.") % (aversion.aid))
|
||||
else:
|
||||
messages.error(request, _("ERROR by rejecting the version.") )
|
||||
else:
|
||||
gen_confirm_form(request, _('Do you really want to reject version <b>%s</b>?') % aversion.aid, reverse('motion_version_reject', args=[aversion.id]))
|
||||
return redirect(reverse('motion_view', args=[motion.id]))
|
||||
|
||||
|
||||
@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','<br/>'), 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("<font name='Ubuntu-Bold'>%s:</font>" % _("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("<font name='Ubuntu-Bold'>%s:</font>" % _("Signature"), stylesheet['Heading4']))
|
||||
cell2b.append(Paragraph("__________________________________________", stylesheet['Signaturefield']))
|
||||
cell2b.append(Spacer(0, 0.1 * cm))
|
||||
cell2b.append(Spacer(0,0.2*cm))
|
||||
data.append([cell2a, cell2b])
|
||||
|
||||
# supporters
|
||||
if config['motion_min_supporters']:
|
||||
cell3a = []
|
||||
cell3b = []
|
||||
cell3a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font><seqreset id='counter'>" % _("Supporters"), stylesheet['Heading4']))
|
||||
for supporter in motion.supporters:
|
||||
cell3b.append(Paragraph("<seq id='counter'/>. %s" % supporter, stylesheet['Signaturefield']))
|
||||
if motion.status == "pub":
|
||||
for x in range(motion.missing_supporters):
|
||||
cell3b.append(Paragraph("<seq id='counter'/>. __________________________________________",stylesheet['Signaturefield']))
|
||||
cell3b.append(Spacer(0, 0.2 * cm))
|
||||
data.append([cell3a, cell3b])
|
||||
|
||||
# status
|
||||
cell4a = []
|
||||
cell4b = []
|
||||
note = " ".join(motion.notes)
|
||||
cell4a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font>" % _("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("<font name='Ubuntu-Bold'>%s:</font>" % _("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("<font name='Ubuntu-Bold'>%s:</font>" % _("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 <br/> %s: %s <br/> %s: %s <br/> %s: %s <br/> %s: %s" % (_("Yes"), result[0], _("No"), result[1], _("Abstention"), result[2], _("Invalid"), result[3], _("Votes cast"), result[4]), stylesheet['Normal']))
|
||||
cell6b.append(Spacer(0, 0.2*cm))
|
||||
data.append([cell6a, cell6b])
|
||||
|
||||
# Creating Table
|
||||
t = Table(data)
|
||||
t._argW[0] = 4.5 * cm
|
||||
t._argW[1] = 11 * cm
|
||||
t.setStyle(TableStyle([('BOX', (0, 0), (-1, -1), 1, colors.black),
|
||||
('VALIGN', (0,0), (-1,-1), 'TOP')]))
|
||||
story.append(t)
|
||||
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','<br/>'), 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','<br/>'), 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 = "<img src='%s' width='15' height='15'/> " % 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()
|
||||
|
@ -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):
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user