Merge branch 'master' into install

This commit is contained in:
Oskar Hahn 2012-08-13 19:35:14 +02:00
commit a2be848932
37 changed files with 1776 additions and 1883 deletions

View File

@ -1,6 +1,6 @@
[ [
{ {
"pk": 2, "pk": 1,
"model": "auth.group", "model": "auth.group",
"fields": { "fields": {
"name": "Beobachter", "name": "Beobachter",
@ -38,7 +38,7 @@
[ [
"can_see_participant", "can_see_participant",
"participant", "participant",
"openslidesuser" "user"
], ],
[ [
"can_see_projector", "can_see_projector",
@ -49,7 +49,7 @@
} }
}, },
{ {
"pk": 3, "pk": 2,
"model": "auth.group", "model": "auth.group",
"fields": { "fields": {
"name": "Delegierter", "name": "Delegierter",
@ -92,7 +92,7 @@
[ [
"can_see_participant", "can_see_participant",
"participant", "participant",
"openslidesuser" "user"
], ],
[ [
"can_see_projector", "can_see_projector",
@ -103,7 +103,7 @@
} }
}, },
{ {
"pk": 4, "pk": 3,
"model": "auth.group", "model": "auth.group",
"fields": { "fields": {
"name": "Versammlungsleitung", "name": "Versammlungsleitung",
@ -161,12 +161,12 @@
[ [
"can_manage_participant", "can_manage_participant",
"participant", "participant",
"openslidesuser" "user"
], ],
[ [
"can_see_participant", "can_see_participant",
"participant", "participant",
"openslidesuser" "user"
], ],
[ [
"can_manage_projector", "can_manage_projector",
@ -182,7 +182,7 @@
} }
}, },
{ {
"pk": 5, "pk": 4,
"model": "auth.group", "model": "auth.group",
"fields": { "fields": {
"name": "Teilnehmerverwaltung", "name": "Teilnehmerverwaltung",
@ -195,12 +195,12 @@
[ [
"can_manage_participant", "can_manage_participant",
"participant", "participant",
"openslidesuser" "user"
], ],
[ [
"can_see_participant", "can_see_participant",
"participant", "participant",
"openslidesuser" "user"
], ],
[ [
"can_see_projector", "can_see_projector",

View File

@ -54,7 +54,7 @@ class Overview(TemplateView):
context = self.get_context_data(**kwargs) context = self.get_context_data(**kwargs)
if not request.user.has_perm('agenda.can_manage_agenda'): if not request.user.has_perm('agenda.can_manage_agenda'):
messages.error(request, messages.error(request,
_('You are not permitted to manage the agenda.')) _('You are not authorized to manage the agenda.'))
return self.render_to_response(context) return self.render_to_response(context)
transaction.commit() transaction.commit()
for item in Item.objects.all(): for item in Item.objects.all():
@ -167,69 +167,24 @@ class ItemDelete(DeleteView):
model = Item model = Item
url = 'item_overview' url = 'item_overview'
def pre_post_redirect(self, request, *args, **kwargs): def get_answer_options(self):
self.object = self.get_object() if self.object.children.exists():
return [('all', _("Yes, with all child items."))] + self.answer_options
else:
return self.answer_options
if 'all' in request.POST: def pre_post_redirect(self, request, *args, **kwargs):
if self.get_answer() == 'all':
self.object.delete(with_children=True) self.object.delete(with_children=True)
messages.success(request, messages.success(request,
_("Item %s and his children were successfully deleted.") \ _("Item %s and his children were successfully deleted.") \
% html_strong(self.object)) % html_strong(self.object))
else: elif self.get_answer() == 'yes':
self.object.delete(with_children=False) self.object.delete(with_children=False)
messages.success(request, messages.success(request,
_("Item %s was successfully deleted.") \ _("Item %s was successfully deleted.") \
% html_strong(self.object)) % html_strong(self.object))
def gen_confirm_form(self, request, message, url, singleitem=False):
if singleitem:
messages.warning(
request,
"""
%s
<form action="%s" method="post">
<input type="hidden" value="%s" name="csrfmiddlewaretoken">
<input type="submit" value="%s">
<input type="button" value="%s">
</form>
"""
% (message, url, csrf(request)['csrf_token'], _("Yes"),
_("No"))
)
else:
messages.warning(
request,
"""
%s
<form action="%s" method="post">
<input type="hidden" value="%s" name="csrfmiddlewaretoken">
<input type="submit" value="%s">
<input type="submit" name="all" value="%s">
<input type="button" value="%s">
</form>
"""
% (message, url, csrf(request)['csrf_token'], _("Yes"),
_("Yes, with all child items."), _("No"))
)
def confirm_form(self, request, object, item=None):
if item is None:
item = object
if item.get_children():
self.gen_confirm_form(
request,
_('Do you really want to delete %s?') % html_strong(item),
item.get_absolute_url('delete'),
False,
)
else:
self.gen_confirm_form(
request,
_('Do you really want to delete %s?') % html_strong(item),
item.get_absolute_url('delete'),
True,
)
class AgendaPDF(PDFView): class AgendaPDF(PDFView):
""" """

View File

@ -51,16 +51,16 @@ class ApplicationImportForm(forms.Form, CssClassMixin):
) )
import_permitted = forms.BooleanField( import_permitted = forms.BooleanField(
required=False, required=False,
label=_("Import applications with status \"permitted\""), label=_("Import motions with status \"authorized\""),
help_text=_('Set the initial status for each application to ' help_text=_('Set the initial status for each motion to '
'"permitted"'), '"authorized"'),
) )
class ConfigForm(forms.Form, CssClassMixin): class ConfigForm(forms.Form, CssClassMixin):
application_min_supporters = forms.IntegerField( application_min_supporters = forms.IntegerField(
widget=forms.TextInput(attrs={'class':'small-input'}), widget=forms.TextInput(attrs={'class':'small-input'}),
label=_("Number of (minimum) required supporters for a application"), label=_("Number of (minimum) required supporters for a motion"),
initial=4, initial=4,
min_value=0, min_value=0,
max_value=8, max_value=8,
@ -69,7 +69,7 @@ class ConfigForm(forms.Form, CssClassMixin):
application_preamble = forms.CharField( application_preamble = forms.CharField(
widget=forms.TextInput(), widget=forms.TextInput(),
required=False, required=False,
label=_("Application preamble") label=_("Motion preamble")
) )
application_pdf_ballot_papers_selection = forms.ChoiceField( application_pdf_ballot_papers_selection = forms.ChoiceField(
widget=forms.Select(), widget=forms.Select(),
@ -90,17 +90,17 @@ class ConfigForm(forms.Form, CssClassMixin):
application_pdf_title = forms.CharField( application_pdf_title = forms.CharField(
widget=forms.TextInput(), widget=forms.TextInput(),
required=False, required=False,
label=_("Title for PDF document (all applications)") label=_("Title for PDF document (all motions)")
) )
application_pdf_preamble = forms.CharField( application_pdf_preamble = forms.CharField(
widget=forms.Textarea(), widget=forms.Textarea(),
required=False, required=False,
label=_("Preamble text for PDF document (all applications)") label=_("Preamble text for PDF document (all motions)")
) )
application_allow_trivial_change = forms.BooleanField( application_allow_trivial_change = forms.BooleanField(
label=_("Allow trivial changes"), label=_("Allow trivial changes"),
help_text=_('Warning: Trivial changes undermine the application ' \ help_text=_('Warning: Trivial changes undermine the motions '
'permission system.'), 'autorisation system.'),
required=False, required=False,
) )

View File

@ -12,7 +12,6 @@
from datetime import datetime from datetime import datetime
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.db.models import Max from django.db.models import Max
@ -26,8 +25,6 @@ from openslides.utils.person import PersonField
from openslides.config.models import config from openslides.config.models import config
from openslides.config.signals import default_config_value from openslides.config.signals import default_config_value
from openslides.participant.models import OpenSlidesUser
from openslides.poll.models import (BaseOption, BasePoll, CountVotesCast, from openslides.poll.models import (BaseOption, BasePoll, CountVotesCast,
CountInvalid, BaseVote) CountInvalid, BaseVote)
@ -53,7 +50,7 @@ class Application(models.Model, SlideMixin):
('adj', _('Adjourned')), ('adj', _('Adjourned')),
('noc', _('Not Concerned')), ('noc', _('Not Concerned')),
('com', _('Commited a bill')), ('com', _('Commited a bill')),
('nop', _('Rejected (not permitted)')), ('nop', _('Rejected (not authorized)')),
('rev', _('Needs Review')), # Where is this status used? ('rev', _('Needs Review')), # Where is this status used?
#additional actions: #additional actions:
# edit # edit
@ -103,14 +100,14 @@ class Application(models.Model, SlideMixin):
self.save(nonewversion=True) self.save(nonewversion=True)
version.rejected = False version.rejected = False
version.save() version.save()
self.writelog(_("Version %d permitted") % (version.aid, ), self.writelog(_("Version %d authorized") % (version.aid, ),
user) user)
def reject_version(self, version, user = None): def reject_version(self, version, user = None):
if version.id > self.permitted.id: if version.id > self.permitted.id:
version.rejected = True version.rejected = True
version.save() version.save()
self.writelog(pgettext("Rejected means not permitted", "Version %d rejected") self.writelog(pgettext("Rejected means not authorized", "Version %d rejected")
% (version.aid, ), user) % (version.aid, ), user)
return True return True
return False return False
@ -141,9 +138,9 @@ class Application(models.Model, SlideMixin):
if self.status == "pub" and not self.enough_supporters: if self.status == "pub" and not self.enough_supporters:
note.append(_("Searching for supporters.")) note.append(_("Searching for supporters."))
if self.status == "pub" and self.permitted is None: if self.status == "pub" and self.permitted is None:
note.append(_("Not yet permitted.")) note.append(_("Not yet authorized."))
elif self.unpermitted_changes and self.permitted: elif self.unpermitted_changes and self.permitted:
note.append(_("Not yet permitted changes.")) note.append(_("Not yet authorized changes."))
return note return note
@property @property
@ -309,7 +306,7 @@ class Application(models.Model, SlideMixin):
self.set_number() self.set_number()
self.permitted = aversion self.permitted = aversion
self.save() self.save()
self.writelog(_("Version %s permitted") % (aversion.aid), user) self.writelog(_("Version %s authorized") % (aversion.aid), user)
return self.permitted return self.permitted
def notpermit(self, user=None): def notpermit(self, user=None):
@ -323,7 +320,7 @@ class Application(models.Model, SlideMixin):
if self.number is None: if self.number is None:
self.set_number() self.set_number()
self.save() self.save()
self.writelog(_("Version %s not permitted") % (self.last_version.aid), user) self.writelog(_("Version %s not authorized") % (self.last_version.aid), user)
def set_status(self, user, status, force=False): def set_status(self, user, status, force=False):
""" """
@ -335,19 +332,22 @@ class Application(models.Model, SlideMixin):
error = False error = False
break break
if error: if error:
#TODO: Use the Right Error
raise NameError(_('%s is not a valid status.') % status) raise NameError(_('%s is not a valid status.') % status)
if self.status == status: if self.status == status:
raise NameError(_('The application status is already \'%s.\'') \ #TODO: Use the Right Error
raise NameError(_('The motion status is already \'%s.\'') \
% self.status) % self.status)
actions = [] actions = []
actions = self.get_allowed_actions(user) actions = self.get_allowed_actions(user)
if status not in actions and not force: if status not in actions and not force:
raise NameError(_('The application status is: \'%(currentstatus)s\'. '\ #TODO: Use the Right Error
raise NameError(_(
'The motion status is: \'%(currentstatus)s\'. '
'You can not set the status to \'%(newstatus)s\'.') % { 'You can not set the status to \'%(newstatus)s\'.') % {
'currentstatus': self.status, 'currentstatus': self.status,
'newstatus': status 'newstatus': status})
})
oldstatus = self.get_status_display() oldstatus = self.get_status_display()
self.status = status self.status = status
@ -360,15 +360,6 @@ class Application(models.Model, SlideMixin):
Return a list of all the allowed status. Return a list of all the allowed status.
""" """
actions = [] actions = []
is_admin = False
if user:
try:
user = user.openslidesuser
except OpenSlidesUser.DoesNotExist:
is_admin = True
except AttributeError:
# For the anonymous-user
pass
# check if user allowed to withdraw an application # check if user allowed to withdraw an application
if ((self.status == "pub" if ((self.status == "pub"
@ -405,11 +396,10 @@ class Application(models.Model, SlideMixin):
# Check if the user can delete the application (admin, manager, owner) # Check if the user can delete the application (admin, manager, owner)
# reworked as requiered in #100 # reworked as requiered in #100
if is_admin \ if (user.has_perm("applicatoin.can_delete_all_applications") or
or (user.has_perm("application.can_manage_application") \ (user.has_perm("application.can_manage_application") and
and (self.status == "pub" or self.number is None)) \ self.number is None) or
or (self.submitter == user \ (self.submitter == user and self.number is None)):
and (self.status == "pub" or self.number is None)):
actions.append("delete") actions.append("delete")
#For the rest, all actions need the manage permission #For the rest, all actions need the manage permission
@ -463,7 +453,7 @@ class Application(models.Model, SlideMixin):
def get_agenda_title_supplement(self): def get_agenda_title_supplement(self):
number = self.number or '<i>[%s]</i>' % ugettext('no number') number = self.number or '<i>[%s]</i>' % ugettext('no number')
return '(%s %s)' % (ugettext('Application'), number) return '(%s %s)' % (ugettext('motion'), number)
def __getattr__(self, name): def __getattr__(self, name):
""" """
@ -539,10 +529,10 @@ class Application(models.Model, SlideMixin):
class Meta: class Meta:
permissions = ( permissions = (
('can_see_application', ugettext_noop("Can see application")), ('can_see_application', ugettext_noop("Can see motions")),
('can_create_application', ugettext_noop("Can create application")), ('can_create_application', ugettext_noop("Can create motions")),
('can_support_application', ugettext_noop("Can support application")), ('can_support_application', ugettext_noop("Can support motions")),
('can_manage_application', ugettext_noop("Can manage application")), ('can_manage_application', ugettext_noop("Can manage motions")),
) )
@ -611,7 +601,7 @@ def default_config(sender, key, **kwargs):
'application_preamble': _('The Assembly may decide,'), 'application_preamble': _('The Assembly may decide,'),
'application_pdf_ballot_papers_selection': 'CUSTOM_NUMBER', 'application_pdf_ballot_papers_selection': 'CUSTOM_NUMBER',
'application_pdf_ballot_papers_number': '8', 'application_pdf_ballot_papers_number': '8',
'application_pdf_title': _('Applications'), 'application_pdf_title': _('Motions'),
'application_pdf_preamble': '', 'application_pdf_preamble': '',
'application_allow_trivial_change': False, 'application_allow_trivial_change': False,
}.get(key) }.get(key)

View File

@ -6,16 +6,16 @@
{% block submenu %} {% block submenu %}
{% url application_overview as url_applicationoverview %} {% url application_overview as url_applicationoverview %}
<h4>{% trans "Applications" %}</h4> <h4>{% trans "Motions" %}</h4>
<ul> <ul>
<li class="{% if request.path == url_applicationoverview %}selected{% endif %}"><a href="{% url application_overview %}">{% trans "All applications" %}</a></li> <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 %} {% 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 application" %}</a></li> <li class="{% active request '/application/new' %}"><a href="{% url application_new %}">{% trans "New motion" %}</a></li>
{% endif %} {% endif %}
{% if perms.application.can_manage_application %} {% if perms.application.can_manage_application %}
<li class="{% active request '/application/import' %}"><a href="{% url application_import %}">{% trans 'Import applications' %}</a></li> <li class="{% active request '/application/import' %}"><a href="{% url application_import %}">{% trans 'Import motions' %}</a></li>
{% endif %} {% endif %}
<li><a href="{% url print_applications %}"><img src="{% static 'images/icons/pdf.png' %}"> {% trans 'All applications as PDF' %}</a></li> <li><a href="{% url print_applications %}"><img src="{% static 'images/icons/pdf.png' %}"> {% trans 'All motions as PDF' %}</a></li>
</ul> </ul>
{# second submenu #} {# second submenu #}
@ -31,18 +31,18 @@
<ul> <ul>
{# view application #} {# view application #}
{% url application_view application.id as url_applicationview %} {% 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 application' %}</a></li> <li class="{% if request.path == url_applicationview %}selected{% endif %}"><a href="{% url application_view application.id %}">{% trans 'View motion' %}</a></li>
{# edit application #} {# edit application #}
{% if "edit" in actions %} {% if "edit" in actions %}
{% url application_edit application.id as url_applicationedit %} {% 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 application' %}</a></li> <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 %} {% endif %}
{# delete application #} {# delete application #}
{% if "delete" in actions %} {% if "delete" in actions %}
<li><a href="{% url application_delete application.id %}"><img src="{% static 'images/icons/delete.png' %}"> {% trans 'Delete application' %}</a></li> <li><a href="{% url application_delete application.id %}"><img src="{% static 'images/icons/delete.png' %}"> {% trans 'Delete motion' %}</a></li>
{% endif %} {% endif %}
{# PDF #} {# PDF #}
<li><a href="{% url print_application application.id %}"><img src="{% static 'images/icons/pdf.png' %}"> {% trans 'Application as PDF' %}</a></li> <li><a href="{% url print_application application.id %}"><img src="{% static 'images/icons/pdf.png' %}"> {% trans 'Motion as PDF' %}</a></li>
{# activate and polls #} {# activate and polls #}
{% if perms.projector.can_manage_projector %} {% if perms.projector.can_manage_projector %}
<li> <li>

View File

@ -5,17 +5,17 @@
{% block title %} {% block title %}
{{ block.super }} {{ block.super }}
{% if application %} {% if application %}
{% trans "Edit application" %} {% trans "Edit motion" %}
{% else %} {% else %}
{% trans "New application" %} {% trans "New motion" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if application %} {% if application %}
<h1>{% trans "Edit application" %}</h1> <h1>{% trans "Edit motion" %}</h1>
{% else %} {% else %}
<h1>{% trans "New application" %}</h1> <h1>{% trans "New motion" %}</h1>
{% endif %} {% endif %}
<form action="" method="post">{% csrf_token %} <form action="" method="post">{% csrf_token %}

View File

@ -2,11 +2,11 @@
{% load i18n %} {% load i18n %}
{% block title %}{{ block.super }} {% trans "Import applications" %} {% endblock %} {% block title %}{{ block.super }} {% trans "Import motions" %} {% endblock %}
{% block content %} {% block content %}
<h1>{% trans "Import applications" %}</h1> <h1>{% trans "Import motions" %}</h1>
<p>{% trans 'Select a CSV file to import applications!' %}</p> <p>{% trans 'Select a CSV file to import motions!' %}</p>
<p>{% trans 'Required comma separated values: <code>{number, title, text, reason, first_name, last_name}</code> (<code>number</code> and <code>reason</code> are optional and may be empty)' %} <p>{% trans 'Required comma separated values: <code>{number, title, text, reason, first_name, last_name}</code> (<code>number</code> and <code>reason</code> are optional and may be empty)' %}
<br> <br>

View File

@ -4,11 +4,11 @@
{% load i18n %} {% load i18n %}
{% load staticfiles %} {% load staticfiles %}
{% block title %}{{ block.super }} {% trans "Applications" %}{% endblock %} {% block title %}{{ block.super }} {% trans "Motions" %}{% endblock %}
{% block content %} {% block content %}
<h1>{% trans "Applications" %}</h1> <h1>{% trans "Motions" %}</h1>
<p><form action="{{request.url}}" name="filter" method="get"> <p><form action="{{ request.url }}" name="filter" method="get">
{% trans "Filter" %}: {% trans "Filter" %}:
{% if min_supporters > 0 %} {% if min_supporters > 0 %}
<input type="checkbox" name="needsup" onchange="document.forms['filter'].submit()" <input type="checkbox" name="needsup" onchange="document.forms['filter'].submit()"
@ -19,21 +19,21 @@
<input type="checkbox" name="status" onchange="document.forms['filter'].submit()" <input type="checkbox" name="status" onchange="document.forms['filter'].submit()"
{% if 'on' in request.GET.status %}checked{% endif %}> {% trans "Status" %}: {% 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 %}"> <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 permitted" %}</option> <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 "Permitted" %}</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="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="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 "Withdrawed (by submitter)" %}</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> <option value="rev" {% if 'rev' in request.GET.statusvalue %}selected{% endif %}>{% trans "Needs Review" %}</option>
</select> </select>
</form> </form>
</p> </p>
{{ applications|length }} {{ applications|length }}
{% blocktrans count counter=applications|length %}application{% plural %}applications{% endblocktrans %} {% blocktrans count counter=applications|length %}motion{% plural %}motions{% endblocktrans %}
<table> <table>
<tr> <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=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 "Application title" %}</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 %} {% 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> <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 %} {% endif %}
@ -64,24 +64,24 @@
<td> <td>
<span style="width: 1px; white-space: nowrap;"> <span style="width: 1px; white-space: nowrap;">
{% if perms.projector.can_manage_projector %} {% 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 application' %}"> <a class="activate_link {% if application.active %}active{% endif %}" href="{% url projector_activate_slide application.sid %}" title="{% trans 'Activate motion' %}">
<span></span> <span></span>
</a> </a>
{% endif %} {% endif %}
{% if perms.application.can_manage_application %} {% if perms.application.can_manage_application %}
<a href="{% url application_edit application.id %}"><img src="{% static 'images/icons/edit.png' %}" title="{% trans 'Edit application' %}"></a> <a href="{% url application_edit application.id %}"><img src="{% static 'images/icons/edit.png' %}" title="{% trans 'Edit motion' %}"></a>
{% if "delete" in useractions %} {% if "delete" in useractions %}
<a href="{% url application_delete application.id %}"><img src="{% static 'images/icons/delete.png' %}" title="{% trans 'Delete application' %}"></a> <a href="{% url application_delete application.id %}"><img src="{% static 'images/icons/delete.png' %}" title="{% trans 'Delete motion' %}"></a>
{% endif %} {% endif %}
{% endif %} {% endif %}
<a href="{% url print_application application.id %}" title="{% trans 'Application as PDF' %}"><img src="{% static 'images/icons/pdf.png' %}"></a> <a href="{% url print_application application.id %}" title="{% trans 'Motion as PDF' %}"><img src="{% static 'images/icons/pdf.png' %}"></a>
</span> </span>
</td> </td>
</tr> </tr>
{% endwith %} {% endwith %}
{% empty %} {% empty %}
<tr> <tr>
<td colspan="7"><i>{% trans "No applications available." %}</i></td> <td colspan="7"><i>{% trans "No motions available." %}</i></td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>

View File

@ -4,12 +4,12 @@
{% load staticfiles %} {% load staticfiles %}
{% block title %} {% block title %}
{{ block.super }} {% trans "Application" %} "{{ application.public_version.title }}" {{ block.super }} {% trans "Motion" %} "{{ application.public_version.title }}"
{{ ballot }}. {% trans "Vote" %} {{ ballot }}. {% trans "Vote" %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>{{ application.public_version.title }} ({% trans "Application" %} <h1>{{ application.public_version.title }} ({% trans "Motion" %}
{{ application.number }}) {{ ballot }}. {% trans "Vote" %}</h1> {{ application.number }}) {{ ballot }}. {% trans "Vote" %}</h1>
<i class="helptext">{% trans "Special values" %}: -1 = {% trans 'majority' %}; -2 = {% trans 'undocumented' %}</i> <i class="helptext">{% trans "Special values" %}: -1 = {% trans 'majority' %}; -2 = {% trans 'undocumented' %}</i>
<form action="" method="post" class="small-form">{% csrf_token %} <form action="" method="post" class="small-form">{% csrf_token %}

View File

@ -4,7 +4,7 @@
{% load i18n %} {% load i18n %}
{% load staticfiles %} {% load staticfiles %}
{% block title %}{{ block.super }} {% trans "Application" %} "{{ version.title }}"{% endblock %} {% block title %}{{ block.super }} {% trans "Motion" %} "{{ version.title }}"{% endblock %}
{% block submenu %} {% block submenu %}
@ -143,7 +143,7 @@
{% if perms.application.can_manage_application %} {% if perms.application.can_manage_application %}
<div class="box"> <div class="box">
<h4><b>{% trans "Manage application" %}</b></h4> <h4><b>{% trans "Manage motion" %}</b></h4>
{% if "pub" in actions or "per" in actions or "nop" in actions or "setnumber" in actions %} {% if "pub" in actions or "per" in actions or "nop" in actions or "setnumber" in actions %}
<h4>{% trans "Formal validation" %}:</h4> <h4>{% trans "Formal validation" %}:</h4>
@ -206,7 +206,7 @@
<div id="main"> <div id="main">
<h1> <h1>
{{ version.title }} {{ version.title }}
({% trans "Application" %} ({% trans "Motion" %}
{% if application.number != None %} {% if application.number != None %}
{{ application.number }}) {{ application.number }})
{% else %} {% else %}
@ -221,11 +221,11 @@
{% if version == application.public_version %} {% 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> {% trans "This is not the newest version." %} <a href="{% url application_view_newest application.id %}">{% trans "Go to version" %} {{ application.last_version.aid }}.</a>
{% else %} {% else %}
{% trans "This is not the permitted 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 application_view application.id %}">{% trans "Go to version" %} {{ application.public_version.aid }}.</a>
{% endif %} {% endif %}
{% endif %} {% endif %}
<h2>{% trans "Application" %}:</h2> <h2>{% trans "Motion" %}:</h2>
{{ version.text|linebreaks }} {{ version.text|linebreaks }}
@ -256,7 +256,7 @@
<td style="white-space:nowrap;"> <td style="white-space:nowrap;">
{% if application.status != "pub" %} {% if application.status != "pub" %}
{% if revision == application.permitted %} {% if revision == application.permitted %}
<img title="{% trans 'Version permitted' %}" src="{% static 'images/icons/accept.png' %}"> <img title="{% trans 'Version authorized' %}" src="{% static 'images/icons/accept.png' %}">
{% else %} {% else %}
{% if perms.application.can_manage_application %} {% 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> <a href="{% url application_version_permit revision.id %}"><img title="{% trans 'Permit Version' %}" src="{% static 'images/icons/accept-grey.png' %}"></a>

View File

@ -20,7 +20,7 @@
<a href="{% model_url application 'view' %}"> <a href="{% model_url application 'view' %}">
{{ application.public_version.title }} {{ application.public_version.title }}
</a> </a>
({% trans 'Application' %} ({% trans "motion" %}
{% if application.number %} {% if application.number %}
{{ application.number }}) {{ application.number }})
{% else %} {% else %}
@ -28,7 +28,7 @@
{% endif %} {% endif %}
</li> </li>
{% empty %} {% empty %}
<li>{% trans 'No applications available.' %}</li> <li>{% trans 'No motion available.' %}</li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@ -4,7 +4,7 @@
{% load i18n %} {% load i18n %}
{% load staticfiles %} {% load staticfiles %}
{% block title %}{{ block.super }} - {% trans "Application" %} {{ application.number }}{% endblock %} {% block title %}{{ block.super }} - {% trans "Motion" %} {{ application.number }}{% endblock %}
{% block content %} {% block content %}
<div id="sidebar"> <div id="sidebar">
<div class="box"> <div class="box">
@ -60,9 +60,9 @@
<h1> <h1>
{% if application.number != None %} {% if application.number != None %}
{% trans "Application No." %} {{ application.number }} {% trans "Motion No." %} {{ application.number }}
{% else %} {% else %}
{% trans "Application" %} <i>[{% trans "no number" %}]</i> {% trans "Motion" %} <i>[{% trans "no number" %}]</i>
{% endif %} {% endif %}
</h1> </h1>
<b>{{ application.public_version.title }}</b> <b>{{ application.public_version.title }}</b>

View File

@ -12,15 +12,17 @@
from django.test import TestCase from django.test import TestCase
from django.test.client import Client from django.test.client import Client
from django.contrib.auth.models import User
from openslides.participant.models import User
from openslides.application.models import Application, AVersion from openslides.application.models import Application, AVersion
class ApplicationTest(TestCase): class ApplicationTest(TestCase):
def setUp(self): def setUp(self):
self.admin = User.objects.create_user('testadmin', '', 'default') self.admin = User(username='testadmin')
self.anonym = User.objects.create_user('testanoym', '', 'default') self.admin.save()
self.app1 = Application(submitter=self.admin.openslidesuser) self.anonym = User(username='testanoym')
self.anonym.save()
self.app1 = Application(submitter=self.admin)
self.app1.save() self.app1.save()
def refresh(self): def refresh(self):

View File

@ -51,7 +51,7 @@ from openslides.projector.projector import Widget
from openslides.poll.views import PollFormView from openslides.poll.views import PollFormView
from openslides.participant.api import gen_username, gen_password from openslides.participant.api import gen_username, gen_password
from openslides.participant.models import OpenSlidesUser from openslides.participant.models import User
from openslides.agenda.models import Item from openslides.agenda.models import Item
@ -124,7 +124,7 @@ def overview(request):
for (i, application) in enumerate(applications): for (i, application) in enumerate(applications):
try: try:
applications[i] = { applications[i] = {
'actions' : application.get_allowed_actions(request.user.openslidesuser), 'actions' : application.get_allowed_actions(request.user),
'application' : application 'application' : application
} }
except: except:
@ -152,8 +152,7 @@ def view(request, application_id, newest=False):
else: else:
version = application.public_version version = application.public_version
revisions = application.versions revisions = application.versions
user = request.user.openslidesuser actions = application.get_allowed_actions(user=request.user)
actions = application.get_allowed_actions(user=user)
return { return {
'application': application, 'application': application,
@ -178,16 +177,14 @@ def edit(request, application_id=None):
if not is_manager \ if not is_manager \
and not request.user.has_perm('application.can_create_application'): and not request.user.has_perm('application.can_create_application'):
messages.error(request, _("You have not the necessary rights to create or edit applications.")) messages.error(request, _("You have not the necessary rights to create or edit motions."))
return redirect(reverse('application_overview')) return redirect(reverse('application_overview'))
if application_id is not None: if application_id is not None:
application = Application.objects.get(id=application_id) application = Application.objects.get(id=application_id)
if (not hasattr(application.submitter, 'user') or if not request.user == application.submitter and not is_manager:
not request.user.openslidesuser == application.submitter.user) \ messages.error(request, _("You can not edit this motion. You are not the submitter."))
and not is_manager:
messages.error(request, _("You can not edit this application. You are not the submitter."))
return redirect(reverse('application_view', args=[application.id])) return redirect(reverse('application_view', args=[application.id]))
actions = application.get_allowed_actions(user=request.user.openslidesuser) actions = application.get_allowed_actions(user=request.user)
else: else:
application = None application = None
actions = None actions = None
@ -221,7 +218,7 @@ def edit(request, application_id=None):
original_supporters = [] original_supporters = []
application = managerform.save(commit=False) application = managerform.save(commit=False)
elif application_id is None: elif application_id is None:
application = Application(submitter=request.user.openslidesuser) application = Application(submitter=request.user)
application.title = dataform.cleaned_data['title'] application.title = dataform.cleaned_data['title']
application.text = dataform.cleaned_data['text'] application.text = dataform.cleaned_data['text']
application.reason = dataform.cleaned_data['reason'] application.reason = dataform.cleaned_data['reason']
@ -231,7 +228,7 @@ def edit(request, application_id=None):
and dataform.cleaned_data['trivial_change'] and dataform.cleaned_data['trivial_change']
except KeyError: except KeyError:
trivial_change = False trivial_change = False
application.save(request.user.openslidesuser, trivial_change=trivial_change) application.save(request.user, trivial_change=trivial_change)
if is_manager: if is_manager:
try: try:
new_supporters = set(managerform.cleaned_data['supporter']) new_supporters = set(managerform.cleaned_data['supporter'])
@ -246,10 +243,11 @@ def edit(request, application_id=None):
# remove old supporters # remove old supporters
for supporter in old_supporters.difference(new_supporters): for supporter in old_supporters.difference(new_supporters):
application.unsupport(supporter) application.unsupport(supporter)
if application_id is None: if application_id is None:
messages.success(request, _('New application was successfully created.')) messages.success(request, _('New motion was successfully created.'))
else: else:
messages.success(request, _('Application was successfully modified.')) messages.success(request, _('Motion was successfully modified.'))
if not 'apply' in request.POST: if not 'apply' in request.POST:
return redirect(reverse('application_view', args=[application.id])) return redirect(reverse('application_view', args=[application.id]))
@ -263,9 +261,9 @@ def edit(request, application_id=None):
else: else:
if application.status == "pub" and application.supporters: if application.status == "pub" and application.supporters:
if request.user.has_perm('application.can_manage_application'): if request.user.has_perm('application.can_manage_application'):
messages.warning(request, _("Attention: Do you really want to edit this application? The supporters will <b>not</b> be removed automatically because you can manage applications. Please check if the supports are valid after your changing!")) 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: else:
messages.warning(request, _("Attention: Do you really want to edit this application? All <b>%s</b> supporters will be removed! Try to convince the supporters again.") % len(application.supporters) ) 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.") % application.supporter.count() )
initial = {'title': application.title, initial = {'title': application.title,
'text': application.text, 'text': application.text,
'reason': application.reason} 'reason': application.reason}
@ -273,7 +271,7 @@ def edit(request, application_id=None):
dataform = formclass(initial=initial, prefix="data") dataform = formclass(initial=initial, prefix="data")
if is_manager: if is_manager:
if application_id is None: if application_id is None:
initial = {'submitter': request.user.openslidesuser.person_id} initial = {'submitter': request.user.person_id}
else: else:
initial = {'submitter': application.submitter.person_id, initial = {'submitter': application.submitter.person_id,
'supporter': [supporter.person_id for supporter in application.supporters]} 'supporter': [supporter.person_id for supporter in application.supporters]}
@ -296,8 +294,8 @@ def set_number(request, application_id):
set a number for an application. set a number for an application.
""" """
try: try:
Application.objects.get(pk=application_id).set_number(user=request.user.openslidesuser) Application.objects.get(pk=application_id).set_number(user=request.user)
messages.success(request, _("Application number was successfully set.")) messages.success(request, _("Motion number was successfully set."))
except Application.DoesNotExist: except Application.DoesNotExist:
pass pass
except NameError: except NameError:
@ -312,8 +310,8 @@ def permit(request, application_id):
permit an application. permit an application.
""" """
try: try:
Application.objects.get(pk=application_id).permit(user=request.user.openslidesuser) Application.objects.get(pk=application_id).permit(user=request.user)
messages.success(request, _("Application was successfully permitted.")) messages.success(request, _("Motion was successfully authorized."))
except Application.DoesNotExist: except Application.DoesNotExist:
pass pass
except NameError, e: except NameError, e:
@ -327,8 +325,8 @@ def notpermit(request, application_id):
reject (not permit) an application. reject (not permit) an application.
""" """
try: try:
Application.objects.get(pk=application_id).notpermit(user=request.user.openslidesuser) Application.objects.get(pk=application_id).notpermit(user=request.user)
messages.success(request, _("Application was successfully rejected.")) messages.success(request, _("Motion was successfully rejected."))
except Application.DoesNotExist: except Application.DoesNotExist:
pass pass
except NameError, e: except NameError, e:
@ -343,8 +341,8 @@ def set_status(request, application_id=None, status=None):
try: try:
if status is not None: if status is not None:
application = Application.objects.get(pk=application_id) application = Application.objects.get(pk=application_id)
application.set_status(user=request.user.openslidesuser, status=status) application.set_status(user=request.user, status=status)
messages.success(request, _("Application status was set to: <b>%s</b>.") % application.get_status_display()) messages.success(request, _("Motion status was set to: <b>%s</b>.") % application.get_status_display())
except Application.DoesNotExist: except Application.DoesNotExist:
pass pass
except NameError, e: except NameError, e:
@ -359,8 +357,8 @@ def reset(request, application_id):
reset an application. reset an application.
""" """
try: try:
Application.objects.get(pk=application_id).reset(user=request.user.openslides.user) Application.objects.get(pk=application_id).reset(user=request.user)
messages.success(request, _("Application status was reset.") ) messages.success(request, _("Motion status was reset.") )
except Application.DoesNotExist: except Application.DoesNotExist:
pass pass
return redirect(reverse('application_view', args=[application_id])) return redirect(reverse('application_view', args=[application_id]))
@ -373,8 +371,8 @@ def support(request, application_id):
support an application. support an application.
""" """
try: try:
Application.objects.get(pk=application_id).support(user=request.user.openslides.user) Application.objects.get(pk=application_id).support(user=request.user)
messages.success(request, _("You have support the application successfully.") ) messages.success(request, _("You have support the motion successfully.") )
except Application.DoesNotExist: except Application.DoesNotExist:
pass pass
return redirect(reverse('application_view', args=[application_id])) return redirect(reverse('application_view', args=[application_id]))
@ -387,8 +385,8 @@ def unsupport(request, application_id):
unsupport an application. unsupport an application.
""" """
try: try:
Application.objects.get(pk=application_id).unsupport(user=request.user.openslidesuser) Application.objects.get(pk=application_id).unsupport(user=request.user)
messages.success(request, _("You have unsupport the application successfully.") ) messages.success(request, _("You have unsupport the motion successfully.") )
except Application.DoesNotExist: except Application.DoesNotExist:
pass pass
return redirect(reverse('application_view', args=[application_id])) return redirect(reverse('application_view', args=[application_id]))
@ -401,7 +399,7 @@ def gen_poll(request, application_id):
gen a poll for this application. gen a poll for this application.
""" """
try: try:
poll = Application.objects.get(pk=application_id).gen_poll(user=request.user.openslidesuser) poll = Application.objects.get(pk=application_id).gen_poll(user=request.user)
messages.success(request, _("New vote was successfully created.") ) messages.success(request, _("New vote was successfully created.") )
except Application.DoesNotExist: except Application.DoesNotExist:
pass # TODO: do not call poll after this excaption pass # TODO: do not call poll after this excaption
@ -418,7 +416,7 @@ def delete_poll(request, poll_id):
count = application.polls.filter(id__lte=poll_id).count() count = application.polls.filter(id__lte=poll_id).count()
if request.method == 'POST': if request.method == 'POST':
poll.delete() poll.delete()
application.writelog(_("Poll deleted"), request.user.openslidesuser) application.writelog(_("Poll deleted"), request.user)
messages.success(request, _('Poll was successfully deleted.')) messages.success(request, _('Poll was successfully deleted.'))
else: else:
del_confirm_form(request, poll, name=_("the %s. poll") % count, delete_link=reverse('application_poll_delete', args=[poll_id])) del_confirm_form(request, poll, name=_("the %s. poll") % count, delete_link=reverse('application_poll_delete', args=[poll_id]))
@ -458,21 +456,21 @@ class ApplicationDelete(DeleteView):
if len(self.applications): if len(self.applications):
for application in self.applications: for application in self.applications:
if not 'delete' in application.get_allowed_actions(user=request.user.openslidesuser): if not 'delete' in application.get_allowed_actions(user=request.user):
messages.error(request, _("You can not delete application <b>%s</b>.") % application) messages.error(request, _("You can not delete motion <b>%s</b>.") % application)
continue continue
title = application.title title = application.title
application.delete(force=True) application.delete(force=True)
messages.success(request, _("Application <b>%s</b> was successfully deleted.") % title) messages.success(request, _("Motion <b>%s</b> was successfully deleted.") % title)
elif self.object: elif self.object:
if not 'delete' in self.object.get_allowed_actions(user=request.user.openslidesuser): if not 'delete' in self.object.get_allowed_actions(user=request.user):
messages.error(request, _("You can not delete application <b>%s</b>.") % self.object) messages.error(request, _("You can not delete motion <b>%s</b>.") % self.object)
else: else:
title = self.object.title title = self.object.title
self.object.delete(force=True) self.object.delete(force=True)
messages.success(request, _("Application <b>%s</b> was successfully deleted.") % title) messages.success(request, _("Motion <b>%s</b> was successfully deleted.") % title)
else: else:
messages.error(request, _("Invalid request")) messages.error(request, _("Invalid request"))
@ -493,7 +491,7 @@ class ApplicationDelete(DeleteView):
self.object = self.get_object() self.object = self.get_object()
if len(self.applications): if len(self.applications):
self.gen_confirm_form(request, _('Do you really want to delete multiple applications?') % self.object.get_absolute_url('delete')) self.gen_confirm_form(request, _('Do you really want to delete multiple motions?') % self.object.get_absolute_url('delete'))
else: else:
self.gen_confirm_form(request, _('Do you really want to delete <b>%s</b>?') % self.object, self.object.get_absolute_url('delete')) self.gen_confirm_form(request, _('Do you really want to delete <b>%s</b>?') % self.object, self.object.get_absolute_url('delete'))
@ -508,12 +506,12 @@ class ViewPoll(PollFormView):
self.application = self.poll.get_application() self.application = self.poll.get_application()
context['application'] = self.application context['application'] = self.application
context['ballot'] = self.poll.get_ballot() context['ballot'] = self.poll.get_ballot()
context['actions'] = self.application.get_allowed_actions(user=self.request.user.openslidesuser) context['actions'] = self.application.get_allowed_actions(user=self.request.user)
return context return context
def get_modelform_class(self): def get_modelform_class(self):
cls = super(ViewPoll, self).get_modelform_class() cls = super(ViewPoll, self).get_modelform_class()
user = self.request.user.openslidesuser user = self.request.user
class ViewPollFormClass(cls): class ViewPollFormClass(cls):
def save(self, commit = True): def save(self, commit = True):
@ -535,10 +533,10 @@ def permit_version(request, aversion_id):
aversion = AVersion.objects.get(pk=aversion_id) aversion = AVersion.objects.get(pk=aversion_id)
application = aversion.application application = aversion.application
if request.method == 'POST': if request.method == 'POST':
application.accept_version(aversion, user=request.user.openslidesuser) application.accept_version(aversion, user=request.user)
messages.success(request, _("Version <b>%s</b> accepted.") % (aversion.aid)) messages.success(request, _("Version <b>%s</b> accepted.") % (aversion.aid))
else: else:
gen_confirm_form(request, _('Do you really want to permit version <b>%s</b>?') % aversion.aid, reverse('application_version_permit', args=[aversion.id])) gen_confirm_form(request, _('Do you really want to authorize version <b>%s</b>?') % aversion.aid, reverse('application_version_permit', args=[aversion.id]))
return redirect(reverse('application_view', args=[application.id])) return redirect(reverse('application_view', args=[application.id]))
@ -547,7 +545,7 @@ def reject_version(request, aversion_id):
aversion = AVersion.objects.get(pk=aversion_id) aversion = AVersion.objects.get(pk=aversion_id)
application = aversion.application application = aversion.application
if request.method == 'POST': if request.method == 'POST':
if application.reject_version(aversion, user=request.user.openslidesuser): if application.reject_version(aversion, user=request.user):
messages.success(request, _("Version <b>%s</b> rejected.") % (aversion.aid)) messages.success(request, _("Version <b>%s</b> rejected.") % (aversion.aid))
else: else:
messages.error(request, _("ERROR by rejecting the version.") ) messages.error(request, _("ERROR by rejecting the version.") )
@ -559,17 +557,6 @@ def reject_version(request, aversion_id):
@permission_required('application.can_manage_application') @permission_required('application.can_manage_application')
@template('application/import.html') @template('application/import.html')
def application_import(request): def application_import(request):
try:
request.user.openslidesuser
except OpenSlidesUser.DoesNotExist:
pass
except AttributeError:
# AnonymousUser
pass
else:
messages.error(request, _('The import function is available for the admin (without user profile) only.'))
return redirect(reverse('application_overview'))
if request.method == 'POST': if request.method == 'POST':
form = ApplicationImportForm(request.POST, request.FILES) form = ApplicationImportForm(request.POST, request.FILES)
if form.is_valid(): if form.is_valid():
@ -652,11 +639,11 @@ def application_import(request):
application.save(user, trivial_change=True) application.save(user, trivial_change=True)
if applications_generated: if applications_generated:
messages.success(request, ungettext('%d application was successfully imported.', messages.success(request, ungettext('%d motion was successfully imported.',
'%d applications were successfully imported.', applications_generated) % applications_generated) '%d motions were successfully imported.', applications_generated) % applications_generated)
if applications_modified: if applications_modified:
messages.success(request, ungettext('%d application was successfully modified.', messages.success(request, ungettext('%d motion was successfully modified.',
'%d applications were successfully modified.', applications_modified) % applications_modified) '%d motions were successfully modified.', applications_modified) % applications_modified)
if users_generated: if users_generated:
messages.success(request, ungettext('%d new user was added.', '%d new users were added.', users_generated) % users_generated) messages.success(request, ungettext('%d new user was added.', '%d new users were added.', users_generated) % users_generated)
return redirect(reverse('application_overview')) return redirect(reverse('application_overview'))
@ -668,8 +655,8 @@ def application_import(request):
else: else:
messages.error(request, _('Please check the form for errors.')) messages.error(request, _('Please check the form for errors.'))
else: else:
messages.warning(request, _("Attention: Existing applications will be modified if you import new applications with the same number.")) messages.warning(request, _("Attention: Existing motions will be modified if you import new motions with the same number."))
messages.warning(request, _("Attention: Importing an application without a number multiple times will create duplicates.")) messages.warning(request, _("Attention: Importing an motions without a number multiple times will create duplicates."))
form = ApplicationImportForm() form = ApplicationImportForm()
return { return {
'form': form, 'form': form,
@ -716,14 +703,14 @@ class ApplicationPDF(PDFView):
story.append(Spacer(0,0.75*cm)) story.append(Spacer(0,0.75*cm))
applications = Application.objects.order_by('number') applications = Application.objects.order_by('number')
if not applications: # No applications existing if not applications: # No applications existing
story.append(Paragraph(_("No applications available."), stylesheet['Heading3'])) story.append(Paragraph(_("No motions available."), stylesheet['Heading3']))
else: # Print all Applications else: # Print all Applications
# List of applications # List of applications
for application in applications: for application in applications:
if application.number: if application.number:
story.append(Paragraph(_("Application No.")+" %s: %s" % (application.number, application.title), stylesheet['Heading3'])) story.append(Paragraph(_("Motion No.")+" %s: %s" % (application.number, application.title), stylesheet['Heading3']))
else: else:
story.append(Paragraph(_("Application No.")+"&nbsp;&nbsp;&nbsp;: %s" % (application.title), stylesheet['Heading3'])) story.append(Paragraph(_("Motion No.")+"&nbsp;&nbsp;&nbsp;: %s" % (application.title), stylesheet['Heading3']))
# Applications details (each application on single page) # Applications details (each application on single page)
for application in applications: for application in applications:
story.append(PageBreak()) story.append(PageBreak())
@ -735,9 +722,9 @@ class ApplicationPDF(PDFView):
def get_application(self, application, story): def get_application(self, application, story):
# application number # application number
if application.number: if application.number:
story.append(Paragraph(_("Application No.")+" %s" % application.number, stylesheet['Heading1'])) story.append(Paragraph(_("Motion No.")+" %s" % application.number, stylesheet['Heading1']))
else: else:
story.append(Paragraph(_("Application No."), stylesheet['Heading1'])) story.append(Paragraph(_("Motion No."), stylesheet['Heading1']))
# submitter # submitter
cell1a = [] cell1a = []
@ -914,7 +901,7 @@ class Config(FormView):
config['application_pdf_title'] = form.cleaned_data['application_pdf_title'] config['application_pdf_title'] = form.cleaned_data['application_pdf_title']
config['application_pdf_preamble'] = form.cleaned_data['application_pdf_preamble'] config['application_pdf_preamble'] = form.cleaned_data['application_pdf_preamble']
config['application_allow_trivial_change'] = form.cleaned_data['application_allow_trivial_change'] config['application_allow_trivial_change'] = form.cleaned_data['application_allow_trivial_change']
messages.success(self.request, _('Application settings successfully saved.')) messages.success(self.request, _('Motion settings successfully saved.'))
return super(Config, self).form_valid(form) return super(Config, self).form_valid(form)

View File

@ -34,7 +34,7 @@ from openslides.utils.person import get_person
from openslides.config.models import config from openslides.config.models import config
from openslides.participant.models import OpenSlidesUser from openslides.participant.models import User
from openslides.projector.projector import Widget from openslides.projector.projector import Widget
@ -97,13 +97,12 @@ def view(request, assignment_id=None):
polls = assignment.poll_set.all() polls = assignment.poll_set.all()
vote_results = assignment.vote_results(only_published=False) vote_results = assignment.vote_results(only_published=False)
user = request.user.openslidesuser
return { return {
'assignment': assignment, 'assignment': assignment,
'form': form, 'form': form,
'vote_results': vote_results, 'vote_results': vote_results,
'polls': polls, 'polls': polls,
'user_is_candidate': assignment.is_candidate(user) 'user_is_candidate': assignment.is_candidate(request.user)
} }
@ -173,7 +172,7 @@ def set_status(request, assignment_id=None, status=None):
def run(request, assignment_id): def run(request, assignment_id):
assignment = Assignment.objects.get(pk=assignment_id) assignment = Assignment.objects.get(pk=assignment_id)
try: try:
assignment.run(request.user.openslidesuser, request.user) assignment.run(request.user, request.user)
messages.success(request, _('You have set your candidature successfully.') ) messages.success(request, _('You have set your candidature successfully.') )
except NameError, e: except NameError, e:
messages.error(request, e) messages.error(request, e)
@ -185,7 +184,7 @@ def delrun(request, assignment_id):
assignment = Assignment.objects.get(pk=assignment_id) assignment = Assignment.objects.get(pk=assignment_id)
try: try:
if assignment.status == 'sea' or user.has_perm("assignment.can_manage_assignment"): if assignment.status == 'sea' or user.has_perm("assignment.can_manage_assignment"):
assignment.delrun(request.user.openslidesuser) assignment.delrun(request.user)
else: else:
messages.error(request, _('The candidate list is already closed.')) messages.error(request, _('The candidate list is already closed.'))
except Exception, e: except Exception, e:

View File

@ -81,7 +81,7 @@ def default_config(sender, key, **kwargs):
return { return {
'event_name': 'OpenSlides', 'event_name': 'OpenSlides',
'event_description': 'event_description':
_('Presentation system for agenda, applications and elections'), _('Presentation system for agenda, motions and elections'),
'event_date': '', 'event_date': '',
'event_location': '', 'event_location': '',
'event_organizer': '', 'event_organizer': '',

View File

@ -67,7 +67,7 @@ class GeneralConfig(FormView):
anonymous = Group.objects.get(name='Anonymous') anonymous = Group.objects.get(name='Anonymous')
except Group.DoesNotExist: except Group.DoesNotExist:
default_perms = [u'can_see_agenda', u'can_see_projector', default_perms = [u'can_see_agenda', u'can_see_projector',
u'can_see_application', 'can_see_assignment'] u'can_see_application', u'can_see_assignment']
anonymous = Group() anonymous = Group()
anonymous.name = 'Anonymous' anonymous.name = 'Anonymous'
anonymous.save() anonymous.save()

File diff suppressed because it is too large Load Diff

View File

@ -46,6 +46,10 @@ USE_I18N = True
# calendars according to the current locale # calendars according to the current locale
USE_L10N = True USE_L10N = True
LOCALE_PATHS = (
_fs2unicode(os.path.join(SITE_ROOT, 'locale'))
)
# Absolute path to the directory that holds media. # Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/" # Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = _fs2unicode(os.path.join(SITE_ROOT, './static/')) MEDIA_ROOT = _fs2unicode(os.path.join(SITE_ROOT, './static/'))
@ -89,7 +93,7 @@ MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'openslides.participant.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.locale.LocaleMiddleware', 'django.middleware.locale.LocaleMiddleware',
) )

View File

@ -10,13 +10,19 @@
:license: GNU GPL, see LICENSE for more details. :license: GNU GPL, see LICENSE for more details.
""" """
# for python 2.5 support
from __future__ import with_statement
from random import choice from random import choice
import string import string
import csv
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import transaction
from openslides.utils.person import get_person from openslides.utils import csv_ext
from openslides.participant.models import OpenSlidesUser
from openslides.participant.models import User
def gen_password(): def gen_password():
@ -47,3 +53,44 @@ def gen_username(first_name, last_name):
User.objects.get(username=testname) User.objects.get(username=testname)
except User.DoesNotExist: except User.DoesNotExist:
return testname return testname
def import_users(csv_file):
error_messages = []
count_success = 0
try:
# check for valid encoding (will raise UnicodeDecodeError if not)
csv_file.read().decode('utf-8')
csv_file.seek(0)
with transaction.commit_on_success():
dialect = csv.Sniffer().sniff(csv_file.readline())
dialect = csv_ext.patchup(dialect)
csv_file.seek(0)
for (line_no, line) in enumerate(csv.reader(csv_file,
dialect=dialect)):
if line_no:
try:
(first_name, last_name, gender, category, type, committee, comment) = line[:7]
except ValueError:
error_messages.append(_('Ignoring malformed line %d in import file.') % line_no + 1)
continue
user = User()
user.last_name = last_name
user.first_name = first_name
user.username = gen_username(first_name, last_name)
user.gender = gender
user.category = category
user.type = type
user.committee = committee
user.comment = comment
user.firstpassword = gen_password()
user.save()
user.reset_password()
count_success += 1
except csv.Error:
error_messages.appen(_('Import aborted because of severe errors in the input file.'))
except UnicodeDecodeError:
error_messages.appen(_('Import file has wrong character encoding, only UTF-8 is supported!'))
return (count_success, error_messages)

View File

@ -11,30 +11,21 @@
""" """
from django import forms from django import forms
from django.contrib.auth.forms import AdminPasswordChangeForm from django.contrib.auth.models import Permission
from django.contrib.auth.models import User, Group, Permission from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext_lazy as _, ugettext_noop
from openslides.utils.forms import ( from openslides.utils.forms import (
CssClassMixin, LocalizedModelMultipleChoiceField) CssClassMixin, LocalizedModelMultipleChoiceField)
from openslides.participant.models import OpenSlidesUser from openslides.participant.models import User, Group
USER_APPLICATION_IMPORT_OPTIONS = [ class UserCreateForm(forms.ModelForm, CssClassMixin):
('REASSIGN', _('Keep applications, try to reassign submitter')),
('INREVIEW', _('Keep applications, set status to "needs review"')),
('DISCARD', _('Discard applications'))
]
class UserNewForm(forms.ModelForm, CssClassMixin):
first_name = forms.CharField(label=_("First name"))
last_name = forms.CharField(label=_("Last name"))
groups = forms.ModelMultipleChoiceField( groups = forms.ModelMultipleChoiceField(
queryset=Group.objects.all(), label=_("User groups"), required=False) queryset=Group.objects.exclude(name__iexact='anonymous'),
is_active = forms.BooleanField( label=_("User groups"), required=False)
label=_("Active"), required=False, initial=True) is_active = forms.BooleanField(label=_("Active"), required=False,
initial=True)
class Meta: class Meta:
model = User model = User
@ -45,45 +36,74 @@ class UserNewForm(forms.ModelForm, CssClassMixin):
class UserEditForm(forms.ModelForm, CssClassMixin): class UserEditForm(forms.ModelForm, CssClassMixin):
first_name = forms.CharField(label=_("First name")) first_name = forms.CharField(label=_("First name"))
last_name = forms.CharField(label=_("Last name")) last_name = forms.CharField(label=_("Last name"))
groups = forms.ModelMultipleChoiceField( groups = forms.ModelMultipleChoiceField(queryset=Group.objects.all(),
queryset=Group.objects.all(), label=_("User groups"), required=False) label=_("User groups"), required=False)
is_active = forms.BooleanField(label=_("Active"), required=False) is_active = forms.BooleanField(label=_("Active"), required=False)
class Meta: class Meta:
model = User model = User
exclude = ('password', 'is_staff', 'is_superuser', 'last_login', fields = ('first_name', 'last_name', 'is_active', 'groups', 'category',
'date_joined', 'user_permissions') 'gender', 'type', 'committee', 'comment', 'default_password')
class UsernameForm(forms.ModelForm, CssClassMixin): class UserUpdateForm(UserCreateForm):
class Meta: class Meta:
model = User model = User
exclude = ('first_name', 'last_name', 'email', 'is_active', fields = ('username', 'first_name', 'last_name', 'is_active', 'groups',
'is_superuser', 'groups', 'password', 'is_staff', 'category', 'gender', 'type', 'committee', 'comment',
'last_login', 'date_joined', 'user_permissions') 'default_password')
class OpenSlidesUserForm(forms.ModelForm, CssClassMixin):
class Meta:
model = OpenSlidesUser
class GroupForm(forms.ModelForm, CssClassMixin): class GroupForm(forms.ModelForm, CssClassMixin):
as_user = forms.BooleanField(
initial=False, required=False, label=_("Treat Group as User"),
help_text=_("The Group will appear on any place, other user does."))
permissions = LocalizedModelMultipleChoiceField( permissions = LocalizedModelMultipleChoiceField(
queryset=Permission.objects.all(), label=_("Persmissions")) queryset=Permission.objects.all(), label=_("Persmissions"),
required=False)
users = forms.ModelMultipleChoiceField(
queryset=User.objects.all(), label=_("Users"), required=False)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(GroupForm, self).__init__(*args, **kwargs) # Initial users
if kwargs.get('instance', None) is not None: if kwargs.get('instance', None) is not None:
self.fields['permissions'].initial = ( initial = kwargs.setdefault('initial', {})
[p.pk for p in kwargs['instance'].permissions.all()]) initial['users'] = [django_user.user.pk for django_user in kwargs['instance'].user_set.all()]
super(GroupForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
instance = forms.ModelForm.save(self, False)
old_save_m2m = self.save_m2m
def save_m2m():
old_save_m2m()
instance.user_set.clear()
for user in self.cleaned_data['users']:
instance.user_set.add(user)
self.save_m2m = save_m2m
if commit:
instance.save()
self.save_m2m()
return instance
def clean_name(self):
# Do not allow to change the name "anonymous" or give another group
# this name
data = self.cleaned_data['name']
if self.instance.name.lower() == 'anonymous':
# Editing the anonymous-user
if self.instance.name.lower() != data.lower():
raise forms.ValidationError(
_('You can not edit the name for the anonymous user'))
else:
if data.lower() == 'anonymous':
raise forms.ValidationError(
_('Group name "%s" is reserved for internal use.') % data)
return data
class Meta: class Meta:
model = Group model = Group
exclude = ('permissions',)
class UsersettingsForm(forms.ModelForm, CssClassMixin): class UsersettingsForm(forms.ModelForm, CssClassMixin):
@ -91,13 +111,9 @@ class UsersettingsForm(forms.ModelForm, CssClassMixin):
model = User model = User
fields = ('username', 'first_name', 'last_name', 'email') fields = ('username', 'first_name', 'last_name', 'email')
class UserImportForm(forms.Form, CssClassMixin): class UserImportForm(forms.Form, CssClassMixin):
csvfile = forms.FileField(widget=forms.FileInput(attrs={'size': '50'}), csvfile = forms.FileField(widget=forms.FileInput(attrs={'size': '50'}),
label=_("CSV File")) label=_("CSV File"))
application_handling = forms.ChoiceField(
required=True, choices=USER_APPLICATION_IMPORT_OPTIONS,
label=_("For existing applications"))
class ConfigForm(forms.Form, CssClassMixin): class ConfigForm(forms.Form, CssClassMixin):

View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.utils.middleware
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Additional definitions for OpenSlides forms.
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.contrib.auth.middleware import AuthenticationMiddleware as _AuthenticationMiddleware
from django.contrib.auth.models import AnonymousUser
class AuthenticationMiddleware(_AuthenticationMiddleware):
def process_request(self, request):
super(AuthenticationMiddleware, self).process_request(request)
if not isinstance(request.user, AnonymousUser):
request.user = request.user.user

View File

@ -10,20 +10,20 @@
:license: GNU GPL, see LICENSE for more details. :license: GNU GPL, see LICENSE for more details.
""" """
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User as DjangoUser, Group as DjangoGroup
from django.db import models from django.db import models
from django.db.models import Q, signals from django.db.models import signals
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _, ugettext_noop from django.utils.translation import ugettext_lazy as _, ugettext_noop
from openslides.utils.person import PersonMixin from openslides.utils.person import PersonMixin
from openslides.utils.person.signals import receiv_persons from openslides.utils.person.signals import receive_persons
from openslides.config.signals import default_config_value from openslides.config.signals import default_config_value
class OpenSlidesUser(models.Model, PersonMixin): class User(DjangoUser, PersonMixin):
person_prefix = 'openslides_user' person_prefix = 'user'
GENDER_CHOICES = ( GENDER_CHOICES = (
('male', _('Male')), ('male', _('Male')),
('female', _('Female')), ('female', _('Female')),
@ -35,10 +35,10 @@ class OpenSlidesUser(models.Model, PersonMixin):
('guest', _('Guest')), ('guest', _('Guest')),
) )
user = models.OneToOneField(User, unique=True, editable=False) django_user = models.OneToOneField(DjangoUser, editable=False, parent_link=True)
name_surfix = models.CharField( category = models.CharField(
max_length=100, null=True, blank=True, verbose_name=_("Name Surfix"), max_length=100, null=True, blank=True, verbose_name=_("Category"),
help_text=_('Shown behind the name.')) help_text=_('Will be shown behind the name.'))
gender = models.CharField( gender = models.CharField(
max_length=50, choices=GENDER_CHOICES, blank=True, max_length=50, choices=GENDER_CHOICES, blank=True,
verbose_name=_("Gender"), help_text=_('Only for filter the userlist.')) verbose_name=_("Gender"), help_text=_('Only for filter the userlist.'))
@ -51,21 +51,26 @@ class OpenSlidesUser(models.Model, PersonMixin):
comment = models.TextField( comment = models.TextField(
null=True, blank=True, verbose_name=_('Comment'), null=True, blank=True, verbose_name=_('Comment'),
help_text=_('Only for notes.')) help_text=_('Only for notes.'))
firstpassword = models.CharField( default_password = models.CharField(
max_length=100, null=True, blank=True, max_length=100, null=True, blank=True,
verbose_name=_("First Password")) verbose_name=_("Default password"))
def get_name_suffix(self):
return self.category
def set_name_suffix(self, value):
self.category = value
name_suffix = property(get_name_suffix, set_name_suffix)
def reset_password(self, password=None): def reset_password(self, password=None):
""" """
Reset the password for the user to his default-password. Reset the password for the user to his default-password.
""" """
if password is None: if password is None:
password = self.firstpassword password = self.default_password
self.user.set_password(password) self.set_password(password)
self.user.save() self.save()
def has_perm(self, perm):
return self.user.has_perm(perm)
@models.permalink @models.permalink
def get_absolute_url(self, link='edit'): def get_absolute_url(self, link='edit'):
@ -77,14 +82,15 @@ class OpenSlidesUser(models.Model, PersonMixin):
* delete * delete
""" """
if link == 'edit': if link == 'edit':
return ('user_edit', [str(self.user.id)]) return ('user_edit', [str(self.id)])
if link == 'delete': if link == 'delete':
return ('user_delete', [str(self.user.id)]) return ('user_delete', [str(self.id)])
def __unicode__(self): def __unicode__(self):
if self.name_surfix: name = self.get_full_name() or self.username
return "%s (%s)" % (self.user.get_full_name(), self.name_surfix) if self.name_suffix:
return "%s" % self.user.get_full_name() return u"%s (%s)" % (name, self.name_suffix)
return u"%s" % name
class Meta: class Meta:
# Rename permissions # Rename permissions
@ -95,46 +101,63 @@ class OpenSlidesUser(models.Model, PersonMixin):
) )
class OpenSlidesGroup(models.Model, PersonMixin): class Group(DjangoGroup, PersonMixin):
person_prefix = 'openslides_group' person_prefix = 'group'
group = models.OneToOneField(Group) django_group = models.OneToOneField(DjangoGroup, editable=False, parent_link=True)
group_as_person = models.BooleanField(default=False) group_as_person = models.BooleanField(default=False)
description = models.TextField(blank=True)
@models.permalink
def get_absolute_url(self, link='edit'):
"""
Return the URL to this user.
link can be:
* edit
* delete
"""
if link == 'edit':
return ('user_group_edit', [str(self.id)])
if link == 'delete':
return ('user_group_delete', [str(self.id)])
def __unicode__(self): def __unicode__(self):
return unicode(self.group) return unicode(self.name)
class OpenSlidesUsersConnecter(object): class UsersConnector(object):
def __init__(self, person_prefix=None, id=None): def __init__(self, person_prefix_filter=None, id_filter=None):
self.person_prefix = person_prefix self.person_prefix_filter = person_prefix_filter
self.id = id self.id_filter = id_filter
self.users = User.objects.all()
self.groups = Group.objects.filter(group_as_person=True)
def __iter__(self): def __iter__(self):
if (not self.person_prefix or if (not self.person_prefix_filter or
self.person_prefix == OpenSlidesUser.person_prefix): self.person_prefix_filter == User.person_prefix):
if self.id: if self.id_filter:
yield OpenSlidesUser.objects.get(pk=self.id) yield self.users.get(pk=self.id_filter)
else: else:
for user in OpenSlidesUser.objects.all(): for user in self.users:
yield user yield user
if (not self.person_prefix or if (not self.person_prefix_filter or
self.person_prefix == OpenSlidesGroup.person_prefix): self.person_prefix_filter == Group.person_prefix):
if self.id: if self.id_filter:
yield OpenSlidesGroup.objects.get(pk=self.id) yield self.groups.get(pk=self.id_filter)
else: else:
for group in OpenSlidesGroup.objects.all(): for group in self.groups:
yield group yield group
def __getitem__(self, key): def __getitem__(self, key):
return OpenSlidesUser.objects.get(pk=key) return User.objects.get(pk=key)
@receiver(receiv_persons, dispatch_uid="participant") @receiver(receive_persons, dispatch_uid="participant")
def receiv_persons(sender, **kwargs): def receive_persons(sender, **kwargs):
return OpenSlidesUsersConnecter(person_prefix=kwargs['person_prefix'], return UsersConnector(person_prefix_filter=kwargs['person_prefix_filter'],
id=kwargs['id']) id_filter=kwargs['id_filter'])
@receiver(default_config_value, dispatch_uid="participant_default_config") @receiver(default_config_value, dispatch_uid="participant_default_config")
@ -150,7 +173,17 @@ def default_config(sender, key, **kwargs):
}.get(key) }.get(key)
@receiver(signals.post_save, sender=User) @receiver(signals.post_save, sender=DjangoUser)
def user_post_save(sender, instance, signal, *args, **kwargs): def user_post_save(sender, instance, signal, *args, **kwargs):
# Creates OpenSlidesUser try:
profile, new = OpenSlidesUser.objects.get_or_create(user=instance) instance.user
except User.DoesNotExist:
User(django_user=instance).save_base(raw=True)
@receiver(signals.post_save, sender=DjangoGroup)
def group_post_save(sender, instance, signal, *args, **kwargs):
try:
instance.group
except Group.DoesNotExist:
Group(django_group=instance).save_base(raw=True)

View File

@ -9,15 +9,18 @@ $(function() {
$('.status_link').click(function(event) { $('.status_link').click(function(event) {
event.preventDefault(); event.preventDefault();
link = $(this); link = $(this);
group = $(this).parent();
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: link.attr('href'), url: link.attr('href'),
dataType: 'json', dataType: 'json',
success: function(data) { success: function(data) {
if (data.active) { if (data.active) {
link.addClass('active'); group.children('.status_link.deactivate').show();
group.children('.status_link.activate').hide();
} else { } else {
link.removeClass('active'); group.children('.status_link.deactivate').hide();
group.children('.status_link.activate').show();
} }
} }
}); });

View File

@ -4,14 +4,19 @@
* :copyright: 2011, 2012 by OpenSlides team, see AUTHORS. * :copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
* :license: GNU GPL, see LICENSE for more details. * :license: GNU GPL, see LICENSE for more details.
*/ */
a.status_link span { a.status_link span {
background-image: url(../images/icons/off.png);
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
width: 16px; width: 16px;
height: 16px; height: 16px;
display: inline-block; display: inline-block;
} }
a.status_link.active span {
a.status_link.deactivate span {
background-image: url(../images/icons/on.png); background-image: url(../images/icons/on.png);
} }
a.status_link.activate span {
background-image: url(../images/icons/off.png);
}

View File

@ -4,7 +4,7 @@
{% block title %} {% block title %}
{{ block.super }} {{ block.super }}
{% if edituser %} {% if edit_user %}
{% trans "Edit participant" %} {% trans "Edit participant" %}
{% else %} {% else %}
{% trans "New participant" %} {% trans "New participant" %}
@ -13,17 +13,18 @@
{% block content %} {% block content %}
{% if edituser %} {% if edit_user %}
<h1>{% trans "Edit participant" %}</h1> <h1>{% trans "Edit participant" %}</h1>
{% else %} {% else %}
<h1>{% trans "New participant" %}</h1> <h1>{% trans "New participant" %}</h1>
{% endif %} {% endif %}
<form action="" method="post">{% csrf_token %} <form action="" method="post">{% csrf_token %}
{{ userform.as_p }} {{ form.as_p }}
{{ profileform.as_p }} {% if edit_user %}
{% if edituser %} <p>
<p><a href="{% url user_reset_password edituser.id %}">{% trans 'Reset to First Password' %}</a></p> <a href="{% url user_reset_password edit_user.id %}">{% trans 'Reset to First Password' %}</a>
</p>
{% endif %} {% endif %}
<p> <p>
<button class="button" type="submit"> <button class="button" type="submit">

View File

@ -13,58 +13,59 @@
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>{% trans "Participants" %}</h1> <h1>{% trans "Participants" %}</h1>
<p><form action="{{request.url}}" name="filter" method="post"> <p>
{% csrf_token %} <form action="" name="filter" method="get">
{% trans "Filter" %}: {% trans "Filter" %}:
<select class="default-input" name="gender" onchange="document.forms['filter'].submit()"> <select class="default-input" name="gender" onchange="document.forms['filter'].submit()">
<option value="---">-- {% trans "Gender" %} --</option> <option value="---">-- {% trans "Gender" %} --</option>
<option value="male" {% if 'male' in sortfilter.gender %}selected{% endif %}>{% trans "Male" %}</option> <option value="male"{% if 'male' in sortfilter.gender %} selected{% endif %}>{% trans "Male" %}</option>
<option value="female" {% if 'female' in sortfilter.gender %}selected{% endif %}>{% trans "Female" %}</option> <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> <option value=""{% if '' in sortfilter.gender %} selected{% endif %}>{% trans "Not specified" %}</option>
</select> </select>
<select class="default-input" name="group" onchange="document.forms['filter'].submit()"> <select class="default-input" name="category" onchange="document.forms['filter'].submit()">
<option value="---">-- {% trans "Group" %} --</option> <option value="---">-- {% trans "Category" %} --</option>
{% for group in groups %} {% for category in categories %}
<option value="{{ group }}" {% if group in sortfilter.group %}selected{% endif %}> <option value="{{ category }}"{% if category in sortfilter.category %} selected{% endif %}>
{{ group }}</option> {{ category }}</option>
{% endfor %} {% endfor %}
</select> </select>
<select class="default-input" name="type" onchange="document.forms['filter'].submit()"> <select class="default-input" name="type" onchange="document.forms['filter'].submit()">
<option value="---">-- {% trans "Type" %} --</option> <option value="---">-- {% trans "Type" %} --</option>
<option value="delegate" {% if 'delegate' in sortfilter.type %}selected{% endif %}>{% trans "Delegate" %}</option> <option value="delegate"{% if 'delegate' in sortfilter.type %} selected{% endif %}>{% trans "Delegate" %}</option>
<option value="observer" {% if 'observer' in sortfilter.type %}selected{% endif %}>{% trans "Observer" %}</option> <option value="observer"{% if 'observer' in sortfilter.type %} selected{% endif %}>{% trans "Observer" %}</option>
<option value="staff" {% if 'staff' in sortfilter.type %}selected{% endif %}>{% trans "Staff" %}</option> <option value="staff"{% if 'staff' in sortfilter.type %} selected{% endif %}>{% trans "Staff" %}</option>
<option value="guest" {% if 'guest' in sortfilter.type %}selected{% endif %}>{% trans "Guest" %}</option> <option value="guest"{% if 'guest' in sortfilter.type %} selected{% endif %}>{% trans "Guest" %}</option>
<option value="" {% if '' in sortfilter.type %}selected{% endif %}>{% trans "Not specified" %}</option> <option value=""{% if '' in sortfilter.type %} selected{% endif %}>{% trans "Not specified" %}</option>
</select> </select>
<select class="default-input" name="committee" onchange="document.forms['filter'].submit()"> <select class="default-input" name="committee" onchange="document.forms['filter'].submit()">
<option value="---">-- {% trans "Committee" %} --</option> <option value="---">-- {% trans "Committee" %} --</option>
{% for committee in committees %} {% for committee in committees %}
<option value="{{ committee }}" {% if committee in sortfilter.committee %}selected{% endif %}> <option value="{{ committee }}"{% if committee in sortfilter.committee %} selected{% endif %}>
{{ committee }}</option> {{ committee }}</option>
{% endfor %} {% endfor %}
</select> </select>
<select class="default-input" name="status" onchange="document.forms['filter'].submit()"> <select class="default-input" name="status" onchange="document.forms['filter'].submit()">
<option value="---">-- {% trans "Status" %} --</option> <option value="---">-- {% trans "Status" %} --</option>
<option value="1" {% if '1' in sortfilter.status %}selected{% endif %}>{% trans "Active" %}</option> <option value="1"{% if '1' in sortfilter.status %} selected{% endif %}>{% trans "Active" %}</option>
<option value="0" {% if '0' in sortfilter.status %}selected{% endif %}>{% trans "Inactive" %}</option> <option value="0"{% if '0' in sortfilter.status %} selected{% endif %}>{% trans "Inactive" %}</option>
</select> </select>
</form> </form>
</p> </p>
{% if users|length == allusers|length %} {% if users.count == allusers %}
{{ users|length }} {{ users.count }}
{% blocktrans count counter=users|length %}participant{% plural %}participants{% endblocktrans %} {% blocktrans count counter=users.count %}participant{% plural %}participants{% endblocktrans %}
{% else %} {% else %}
{{ users|length }} {% trans "of" %} {{ allusers|length }} {% trans "Participants" %} (= {{ percent }} %) {{ users.count }} {% trans "of" %} {{ allusers }} {% trans "Participants" %} (= {{ percent }} %)
{% endif %} {% endif %}
<table> <table>
<tr> <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=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=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=group&reverse={% if 'group' in sortfilter.sort and 'reverse' not in sortfilter %}1{% else %}---{%endif%}">{% trans "Group" %}</a></th> <th><a href="?sort=group&reverse={% if 'category' in sortfilter.sort and 'reverse' not in sortfilter %}1{% else %}---{%endif%}">{% trans "Category" %}</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=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> <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 %} {% if perms.participant.can_manage_participant %}
@ -77,20 +78,28 @@
<tr class="{% cycle '' 'odd' %}"> <tr class="{% cycle '' 'odd' %}">
<td>{{ user.first_name }}</td> <td>{{ user.first_name }}</td>
<td>{{ user.last_name }}</td> <td>{{ user.last_name }}</td>
<td>{{ user.openslidesuser.name_surfix }}</td> <td>{{ user.category }}</td>
<td>{{ user.openslidesuser.get_type_display }}</td> <td>{{ user.get_type_display }}</td>
<td>{{ user.openslidesuser.committee }}</td> <td>{{ user.committee }}</td>
{% if perms.participant.can_manage_participant %} {% if perms.participant.can_manage_participant %}
<td>{{ user.openslidesuser.comment|first_line }}</td> <td>{{ user.comment|first_line }}</td>
<td>{% if user.last_login > user.date_joined %} <td>
{% if user.last_login > user.date_joined %}
{{ user.last_login }} {{ user.last_login }}
{% endif %}</td> {% endif %}
<td><span style="width: 1px; white-space: nowrap;"> </td>
<a href="{% url user_edit user.id %}"><img src="{% static 'images/icons/edit.png' %}" title="{% trans 'Edit participant' %}"></a> <td>
<a href="{% url user_delete user.id %}"><img src="{% static 'images/icons/delete.png' %}" title="{% trans 'Delete participant' %}"></a> <span style="width: 1px; white-space: nowrap;">
<a class="status_link {% if user.is_active %}active{% endif %}" <a href="{% url user_edit user.id %}">
href="{% url user_status user.id %}" <img src="{% static 'images/icons/edit.png' %}" title="{% trans 'Edit participant' %}">
title="{% trans 'Change status (active/inactive)' %}"> </a>
<a href="{% url user_delete user.id %}">
<img src="{% static 'images/icons/delete.png' %}" title="{% trans 'Delete participant' %}">
</a>
<a class="status_link deactivate" href="{% url user_status_deactivate user.id %}" title="{% trans 'Change status to inactive' %}"{% if not user.is_active %} style="display:none"{% endif %}>
<span></span>
</a>
<a class="status_link activate" href="{% url user_status_activate user.id %}" title="{% trans 'Change status to active' %}"{% if user.is_active %} style="display:none"{% endif %}>
<span></span> <span></span>
</a> </a>
</span> </span>

View File

@ -12,61 +12,65 @@
from django.test import TestCase from django.test import TestCase
from django.test.client import Client from django.test.client import Client
from django.contrib.auth.models import User, Group
from django.db.models.query import EmptyQuerySet
from django.contrib.auth.hashers import check_password from django.contrib.auth.hashers import check_password
from openslides.utils.person import get_person, Persons from openslides.utils.person import get_person, Persons
from openslides.participant.api import gen_username, gen_password from openslides.participant.api import gen_username, gen_password
from openslides.participant.models import OpenSlidesUser, OpenSlidesGroup from openslides.participant.models import User, Group
class OpenSlidesUserTest(TestCase): class UserTest(TestCase):
def setUp(self): def setUp(self):
self.user1 = User(first_name=u'Max', last_name=u'Mustermann') self.user1 = User()
self.user1.username = gen_username(self.user1.first_name, self.user1.last_name) self.user1.first_name = u'Max'
self.user1.last_name = u'Mustermann'
self.user1.username = gen_username(
self.user1.first_name, self.user1.last_name)
self.user1.default_password = gen_password()
self.user1.save() self.user1.save()
self.openslidesuser1 = self.user1.openslidesuser self.django_user1 = self.user1.django_user
self.openslidesuser1.firstpassword = gen_password()
self.openslidesuser1.save()
self.user1 = self.openslidesuser1.user
def test_participant_user(self): def test_participant_user(self):
self.assertEqual(self.user1.openslidesuser, self.openslidesuser1) self.assertEqual(self.django_user1.user, self.user1)
self.assertEqual(self.user1, self.openslidesuser1.user) self.assertEqual(self.django_user1, self.user1.django_user)
def test_repr(self): def test_repr(self):
self.assertEqual(unicode(self.openslidesuser1), u'Max Mustermann') self.assertEqual(unicode(self.user1), u'Max Mustermann')
def test_name_surfix(self): def test_name_surfix(self):
self.openslidesuser1.name_surfix = u'München' self.user1.category = u'München'
self.openslidesuser1.save() self.user1.save()
self.assertEqual(unicode(self.openslidesuser1), u'Max Mustermann (München)') self.assertEqual(unicode(self.user1), u'Max Mustermann (München)')
def test_reset_password(self): def test_reset_password(self):
self.assertIsInstance(self.openslidesuser1.firstpassword, basestring) self.assertIsInstance(self.user1.default_password, basestring)
self.assertEqual(len(self.openslidesuser1.firstpassword), 8) self.assertEqual(len(self.user1.default_password), 8)
self.user1.set_unusable_password() self.user1.set_unusable_password()
self.assertFalse(self.user1.check_password(self.openslidesuser1.firstpassword)) self.assertFalse(self.user1.check_password(self.user1.default_password))
self.openslidesuser1.reset_password() self.user1.reset_password()
self.assertTrue(self.user1.check_password(self.openslidesuser1.firstpassword)) self.assertTrue(self.user1.check_password(self.user1.default_password))
def test_person_api(self): def test_person_api(self):
self.assertTrue(hasattr(self.openslidesuser1, 'person_id')) self.assertTrue(hasattr(self.user1, 'person_id'))
self.assertEqual(self.openslidesuser1.person_id, 'openslides_user:1') self.assertEqual(self.user1.person_id, 'user:1')
self.assertEqual(get_person('openslides_user:1'), self.openslidesuser1) self.assertEqual(get_person('user:1'), self.user1)
self.assertEqual(len(Persons()), 1) self.assertEqual(len(Persons(person_prefix_filter='user')), 1)
class OpenSlidesGroupTest(TestCase): class GroupTest(TestCase):
def setUp(self): def setUp(self):
self.group1 = Group.objects.create(name='Test Group') self.group1 = Group.objects.create(name='Test Group')
self.openslidesgroup1 = OpenSlidesGroup.objects.create(group=self.group1) self.django_group1 = self.group1.django_group
def test_group_openslidesgroup(self): def test_group_group(self):
self.assertEqual(self.openslidesgroup1.group, self.group1) self.assertEqual(self.group1.django_group, self.django_group1)
self.assertEqual(self.group1, self.django_group1.group)
def test_person_api(self): def test_person_api(self):
self.assertTrue(hasattr(self.openslidesgroup1, 'person_id')) self.assertTrue(hasattr(self.group1, 'person_id'))
self.assertEqual(self.openslidesgroup1.person_id, 'openslides_group:1') person_id = "group:%d" % self.group1.id
self.assertEqual(get_person('openslides_group:1'), self.openslidesgroup1) self.assertEqual(self.group1.person_id, person_id)
self.assertRaises(Group.DoesNotExist)
self.group1.group_as_person = True
self.group1.save()
self.assertEqual(get_person(person_id), self.group1)

View File

@ -13,70 +13,86 @@
from django.conf.urls.defaults import url, patterns from django.conf.urls.defaults import url, patterns
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from openslides.participant.views import (ParticipantsListPDF, from openslides.participant.views import (
ParticipantsPasswordsPDF) ParticipantsListPDF, ParticipantsPasswordsPDF, Overview, UserCreateView,
UserUpdateView, UserDeleteView, SetUserStatusView, UserImportView,
ResetPasswordView, GroupOverviewView, GroupCreateView, GroupUpdateView,
GroupDeleteView)
urlpatterns = patterns('openslides.participant.views', urlpatterns = patterns('openslides.participant.views',
url(r'^$', url(r'^$',
'get_overview', Overview.as_view(),
name='user_overview', name='user_overview',
), ),
url(r'^new/$', url(r'^new/$',
'edit', UserCreateView.as_view(),
name='user_new', name='user_new',
), ),
url(r'^(?P<user_id>\d+)/edit/$', url(r'^(?P<pk>\d+)/edit/$',
'edit', UserUpdateView.as_view(),
name='user_edit', name='user_edit',
), ),
url(r'^(?P<pk>\d+)/del/$',
UserDeleteView.as_view(),
name='user_delete',
),
url(r'^(?P<pk>\d+)/reset_password/$',
ResetPasswordView.as_view(),
name='user_reset_password',
),
url(r'^(?P<pk>\d+)/status/toggle/$',
SetUserStatusView.as_view(),
{'action': 'toggle'},
name='user_status_toggle',
),
url(r'^(?P<pk>\d+)/status/activate/$',
SetUserStatusView.as_view(),
{'action': 'activate'},
name='user_status_activate',
),
url(r'^(?P<pk>\d+)/status/deactivate/$',
SetUserStatusView.as_view(),
{'action': 'deactivate'},
name='user_status_deactivate',
),
url(r'^import/$',
UserImportView.as_view(),
name='user_import',
),
url(r'^group/$',
GroupOverviewView.as_view(),
name='user_group_overview',
),
url(r'^group/new/$',
GroupCreateView.as_view(),
name='user_group_new',
),
url(r'^group/(?P<pk>\d+)/edit/$',
GroupUpdateView.as_view(),
name='user_group_edit',
),
url(r'^group/(?P<pk>\d+)/del/$',
GroupDeleteView.as_view(),
name='user_group_delete',
),
url(r'^print/$', url(r'^print/$',
ParticipantsListPDF.as_view(), ParticipantsListPDF.as_view(),
name='user_print', name='user_print',
), ),
url(r'^(?P<user_id>\d+)/del/$',
'user_delete',
name='user_delete',
),
url(r'^(?P<user_id>\d+)/status/$',
'user_set_status',
name='user_status',
),
url(r'^import/$',
'user_import',
name='user_import',
),
url(r'^group/$',
'get_group_overview',
name='user_group_overview',
),
url(r'^group/new/$',
'group_edit',
name='user_group_new',
),
url(r'^group/(?P<group_id>\d+)/edit/$',
'group_edit',
name='user_group_edit',
),
url(r'^group/(?P<group_id>\d+)/del/$',
'group_delete',
name='user_group_delete',
),
url(r'^resetpassword/(?P<user_id>\d+)/$',
'reset_password',
name='user_reset_password',
),
url(r'^passwords/print/$', url(r'^passwords/print/$',
ParticipantsPasswordsPDF.as_view(), ParticipantsPasswordsPDF.as_view(),
name='print_passwords', name='print_passwords',

File diff suppressed because it is too large Load Diff

View File

@ -55,7 +55,6 @@ for plugin in settings.INSTALLED_PLUGINS:
urlpatterns += patterns('', urlpatterns += patterns('',
(r'^500/$', 'openslides.utils.views.server_error'),
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
url(r'^login/$', url(r'^login/$',

View File

@ -10,7 +10,7 @@
:license: GNU GPL, see LICENSE for more details. :license: GNU GPL, see LICENSE for more details.
""" """
from openslides.utils.person.signals import receiv_persons 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, Persons
from openslides.utils.person.forms import PersonFormField, MultiplePersonFormField from openslides.utils.person.forms import PersonFormField, MultiplePersonFormField
from openslides.utils.person.models import PersonField, PersonMixin from openslides.utils.person.models import PersonField, PersonMixin

View File

@ -10,16 +10,16 @@
:license: GNU GPL, see LICENSE for more details. :license: GNU GPL, see LICENSE for more details.
""" """
from openslides.utils.person.signals import receiv_persons from openslides.utils.person.signals import receive_persons
class Persons(object): class Persons(object):
""" """
A Storage for a multiplicity of different Person-Objects. A Storage for a multiplicity of different Person-Objects.
""" """
def __init__(self, person_prefix=None, id=None): def __init__(self, person_prefix_filter=None, id_filter=None):
self.person_prefix = person_prefix self.person_prefix_filter = person_prefix_filter
self.id = id self.id_filter = id_filter
def __iter__(self): def __iter__(self):
try: try:
@ -35,14 +35,16 @@ class Persons(object):
def iter_persons(self): def iter_persons(self):
self._cache = list() self._cache = list()
for receiver, persons in receiv_persons.send( for receiver, persons in receive_persons.send(
sender='persons', person_prefix=self.person_prefix, id=self.id): sender='persons', person_prefix_filter=self.person_prefix_filter, id_filter=self.id_filter):
for person in persons: for person in persons:
self._cache.append(person) self._cache.append(person)
yield person yield person
def generate_person_id(prefix, id): def generate_person_id(prefix, id):
assert prefix is not None
assert id is not None
if ':' in prefix: if ':' in prefix:
raise ValueError("':' is not allowed in a the 'person_prefix'") raise ValueError("':' is not allowed in a the 'person_prefix'")
return "%s:%d" % (prefix, id) return "%s:%d" % (prefix, id)
@ -61,4 +63,4 @@ def get_person(person_id):
except TypeError: except TypeError:
from openslides.utils.person import EmtyPerson from openslides.utils.person import EmtyPerson
return EmtyPerson() return EmtyPerson()
return Persons(person_prefix=person_prefix, id=id)[0] return Persons(person_prefix_filter=person_prefix, id_filter=id)[0]

View File

@ -57,7 +57,7 @@ class PersonMixin(object):
try: try:
return generate_person_id(self.person_prefix, self.pk) return generate_person_id(self.person_prefix, self.pk)
except AttributeError: except AttributeError:
raise AttributeError("%s has to have a attribute 'user_prefix'" raise AttributeError("%s has to have a attribute 'person_prefix'"
% self) % self)
def __repr__(self): def __repr__(self):

View File

@ -12,4 +12,4 @@
from django.dispatch import Signal from django.dispatch import Signal
receiv_persons = Signal(providing_args=['person_prefix', 'id']) receive_persons = Signal(providing_args=['person_prefix_filter', 'id_filter'])

View File

@ -34,7 +34,7 @@ from django.conf import settings
from django.dispatch import receiver from django.dispatch import receiver
from django.http import HttpResponseServerError, HttpResponse, HttpResponseRedirect from django.http import HttpResponseServerError, HttpResponse, HttpResponseRedirect
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _, ugettext_noop, ugettext_lazy
from django.utils.importlib import import_module from django.utils.importlib import import_module
from django.template import loader, RequestContext from django.template import loader, RequestContext
from django.template.loader import render_to_string from django.template.loader import render_to_string
@ -105,6 +105,50 @@ class AjaxMixin(object):
return HttpResponse(json.dumps(self.get_ajax_context(**kwargs))) return HttpResponse(json.dumps(self.get_ajax_context(**kwargs)))
class QuestionMixin(object):
question = ugettext_lazy('Are you sure?')
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 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_url(self):
return self.answer_url
def confirm_form(self):
option_fields = "\n".join([
'<input type="submit" name="%s" value="%s">' % (option[0], unicode(option[1]))
for option in self.get_answer_options()])
messages.warning(self.request,
"""
%(message)s
<form action="%(url)s" method="post">
<input type="hidden" value="%(csrf)s" name="csrfmiddlewaretoken">
%(option_fields)s
</form>
""" % {
'message': self.get_question(),
'url': self.get_answer_url(),
'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)
class TemplateView(PermissionMixin, _TemplateView): class TemplateView(PermissionMixin, _TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(TemplateView, self).get_context_data(**kwargs) context = super(TemplateView, self).get_context_data(**kwargs)
@ -208,21 +252,32 @@ class CreateView(PermissionMixin, _CreateView):
messages.error(self.request, _('Please check the form for errors.')) messages.error(self.request, _('Please check the form for errors.'))
return super(CreateView, self).form_invalid(form) 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): def get_success_message(self):
return _('%s was successfully created.') % html_strong(self.object) return _('%s was successfully created.') % html_strong(self.object)
def manipulate_object(self, form):
pass
class DeleteView(RedirectView, SingleObjectMixin):
def get_confirm_question(self): class DeleteView(RedirectView, SingleObjectMixin, QuestionMixin):
def get_question(self):
return _('Do you really want to delete %s?') % html_strong(self.object) return _('Do you really want to delete %s?') % html_strong(self.object)
def get_success_message(self): def get_success_message(self):
return _('%s was successfully deleted.') % html_strong(self.object) return _('%s was successfully deleted.') % html_strong(self.object)
def pre_redirect(self, request, *args, **kwargs): def pre_redirect(self, request, *args, **kwargs):
self.confirm_form(request, self.object) self.confirm_form()
def pre_post_redirect(self, request, *args, **kwargs): def pre_post_redirect(self, request, *args, **kwargs):
if self.get_answer().lower() == 'yes':
self.object.delete() self.object.delete()
messages.success(request, self.get_success_message()) messages.success(request, self.get_success_message())
@ -230,20 +285,8 @@ class DeleteView(RedirectView, SingleObjectMixin):
self.object = self.get_object() self.object = self.get_object()
return super(DeleteView, self).get(request, *args, **kwargs) return super(DeleteView, self).get(request, *args, **kwargs)
def confirm_form(self, request, object): def get_answer_url(self):
self.gen_confirm_form(request, self.get_confirm_question(), return self.object.get_absolute_url('delete')
object.get_absolute_url('delete'))
def gen_confirm_form(self, request, message, url):
messages.warning(request,
"""
%s
<form action="%s" method="post">
<input type="hidden" value="%s" name="csrfmiddlewaretoken">
<input type="submit" value="%s">
<input type="button" value="%s">
</form>
""" % (message, url, csrf(request)['csrf_token'], _("Yes"), _("No")))
class DetailView(TemplateView, SingleObjectMixin): class DetailView(TemplateView, SingleObjectMixin):