Merge remote branch 'upstream/master'

This commit is contained in:
Emanuel Schuetze 2012-10-30 20:03:17 +01:00
commit 0c797aa476
44 changed files with 2009 additions and 1929 deletions

View File

@ -11,14 +11,14 @@
"item"
],
[
"can_create_application",
"application",
"application"
"can_create_motion",
"motion",
"motion"
],
[
"can_see_application",
"application",
"application"
"can_see_motion",
"motion",
"motion"
],
[
"can_nominate_other",
@ -44,6 +44,11 @@
"can_see_projector",
"projector",
"projectorslide"
],
[
"can_see_dashboard",
"projector",
"projectorslide"
]
]
}
@ -60,19 +65,19 @@
"item"
],
[
"can_create_application",
"application",
"application"
"can_create_motion",
"motion",
"motion"
],
[
"can_see_application",
"application",
"application"
"can_see_motion",
"motion",
"motion"
],
[
"can_support_application",
"application",
"application"
"can_support_motion",
"motion",
"motion"
],
[
"can_nominate_other",
@ -98,6 +103,11 @@
"can_see_projector",
"projector",
"projectorslide"
],
[
"can_see_dashboard",
"projector",
"projectorslide"
]
]
}
@ -119,19 +129,19 @@
"item"
],
[
"can_create_application",
"application",
"application"
"can_create_motion",
"motion",
"motion"
],
[
"can_manage_application",
"application",
"application"
"can_manage_motion",
"motion",
"motion"
],
[
"can_see_application",
"application",
"application"
"can_see_motion",
"motion",
"motion"
],
[
"can_manage_assignment",
@ -177,6 +187,11 @@
"can_see_projector",
"projector",
"projectorslide"
],
[
"can_see_dashboard",
"projector",
"projectorslide"
]
]
}
@ -206,6 +221,11 @@
"can_see_projector",
"projector",
"projectorslide"
],
[
"can_see_dashboard",
"projector",
"projectorslide"
]
]
}

View File

@ -5,11 +5,14 @@
:license: GNU GPL, see LICENSE for more details.
"""
VERSION = (1, 2, 0, 'final', 1)
VERSION = (1, 3, 0, 'alpha', 1)
def get_version(version=None):
"""Derives a PEP386-compliant version number from VERSION."""
# TODO: Get the Version Hash from GIT.
"""
Derives a PEP386-compliant version number from VERSION. Adds id of
the current git commit.
"""
if version is None:
version = VERSION
assert len(version) == 5
@ -17,67 +20,24 @@ def get_version(version=None):
# Now build the two parts of the version number:
# main = X.Y[.Z]
# sub = .devN - for pre-alpha releases
# | {a|b|c}N - for alpha, beta and rc releases
# sub = {a|b|c}N for alpha, beta and rc releases
# git's commit id is added
parts = 2 if version[2] == 0 else 3
main = '.'.join(str(x) for x in version[:parts])
main_parts = 2 if version[2] == 0 else 3
main = '.'.join(str(x) for x in version[:main_parts])
sub = ''
if version[3] == 'alpha' and version[4] == 0:
mercurial_version = hg_version()
if mercurial_version != 'unknown':
sub = '.dev%s' % mercurial_version
if version[3] != 'final':
mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'}
sub = mapping[version[3]] + str(version[4])
try:
git_head_path = '.git/' + open('.git/HEAD', 'r').read()[5:].rstrip()
except IOError:
git_commit_id = 'unknown'
else:
sub = '.dev'
elif version[3] != 'final':
sub = "-" + version[3] + str(version[4])
import os
git_commit_id = open(os.path.abspath(git_head_path), 'r').read().rstrip()
sub = '%s commit %s' % (sub, git_commit_id)
else:
sub = ''
return main + sub
def hg_version():
import socket
import os
import sys
from os.path import realpath, join, dirname
try:
from mercurial import ui as hgui
from mercurial.localrepo import localrepository
from mercurial.node import short as shorthex
from mercurial.error import RepoError
nomercurial = False
except ImportError:
return 'unknown'
os.environ['HGRCPATH'] = ''
conts = realpath(join(dirname(__file__)))
try:
ui = hgui.ui()
repository = localrepository(ui, join(conts, '..'))
ctx = repository['.']
if ctx.tags() and ctx.tags() != ['tip']:
version = ' '.join(ctx.tags())
else:
version = '%(num)s:%(id)s' % {
'num': ctx.rev(), 'id': shorthex(ctx.node())
}
except TypeError:
version = 'unknown'
except RepoError:
return 0
# This value defines the timeout for sockets in seconds. Per default python
# sockets do never timeout and as such we have blocking workers.
# Socket timeouts are set globally within the whole application.
# The value *must* be a floating point value.
socket.setdefaulttimeout(10.0)
return version
## import os, site
##
## SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
## site.addsitedir(SITE_ROOT)

View File

@ -177,12 +177,12 @@ class ItemDelete(DeleteView):
if self.get_answer() == 'all':
self.object.delete(with_children=True)
messages.success(request,
_("Item %s and his children were successfully deleted.") \
_("Item %s and his children were successfully deleted.")
% html_strong(self.object))
elif self.get_answer() == 'yes':
self.object.delete(with_children=False)
messages.success(request,
_("Item %s was successfully deleted.") \
_("Item %s was successfully deleted.")
% html_strong(self.object))

View File

@ -1,66 +0,0 @@
{% extends "base.html" %}
{% load tags %}
{% load i18n %}
{% load staticfiles %}
{% block submenu %}
{% url application_overview as url_applicationoverview %}
<h4>{% trans "Motions" %}</h4>
<ul>
<li class="{% if request.path == url_applicationoverview %}selected{% endif %}"><a href="{% url application_overview %}">{% trans "All motions" %}</a></li>
{% if perms.application.can_create_application or perms.application.can_manage_application %}
<li class="{% active request '/application/new' %}"><a href="{% url application_new %}">{% trans "New motion" %}</a></li>
{% endif %}
{% if perms.application.can_manage_application %}
<li class="{% active request '/application/import' %}"><a href="{% url application_import %}">{% trans 'Import motions' %}</a></li>
{% endif %}
<li><a href="{% url print_applications %}"><img src="{% static 'images/icons/pdf.png' %}"> {% trans 'All motions as PDF' %}</a></li>
</ul>
{# second submenu #}
{% if application %}
<br>
<h3>{% trans "Application No." %}
{% if application.number != None %}
{{ application.number }}
{% else %}
<i>[-]</i>
{% endif %}
</h3>
<ul>
{# view application #}
{% url application_view application.id as url_applicationview %}
<li class="{% if request.path == url_applicationview %}selected{% endif %}"><a href="{% url application_view application.id %}">{% trans 'View motion' %}</a></li>
{# edit application #}
{% if "edit" in actions %}
{% url application_edit application.id as url_applicationedit %}
<li class="{% if request.path == url_applicationedit %}selected{% endif %}"><a href="{% url application_edit application.id %}"><img src="{% static 'images/icons/edit.png' %}"> {% trans 'Edit motion' %}</a></li>
{% endif %}
{# delete application #}
{% if "delete" in actions %}
<li><a href="{% url application_delete application.id %}"><img src="{% static 'images/icons/delete.png' %}"> {% trans 'Delete motion' %}</a></li>
{% endif %}
{# PDF #}
<li><a href="{% url print_application application.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 application.sid %}"><img src="{% static 'images/icons/projector.png' %}"> {% trans 'Show Application' %}</a>
</li>
{% endif %}
{% if perms.application.can_manage_application %}
{% for poll in application.polls %}
{% url application_poll_view poll.id as url_applicationpollview %}
<li class="{% if request.path == url_applicationpollview %}selected{% endif %}"><a href="{% url application_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 application_create_agenda application.id %}">{% trans 'New agenda item' %}</a>
</li>
{% endif %}
</ul>
{% endif %}
{% endblock %}

View File

@ -1,34 +0,0 @@
{% load staticfiles %}
{% load i18n %}
{% load tags %}
<ul style="line-height: 180%">
{% for application in applications %}
<li class="{% if application.active %}activeline{% endif %}">
<a href="{% url projector_activate_slide application.sid %}" class="activate_link {% if application.active %}active{% endif %}">
<div></div>
</a>
<a href="{% model_url application 'delete' %}" title="{% trans 'Delete' %}" class="icon delete right">
<span></span>
</a>
<a href="{% model_url application 'edit' %}" title="{% trans 'Edit' %}" class="icon edit right">
<span></span>
</a>
<a href="{% url projctor_preview_slide application.sid %}" title="{% trans 'Preview' %}" class="icon preview right">
<span></span>
</a>
<a href="{% model_url application 'view' %}">
{{ application.public_version.title }}
</a>
({% trans "motion" %}
{% if application.number %}
{{ application.number }})
{% else %}
<i>[{% trans "no number" %}]</i>)
{% endif %}
</li>
{% empty %}
<li>{% trans 'No motion available.' %}</li>
{% endfor %}
</ul>

View File

@ -1,141 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.application.urls
~~~~~~~~~~~~~~~~~~~~~~~~~~~
URL list for the application app.
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.conf.urls.defaults import url, patterns
from openslides.application.views import (ApplicationDelete, ViewPoll,
ApplicationPDF, ApplicationPollPDF, CreateAgendaItem)
urlpatterns = patterns('openslides.application.views',
url(r'^$',
'overview',
name='application_overview',
),
url(r'^(?P<application_id>\d+)/$',
'view',
name='application_view',
),
url(r'^(?P<application_id>\d+)/agenda/$',
CreateAgendaItem.as_view(),
name='application_create_agenda',
),
url(r'^(?P<application_id>\d+)/newest/$',
'view',
{'newest': True},
name='application_view_newest',
),
url(r'^new/$',
'edit',
name='application_new',
),
url(r'^import/$',
'application_import',
name='application_import',
),
url(r'^(?P<application_id>\d+)/edit/$',
'edit',
name='application_edit',
),
url(r'^(?P<application_id>\d+)/del/$',
ApplicationDelete.as_view(),
name='application_delete',
),
url(r'^del/$',
ApplicationDelete.as_view(),
{ 'application_id' : None , 'application_ids' : None },
name='application_delete',
),
url(r'^(?P<application_id>\d+)/setnumber/$',
'set_number',
name='application_set_number',
),
url(r'^(?P<application_id>\d+)/setstatus/(?P<status>[a-z]{3})/$',
'set_status',
name='application_set_status',
),
url(r'^(?P<application_id>\d+)/permit/$',
'permit',
name='application_permit',
),
url(r'^version/(?P<aversion_id>\d+)/permit/$',
'permit_version',
name='application_version_permit',
),
url(r'^version/(?P<aversion_id>\d+)/reject/$',
'reject_version',
name='application_version_reject',
),
url(r'^(?P<application_id>\d+)/notpermit/$',
'notpermit',
name='application_notpermit',
),
url(r'^(?P<application_id>\d+)/reset/$',
'reset',
name='application_reset',
),
url(r'^(?P<application_id>\d+)/support/$',
'support',
name='application_support',
),
url(r'^(?P<application_id>\d+)/unsupport/$',
'unsupport',
name='application_unsupport',
),
url(r'^(?P<application_id>\d+)/gen_poll/$',
'gen_poll',
name='application_gen_poll',
),
url(r'^print/$',
ApplicationPDF.as_view(),
{'application_id': None},
name='print_applications',
),
url(r'^(?P<application_id>\d+)/print/$',
ApplicationPDF.as_view(),
name='print_application',
),
url(r'^poll/(?P<poll_id>\d+)/print/$',
ApplicationPollPDF.as_view(),
name='print_application_poll',
),
url(r'^poll/(?P<poll_id>\d+)/$',
ViewPoll.as_view(),
name='application_poll_view',
),
url(r'^poll/(?P<poll_id>\d+)/del/$',
'delete_poll',
name='application_poll_delete',
),
)

View File

@ -218,7 +218,7 @@ class Assignment(models.Model, SlideMixin):
return self.name
def delete(self):
# Remove any Agenda-Item, which is related to this application.
# Remove any Agenda-Item, which is related to this assignment.
for item in Item.objects.filter(related_sid=self.sid):
item.delete()
super(Assignment, self).delete()

View File

@ -31,7 +31,7 @@
<div style="margin-right: 250px; min-width: 400px;">
<h1>{{ assignment }}</h1>
<p>{{ assignment.description }}</p>
<p>{{ assignment.description|linebreaks }}</p>
<h3>{% trans "Candidates" %}</h3>
<ol>

View File

@ -430,11 +430,11 @@ class AssignmentPDF(PDFView):
for candidate, poll_list in vote_results.iteritems():
row = []
candidate_string = candidate.user.get_full_name()
candidate_string = candidate.clean_name
if candidate in elected_candidates:
candidate_string = "* " + candidate_string
if candidate.category:
candidate_string += "\n(%s)" % candidate.category
if candidate.name_suffix:
candidate_string += "\n(%s)" % candidate.name_suffix
row.append(candidate_string)
for vote in poll_list:
if vote == None:
@ -565,7 +565,7 @@ class AssignmentPollPDF(PDFView):
if self.poll.yesnoabstain:
for option in options:
candidate = option.candidate
cell.append(Paragraph(candidate.user.get_full_name(),
cell.append(Paragraph(candidate.clean_name,
stylesheet['Ballot_option_name']))
if candidate.name_suffix:
cell.append(Paragraph("(%s)" % candidate.name_suffix,
@ -591,10 +591,10 @@ class AssignmentPollPDF(PDFView):
else:
for option in options:
candidate = option.candidate
cell.append(Paragraph(circle + candidate.user.get_full_name(),
cell.append(Paragraph(circle + candidate.clean_name,
stylesheet['Ballot_option_name']))
if candidate.category:
cell.append(Paragraph("(%s)" % candidate.category,
if candidate.name_suffix:
cell.append(Paragraph("(%s)" % candidate.name_suffix,
stylesheet['Ballot_option_group_right']))
else:
cell.append(Paragraph("&nbsp;",

View File

@ -66,8 +66,9 @@ class GeneralConfig(FormView):
try:
anonymous = Group.objects.get(name='Anonymous')
except Group.DoesNotExist:
default_perms = [u'can_see_agenda', u'can_see_projector',
u'can_see_application', u'can_see_assignment']
default_perms = ['can_see_agenda', 'can_see_projector',
'can_see_motion', 'can_see_assignment',
'can_see_dashboard']
anonymous = Group()
anonymous.name = 'Anonymous'
anonymous.save()

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.application.forms
openslides.motion.forms
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Forms for the application app.
Forms for the motion app.
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
@ -15,36 +15,36 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop
from openslides.utils.forms import CssClassMixin
from openslides.utils.person import PersonFormField, MultiplePersonFormField
from openslides.application.models import Application
from openslides.motion.models import Motion
class ApplicationForm(forms.Form, CssClassMixin):
class MotionForm(forms.Form, CssClassMixin):
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 ApplicationFormTrivialChanges(ApplicationForm):
class MotionFormTrivialChanges(MotionForm):
trivial_change = forms.BooleanField(required=False,
label=_("Trivial change"),
help_text=_("Trivial changes don't create a new version."))
class ApplicationManagerForm(forms.ModelForm, CssClassMixin):
class MotionManagerForm(forms.ModelForm, CssClassMixin):
submitter = PersonFormField()
class Meta:
model = Application
model = Motion
exclude = ('number', 'status', 'permitted', 'log', 'supporter')
class ApplicationManagerFormSupporter(ApplicationManagerForm):
class MotionManagerFormSupporter(MotionManagerForm):
# TODO: Do not show the submitter in the user-list
supporter = MultiplePersonFormField(required=False, label=_("Supporters"))
class ApplicationImportForm(forms.Form, CssClassMixin):
class MotionImportForm(forms.Form, CssClassMixin):
csvfile = forms.FileField(
widget=forms.FileInput(attrs={'size':'50'}),
label=_("CSV File"),
@ -58,7 +58,7 @@ class ApplicationImportForm(forms.Form, CssClassMixin):
class ConfigForm(forms.Form, CssClassMixin):
application_min_supporters = forms.IntegerField(
motion_min_supporters = forms.IntegerField(
widget=forms.TextInput(attrs={'class':'small-input'}),
label=_("Number of (minimum) required supporters for a motion"),
initial=4,
@ -66,12 +66,12 @@ class ConfigForm(forms.Form, CssClassMixin):
max_value=8,
help_text=_("Choose 0 to disable the supporting system"),
)
application_preamble = forms.CharField(
motion_preamble = forms.CharField(
widget=forms.TextInput(),
required=False,
label=_("Motion preamble")
)
application_pdf_ballot_papers_selection = forms.ChoiceField(
motion_pdf_ballot_papers_selection = forms.ChoiceField(
widget=forms.Select(),
required=False,
label=_("Number of ballot papers (selection)"),
@ -81,24 +81,24 @@ class ConfigForm(forms.Form, CssClassMixin):
("CUSTOM_NUMBER", _("Use the following custom number")),
]
)
application_pdf_ballot_papers_number = forms.IntegerField(
motion_pdf_ballot_papers_number = forms.IntegerField(
widget=forms.TextInput(attrs={'class':'small-input'}),
required=False,
min_value=1,
label=_("Custom number of ballot papers")
)
application_pdf_title = forms.CharField(
motion_pdf_title = forms.CharField(
widget=forms.TextInput(),
required=False,
label=_("Title for PDF document (all motions)")
)
application_pdf_preamble = forms.CharField(
motion_pdf_preamble = forms.CharField(
widget=forms.Textarea(),
required=False,
label=_("Preamble text for PDF document (all motions)")
)
application_allow_trivial_change = forms.BooleanField(
motion_allow_trivial_change = forms.BooleanField(
label=_("Allow trivial changes"),
help_text=_('Warning: Trivial changes undermine the motions '
'autorisation system.'),

View File

@ -1,10 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.application.models
openslides.motion.models
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Models for the application app.
Models for the motion app.
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
@ -34,13 +34,13 @@ from openslides.projector.models import SlideMixin
from openslides.agenda.models import Item
class ApplicationSupporter(models.Model):
application = models.ForeignKey("Application")
class MotionSupporter(models.Model):
motion = models.ForeignKey("Motion")
person = PersonField()
class Application(models.Model, SlideMixin):
prefix = "application"
class Motion(models.Model, SlideMixin):
prefix = "motion"
STATUS = (
('pub', _('Published')),
('per', _('Permitted')),
@ -74,10 +74,10 @@ class Application(models.Model, SlideMixin):
@property
def last_version(self):
"""
Return last version of the application.
Return last version of the motion.
"""
try:
return AVersion.objects.filter(application=self).order_by('id') \
return AVersion.objects.filter(motion=self).order_by('id') \
.reverse()[0]
except IndexError:
return None
@ -85,7 +85,7 @@ class Application(models.Model, SlideMixin):
@property
def public_version(self):
"""
Return permitted, if the application was permitted, else last_version
Return permitted, if the motion was permitted, else last_version
"""
if self.permitted is not None:
return self.permitted
@ -115,14 +115,14 @@ class Application(models.Model, SlideMixin):
@property
def versions(self):
"""
Return a list of all versions of the application.
Return a list of all versions of the motion.
"""
return AVersion.objects.filter(application=self)
return AVersion.objects.filter(motion=self)
@property
def creation_time(self):
"""
Return the time of the creation of the application.
Return the time of the creation of the motion.
"""
try:
return self.versions[0].time
@ -132,7 +132,7 @@ class Application(models.Model, SlideMixin):
@property
def notes(self):
"""
Return some information of the application.
Return some information of the motion.
"""
note = []
if self.status == "pub" and not self.enough_supporters:
@ -146,9 +146,9 @@ class Application(models.Model, SlideMixin):
@property
def unpermitted_changes(self):
"""
Return True if the application has unpermitted changes.
Return True if the motion has unpermitted changes.
The application has unpermitted changes, if the permitted-version
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
"""
@ -160,35 +160,35 @@ class Application(models.Model, SlideMixin):
@property
def supporters(self):
for object in self.applicationsupporter_set.all():
for object in self.motionsupporter_set.all():
yield object.person
def is_supporter(self, person):
try:
return self.applicationsupporter_set.filter(person=person).exists()
return self.motionsupporter_set.filter(person=person).exists()
except AttributeError:
return False
@property
def enough_supporters(self):
"""
Return True, if the application has enough supporters
Return True, if the motion has enough supporters
"""
min_supporters = int(config['application_min_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.applicationsupporter_set.count()
return self.motionsupporter_set.count()
@property
def missing_supporters(self):
"""
Return number of missing supporters
"""
min_supporters = int(config['application_min_supporters'])
min_supporters = int(config['motion_min_supporters'])
delta = min_supporters - self.count_supporters()
if delta > 0:
return delta
@ -197,9 +197,9 @@ class Application(models.Model, SlideMixin):
def save(self, user=None, nonewversion=False, trivial_change=False):
"""
Save the Application, and create a new AVersion if necessary
Save the Motion, and create a new AVersion if necessary
"""
super(Application, self).save()
super(Motion, self).save()
if nonewversion:
return
last_version = self.last_version
@ -229,14 +229,14 @@ class Application(models.Model, SlideMixin):
version = AVersion(title=getattr(self, 'title', ''),
text=getattr(self, 'text', ''),
reason=getattr(self, 'reason', ''),
application=self)
motion=self)
version.save()
self.writelog(_("Version %s created") % version.aid, user)
is_manager = user.has_perm('application.can_manage_application')
is_manager = user.has_perm('motion.can_manage_motion')
except AttributeError:
is_manager = False
supporters = self.applicationsupporter_set.all()
supporters = self.motionsupporter_set.all()
if (self.status == "pub"
and supporters
and not is_manager):
@ -245,7 +245,7 @@ class Application(models.Model, SlideMixin):
def reset(self, user):
"""
Reset the application.
Reset the motion.
"""
self.status = "pub"
self.permitted = None
@ -254,43 +254,39 @@ class Application(models.Model, SlideMixin):
def support(self, person):
"""
Add a Supporter to the list of supporters of the application.
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 ' \
'application.')
if self.permitted is not None:
# TODO: Use own Exception
raise NameError('This application is already permitted.')
raise NameError('Supporter can not be the submitter of a '
'motion.')
if not self.is_supporter(person):
ApplicationSupporter(application=self, person=person).save()
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 application
remove a supporter from the list of supporters of the motion
"""
if self.permitted is not None:
# TODO: Use own Exception
raise NameError('This application is already permitted.')
try:
object = self.applicationsupporter_set.get(person=person).delete()
except ApplicationSupporter.DoesNotExist:
object = 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 application.
Set a number for ths motion.
"""
if self.number is not None:
# TODO: Use own Exception
raise NameError('This application has already a number.')
raise NameError('This motion has already a number.')
if number is None:
try:
number = Application.objects.aggregate(Max('number')) \
number = Motion.objects.aggregate(Max('number')) \
['number__max'] + 1
except TypeError:
number = 1
@ -301,7 +297,7 @@ class Application(models.Model, SlideMixin):
def permit(self, user=None):
"""
Change the status of this application to permit.
Change the status of this motion to permit.
"""
self.set_status(user, "per")
aversion = self.last_version
@ -314,7 +310,7 @@ class Application(models.Model, SlideMixin):
def notpermit(self, user=None):
"""
Change the status of this application to 'not permitted (rejected)'.
Change the status of this motion to 'not permitted (rejected)'.
"""
self.set_status(user, "nop")
#TODO: reject last version
@ -327,10 +323,10 @@ class Application(models.Model, SlideMixin):
def set_status(self, user, status, force=False):
"""
Set the status of the application.
Set the status of the motion.
"""
error = True
for a, b in Application.STATUS:
for a, b in Motion.STATUS:
if status == a:
error = False
break
@ -364,25 +360,25 @@ class Application(models.Model, SlideMixin):
"""
actions = []
# check if user allowed to withdraw an application
# 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("application.can_manage_application"))
and user.has_perm("motion.can_manage_motion"))
or (self.status == "per"
and user == self.submitter)
or (self.status == "per"
and user.has_perm("application.can_manage_application"))):
and user.has_perm("motion.can_manage_motion"))):
actions.append("wit")
#Check if the user can review the application
#Check if the user can review the motion
if (self.status == "rev"
and (self.submitter == user
or user.has_perm("application.can_manage_application"))):
or user.has_perm("motion.can_manage_motion"))):
actions.append("pub")
# Check if the user can support and unspoort the application
# Check if the user can support and unspoort the motion
if (self.status == "pub"
and user != self.submitter
and not self.is_supporter(user)):
@ -391,22 +387,22 @@ class Application(models.Model, SlideMixin):
if self.status == "pub" and self.is_supporter(user):
actions.append("unsupport")
#Check if the user can edit the application
#Check if the user can edit the motion
if (user == self.submitter \
and (self.status in ('pub', 'per'))) \
or user.has_perm("application.can_manage_application"):
or user.has_perm("motion.can_manage_motion"):
actions.append("edit")
# Check if the user can delete the application (admin, manager, owner)
# Check if the user can delete the motion (admin, manager, owner)
# reworked as requiered in #100
if (user.has_perm("applicatoin.can_delete_all_applications") or
(user.has_perm("application.can_manage_application") and
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("application.can_manage_application"):
if not user.has_perm("motion.can_manage_motion"):
return actions
if self.status == "pub":
@ -430,17 +426,17 @@ class Application(models.Model, SlideMixin):
def delete(self, force=False):
"""
Delete the application. It is not possible, if the application has
Delete the motion. It is not possible, if the motion has
allready a number
"""
if self.number and not force:
raise NameError('The application has already a number. ' \
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(Application, self).delete()
super(Motion, self).delete()
def writelog(self, text, user=None):
if not self.log:
@ -461,7 +457,7 @@ class Application(models.Model, SlideMixin):
def __getattr__(self, name):
"""
if name is title, text, reason or time,
Return this attribute from the newest version of the application
Return this attribute from the newest version of the motion
"""
if name in ('title', 'text', 'reason', 'time', 'aid'):
try:
@ -476,9 +472,9 @@ class Application(models.Model, SlideMixin):
def gen_poll(self, user=None):
"""
Generates a poll object for the application
Generates a poll object for the motion
"""
poll = ApplicationPoll(application=self)
poll = MotionPoll(motion=self)
poll.save()
poll.set_options()
self.writelog(_("Poll created"), user)
@ -486,7 +482,7 @@ class Application(models.Model, SlideMixin):
@property
def polls(self):
return self.applicationpoll_set.all()
return self.motionpoll_set.all()
@property
def results(self):
@ -510,19 +506,19 @@ class Application(models.Model, SlideMixin):
"""
return the slide dict
"""
data = super(Application, self).slide()
data['application'] = self
data = super(Motion, self).slide()
data['motion'] = self
data['title'] = self.title
data['template'] = 'projector/Application.html'
data['template'] = 'projector/Motion.html'
return data
def get_absolute_url(self, link='view'):
if link == 'view':
return reverse('application_view', args=[str(self.id)])
return reverse('motion_view', args=[str(self.id)])
if link == 'edit':
return reverse('application_edit', args=[str(self.id)])
return reverse('motion_edit', args=[str(self.id)])
if link == 'delete':
return reverse('application_delete', args=[str(self.id)])
return reverse('motion_delete', args=[str(self.id)])
def __unicode__(self):
try:
@ -532,10 +528,10 @@ class Application(models.Model, SlideMixin):
class Meta:
permissions = (
('can_see_application', ugettext_noop("Can see motions")),
('can_create_application', ugettext_noop("Can create motions")),
('can_support_application', ugettext_noop("Can support motions")),
('can_manage_application', ugettext_noop("Can manage motions")),
('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',)
@ -546,7 +542,7 @@ class AVersion(models.Model):
reason = models.TextField(null=True, blank=True, verbose_name = _("Reason"))
rejected = models.BooleanField() # = Not Permitted
time = models.DateTimeField(auto_now=True)
application = models.ForeignKey(Application)
motion = models.ForeignKey(Motion)
def __unicode__(self):
return "%s %s" % (self.id, self.title)
@ -557,31 +553,31 @@ class AVersion(models.Model):
return self._aid
except AttributeError:
self._aid = AVersion.objects \
.filter(application=self.application) \
.filter(motion=self.motion) \
.filter(id__lte=self.id).count()
return self._aid
register_slidemodel(Application)
register_slidemodel(Motion)
class ApplicationVote(BaseVote):
option = models.ForeignKey('ApplicationOption')
class MotionVote(BaseVote):
option = models.ForeignKey('MotionOption')
class ApplicationOption(BaseOption):
poll = models.ForeignKey('ApplicationPoll')
vote_class = ApplicationVote
class MotionOption(BaseOption):
poll = models.ForeignKey('MotionPoll')
vote_class = MotionVote
class ApplicationPoll(BasePoll, CountInvalid, CountVotesCast):
option_class = ApplicationOption
class MotionPoll(BasePoll, CountInvalid, CountVotesCast):
option_class = MotionOption
vote_values = [ugettext_noop('Yes'), ugettext_noop('No'),
ugettext_noop('Abstain')]
application = models.ForeignKey(Application)
motion = models.ForeignKey(Motion)
def get_application(self):
return self.application
def get_motion(self):
return self.motion
def set_options(self):
#TODO: maybe it is possible with .create() to call this without poll=self
@ -592,20 +588,20 @@ class ApplicationPoll(BasePoll, CountInvalid, CountVotesCast):
CountVotesCast.append_pollform_fields(self, fields)
def get_absolute_url(self):
return reverse('application_poll_view', args=[self.id])
return reverse('motion_poll_view', args=[self.id])
def get_ballot(self):
return self.application.applicationpoll_set.filter(id__lte=self.id).count()
return self.motion.motionpoll_set.filter(id__lte=self.id).count()
@receiver(default_config_value, dispatch_uid="application_default_config")
@receiver(default_config_value, dispatch_uid="motion_default_config")
def default_config(sender, key, **kwargs):
return {
'application_min_supporters': 0,
'application_preamble': _('The assembly may decide,'),
'application_pdf_ballot_papers_selection': 'CUSTOM_NUMBER',
'application_pdf_ballot_papers_number': '8',
'application_pdf_title': _('Motions'),
'application_pdf_preamble': '',
'application_allow_trivial_change': False,
'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)

View File

@ -0,0 +1,66 @@
{% 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 %}

View File

@ -2,18 +2,18 @@
{% load i18n %}
{% block title %}{{ block.super }} {% trans "Application settings" %}{% endblock %}
{% block title %}{{ block.super }} {% trans "Motion settings" %}{% endblock %}
{% block content %}
<h1>{% trans "Application settings" %}</h1>
<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_application %}'>
<button class="button" type="button" onclick="window.location='{% url config_application %}'">
<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>

View File

@ -1,10 +1,10 @@
{% extends "application/base_application.html" %}
{% extends "motion/base_motion.html" %}
{% load i18n %}
{% block title %}
{{ block.super }}
{% if application %}
{% if motion %}
{% trans "Edit motion" %}
{% else %}
{% trans "New motion" %}
@ -12,7 +12,7 @@
{% endblock %}
{% block content %}
{% if application %}
{% if motion %}
<h1>{% trans "Edit motion" %}</h1>
{% else %}
<h1>{% trans "New motion" %}</h1>
@ -28,8 +28,8 @@
<button class="button" type="submit" name="apply">
<span class="icon apply">{% trans 'Apply' %}</span>
</button>
<a href='{% url application_overview %}'>
<button class="button" type="button" onclick="window.location='{% url application_overview %}'">
<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>

View File

@ -1,4 +1,4 @@
{% extends "application/base_application.html" %}
{% extends "motion/base_motion.html" %}
{% load i18n %}
@ -22,8 +22,8 @@
<button class="button" type="submit">
<span class="icon import">{% trans 'Import' %}</span>
</button>
<a href="{% url application_overview%}">
<button class="button" type="button" onclick="window.location='{% url application_overview %}'">
<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>

View File

@ -1,4 +1,4 @@
{% extends "application/base_application.html" %}
{% extends "motion/base_motion.html" %}
{% load tags %}
{% load i18n %}
@ -28,8 +28,8 @@
</select>
</form>
</p>
{{ applications|length }}
{% blocktrans count counter=applications|length %}motion{% plural %}motions{% endblocktrans %}
{{ motions|length }}
{% blocktrans count counter=motions|length %}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>
@ -42,39 +42,39 @@
<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 applications %}
{% with application=app_info.application useractions=app_info.actions %}
{% for app_info in motions %}
{% with motion=app_info.motion useractions=app_info.actions %}
<tr class="{% cycle '' 'odd' %}
{% if application.active %}activeline{% endif %}">
<td>{% if application.number %}{{ application.number }}{% else %}-{% endif %}</td>
<td><a href="{% url application_view application.id %}">{{ application.public_version.title }}</a></td>
{% 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>{{ application.count_supporters }}</td>
<td>{{ motion.count_supporters }}</td>
{% endif %}
<td>{% if application.status != "pub" %}
{{ application.get_status_display }}<br>
<td>{% if motion.status != "pub" %}
{{ motion.get_status_display }}<br>
{% endif %}
{% for note in application.notes %}
{% for note in motion.notes %}
{{ note }}
{% if not forloop.last %}<br>{%endif%}
{% endfor %}
</td>
<td>{{ application.submitter }}</td>
<td>{{ application.creation_time }}</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 application.active %}active{% endif %}" href="{% url projector_activate_slide application.sid %}" title="{% trans 'Activate motion' %}">
<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.application.can_manage_application %}
<a href="{% url application_edit application.id %}"><img src="{% static 'images/icons/edit.png' %}" title="{% trans 'Edit motion' %}"></a>
{% 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 application_delete application.id %}"><img src="{% static 'images/icons/delete.png' %}" title="{% trans 'Delete motion' %}"></a>
<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_application application.id %}" title="{% trans 'Motion as PDF' %}"><img src="{% static 'images/icons/pdf.png' %}"></a>
<a href="{% url print_motion motion.id %}" title="{% trans 'Motion as PDF' %}"><img src="{% static 'images/icons/pdf.png' %}"></a>
</span>
</td>
</tr>

View File

@ -1,16 +1,16 @@
{% extends 'application/base_application.html' %}
{% extends 'motion/base_motion.html' %}
{% load i18n %}
{% load staticfiles %}
{% block title %}
{{ block.super }} {% trans "Motion" %} "{{ application.public_version.title }}"
{{ block.super }} {% trans "Motion" %} "{{ motion.public_version.title }}"
{{ ballot }}. {% trans "Vote" %}
{% endblock %}
{% block content %}
<h1>{{ application.public_version.title }} ({% trans "Motion" %}
{{ application.number }}) {{ ballot }}. {% trans "Vote" %}</h1>
<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 }}
@ -40,8 +40,8 @@
{{ post_form }}
<p>
<a href='{% url print_application_poll poll.id %}'>
<button class="button" type="button" onclick="window.location='{% url print_application_poll poll.id %}'">
<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>
@ -54,8 +54,8 @@
<button class="button" type="submit" name="apply">
<span class="icon apply">{% trans 'Apply' %}</span>
</button>
<a href='{% url application_view application.id %}'>
<button class="button" type="button" onclick="window.location='{% url application_view application.id %}'">
<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>

View File

@ -1,4 +1,4 @@
{% extends "application/base_application.html" %}
{% extends "motion/base_motion.html" %}
{% load tags %}
{% load i18n %}
@ -16,15 +16,15 @@
<div id="sidebar">
<div class="box">
<h4>{% trans "Submitter" %}:</h4>
{{ application.submitter }}
{{ motion.submitter }}
{% if min_supporters > 0 %}
<h4>{% trans "Supporters" %}: *</h4>
{% if not application.supporters %}
{% if not motion.supporters %}
-
{% else %}
<ol>
{% for supporter in application.supporters %}
{% for supporter in motion.supporters %}
<li> {{ supporter }}</li>
{% endfor %}
</ol>
@ -32,21 +32,21 @@
{% endif %}
<h4>{% trans "Status" %}:</h4>
{% if application.status != "pub" %}
{% trans application.get_status_display %}
{% if motion.status != "pub" %}
{% trans motion.get_status_display %}
<br>
{% endif %}
{% for note in application.notes %}
{% for note in motion.notes %}
{{ note }}
{% if not forloop.last %}<br>{% endif %}
{% endfor %}
<h4>{% trans "Vote results" %}:</h4>
{% with application.polls as polls %}
{% with motion.polls as polls %}
{% if not polls.exists %}
{% if perms.application.can_manage_application %}
{% if perms.motion.can_manage_motion %}
{% if "genpoll" in actions %}
<a href='{% url application_gen_poll application.id %}'>
<a href='{% url motion_gen_poll motion.id %}'>
<span class="button">
<span class="icon statistics">{% trans 'New vote' %}</span>
</span>
@ -60,14 +60,14 @@
{% endif %}
<ul class="results">
{% for poll in polls %}
{% if perms.application.can_manage_application or poll.has_votes %}
{% if perms.motion.can_manage_motion or poll.has_votes %}
<li>
{% if perms.application.can_manage_application %}
{% if perms.motion.can_manage_motion %}
<strong>{{ forloop.counter }}. {% trans "Vote" %} </strong>
<a class="icon edit" href="{% url application_poll_view poll.id %}" title="{% trans 'Edit Vote' %}">
<a class="icon edit" href="{% url motion_poll_view poll.id %}" title="{% trans 'Edit Vote' %}">
<span></span>
</a>
<a class="icon delete" href="{% url application_poll_delete poll.id %}" title="{% trans 'Delete Vote' %}">
<a class="icon delete" href="{% url motion_poll_delete poll.id %}" title="{% trans 'Delete Vote' %}">
<span></span>
</a>
{% elif poll.has_votes %}
@ -84,18 +84,18 @@
<img src="{% static 'images/icons/voting-total.png' %}" title="{% trans 'Votes cast' %}"> {{ poll.print_votescast }}
</div>
{% endwith %}
{% if perms.application.can_manage_application %}
{% if perms.motion.can_manage_motion %}
{% if forloop.last %}
{% if "genpoll" in actions %}
<a href='{% url application_gen_poll application.id %}'>
<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.application.can_manage_application %}
<a href='{% url application_poll_view poll.id %}'>
{% 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 %}
@ -107,26 +107,26 @@
{% endwith %}
<h4>{% trans "Creation Time" %}:</h4>
{{ application.creation_time }}
{{ motion.creation_time }}
<p></p>
{% if "wit" in actions and user == application.submitter.user %}
{% if "wit" in actions and user == motion.submitter.user %}
<p></p>
<a href='{% url application_set_status application.id 'wit' %}'>
<a href='{% url motion_set_status motion.id 'wit' %}'>
<span class="button"><span class="icon revert">{% trans 'Withdraw' %}</span></span>
</a>
{% endif %}
{% if perms.application.can_support_application and min_supporters > 0 %}
{% if perms.motion.can_support_motion and min_supporters > 0 %}
{% if "unsupport" in actions %}
<p></p>
<a href='{% url application_unsupport application.id %}'>
<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 application_support application.id %}'>
<a href='{% url motion_support motion.id %}'>
<span class="button"><span class="icon add">{% trans 'Support' %}</span></span>
</a>
{% endif %}
@ -138,23 +138,23 @@
<br><br>
{% if perms.application.can_manage_application %}
{% 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 application_set_status application.id 'pub' %}'><span class="button"><span class="icon ok-blue">{% trans 'Publish' %}</span></span></a>
<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 application_permit application.id %}'><span class="button"><span class="icon ok-blue">{% trans 'Permit' %}</span></span></a>
<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 application_notpermit application.id %}'><span class="button"><span class="icon reject">{% trans 'Not permit (reject)' %}</span></span></a>
<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 application_set_number application.id %}'><span class="button"><span class="icon number">{% trans 'Set Number' %}</span></span></a>
<a href='{% url motion_set_number motion.id %}'><span class="button"><span class="icon number">{% trans 'Set Number' %}</span></span></a>
{% endif %}
</p>
{% endif %}
@ -163,12 +163,12 @@
{% if "acc" in actions or "rej" in actions %}
<h4>{% trans "Result after vote" %}:</h4>
{% if "acc" in actions %}
<a href='{% url application_set_status application.id 'acc' %}'>
<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 application_set_status application.id 'rej' %}'>
<a href='{% url motion_set_status motion.id 'rej' %}'>
<span class="button"><span class="icon reject">{% trans 'Rejected' %}</span></span>
</a>
{% endif %}
@ -177,26 +177,26 @@
{% 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 application_set_status application.id 'adj' %}'><span class="button">{% trans 'Adjourned' %}</span></a><br>
<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 application_set_status application.id 'noc' %}'><span class="button">{% trans 'Not Concerned' %}</span></a><br>
<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 application_set_status application.id 'com' %}'><span class="button">{% trans 'Commited a bill' %}</span></a><br>
<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 application_set_status application.id 'wit' %}'><span class="button">{% trans 'Withdrawed by Submitter' %}</span></a>
<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 application_reset application.id %}'>
<a href='{% url motion_reset motion.id %}'>
<span class="button"><span class="icon undo">{% trans 'Reset' %}</span></span>
</a>
</div>
{% endif %} {# end perms.application.can_support_application #}
{% endif %} {# end perms.motion.can_support_motion #}
</div> <!-- end sidebar -->
@ -204,8 +204,8 @@
<h1>
{{ version.title }}
({% trans "Motion" %}
{% if application.number != None %}
{{ application.number }})
{% if motion.number != None %}
{{ motion.number }})
{% else %}
<i>[{% trans "no number" %}]</i>)
{% endif %}
@ -213,12 +213,12 @@
{% trans "Version" %} {{ version.aid }}
{% if application.public_version != application.last_version %}
{% if motion.public_version != motion.last_version %}
&#8901;
{% if version == application.public_version %}
{% trans "This is not the newest version." %} <a href="{% url application_view_newest application.id %}">{% trans "Go to version" %} {{ application.last_version.aid }}.</a>
{% 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 application_view application.id %}">{% trans "Go to version" %} {{ application.public_version.aid }}.</a>
{% 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 %}
@ -235,7 +235,7 @@
{% endif %}
{% if application.versions|length > 1 %}
{% if motion.versions|length > 1 %}
<h2>{% trans "Version History" %}:</h2>
<table class="table valigntop" style="width: auto;">
@ -248,18 +248,18 @@
<th>{% trans "Reason" %}</th>
</tr>
{% for revision in application.versions %}
{% for revision in motion.versions %}
<tr class="{% cycle 'odd' '' %}">
<td style="white-space:nowrap;">
{% if application.status != "pub" %}
{% if revision == application.permitted %}
{% if motion.status != "pub" %}
{% if revision == motion.permitted %}
<img title="{% trans 'Version authorized' %}" src="{% static 'images/icons/accept.png' %}">
{% else %}
{% if perms.application.can_manage_application %}
<a href="{% url application_version_permit revision.id %}"><img title="{% trans 'Permit Version' %}" src="{% static 'images/icons/accept-grey.png' %}"></a>
{% 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 > application.permitted.id and perms.application.can_manage_application %}
<a href="{% url application_version_reject revision.id %}"><img title="{% trans 'Reject Version' %}" src="{% static 'images/icons/reject-grey.png' %}"></a>
{% 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 %}
@ -295,9 +295,9 @@
</table>
{% endif %}
{% if perms.application.can_manage_application %}
{% if perms.motion.can_manage_motion %}
<h2>{% trans "Log" %}:</h2>
{{ application.log|linebreaks }}
{{ motion.log|linebreaks }}
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,34 @@
{% 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>

View File

@ -4,27 +4,27 @@
{% load i18n %}
{% load staticfiles %}
{% block title %}{{ block.super }} - {% trans "Motion" %} {{ application.number }}{% endblock %}
{% block title %}{{ block.super }} - {% trans "Motion" %} {{ motion.number }}{% endblock %}
{% block content %}
<div id="sidebar">
<div class="box">
<p><b>{% trans "Status" %}:</b><br>
{% if application.status != "pub" %}
{% if application.status == "acc" %}
{% if motion.status != "pub" %}
{% if motion.status == "acc" %}
<img src="{% static 'images/icons/voting-yes.png' %}">
{% endif %}
{% if application.status == "rej" %}
{% if motion.status == "rej" %}
<img src="{% static 'images/icons/voting-no.png' %}">
{% endif %}
{% trans application.get_status_display %}
{% trans motion.get_status_display %}
{% else %}
{% for note in application.notes %}
{% for note in motion.notes %}
{{ note }}
{% endfor %}
{% endif %}
</p>
{% with application.polls as polls %}
{% with motion.polls as polls %}
{% if polls.exists and polls.0.has_votes %}
<p><b>{% trans "Poll result" %}:</b>
{% for poll in polls %}
@ -53,29 +53,29 @@
{% endwith %}
<p><b>{% trans "Submitter" %}:</b><br>
{{ application.submitter }}
{{ motion.submitter }}
</p>
</div>
</div>
<h1>
{% if application.number != None %}
{% trans "Motion No." %} {{ application.number }}
{% if motion.number != None %}
{% trans "Motion No." %} {{ motion.number }}
{% else %}
{% trans "Motion" %} <i>[{% trans "no number" %}]</i>
{% endif %}
</h1>
<b>{{ application.public_version.title }}</b>
<b>{{ motion.public_version.title }}</b>
<hr>
{% endblock %}
{% block scrollcontent %}
<p>
<div class="text">{{ application.public_version.text|linebreaks }}</div>
{% if application.public_version.reason %}
<div class="text">{{ motion.public_version.text|linebreaks }}</div>
{% if motion.public_version.reason %}
<br>
<div class="reason"><p><b>{% trans "Reason" %}:</b></p>
{{ application.public_version.reason|linebreaks }}</div>
{{ motion.public_version.reason|linebreaks }}</div>
{% endif %}
</p>
{% endblock %}

View File

@ -1,10 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.application.tests
openslides.motion.tests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unit tests for the application app.
Unit tests for the motion app.
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
@ -14,19 +14,19 @@ from django.test import TestCase
from django.test.client import Client
from openslides.participant.models import User
from openslides.application.models import Application, AVersion
from openslides.motion.models import Motion, AVersion
class ApplicationTest(TestCase):
class MotionTest(TestCase):
def setUp(self):
self.admin = User(username='testadmin')
self.admin.save()
self.anonym = User(username='testanoym')
self.anonym.save()
self.app1 = Application(submitter=self.admin)
self.app1 = Motion(submitter=self.admin)
self.app1.save()
def refresh(self):
self.app1 = Application.objects.get(pk=self.app1.id)
self.app1 = Motion.objects.get(pk=self.app1.id)
def testVersion(self):
self.assertTrue(self.app1.versions.exists())

141
openslides/motion/urls.py Normal file
View File

@ -0,0 +1,141 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.motion.urls
~~~~~~~~~~~~~~~~~~~~~~~~~~~
URL list for the motion app.
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.conf.urls.defaults 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',
),
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',
name='motion_new',
),
url(r'^import/$',
'motion_import',
name='motion_import',
),
url(r'^(?P<motion_id>\d+)/edit/$',
'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',
),
)

View File

@ -119,7 +119,7 @@ INSTALLED_APPS = (
'openslides.poll',
'openslides.projector',
'openslides.agenda',
'openslides.application',
'openslides.motion',
'openslides.assignment',
'openslides.participant',
'openslides.config',
@ -134,3 +134,10 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'openslides.utils.utils.revision',
'openslides.utils.auth.anonymous_context_additions',
)
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'openslidecache'
}
}

View File

@ -47,7 +47,7 @@ def gen_username(first_name, last_name):
i = 0
while True:
i += 1
testname = "%s%s%s" % (first_name, last_name, i)
testname = "%s %s %s" % (first_name, last_name, i)
try:
User.objects.get(username=testname)
except User.DoesNotExist:

View File

@ -27,7 +27,7 @@ class UserCreateForm(forms.ModelForm, CssClassMixin):
class Meta:
model = User
fields = ('first_name', 'last_name', 'is_active', 'groups', 'category',
fields = ('first_name', 'last_name', 'is_active', 'groups', 'detail',
'gender', 'type', 'committee', 'comment', 'default_password')
@ -35,7 +35,7 @@ class UserUpdateForm(UserCreateForm):
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'is_active', 'groups',
'category', 'gender', 'type', 'committee', 'comment',
'detail', 'gender', 'type', 'committee', 'comment',
'default_password')

View File

@ -16,14 +16,14 @@ from django.db.models import signals
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _, ugettext_noop
from openslides.utils.person import PersonMixin
from openslides.utils.person import PersonMixin, Person
from openslides.utils.person.signals import receive_persons
from openslides.config.models import config
from openslides.config.signals import default_config_value
class User(DjangoUser, PersonMixin):
class User(DjangoUser, PersonMixin, Person):
person_prefix = 'user'
GENDER_CHOICES = (
('male', _('Male')),
@ -37,8 +37,8 @@ class User(DjangoUser, PersonMixin):
)
django_user = models.OneToOneField(DjangoUser, editable=False, parent_link=True)
category = models.CharField(
max_length=100, null=True, blank=True, verbose_name=_("Category"),
detail = models.CharField(
max_length=100, blank=True, default='', verbose_name=_("Detail"),
help_text=_('Will be shown behind the name.'))
gender = models.CharField(
max_length=50, choices=GENDER_CHOICES, blank=True,
@ -47,20 +47,24 @@ class User(DjangoUser, PersonMixin):
max_length=100, choices=TYPE_CHOICES, blank=True,
verbose_name=_("Typ"), help_text=_('Only for filter the userlist.'))
committee = models.CharField(
max_length=100, null=True, blank=True, verbose_name=_("Committee"),
max_length=100, blank=True, default='', verbose_name=_("Committee"),
help_text=_('Only for filter the userlist.'))
comment = models.TextField(
null=True, blank=True, verbose_name=_('Comment'),
blank=True, default='', verbose_name=_('Comment'),
help_text=_('Only for notes.'))
default_password = models.CharField(
max_length=100, null=True, blank=True,
max_length=100, blank=True, default='',
verbose_name=_("Default password"))
@property
def clean_name(self):
return self.get_full_name() or self.username
def get_name_suffix(self):
return self.category
return self.detail
def set_name_suffix(self, value):
self.category = value
self.detail = value
name_suffix = property(get_name_suffix, set_name_suffix)
@ -88,10 +92,9 @@ class User(DjangoUser, PersonMixin):
return ('user_delete', [str(self.id)])
def __unicode__(self):
name = self.get_full_name() or self.username
if self.name_suffix:
return u"%s (%s)" % (name, self.name_suffix)
return u"%s" % name
return u"%s (%s)" % (self.clean_name, self.name_suffix)
return u"%s" % self.clean_name
class Meta:
# Rename permissions
@ -103,7 +106,7 @@ class User(DjangoUser, PersonMixin):
ordering = ('last_name',)
class Group(DjangoGroup, PersonMixin):
class Group(DjangoGroup, PersonMixin, Person):
person_prefix = 'group'
django_group = models.OneToOneField(DjangoGroup, editable=False, parent_link=True)

View File

@ -12,8 +12,8 @@
<button class="button" type="submit">
<span class="icon ok">{% trans 'Save' %}</span>
</button>
<a href='{% url config_application %}'>
<button class="button" type="button" onclick="window.location='{% url config_application %}'">
<a href='{% url config_participant %}'>
<button class="button" type="button" onclick="window.location='{% url config_participant %}'">
<span class="icon cancel">{% trans 'Cancel' %}</span>
</button>
</a>

View File

@ -26,11 +26,11 @@
<option value="female"{% if 'female' in sortfilter.gender %} selected{% endif %}>{% trans "Female" %}</option>
<option value=""{% if '' in sortfilter.gender %} selected{% endif %}>{% trans "Not specified" %}</option>
</select>
<select class="default-input" name="category" onchange="document.forms['filter'].submit()">
<option value="---">-- {% trans "Category" %} --</option>
{% for category in categories %}
<option value="{{ category }}"{% if category in sortfilter.category %} selected{% endif %}>
{{ category }}</option>
<select class="default-input" name="detail" onchange="document.forms['filter'].submit()">
<option value="---">-- {% trans "Detail" %} --</option>
{% for detail in details %}
<option value="{{ detail }}"{% if detail in sortfilter.detail %} selected{% endif %}>
{{ detail }}</option>
{% endfor %}
</select>
<select class="default-input" name="type" onchange="document.forms['filter'].submit()">
@ -65,7 +65,7 @@
<tr>
<th><a href="?sort=first_name&reverse={% if 'first_name' in sortfilter.sort and 'reverse' not in sortfilter %}1{% else %}---{%endif%}">{% trans "First Name" %}</a></th>
<th><a href="?sort=last_name&reverse={% if 'last_name' in sortfilter.sort and 'reverse' not in sortfilter %}1{% else %}---{%endif%}">{% trans "Last Name" %}</a></th>
<th><a href="?sort=category&reverse={% if 'category' in sortfilter.sort and 'reverse' not in sortfilter %}1{% else %}---{%endif%}">{% trans "Category" %}</a></th>
<th><a href="?sort=detail&reverse={% if 'detail' in sortfilter.sort and 'reverse' not in sortfilter %}1{% else %}---{%endif%}">{% trans "Detail" %}</a></th>
<th><a href="?sort=type&reverse={% if 'type' in sortfilter.sort and 'reverse' not in sortfilter %}1{% else %}---{%endif%}">{% trans "Type" %}</a></th>
<th><a href="?sort=committee&reverse={% if 'committee' in sortfilter.sort and 'reverse' not in sortfilter %}1{% else %}---{%endif%}">{% trans "Committee" %}</a></th>
{% if perms.participant.can_manage_participant %}
@ -78,7 +78,7 @@
<tr class="{% cycle '' 'odd' %}">
<td>{{ user.first_name }}</td>
<td>{{ user.last_name }}</td>
<td>{{ user.category }}</td>
<td>{{ user.detail }}</td>
<td>{{ user.get_type_display }}</td>
<td>{{ user.committee }}</td>
{% if perms.participant.can_manage_participant %}

View File

@ -35,12 +35,12 @@ class UserTest(TestCase):
self.assertEqual(self.django_user1, self.user1.django_user)
def test_repr(self):
self.assertEqual(unicode(self.user1), u'Max Mustermann')
self.assertEqual(unicode(self.user1), 'Max Mustermann')
def test_name_surfix(self):
self.user1.category = u'München'
def test_name_suffix(self):
self.user1.detail = 'München'
self.user1.save()
self.assertEqual(unicode(self.user1), u'Max Mustermann (München)')
self.assertEqual(unicode(self.user1), 'Max Mustermann (München)')
def test_reset_password(self):
self.assertIsInstance(self.user1.default_password, basestring)

View File

@ -66,8 +66,8 @@ class Overview(ListView):
except KeyError:
sortfilter = {}
for value in [u'gender', u'category', u'type', u'committee', u'status',
u'sort', u'reverse']:
for value in ['gender', 'detail', 'type', 'committee', 'status',
'sort', 'reverse']:
if value in self.request.REQUEST:
if self.request.REQUEST[value] == '---':
try:
@ -80,8 +80,8 @@ class Overview(ListView):
query = User.objects
if 'gender' in sortfilter:
query = query.filter(gender__iexact=sortfilter['gender'][0])
if 'category' in sortfilter:
query = query.filter(category__iexact=sortfilter['category'][0])
if 'detail' in sortfilter:
query = query.filter(detail__iexact=sortfilter['detail'][0])
if 'type' in sortfilter:
query = query.filter(type__iexact=sortfilter['type'][0])
if 'committee' in sortfilter:
@ -92,7 +92,7 @@ class Overview(ListView):
if sortfilter['sort'][0] in ['first_name', 'last_name', 'last_login']:
query = query.order_by(sortfilter['sort'][0])
elif (sortfilter['sort'][0] in
['category', 'type', 'committee', 'comment']):
['detail', 'type', 'committee', 'comment']):
query = query.order_by(
'%s' % sortfilter['sort'][0])
else:
@ -118,8 +118,8 @@ class Overview(ListView):
percent = 0
# list of all existing categories
categories = [p['category'] for p in User.objects.values('category')
.exclude(category='').distinct()]
details = [p['detail'] for p in User.objects.values('detail')
.exclude(detail='').distinct()]
# list of all existing committees
committees = [p['committee'] for p in User.objects.values('committee')
@ -127,7 +127,7 @@ class Overview(ListView):
context.update({
'allusers': all_users,
'percent': round(percent, 1),
'categories': categories,
'details': details,
'committees': committees,
'cookie': ['participant_sortfilter', urlencode(decodedict(self.sortfilter),
doseq=True)],
@ -223,7 +223,7 @@ class ParticipantsListPDF(PDFView):
counter,
Paragraph(user.last_name, stylesheet['Tablecell']),
Paragraph(user.first_name, stylesheet['Tablecell']),
Paragraph(user.category, stylesheet['Tablecell']),
Paragraph(user.detail, stylesheet['Tablecell']),
Paragraph(user.type, stylesheet['Tablecell']),
Paragraph(user.committee, stylesheet['Tablecell'])])
t = LongTable(data, style=[
@ -320,7 +320,7 @@ class UserImportView(FormView):
return super(UserImportView, self).form_valid(form)
class ResetPasswordView(RedirectView, SingleObjectMixin, QuestionMixin):
class ResetPasswordView(SingleObjectMixin, QuestionMixin, RedirectView):
"""
Set the Passwort for a user to his default password.
"""
@ -336,17 +336,11 @@ class ResetPasswordView(RedirectView, SingleObjectMixin, QuestionMixin):
def get_redirect_url(self, **kwargs):
return reverse('user_edit', args=[self.object.id])
def pre_redirect(self, request, *args, **kwargs):
self.confirm_form()
def pre_post_redirect(self, request, *args, **kwargs):
if self.get_answer().lower() == 'yes':
def case_yes(self):
self.object.reset_password()
messages.success(request,
_('The Password for %s was successfully reset.') % html_strong(self.object))
def get_answer_url(self):
return reverse('user_reset_password', args=[self.object.id])
def get_success_message(self):
return _('The Password for %s was successfully reset.') % html_strong(self.object)
class GroupOverviewView(ListView):

View File

@ -11,6 +11,7 @@
"""
from django.conf import settings
from django.core.cache import cache
from django.template.loader import render_to_string
from django.utils.datastructures import SortedDict
from django.utils.importlib import import_module
@ -85,6 +86,13 @@ def set_active_slide(sid, argument=None):
"""
config["presentation"] = sid
config['presentation_argument'] = argument
clear_projector_cache()
def clear_projector_cache():
cache.delete('projector_content')
cache.delete('projector_scrollcontent')
cache.delete('projector_data')
def register_slidemodel(model, model_name=None, control_template=None,

View File

@ -58,6 +58,12 @@ class SlideMixin(object):
"""
set_active_slide(self.sid)
def save(self, *args, **kwargs):
if self.active:
from api import clear_projector_cache
clear_projector_cache()
return super(SlideMixin, self).save(*args, **kwargs)
class Slide(object):
"""

View File

@ -15,6 +15,7 @@ from time import time
from django.conf import settings
from django.contrib import messages
from django.core.cache import cache
from django.core.context_processors import csrf
from django.core.urlresolvers import reverse
from django.db import transaction
@ -73,6 +74,7 @@ class Projector(TemplateView, AjaxMixin):
except AttributeError: #TODO: It has to be an Slide.DoesNotExist
data = None
ajax = 'on'
active_sid = get_active_slide(True)
else:
data = get_slide_from_sid(sid)
ajax = 'off'
@ -88,7 +90,7 @@ class Projector(TemplateView, AjaxMixin):
# Projector Overlays
if self.kwargs['sid'] is None:
active_defs = ProjectorOverlay.objects.filter(active=True) \
.filter(Q(sid=sid) | Q(sid=None)).values_list('def_name',
.filter(Q(sid=active_sid) | Q(sid=None)).values_list('def_name',
flat=True)
for receiver, response in projector_overlays.send(sender=sid,
register=False, call=active_defs):
@ -106,10 +108,26 @@ class Projector(TemplateView, AjaxMixin):
return context
def get_ajax_context(self, **kwargs):
content = render_block_to_string(self.get_template_names()[0],
content = cache.get('projector_content')
if not content:
content = render_block_to_string(
self.get_template_names()[0],
'content', self.data)
scrollcontent = render_block_to_string(self.get_template_names()[0],
cache.set('projector_content', content)
scrollcontent = cache.get('projector_scrollcontent')
if not scrollcontent:
scrollcontent = render_block_to_string(
self.get_template_names()[0],
'scrollcontent', self.data)
cache.set('projector_scrollcontent', scrollcontent)
# TODO: do not call the hole data-methode, if we only need some vars
data = cache.get('projector_data')
if not data:
data = self.data
cache.set('projector_data', data)
context = super(Projector, self).get_ajax_context(**kwargs)
content_hash = hash(content)
@ -117,8 +135,8 @@ class Projector(TemplateView, AjaxMixin):
'content': content,
'scrollcontent': scrollcontent,
'time': datetime.now().strftime('%H:%M'),
'overlays': self.data['overlays'],
'title': self.data['title'],
'overlays': data['overlays'],
'title': data['title'],
'bigger': config['bigger'],
'up': config['up'],
'content_hash': content_hash,

View File

@ -26,7 +26,7 @@ urlpatterns = patterns('',
(r'^$', FrontPage.as_view()),
(r'^agenda/', include('openslides.agenda.urls')),
(r'^application/', include('openslides.application.urls')),
(r'^motion/', include('openslides.motion.urls')),
(r'^assignment/', include('openslides.assignment.urls')),
(r'^participant/', include('openslides.participant.urls')),
(r'^config/', include('openslides.config.urls')),

View File

@ -11,7 +11,8 @@
"""
from openslides.utils.person.signals import receive_persons
from openslides.utils.person.api import generate_person_id, get_person, Persons
from openslides.utils.person.api import (generate_person_id, get_person,
Person, Persons)
from openslides.utils.person.forms import PersonFormField, MultiplePersonFormField
from openslides.utils.person.models import PersonField, PersonMixin

View File

@ -13,6 +13,37 @@
from openslides.utils.person.signals import receive_persons
class Person(object):
"""
Meta-class for all person objects
"""
def person_id(self):
"""
Return an id for representation of ths person. Has to be unique.
"""
raise NotImplementedError('Any person object needs a person_id')
def __repr__(self):
"""
Return a string for this person.
"""
return str(self.person_id)
@property
def clean_name(self):
"""
Return the name of this person without a suffix
"""
return unicode(self)
@property
def name_suffix(self):
"""
Return a suffix for the person-name.
"""
return ''
class Persons(object):
"""
A Storage for a multiplicity of different Person-Objects.

View File

@ -18,7 +18,7 @@ register = template.Library()
@register.simple_tag
def get_min_supporters():
return config['application_min_supporters']
return config['motion_min_supporters']
@register.simple_tag

View File

@ -101,13 +101,13 @@ def permission_required(perm, login_url=None):
if request.user.has_perm(perm):
return func(request, *args, **kw)
if request.user.is_authenticated():
return render_to_forbitten(request)
return render_to_forbidden(request)
return redirect(reverse('user_login'))
return wrapper
return renderer
def render_to_forbitten(request, error=
def render_to_forbidden(request, error=
ugettext_lazy("Sorry, you have no rights to see this page.")):
return HttpResponseForbidden(render_to_string('403.html',
{'error': error}, context_instance=RequestContext(request)))

View File

@ -52,7 +52,7 @@ from django.views.generic.list import TemplateResponseMixin
from openslides.config.models import config
from openslides.utils.utils import render_to_forbitten, html_strong
from openslides.utils.utils import render_to_forbidden, html_strong
from openslides.utils.signals import template_manipulation
from openslides.utils.pdf import firstPage, laterPages
@ -80,20 +80,20 @@ class LoginMixin(object):
class PermissionMixin(object):
permission_required = NO_PERMISSION_REQUIRED
def has_permission(self, request):
def has_permission(self, request, *args, **kwargs):
if self.permission_required == NO_PERMISSION_REQUIRED:
return True
else:
return request.user.has_perm(self.permission_required)
def dispatch(self, request, *args, **kwargs):
if not self.has_permission(request):
if not self.has_permission(request, *args, **kwargs):
if not request.user.is_authenticated():
path = request.get_full_path()
return HttpResponseRedirect("%s?next=%s" % (settings.LOGIN_URL,
path))
else:
return render_to_forbitten(request)
return render_to_forbidden(request)
return _View.dispatch(self, request, *args, **kwargs)
@ -110,20 +110,21 @@ class QuestionMixin(object):
success_message = ugettext_lazy('Thank you for your answer')
answer_options = [('yes', ugettext_lazy("Yes")), ('no', ugettext_lazy("No"))]
def get_answer_options(self):
return self.answer_options
def pre_redirect(self, request, *args, **kwargs):
# Prints the question in a GET request
self.confirm_form()
def get_question(self):
return unicode(self.question)
def get_answer(self):
for option in self.get_answer_options():
if option[0] in self.request.POST:
return option[0]
return None
def get_answer_options(self):
return self.answer_options
def get_answer_url(self):
try:
return self.answer_url
except AttributeError:
return self.request.path
def confirm_form(self):
option_fields = "\n".join([
@ -142,11 +143,25 @@ class QuestionMixin(object):
'csrf': csrf(self.request)['csrf_token'],
'option_fields': option_fields})
def pre_redirect(self, request, *args, **kwargs):
self.confirm_form(request, self.object)
def pre_post_redirect(self, request, *args, **kwargs):
messages.success(request)
# Reacts on the response of the user in a POST-request.
# TODO: call the methodes for all possible answers.
if self.get_answer() == 'yes':
self.case_yes()
messages.success(request, self.get_success_message())
def get_answer(self):
for option in self.get_answer_options():
if option[0] in self.request.POST:
return option[0]
return None
def case_yes(self):
# TODO: raise a warning
pass
def get_success_message(self):
return self.success_message
class TemplateView(PermissionMixin, _TemplateView):
@ -266,27 +281,19 @@ class CreateView(PermissionMixin, _CreateView):
pass
class DeleteView(RedirectView, SingleObjectMixin, QuestionMixin):
def get_question(self):
return _('Do you really want to delete %s?') % html_strong(self.object)
def get_success_message(self):
return _('%s was successfully deleted.') % html_strong(self.object)
def pre_redirect(self, request, *args, **kwargs):
self.confirm_form()
def pre_post_redirect(self, request, *args, **kwargs):
if self.get_answer().lower() == 'yes':
self.object.delete()
messages.success(request, self.get_success_message())
class DeleteView(SingleObjectMixin, QuestionMixin, RedirectView):
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super(DeleteView, self).get(request, *args, **kwargs)
def get_answer_url(self):
return self.object.get_absolute_url('delete')
def get_question(self):
return _('Do you really want to delete %s?') % html_strong(self.object)
def case_yes(self):
self.object.delete()
def get_success_message(self):
return _('%s was successfully deleted.') % html_strong(self.object)
class DetailView(TemplateView, SingleObjectMixin):