Merge branch 'master' into install
Conflicts: openslides/__init__.py
This commit is contained in:
commit
b19f6a7d74
@ -38,7 +38,7 @@
|
|||||||
[
|
[
|
||||||
"can_see_participant",
|
"can_see_participant",
|
||||||
"participant",
|
"participant",
|
||||||
"profile"
|
"openslidesuser"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"can_see_projector",
|
"can_see_projector",
|
||||||
@ -92,7 +92,7 @@
|
|||||||
[
|
[
|
||||||
"can_see_participant",
|
"can_see_participant",
|
||||||
"participant",
|
"participant",
|
||||||
"profile"
|
"openslidesuser"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"can_see_projector",
|
"can_see_projector",
|
||||||
@ -161,12 +161,12 @@
|
|||||||
[
|
[
|
||||||
"can_manage_participant",
|
"can_manage_participant",
|
||||||
"participant",
|
"participant",
|
||||||
"profile"
|
"openslidesuser"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"can_see_participant",
|
"can_see_participant",
|
||||||
"participant",
|
"participant",
|
||||||
"profile"
|
"openslidesuser"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"can_manage_projector",
|
"can_manage_projector",
|
||||||
@ -195,12 +195,12 @@
|
|||||||
[
|
[
|
||||||
"can_manage_participant",
|
"can_manage_participant",
|
||||||
"participant",
|
"participant",
|
||||||
"profile"
|
"openslidesuser"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"can_see_participant",
|
"can_see_participant",
|
||||||
"participant",
|
"participant",
|
||||||
"profile"
|
"openslidesuser"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"can_see_projector",
|
"can_see_projector",
|
||||||
@ -210,4 +210,4 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -5,10 +5,11 @@
|
|||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = (1, 3, 0, 'alpha', 0)
|
VERSION = (1, 2, 0, 'final', 1)
|
||||||
|
|
||||||
def get_version(version=None):
|
def get_version(version=None):
|
||||||
"""Derives a PEP386-compliant version number from VERSION."""
|
"""Derives a PEP386-compliant version number from VERSION."""
|
||||||
|
# TODO: Get the Version Hash from GIT.
|
||||||
if version is None:
|
if version is None:
|
||||||
version = VERSION
|
version = VERSION
|
||||||
assert len(version) == 5
|
assert len(version) == 5
|
||||||
|
@ -52,7 +52,14 @@ class Item(MPTTModel, SlideMixin):
|
|||||||
"""
|
"""
|
||||||
return the object, of which the item points.
|
return the object, of which the item points.
|
||||||
"""
|
"""
|
||||||
return get_slide_from_sid(self.related_sid, True)
|
object = get_slide_from_sid(self.related_sid, element=True)
|
||||||
|
if object is None:
|
||||||
|
self.title = 'Item for deleted slide: %s' % self.related_sid
|
||||||
|
self.related_sid = None
|
||||||
|
self.save()
|
||||||
|
return self
|
||||||
|
else:
|
||||||
|
return object
|
||||||
|
|
||||||
def get_related_type(self):
|
def get_related_type(self):
|
||||||
"""
|
"""
|
||||||
|
@ -15,9 +15,9 @@ from django.test.client import Client
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db.models.query import EmptyQuerySet
|
from django.db.models.query import EmptyQuerySet
|
||||||
|
|
||||||
from projector.api import get_active_slide
|
from openslides.projector.api import get_active_slide
|
||||||
|
|
||||||
from agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
|
|
||||||
class ItemTest(TestCase):
|
class ItemTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -60,6 +60,10 @@ class ItemTest(TestCase):
|
|||||||
self.assertEqual(initial['parent'], 0)
|
self.assertEqual(initial['parent'], 0)
|
||||||
self.assertEqual(initial['weight'], item.weight)
|
self.assertEqual(initial['weight'], item.weight)
|
||||||
|
|
||||||
|
def testRelated_sid(self):
|
||||||
|
self.item1.related_sid = 'foobar'
|
||||||
|
self.assertFalse(self.item1.get_related_slide() is None)
|
||||||
|
|
||||||
|
|
||||||
class ViewTest(TestCase):
|
class ViewTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -11,31 +11,13 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
||||||
|
|
||||||
from openslides.utils.forms import CssClassMixin
|
from openslides.utils.forms import CssClassMixin
|
||||||
|
from openslides.utils.person import PersonFormField, MultiplePersonFormField
|
||||||
from openslides.application.models import Application
|
from openslides.application.models import Application
|
||||||
|
|
||||||
|
|
||||||
class UserModelChoiceField(forms.ModelChoiceField):
|
|
||||||
"""
|
|
||||||
Extend ModelChoiceField for users so that the choices are
|
|
||||||
listed as 'first_name last_name' instead of just 'username'.
|
|
||||||
"""
|
|
||||||
def label_from_instance(self, obj):
|
|
||||||
return obj.get_full_name()
|
|
||||||
|
|
||||||
|
|
||||||
class UserModelMultipleChoiceField(forms.ModelMultipleChoiceField):
|
|
||||||
"""
|
|
||||||
Extend ModelMultipleChoiceField for users so that the choices are
|
|
||||||
listed as 'first_name last_name' instead of just 'username'.
|
|
||||||
"""
|
|
||||||
def label_from_instance(self, obj):
|
|
||||||
return obj.get_full_name()
|
|
||||||
|
|
||||||
|
|
||||||
class ApplicationForm(forms.Form, CssClassMixin):
|
class ApplicationForm(forms.Form, CssClassMixin):
|
||||||
title = forms.CharField(widget=forms.TextInput(), label=_("Title"))
|
title = forms.CharField(widget=forms.TextInput(), label=_("Title"))
|
||||||
text = forms.CharField(widget=forms.Textarea(), label=_("Text"))
|
text = forms.CharField(widget=forms.Textarea(), label=_("Text"))
|
||||||
@ -50,11 +32,7 @@ class ApplicationFormTrivialChanges(ApplicationForm):
|
|||||||
|
|
||||||
|
|
||||||
class ApplicationManagerForm(forms.ModelForm, CssClassMixin):
|
class ApplicationManagerForm(forms.ModelForm, CssClassMixin):
|
||||||
submitter = UserModelChoiceField(
|
submitter = PersonFormField()
|
||||||
queryset=User.objects.all().exclude(profile=None).
|
|
||||||
order_by("first_name"),
|
|
||||||
label=_("Submitter"),
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Application
|
model = Application
|
||||||
@ -62,11 +40,8 @@ class ApplicationManagerForm(forms.ModelForm, CssClassMixin):
|
|||||||
|
|
||||||
|
|
||||||
class ApplicationManagerFormSupporter(ApplicationManagerForm):
|
class ApplicationManagerFormSupporter(ApplicationManagerForm):
|
||||||
supporter = UserModelMultipleChoiceField(
|
# TODO: Do not show the submitter in the user-list
|
||||||
queryset=User.objects.all().exclude(profile=None).
|
supporter = MultiplePersonFormField(required=False, label=_("Supporters"))
|
||||||
order_by("first_name"),
|
|
||||||
required=False, label=_("Supporters"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ApplicationImportForm(forms.Form, CssClassMixin):
|
class ApplicationImportForm(forms.Form, CssClassMixin):
|
||||||
|
@ -21,11 +21,12 @@ from django.utils.translation import pgettext
|
|||||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop, ugettext
|
from django.utils.translation import ugettext_lazy as _, ugettext_noop, ugettext
|
||||||
|
|
||||||
from openslides.utils.utils import _propper_unicode
|
from openslides.utils.utils import _propper_unicode
|
||||||
|
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 Profile
|
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)
|
||||||
@ -36,6 +37,11 @@ from openslides.projector.models import SlideMixin
|
|||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationSupporter(models.Model):
|
||||||
|
application = models.ForeignKey("Application")
|
||||||
|
person = PersonField()
|
||||||
|
|
||||||
|
|
||||||
class Application(models.Model, SlideMixin):
|
class Application(models.Model, SlideMixin):
|
||||||
prefix = "application"
|
prefix = "application"
|
||||||
STATUS = (
|
STATUS = (
|
||||||
@ -60,9 +66,7 @@ class Application(models.Model, SlideMixin):
|
|||||||
# genpoll
|
# genpoll
|
||||||
)
|
)
|
||||||
|
|
||||||
submitter = models.ForeignKey(User, verbose_name=_("Submitter"))
|
submitter = PersonField(verbose_name=_("Submitter"))
|
||||||
supporter = models.ManyToManyField(User, related_name='supporter', \
|
|
||||||
null=True, blank=True, verbose_name=_("Supporters"))
|
|
||||||
number = models.PositiveSmallIntegerField(blank=True, null=True,
|
number = models.PositiveSmallIntegerField(blank=True, null=True,
|
||||||
unique=True)
|
unique=True)
|
||||||
status = models.CharField(max_length=3, choices=STATUS, default='pub')
|
status = models.CharField(max_length=3, choices=STATUS, default='pub')
|
||||||
@ -157,6 +161,14 @@ class Application(models.Model, SlideMixin):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supporters(self):
|
||||||
|
for object in self.applicationsupporter_set.all():
|
||||||
|
yield object.person
|
||||||
|
|
||||||
|
def is_supporter(self, person):
|
||||||
|
return self.applicationsupporter_set.filter(person=person).exists()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enough_supporters(self):
|
def enough_supporters(self):
|
||||||
"""
|
"""
|
||||||
@ -164,17 +176,20 @@ class Application(models.Model, SlideMixin):
|
|||||||
"""
|
"""
|
||||||
min_supporters = int(config['application_min_supporters'])
|
min_supporters = int(config['application_min_supporters'])
|
||||||
if self.status == "pub":
|
if self.status == "pub":
|
||||||
return self.supporter.count() >= min_supporters
|
return self.count_supporters() >= min_supporters
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def count_supporters(self):
|
||||||
|
return self.applicationsupporter_set.count()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def missing_supporters(self):
|
def missing_supporters(self):
|
||||||
"""
|
"""
|
||||||
Return number of missing supporters
|
Return number of missing supporters
|
||||||
"""
|
"""
|
||||||
min_supporters = int(config['application_min_supporters'])
|
min_supporters = int(config['application_min_supporters'])
|
||||||
delta = min_supporters - self.supporter.count()
|
delta = min_supporters - self.count_supporters()
|
||||||
if delta > 0:
|
if delta > 0:
|
||||||
return delta
|
return delta
|
||||||
else:
|
else:
|
||||||
@ -221,10 +236,11 @@ class Application(models.Model, SlideMixin):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
is_manager = False
|
is_manager = False
|
||||||
|
|
||||||
|
supporters = self.applicationsupporter_set.all()
|
||||||
if (self.status == "pub"
|
if (self.status == "pub"
|
||||||
and self.supporter.exists()
|
and supporters
|
||||||
and not is_manager):
|
and not is_manager):
|
||||||
self.supporter.clear()
|
supporters.delete()
|
||||||
self.writelog(_("Supporters removed"), user)
|
self.writelog(_("Supporters removed"), user)
|
||||||
|
|
||||||
def reset(self, user):
|
def reset(self, user):
|
||||||
@ -236,34 +252,41 @@ class Application(models.Model, SlideMixin):
|
|||||||
self.save()
|
self.save()
|
||||||
self.writelog(_("Status reseted to: %s") % (self.get_status_display()), user)
|
self.writelog(_("Status reseted to: %s") % (self.get_status_display()), user)
|
||||||
|
|
||||||
def support(self, user):
|
def support(self, person):
|
||||||
"""
|
"""
|
||||||
Add a Supporter to the list of supporters of the application.
|
Add a Supporter to the list of supporters of the application.
|
||||||
"""
|
"""
|
||||||
if user == self.submitter:
|
if person == self.submitter:
|
||||||
|
# TODO: Use own Exception
|
||||||
raise NameError('Supporter can not be the submitter of a ' \
|
raise NameError('Supporter can not be the submitter of a ' \
|
||||||
'application.')
|
'application.')
|
||||||
if self.permitted is not None:
|
if self.permitted is not None:
|
||||||
|
# TODO: Use own Exception
|
||||||
raise NameError('This application is already permitted.')
|
raise NameError('This application is already permitted.')
|
||||||
if user not in self.supporter.all():
|
if not self.is_supporter(person):
|
||||||
self.supporter.add(user)
|
ApplicationSupporter(application=self, person=person).save()
|
||||||
self.writelog(_("Supporter: +%s") % (user))
|
self.writelog(_("Supporter: +%s") % (person))
|
||||||
|
|
||||||
def unsupport(self, user):
|
def unsupport(self, user):
|
||||||
"""
|
"""
|
||||||
remove a supporter from the list of supporters of the application
|
remove a supporter from the list of supporters of the application
|
||||||
"""
|
"""
|
||||||
if self.permitted is not None:
|
if self.permitted is not None:
|
||||||
|
# TODO: Use own Exception
|
||||||
raise NameError('This application is already permitted.')
|
raise NameError('This application is already permitted.')
|
||||||
if user in self.supporter.all():
|
try:
|
||||||
self.supporter.remove(user)
|
object = self.applicationsupporter_set.get(user=user).delete()
|
||||||
self.writelog(_("Supporter: -%s") % (user))
|
except ApplicationSupporter.DoesNotExist:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.writelog(_("Supporter: -%s") % (user))
|
||||||
|
|
||||||
def set_number(self, number=None, user=None):
|
def set_number(self, number=None, user=None):
|
||||||
"""
|
"""
|
||||||
Set a number for ths application.
|
Set a number for ths application.
|
||||||
"""
|
"""
|
||||||
if self.number is not None:
|
if self.number is not None:
|
||||||
|
# TODO: Use own Exception
|
||||||
raise NameError('This application has already a number.')
|
raise NameError('This application has already a number.')
|
||||||
if number is None:
|
if number is None:
|
||||||
try:
|
try:
|
||||||
@ -332,7 +355,7 @@ class Application(models.Model, SlideMixin):
|
|||||||
self.writelog(_("Status modified")+": %s -> %s" \
|
self.writelog(_("Status modified")+": %s -> %s" \
|
||||||
% (oldstatus, self.get_status_display()), user)
|
% (oldstatus, self.get_status_display()), user)
|
||||||
|
|
||||||
def get_allowed_actions(self, user=None):
|
def get_allowed_actions(self, user):
|
||||||
"""
|
"""
|
||||||
Return a list of all the allowed status.
|
Return a list of all the allowed status.
|
||||||
"""
|
"""
|
||||||
@ -340,8 +363,8 @@ class Application(models.Model, SlideMixin):
|
|||||||
is_admin = False
|
is_admin = False
|
||||||
if user:
|
if user:
|
||||||
try:
|
try:
|
||||||
user.profile
|
user = user.openslidesuser
|
||||||
except Profile.DoesNotExist:
|
except OpenSlidesUser.DoesNotExist:
|
||||||
is_admin = True
|
is_admin = True
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# For the anonymous-user
|
# For the anonymous-user
|
||||||
@ -366,16 +389,12 @@ class Application(models.Model, SlideMixin):
|
|||||||
actions.append("pub")
|
actions.append("pub")
|
||||||
|
|
||||||
# Check if the user can support and unspoort the application
|
# Check if the user can support and unspoort the application
|
||||||
try:
|
if (self.status == "pub"
|
||||||
if (self.status == "pub"
|
and user != self.submitter
|
||||||
and user != self.submitter
|
and not self.is_supporter(user)):
|
||||||
and user not in self.supporter.all()
|
actions.append("support")
|
||||||
and getattr(user, 'profile', None)):
|
|
||||||
actions.append("support")
|
|
||||||
except Profile.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if self.status == "pub" and user in self.supporter.all():
|
if self.status == "pub" and self.is_supporter(user):
|
||||||
actions.append("unsupport")
|
actions.append("unsupport")
|
||||||
|
|
||||||
#Check if the user can edit the application
|
#Check if the user can edit the application
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
{% if not forloop.last %}<br>{%endif%}
|
{% if not forloop.last %}<br>{%endif%}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ application.submitter.profile }}</td>
|
<td>{{ application.submitter }}</td>
|
||||||
<td>{{ application.creation_time }}</td>
|
<td>{{ application.creation_time }}</td>
|
||||||
<td>
|
<td>
|
||||||
<span style="width: 1px; white-space: nowrap;">
|
<span style="width: 1px; white-space: nowrap;">
|
||||||
|
@ -16,22 +16,22 @@
|
|||||||
<div id="sidebar">
|
<div id="sidebar">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h4>{% trans "Submitter" %}:</h4>
|
<h4>{% trans "Submitter" %}:</h4>
|
||||||
{{ application.submitter.profile }}
|
{{ application.submitter }}
|
||||||
{% if user == application.submitter %}
|
{% if user == application.submitter.user %}
|
||||||
<img src="{% static 'images/icons/user-information.png' %}" title="{% trans 'You!' %}">
|
<img src="{% static 'images/icons/user-information.png' %}" title="{% trans 'You!' %}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if min_supporters > 0 %}
|
{% if min_supporters > 0 %}
|
||||||
<h4>{% trans "Supporters" %}: *</h4>
|
<h4>{% trans "Supporters" %}: *</h4>
|
||||||
{% if application.supporter.count == 0 %}
|
{% if not application.supporters %}
|
||||||
-
|
-
|
||||||
{% else %}
|
{% else %}
|
||||||
<ol>
|
<ol>
|
||||||
{% for supporter in application.supporter.all %}
|
{% for supporter in application.supporters %}
|
||||||
<li> {{ supporter.profile }}</li>
|
<li> {{ supporter }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h4>{% trans "Status" %}:</h4>
|
<h4>{% trans "Status" %}:</h4>
|
||||||
@ -113,7 +113,7 @@
|
|||||||
{{ application.creation_time }}
|
{{ application.creation_time }}
|
||||||
|
|
||||||
<p></p>
|
<p></p>
|
||||||
{% if "wit" in actions and user == application.submitter %}
|
{% if "wit" in actions and user == application.submitter.user %}
|
||||||
<p></p>
|
<p></p>
|
||||||
<a href='{% url application_set_status application.id 'wit' %}'>
|
<a href='{% url application_set_status application.id 'wit' %}'>
|
||||||
<span class="button"><span class="icon revert">{% trans 'Withdraw' %}</span></span>
|
<span class="button"><span class="icon revert">{% trans 'Withdraw' %}</span></span>
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
<p><b>{% trans "Submitter" %}:</b><br>
|
<p><b>{% trans "Submitter" %}:</b><br>
|
||||||
{{ application.submitter.profile }}
|
{{ application.submitter }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,7 +20,7 @@ class ApplicationTest(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.admin = User.objects.create_user('testadmin', '', 'default')
|
self.admin = User.objects.create_user('testadmin', '', 'default')
|
||||||
self.anonym = User.objects.create_user('testanoym', '', 'default')
|
self.anonym = User.objects.create_user('testanoym', '', 'default')
|
||||||
self.app1 = Application(submitter=self.admin)
|
self.app1 = Application(submitter=self.admin.openslidesuser)
|
||||||
self.app1.save()
|
self.app1.save()
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
|
@ -42,6 +42,7 @@ from openslides.utils.template import Tab
|
|||||||
from openslides.utils.utils import (template, permission_required,
|
from openslides.utils.utils import (template, permission_required,
|
||||||
del_confirm_form, gen_confirm_form)
|
del_confirm_form, gen_confirm_form)
|
||||||
from openslides.utils.views import PDFView, RedirectView, DeleteView, FormView
|
from openslides.utils.views import PDFView, RedirectView, DeleteView, FormView
|
||||||
|
from openslides.utils.person import get_person
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
|
|
||||||
@ -50,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 Profile
|
from openslides.participant.models import OpenSlidesUser
|
||||||
|
|
||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
|
|
||||||
@ -123,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),
|
'actions' : application.get_allowed_actions(request.user.openslidesuser),
|
||||||
'application' : application
|
'application' : application
|
||||||
}
|
}
|
||||||
except:
|
except:
|
||||||
@ -151,7 +152,8 @@ def view(request, application_id, newest=False):
|
|||||||
else:
|
else:
|
||||||
version = application.public_version
|
version = application.public_version
|
||||||
revisions = application.versions
|
revisions = application.versions
|
||||||
actions = application.get_allowed_actions(user=request.user)
|
user = request.user.openslidesuser
|
||||||
|
actions = application.get_allowed_actions(user=user)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'application': application,
|
'application': application,
|
||||||
@ -180,10 +182,12 @@ def edit(request, application_id=None):
|
|||||||
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 request.user == application.submitter and not is_manager:
|
if (not hasattr(application.submitter, 'user') or
|
||||||
|
not request.user.openslidesuser == application.submitter.user) \
|
||||||
|
and not is_manager:
|
||||||
messages.error(request, _("You can not edit this application. You are not the submitter."))
|
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)
|
actions = application.get_allowed_actions(user=request.user.openslidesuser)
|
||||||
else:
|
else:
|
||||||
application = None
|
application = None
|
||||||
actions = None
|
actions = None
|
||||||
@ -210,14 +214,14 @@ def edit(request, application_id=None):
|
|||||||
|
|
||||||
if valid:
|
if valid:
|
||||||
del_supporters = True
|
del_supporters = True
|
||||||
original_supporters = []
|
|
||||||
if is_manager:
|
if is_manager:
|
||||||
if application: # Edit application
|
if application: # Edit application
|
||||||
for s in application.supporter.all():
|
original_supporters = list(application.supporters)
|
||||||
original_supporters.append(s)
|
else:
|
||||||
|
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)
|
application = Application(submitter=request.user.openslidesuser)
|
||||||
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']
|
||||||
@ -227,30 +231,21 @@ 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, trivial_change=trivial_change)
|
application.save(request.user.openslidesuser, trivial_change=trivial_change)
|
||||||
if is_manager:
|
if is_manager:
|
||||||
# log added supporters
|
try:
|
||||||
supporters_added = []
|
new_supporters = set(managerform.cleaned_data['supporter'])
|
||||||
for s in application.supporter.all():
|
except KeyError:
|
||||||
if s not in original_supporters:
|
# The managerform has no field for the supporters
|
||||||
try:
|
pass
|
||||||
supporters_added.append(unicode(s.profile))
|
else:
|
||||||
except Profile.DoesNotExist:
|
old_supporters = set(application.supporters)
|
||||||
pass
|
# add new supporters
|
||||||
if len(supporters_added) > 0:
|
for supporter in new_supporters.difference(old_supporters):
|
||||||
log_added = ", ".join(supporters_added)
|
application.support(supporter)
|
||||||
application.writelog(_("Supporter: +%s") % log_added, request.user)
|
# remove old supporters
|
||||||
# log removed supporters
|
for supporter in old_supporters.difference(new_supporters):
|
||||||
supporters_removed = []
|
application.unsupport(supporter)
|
||||||
for s in original_supporters:
|
|
||||||
if s not in application.supporter.all():
|
|
||||||
try:
|
|
||||||
supporters_removed.append(unicode(s.profile))
|
|
||||||
except Profile.DoesNotExist:
|
|
||||||
pass
|
|
||||||
if len(supporters_removed) > 0:
|
|
||||||
log_removed = ", ".join(supporters_removed)
|
|
||||||
application.writelog(_("Supporter: -%s") % log_removed, request.user)
|
|
||||||
if application_id is None:
|
if application_id is None:
|
||||||
messages.success(request, _('New application was successfully created.'))
|
messages.success(request, _('New application was successfully created.'))
|
||||||
else:
|
else:
|
||||||
@ -266,11 +261,11 @@ def edit(request, application_id=None):
|
|||||||
if application_id is None:
|
if application_id is None:
|
||||||
initial = {'text': config['application_preamble']}
|
initial = {'text': config['application_preamble']}
|
||||||
else:
|
else:
|
||||||
if application.status == "pub" and application.supporter.exists():
|
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 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!"))
|
||||||
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.") % application.supporter.count() )
|
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) )
|
||||||
initial = {'title': application.title,
|
initial = {'title': application.title,
|
||||||
'text': application.text,
|
'text': application.text,
|
||||||
'reason': application.reason}
|
'reason': application.reason}
|
||||||
@ -278,12 +273,12 @@ 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': str(request.user.id)}
|
initial = {'submitter': request.user.openslidesuser.person_id}
|
||||||
else:
|
else:
|
||||||
initial = {}
|
initial = {'submitter': application.submitter.person_id,
|
||||||
managerform = managerformclass(initial=initial, \
|
'supporter': [supporter.person_id for supporter in application.supporters]}
|
||||||
instance=application, \
|
managerform = managerformclass(initial=initial,
|
||||||
prefix="manager")
|
instance=application, prefix="manager")
|
||||||
else:
|
else:
|
||||||
managerform = None
|
managerform = None
|
||||||
return {
|
return {
|
||||||
@ -301,7 +296,7 @@ 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)
|
Application.objects.get(pk=application_id).set_number(user=request.user.openslidesuser)
|
||||||
messages.success(request, _("Application number was successfully set."))
|
messages.success(request, _("Application number was successfully set."))
|
||||||
except Application.DoesNotExist:
|
except Application.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
@ -317,7 +312,7 @@ def permit(request, application_id):
|
|||||||
permit an application.
|
permit an application.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
Application.objects.get(pk=application_id).permit(user=request.user)
|
Application.objects.get(pk=application_id).permit(user=request.user.openslidesuser)
|
||||||
messages.success(request, _("Application was successfully permitted."))
|
messages.success(request, _("Application was successfully permitted."))
|
||||||
except Application.DoesNotExist:
|
except Application.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
@ -332,7 +327,7 @@ 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)
|
Application.objects.get(pk=application_id).notpermit(user=request.user.openslidesuser)
|
||||||
messages.success(request, _("Application was successfully rejected."))
|
messages.success(request, _("Application was successfully rejected."))
|
||||||
except Application.DoesNotExist:
|
except Application.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
@ -348,7 +343,7 @@ 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, status=status)
|
application.set_status(user=request.user.openslidesuser, status=status)
|
||||||
messages.success(request, _("Application status was set to: <b>%s</b>.") % application.get_status_display())
|
messages.success(request, _("Application status was set to: <b>%s</b>.") % application.get_status_display())
|
||||||
except Application.DoesNotExist:
|
except Application.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
@ -364,7 +359,7 @@ def reset(request, application_id):
|
|||||||
reset an application.
|
reset an application.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
Application.objects.get(pk=application_id).reset(user=request.user)
|
Application.objects.get(pk=application_id).reset(user=request.user.openslides.user)
|
||||||
messages.success(request, _("Application status was reset.") )
|
messages.success(request, _("Application status was reset.") )
|
||||||
except Application.DoesNotExist:
|
except Application.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
@ -378,7 +373,7 @@ def support(request, application_id):
|
|||||||
support an application.
|
support an application.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
Application.objects.get(pk=application_id).support(user=request.user)
|
Application.objects.get(pk=application_id).support(user=request.user.openslides.user)
|
||||||
messages.success(request, _("You have support the application successfully.") )
|
messages.success(request, _("You have support the application successfully.") )
|
||||||
except Application.DoesNotExist:
|
except Application.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
@ -392,7 +387,7 @@ def unsupport(request, application_id):
|
|||||||
unsupport an application.
|
unsupport an application.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
Application.objects.get(pk=application_id).unsupport(user=request.user)
|
Application.objects.get(pk=application_id).unsupport(user=request.user.openslidesuser)
|
||||||
messages.success(request, _("You have unsupport the application successfully.") )
|
messages.success(request, _("You have unsupport the application successfully.") )
|
||||||
except Application.DoesNotExist:
|
except Application.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
@ -406,7 +401,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)
|
poll = Application.objects.get(pk=application_id).gen_poll(user=request.user.openslidesuser)
|
||||||
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
|
||||||
@ -423,7 +418,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)
|
application.writelog(_("Poll deleted"), request.user.openslidesuser)
|
||||||
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]))
|
||||||
@ -463,7 +458,7 @@ 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):
|
if not 'delete' in application.get_allowed_actions(user=request.user.openslidesuser):
|
||||||
messages.error(request, _("You can not delete application <b>%s</b>.") % application)
|
messages.error(request, _("You can not delete application <b>%s</b>.") % application)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -472,12 +467,12 @@ class ApplicationDelete(DeleteView):
|
|||||||
messages.success(request, _("Application <b>%s</b> was successfully deleted.") % title)
|
messages.success(request, _("Application <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):
|
if not 'delete' in self.object.get_allowed_actions(user=request.user.openslidesuser):
|
||||||
messages.error(request, _("You can not delete application <b>%s</b>.") % self.object)
|
messages.error(request, _("You can not delete application <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, _("Application <b>%s</b> was successfully deleted.") % title)
|
||||||
else:
|
else:
|
||||||
messages.error(request, _("Invalid request"))
|
messages.error(request, _("Invalid request"))
|
||||||
|
|
||||||
@ -513,12 +508,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)
|
context['actions'] = self.application.get_allowed_actions(user=self.request.user.openslidesuser)
|
||||||
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
|
user = self.request.user.openslidesuser
|
||||||
|
|
||||||
class ViewPollFormClass(cls):
|
class ViewPollFormClass(cls):
|
||||||
def save(self, commit = True):
|
def save(self, commit = True):
|
||||||
@ -540,7 +535,7 @@ 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)
|
application.accept_version(aversion, user=request.user.openslidesuser)
|
||||||
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 permit version <b>%s</b>?') % aversion.aid, reverse('application_version_permit', args=[aversion.id]))
|
||||||
@ -552,7 +547,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):
|
if application.reject_version(aversion, user=request.user.openslidesuser):
|
||||||
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.") )
|
||||||
@ -565,14 +560,15 @@ def reject_version(request, aversion_id):
|
|||||||
@template('application/import.html')
|
@template('application/import.html')
|
||||||
def application_import(request):
|
def application_import(request):
|
||||||
try:
|
try:
|
||||||
request.user.profile
|
request.user.openslidesuser
|
||||||
messages.error(request, _('The import function is available for the admin (without user profile) only.'))
|
except OpenSlidesUser.DoesNotExist:
|
||||||
return redirect(reverse('application_overview'))
|
|
||||||
except Profile.DoesNotExist:
|
|
||||||
pass
|
pass
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# AnonymousUser
|
# AnonymousUser
|
||||||
pass
|
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)
|
||||||
@ -752,13 +748,10 @@ class ApplicationPDF(PDFView):
|
|||||||
if application.status == "pub":
|
if application.status == "pub":
|
||||||
cell1b.append(Paragraph("__________________________________________",stylesheet['Signaturefield']))
|
cell1b.append(Paragraph("__________________________________________",stylesheet['Signaturefield']))
|
||||||
cell1b.append(Spacer(0,0.1*cm))
|
cell1b.append(Spacer(0,0.1*cm))
|
||||||
cell1b.append(Paragraph(" "+unicode(application.submitter.profile), stylesheet['Small']))
|
cell1b.append(Paragraph(" "+unicode(application.submitter), stylesheet['Small']))
|
||||||
cell1b.append(Spacer(0,0.2*cm))
|
cell1b.append(Spacer(0,0.2*cm))
|
||||||
else:
|
else:
|
||||||
try:
|
cell1b.append(Paragraph(unicode(application.submitter), stylesheet['Normal']))
|
||||||
cell1b.append(Paragraph(unicode(application.submitter.profile), stylesheet['Normal']))
|
|
||||||
except Profile.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# supporters
|
# supporters
|
||||||
cell2a = []
|
cell2a = []
|
||||||
@ -767,8 +760,8 @@ class ApplicationPDF(PDFView):
|
|||||||
|
|
||||||
cell2a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font><seqreset id='counter'>" % _("Supporters"), stylesheet['Heading4']))
|
cell2a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font><seqreset id='counter'>" % _("Supporters"), stylesheet['Heading4']))
|
||||||
|
|
||||||
for s in application.supporter.all():
|
for supporter in application.supporters:
|
||||||
cell2b.append(Paragraph("<seq id='counter'/>. %s" % unicode(s.profile), stylesheet['Signaturefield']))
|
cell2b.append(Paragraph("<seq id='counter'/>. %s" % unicode(supporter), stylesheet['Signaturefield']))
|
||||||
if application.status == "pub":
|
if application.status == "pub":
|
||||||
for x in range(0,application.missing_supporters):
|
for x in range(0,application.missing_supporters):
|
||||||
cell2b.append(Paragraph("<seq id='counter'/>. __________________________________________",stylesheet['Signaturefield']))
|
cell2b.append(Paragraph("<seq id='counter'/>. __________________________________________",stylesheet['Signaturefield']))
|
||||||
|
@ -14,8 +14,8 @@ from django import forms
|
|||||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
||||||
|
|
||||||
from openslides.utils.forms import CssClassMixin
|
from openslides.utils.forms import CssClassMixin
|
||||||
|
from openslides.utils.person import PersonFormField
|
||||||
|
|
||||||
from openslides.participant.models import Profile
|
|
||||||
from openslides.assignment.models import Assignment
|
from openslides.assignment.models import Assignment
|
||||||
|
|
||||||
|
|
||||||
@ -25,13 +25,12 @@ class AssignmentForm(forms.ModelForm, CssClassMixin):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Assignment
|
model = Assignment
|
||||||
exclude = ('status', 'profile', 'elected')
|
exclude = ('status', 'elected')
|
||||||
|
|
||||||
|
|
||||||
class AssignmentRunForm(forms.Form, CssClassMixin):
|
class AssignmentRunForm(forms.Form, CssClassMixin):
|
||||||
candidate = forms.ModelChoiceField(
|
candidate = PersonFormField(
|
||||||
widget=forms.Select(attrs={'class': 'medium-input'}),
|
widget=forms.Select(attrs={'class': 'medium-input'}),
|
||||||
queryset=Profile.objects.all().order_by('user__first_name'),
|
|
||||||
label=_("Nominate a participant"),
|
label=_("Nominate a participant"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,20 +15,29 @@ from django.db import models
|
|||||||
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 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.projector.api import register_slidemodel
|
from openslides.projector.api import register_slidemodel
|
||||||
from openslides.projector.projector import SlideMixin
|
from openslides.projector.projector import SlideMixin
|
||||||
|
|
||||||
from openslides.participant.models import Profile
|
|
||||||
|
|
||||||
from openslides.poll.models import (BasePoll, CountInvalid, CountVotesCast,
|
from openslides.poll.models import (BasePoll, CountInvalid, CountVotesCast,
|
||||||
BaseOption, PublishPollMixin, BaseVote)
|
BaseOption, PublishPollMixin, BaseVote)
|
||||||
|
|
||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
|
|
||||||
|
|
||||||
|
class AssignmentCandidate(models.Model):
|
||||||
|
assignment = models.ForeignKey("Assignment")
|
||||||
|
person = PersonField(db_index=True)
|
||||||
|
elected = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.person)
|
||||||
|
|
||||||
|
|
||||||
class Assignment(models.Model, SlideMixin):
|
class Assignment(models.Model, SlideMixin):
|
||||||
prefix = 'assignment'
|
prefix = 'assignment'
|
||||||
STATUS = (
|
STATUS = (
|
||||||
@ -44,9 +53,6 @@ class Assignment(models.Model, SlideMixin):
|
|||||||
verbose_name=_("Number of available posts"))
|
verbose_name=_("Number of available posts"))
|
||||||
polldescription = models.CharField(max_length=100, null=True, blank=True,
|
polldescription = models.CharField(max_length=100, null=True, blank=True,
|
||||||
verbose_name=_("Comment on the ballot paper"))
|
verbose_name=_("Comment on the ballot paper"))
|
||||||
profile = models.ManyToManyField(Profile, null=True, blank=True)
|
|
||||||
elected = models.ManyToManyField(Profile, null=True, blank=True,
|
|
||||||
related_name='elected_set')
|
|
||||||
status = models.CharField(max_length=3, choices=STATUS, default='sea')
|
status = models.CharField(max_length=3, choices=STATUS, default='sea')
|
||||||
|
|
||||||
def set_status(self, status):
|
def set_status(self, status):
|
||||||
@ -63,61 +69,75 @@ class Assignment(models.Model, SlideMixin):
|
|||||||
self.status = status
|
self.status = status
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def run(self, profile, user=None):
|
def run(self, candidate, person=None):
|
||||||
"""
|
"""
|
||||||
run for a vote
|
run for a vote
|
||||||
"""
|
"""
|
||||||
if self.is_candidate(profile):
|
# TODO: don't make any permission checks here.
|
||||||
raise NameError(_('<b>%s</b> is already a candidate.') % profile)
|
# Use other Exceptions
|
||||||
if not user.has_perm("assignment.can_manage_assignment") and self.status != 'sea':
|
if self.is_candidate(candidate):
|
||||||
|
raise NameError(_('<b>%s</b> is already a candidate.') % candidate)
|
||||||
|
if not person.has_perm("assignment.can_manage_assignment") and self.status != 'sea':
|
||||||
raise NameError(_('The candidate list is already closed.'))
|
raise NameError(_('The candidate list is already closed.'))
|
||||||
self.profile.add(profile)
|
AssignmentCandidate(assignment=self, person=candidate, elected=False).save()
|
||||||
|
|
||||||
def delrun(self, profile, user=None):
|
def delrun(self, candidate):
|
||||||
"""
|
"""
|
||||||
stop running for a vote
|
stop running for a vote
|
||||||
"""
|
"""
|
||||||
if not user.has_perm("assignment.can_manage_assignment") and self.status != 'sea':
|
if self.is_candidate(candidate):
|
||||||
raise NameError(_('The candidate list is already closed.'))
|
self.assignment_candidats.get(person=candidate).delete()
|
||||||
if self.is_candidate(profile):
|
|
||||||
self.profile.remove(profile)
|
|
||||||
self.elected.remove(profile)
|
|
||||||
else:
|
else:
|
||||||
raise NameError(_('%s is no candidate') % profile)
|
# TODO: Use an OpenSlides Error
|
||||||
|
raise Exception(_('%s is no candidate') % candidate)
|
||||||
|
|
||||||
def is_candidate(self, profile):
|
def is_candidate(self, person):
|
||||||
if profile in self.profile.get_query_set():
|
if self.assignment_candidats.filter(person=person).exists():
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def assignment_candidats(self):
|
||||||
|
return AssignmentCandidate.objects.filter(assignment=self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def candidates(self):
|
def candidates(self):
|
||||||
return self.profile.get_query_set()
|
return self.get_participants(only_candidate=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def elected(self):
|
||||||
|
return self.get_participants(only_elected=True)
|
||||||
|
|
||||||
|
def get_participants(self, only_elected=False, only_candidate=False):
|
||||||
|
candidates = self.assignment_candidats
|
||||||
|
|
||||||
|
if only_elected and only_candidate:
|
||||||
|
# TODO: Use right Exception
|
||||||
|
raise Exception("only_elected and only_candidate can not both be Treu")
|
||||||
|
|
||||||
|
if only_elected:
|
||||||
|
candidates = candidates.filter(elected=True)
|
||||||
|
|
||||||
|
if only_candidate:
|
||||||
|
candidates = candidates.filter(elected=False)
|
||||||
|
|
||||||
|
for candidate in candidates.all():
|
||||||
|
yield candidate.person
|
||||||
|
|
||||||
|
|
||||||
def set_elected(self, profile, value=True):
|
def set_elected(self, person, value=True):
|
||||||
if profile in self.candidates:
|
candidate = self.assignment_candidats.get(person=person)
|
||||||
if value and not self.is_elected(profile):
|
candidate.elected = value
|
||||||
self.elected.add(profile)
|
candidate.save()
|
||||||
elif not value:
|
|
||||||
self.elected.remove(profile)
|
|
||||||
|
|
||||||
def is_elected(self, profile):
|
def is_elected(self, person):
|
||||||
if profile in self.elected.all():
|
return person in self.elected
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def gen_poll(self):
|
def gen_poll(self):
|
||||||
poll = AssignmentPoll(assignment=self)
|
poll = AssignmentPoll(assignment=self)
|
||||||
poll.save()
|
poll.save()
|
||||||
candidates = list(self.profile.all())
|
poll.set_options([{'candidate': person} for person in self.candidates])
|
||||||
for elected in self.elected.all():
|
|
||||||
try:
|
|
||||||
candidates.remove(elected)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
poll.set_options([{'candidate': profile} for profile in candidates])
|
|
||||||
return poll
|
return poll
|
||||||
|
|
||||||
|
|
||||||
@ -159,6 +179,7 @@ class Assignment(models.Model, SlideMixin):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
|
# Remove any Agenda-Item, which is related to this application.
|
||||||
for item in Item.objects.filter(related_sid=self.sid):
|
for item in Item.objects.filter(related_sid=self.sid):
|
||||||
item.delete()
|
item.delete()
|
||||||
super(Assignment, self).delete()
|
super(Assignment, self).delete()
|
||||||
@ -206,7 +227,7 @@ class AssignmentVote(BaseVote):
|
|||||||
|
|
||||||
class AssignmentOption(BaseOption):
|
class AssignmentOption(BaseOption):
|
||||||
poll = models.ForeignKey('AssignmentPoll')
|
poll = models.ForeignKey('AssignmentPoll')
|
||||||
candidate = models.ForeignKey(Profile)
|
candidate = PersonField()
|
||||||
vote_class = AssignmentVote
|
vote_class = AssignmentVote
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
@ -230,8 +251,7 @@ class AssignmentPoll(BasePoll, CountInvalid, CountVotesCast, PublishPollMixin):
|
|||||||
self.yesnoabstain = True
|
self.yesnoabstain = True
|
||||||
else:
|
else:
|
||||||
# candidates <= available posts -> yes/no/abstain
|
# candidates <= available posts -> yes/no/abstain
|
||||||
if self.assignment.candidates.count() <= (self.assignment.posts
|
if self.assignment.assignment_candidats.filter(elected=False).count() <= (self.assignment.posts):
|
||||||
- self.assignment.elected.count()):
|
|
||||||
self.yesnoabstain = True
|
self.yesnoabstain = True
|
||||||
else:
|
else:
|
||||||
self.yesnoabstain = False
|
self.yesnoabstain = False
|
||||||
@ -260,8 +280,6 @@ class AssignmentPoll(BasePoll, CountInvalid, CountVotesCast, PublishPollMixin):
|
|||||||
return _("Ballot %d") % self.get_ballot()
|
return _("Ballot %d") % self.get_ballot()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(default_config_value, dispatch_uid="assignment_default_config")
|
@receiver(default_config_value, dispatch_uid="assignment_default_config")
|
||||||
def default_config(sender, key, **kwargs):
|
def default_config(sender, key, **kwargs):
|
||||||
return {
|
return {
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
<tr class="{% cycle '' 'odd' %}
|
<tr class="{% cycle '' 'odd' %}
|
||||||
{% if assignment.active %}activeline{% endif %}">
|
{% if assignment.active %}activeline{% endif %}">
|
||||||
<td><a href="{% url assignment_view assignment.id %}">{{ assignment }}</a></td>
|
<td><a href="{% url assignment_view assignment.id %}">{{ assignment }}</a></td>
|
||||||
<td>{{ assignment.profile.count }} / {{ assignment.posts }}</td>
|
<td>{{ assignment.candidates|length }} / {{ assignment.posts }}</td>
|
||||||
<td>{{ assignment.get_status_display }}</td>
|
<td>{{ assignment.get_status_display }}</td>
|
||||||
<td>
|
<td>
|
||||||
<span style="width: 1px; white-space: nowrap;">
|
<span style="width: 1px; white-space: nowrap;">
|
||||||
|
@ -21,36 +21,36 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
{% for form in forms %}
|
{% for form in forms %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ form.option }}</td>
|
<td>{{ form.option }}</td>
|
||||||
{% for value in form %}
|
{% for value in form %}
|
||||||
<td>
|
<td>
|
||||||
{{ value.errors }}
|
{{ value.errors }}
|
||||||
{{ value }}
|
{{ value }}
|
||||||
</td>
|
</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
<tr class="total">
|
||||||
|
<td>{% trans "Invalid votes" %}</td>
|
||||||
|
{% for value in poll.get_vote_values %}
|
||||||
|
{% if forloop.first %}
|
||||||
|
<td>{{ pollform.votesinvalid.errors }}{{ pollform.votesinvalid }}</td>
|
||||||
|
{% else %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
<tr class="total">
|
||||||
|
<td>{% trans "Votes cast" %}</td>
|
||||||
|
{% for value in poll.get_vote_values %}
|
||||||
|
{% if forloop.first %}
|
||||||
|
<td>{{ pollform.votescast.errors }}{{ pollform.votescast }}</td>
|
||||||
|
{% else %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
|
||||||
<tr class="total">
|
|
||||||
<td>{% trans "Invalid votes" %}</td>
|
|
||||||
{% for value in poll.get_vote_values %}
|
|
||||||
{% if forloop.first %}
|
|
||||||
<td>{{ pollform.votesinvalid.errors }}{{ pollform.votesinvalid }}</td>
|
|
||||||
{% else %}
|
|
||||||
<td></td>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</tr>
|
|
||||||
<tr class="total">
|
|
||||||
<td>{% trans "Votes cast" %}</td>
|
|
||||||
{% for value in poll.get_vote_values %}
|
|
||||||
{% if forloop.first %}
|
|
||||||
<td>{{ pollform.votescast.errors }}{{ pollform.votescast }}</td>
|
|
||||||
{% else %}
|
|
||||||
<td></td>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -35,13 +35,14 @@
|
|||||||
|
|
||||||
<h3>{% trans "Candidates" %}</h3>
|
<h3>{% trans "Candidates" %}</h3>
|
||||||
<ol>
|
<ol>
|
||||||
{% for profile in assignment.profile.all|dictsort:"user.first_name" %}
|
{% for person in assignment.candidates %}
|
||||||
<li>{{ profile }}
|
<li>
|
||||||
{% if perms.assignment.can_manage_assignment %}
|
{{ person }}
|
||||||
{% if assignment.status == "sea" or assignment.status == "vot" %}
|
{% if perms.assignment.can_manage_assignment %}
|
||||||
<a href="{% url assignment_delother assignment.id profile.id %}"><img src="{% static 'images/icons/delete.png' %}" title="{% trans 'Remove candidate' %}"></a>
|
{% if assignment.status == "sea" or assignment.status == "vot" %}
|
||||||
{% endif %}
|
<a href="{% url assignment_delother assignment.id person.person_id %}"><img src="{% static 'images/icons/delete.png' %}" title="{% trans 'Remove candidate' %}"></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<li style="list-style: none outside none;"><i>{% trans "No candidates available." %}</i></li>
|
<li style="list-style: none outside none;"><i>{% trans "No candidates available." %}</i></li>
|
||||||
@ -53,21 +54,18 @@
|
|||||||
<form action="" method="post">{% csrf_token %}
|
<form action="" method="post">{% csrf_token %}
|
||||||
{% if perms.assignment.can_nominate_self %}
|
{% if perms.assignment.can_nominate_self %}
|
||||||
<p>
|
<p>
|
||||||
{% if user.profile in assignment.profile.all %}
|
{% if user_is_candidate %}
|
||||||
|
<a href='{% url assignment_delrun assignment.id %}'>
|
||||||
<a href='{% url assignment_delrun assignment.id %}'>
|
<span class="button">
|
||||||
<span class="button">
|
<span class="icon removeuser">{% trans 'Withdraw self candidature' %}</span>
|
||||||
<span class="icon removeuser">{% trans 'Withdraw self candidature' %}</span>
|
</span>
|
||||||
</span>
|
</a>
|
||||||
</a>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if user.profile %}
|
<a href='{% url assignment_run assignment.id %}'>
|
||||||
<a href='{% url assignment_run assignment.id %}'>
|
<span class="button">
|
||||||
<span class="button">
|
<span class="icon adduser">{% trans 'Self candidature' %}</span>
|
||||||
<span class="icon adduser">{% trans 'Self candidature' %}</span>
|
</span>
|
||||||
</span>
|
</a>
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -123,7 +121,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</th>
|
</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if assignment.profile.exists and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||||
<th>
|
<th>
|
||||||
<a href='{% url assignment_gen_poll assignment.id %}'>
|
<a href='{% url assignment_gen_poll assignment.id %}'>
|
||||||
<span class="button">
|
<span class="button">
|
||||||
@ -137,9 +135,9 @@
|
|||||||
{% for candidate, poll_list in vote_results.items %}
|
{% for candidate, poll_list in vote_results.items %}
|
||||||
<tr class="{% cycle 'odd' '' %}">
|
<tr class="{% cycle 'odd' '' %}">
|
||||||
<td class="candidate">
|
<td class="candidate">
|
||||||
{% if candidate in assignment.elected.all %}
|
{% if candidate in assignment.elected %}
|
||||||
{% if perms.assignment.can_manage_assignment %}
|
{% if perms.assignment.can_manage_assignment %}
|
||||||
<a class="election_link elected" href='{% url assignment_user_not_elected assignment.id candidate.id %}'></a>
|
<a class="election_link elected" href='{% url assignment_user_not_elected assignment.id candidate.person_id %}'></a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a class="elected">
|
<a class="elected">
|
||||||
<img src="{% static 'images/icons/voting-yes.png' %}" title="{% trans 'Candidate is elected' %}">
|
<img src="{% static 'images/icons/voting-yes.png' %}" title="{% trans 'Candidate is elected' %}">
|
||||||
@ -147,7 +145,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if perms.assignment.can_manage_assignment %}
|
{% if perms.assignment.can_manage_assignment %}
|
||||||
<a class="election_link" href='{% url assignment_user_elected assignment.id candidate.id %}'></a>
|
<a class="election_link" href='{% url assignment_user_elected assignment.id candidate.person_id %}'></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ candidate }}
|
{{ candidate }}
|
||||||
@ -167,7 +165,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if assignment.profile.exists and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||||
<td></td>
|
<td></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
@ -185,7 +183,7 @@
|
|||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if assignment.profile.exists and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||||
<td></td>
|
<td></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
@ -202,7 +200,7 @@
|
|||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if assignment.profile.exists and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||||
<td></td>
|
<td></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
@ -212,7 +210,7 @@
|
|||||||
|
|
||||||
<i>{% trans "No ballots available." %}</i>
|
<i>{% trans "No ballots available." %}</i>
|
||||||
|
|
||||||
{% if assignment.profile.count > 0 and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||||
<p><a href='{% url assignment_gen_poll assignment.id %}'>
|
<p><a href='{% url assignment_gen_poll assignment.id %}'>
|
||||||
<span class="button">
|
<span class="button">
|
||||||
<span class="icon statistics">{% trans 'New ballot' %}</span>
|
<span class="icon statistics">{% trans 'New ballot' %}</span>
|
||||||
|
@ -28,17 +28,17 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scrollcontent %}
|
{% block scrollcontent %}
|
||||||
{% if not assignment.profile.exists %}
|
{% if not assignment.candidates %}
|
||||||
<p>
|
<p>
|
||||||
<div class="text">{{ assignment.description|linebreaks }}</div>
|
<div class="text">{{ assignment.description|linebreaks }}</div>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if assignment.profile.exists and assignment.status != "fin" %}
|
{% if assignment.candidates and assignment.status != "fin" %}
|
||||||
<h3>{% trans "Candidates" %}</h3>
|
<h3>{% trans "Candidates" %}</h3>
|
||||||
<ol>
|
<ol>
|
||||||
{% for profile in assignment.profile.all|dictsort:"user.first_name" %}
|
{% for candidate in assignment.candidates %}
|
||||||
<li>{{ profile }} </li>
|
<li>{{ candidate }} </li>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<li style="list-style: none outside none;">
|
<li style="list-style: none outside none;">
|
||||||
<i>{% trans "No candidates available." %}</i>
|
<i>{% trans "No candidates available." %}</i>
|
||||||
@ -122,7 +122,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
{% elif assignment.profile.exists %}
|
{% elif assignment.candidates %}
|
||||||
<i>{% trans "No ballots available." %}</i>
|
<i>{% trans "No ballots available." %}</i>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<br>
|
<br>
|
||||||
|
@ -55,7 +55,7 @@ urlpatterns = patterns('openslides.assignment.views',
|
|||||||
name='assignment_delrun',
|
name='assignment_delrun',
|
||||||
),
|
),
|
||||||
|
|
||||||
url(r'^(?P<assignment_id>\d+)/delother/(?P<profile_id>\d+)/$',
|
url(r'^(?P<assignment_id>\d+)/delother/(?P<user_id>[^/]+)/$',
|
||||||
'delother',
|
'delother',
|
||||||
name='assignment_delother',
|
name='assignment_delother',
|
||||||
),
|
),
|
||||||
@ -105,13 +105,13 @@ urlpatterns = patterns('openslides.assignment.views',
|
|||||||
name='assignment_poll_publish_status',
|
name='assignment_poll_publish_status',
|
||||||
),
|
),
|
||||||
|
|
||||||
url(r'^(?P<assignment_id>\d+)/elected/(?P<profile_id>\d+)/$',
|
url(r'^(?P<assignment_id>\d+)/elected/(?P<user_id>[^/]+)/$',
|
||||||
'set_elected',
|
'set_elected',
|
||||||
{'elected': True},
|
{'elected': True},
|
||||||
name='assignment_user_elected',
|
name='assignment_user_elected',
|
||||||
),
|
),
|
||||||
|
|
||||||
url(r'^(?P<assignment_id>\d+)/notelected/(?P<profile_id>\d+)/$',
|
url(r'^(?P<assignment_id>\d+)/notelected/(?P<user_id>[^/]+)/$',
|
||||||
'set_elected',
|
'set_elected',
|
||||||
{'elected': False},
|
{'elected': False},
|
||||||
name='assignment_user_not_elected',
|
name='assignment_user_not_elected',
|
||||||
|
@ -30,9 +30,11 @@ from openslides.utils.template import Tab
|
|||||||
from openslides.utils.utils import (template, permission_required,
|
from openslides.utils.utils import (template, permission_required,
|
||||||
gen_confirm_form, del_confirm_form, ajax_request)
|
gen_confirm_form, del_confirm_form, ajax_request)
|
||||||
from openslides.utils.views import FormView, DeleteView, PDFView, RedirectView
|
from openslides.utils.views import FormView, DeleteView, PDFView, RedirectView
|
||||||
|
from openslides.utils.person import get_person
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
from openslides.participant.models import Profile
|
|
||||||
|
from openslides.participant.models import OpenSlidesUser
|
||||||
|
|
||||||
from openslides.projector.projector import Widget
|
from openslides.projector.projector import Widget
|
||||||
|
|
||||||
@ -94,11 +96,14 @@ def view(request, assignment_id=None):
|
|||||||
else:
|
else:
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -168,13 +173,10 @@ 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.profile, request.user)
|
assignment.run(request.user.openslidesuser, 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)
|
||||||
except Profile.DoesNotExist:
|
|
||||||
messages.error(request,
|
|
||||||
_("You can't candidate. Your user account is only for administration."))
|
|
||||||
return redirect(reverse('assignment_view', args=[assignment_id]))
|
return redirect(reverse('assignment_view', args=[assignment_id]))
|
||||||
|
|
||||||
|
|
||||||
@ -182,28 +184,33 @@ def run(request, assignment_id):
|
|||||||
def delrun(request, assignment_id):
|
def delrun(request, assignment_id):
|
||||||
assignment = Assignment.objects.get(pk=assignment_id)
|
assignment = Assignment.objects.get(pk=assignment_id)
|
||||||
try:
|
try:
|
||||||
assignment.delrun(request.user.profile, request.user)
|
if assignment.status == 'sea' or user.has_perm("assignment.can_manage_assignment"):
|
||||||
messages.success(request, _("You have withdrawn your candidature successfully.") )
|
assignment.delrun(request.user.openslidesuser)
|
||||||
except NameError, e:
|
else:
|
||||||
|
messages.error(request, _('The candidate list is already closed.'))
|
||||||
|
except Exception, e:
|
||||||
messages.error(request, e)
|
messages.error(request, e)
|
||||||
|
else:
|
||||||
|
messages.success(request, _("You have withdrawn your candidature successfully.") )
|
||||||
return redirect(reverse('assignment_view', args=[assignment_id]))
|
return redirect(reverse('assignment_view', args=[assignment_id]))
|
||||||
|
|
||||||
|
|
||||||
@permission_required('assignment.can_manage_assignment')
|
@permission_required('assignment.can_manage_assignment')
|
||||||
def delother(request, assignment_id, profile_id):
|
def delother(request, assignment_id, user_id):
|
||||||
assignment = Assignment.objects.get(pk=assignment_id)
|
assignment = Assignment.objects.get(pk=assignment_id)
|
||||||
profile = Profile.objects.get(pk=profile_id)
|
person = get_person(user_id)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
try:
|
try:
|
||||||
assignment.delrun(profile, request.user)
|
assignment.delrun(person)
|
||||||
messages.success(request, _("Candidate <b>%s</b> was withdrawn successfully.") % (profile))
|
except Exception, e:
|
||||||
except NameError, e:
|
|
||||||
messages.error(request, e)
|
messages.error(request, e)
|
||||||
|
else:
|
||||||
|
messages.success(request, _("Candidate <b>%s</b> was withdrawn successfully.") % (person))
|
||||||
else:
|
else:
|
||||||
gen_confirm_form(request,
|
gen_confirm_form(request,
|
||||||
_("Do you really want to withdraw <b>%s</b> from the election?") \
|
_("Do you really want to withdraw <b>%s</b> from the election?") \
|
||||||
% profile, reverse('assignment_delother', args=[assignment_id, profile_id]))
|
% person, reverse('assignment_delother', args=[assignment_id, user_id]))
|
||||||
return redirect(reverse('assignment_view', args=[assignment_id]))
|
return redirect(reverse('assignment_view', args=[assignment_id]))
|
||||||
|
|
||||||
|
|
||||||
@ -263,21 +270,19 @@ def set_publish_status(request, poll_id):
|
|||||||
|
|
||||||
|
|
||||||
@permission_required('assignment.can_manage_assignment')
|
@permission_required('assignment.can_manage_assignment')
|
||||||
def set_elected(request, assignment_id, profile_id, elected=True):
|
def set_elected(request, assignment_id, user_id, elected=True):
|
||||||
assignment = Assignment.objects.get(pk=assignment_id)
|
assignment = Assignment.objects.get(pk=assignment_id)
|
||||||
profile = Profile.objects.get(pk=profile_id)
|
person = get_person(user_id)
|
||||||
assignment.set_elected(profile, elected)
|
assignment.set_elected(person, elected)
|
||||||
|
|
||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
if elected:
|
if elected:
|
||||||
link = reverse('assignment_user_not_elected', args=[assignment.id, profile.id])
|
link = reverse('assignment_user_not_elected', args=[assignment.id, person.person_id])
|
||||||
text = _('not elected')
|
text = _('not elected')
|
||||||
else:
|
else:
|
||||||
link = reverse('assignment_user_elected', args=[assignment.id, profile.id])
|
link = reverse('assignment_user_elected', args=[assignment.id, person.person_id])
|
||||||
text = _('elected')
|
text = _('elected')
|
||||||
return ajax_request({'elected': elected,
|
return ajax_request({'elected': elected, 'link': link, 'text': text})
|
||||||
'link': link,
|
|
||||||
'text': text})
|
|
||||||
|
|
||||||
return redirect(reverse('assignment_view', args=[assignment_id]))
|
return redirect(reverse('assignment_view', args=[assignment_id]))
|
||||||
|
|
||||||
@ -369,7 +374,7 @@ class AssignmentPDF(PDFView):
|
|||||||
cell2a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font><seqreset" \
|
cell2a.append(Paragraph("<font name='Ubuntu-Bold'>%s:</font><seqreset" \
|
||||||
" id='counter'>" % _("Candidates"), stylesheet['Heading4']))
|
" id='counter'>" % _("Candidates"), stylesheet['Heading4']))
|
||||||
cell2b = []
|
cell2b = []
|
||||||
for candidate in assignment.profile.all():
|
for candidate in assignment.candidates:
|
||||||
cell2b.append(Paragraph("<seq id='counter'/>. %s" % candidate,
|
cell2b.append(Paragraph("<seq id='counter'/>. %s" % candidate,
|
||||||
stylesheet['Signaturefield']))
|
stylesheet['Signaturefield']))
|
||||||
if assignment.status == "sea":
|
if assignment.status == "sea":
|
||||||
@ -407,7 +412,7 @@ class AssignmentPDF(PDFView):
|
|||||||
|
|
||||||
|
|
||||||
# Add result rows
|
# Add result rows
|
||||||
elected_candidates = assignment.elected.all()
|
elected_candidates = list(assignment.elected)
|
||||||
for candidate, poll_list in vote_results.iteritems():
|
for candidate, poll_list in vote_results.iteritems():
|
||||||
row = []
|
row = []
|
||||||
|
|
||||||
@ -548,8 +553,8 @@ class AssignmentPollPDF(PDFView):
|
|||||||
candidate = option.candidate
|
candidate = option.candidate
|
||||||
cell.append(Paragraph(candidate.user.get_full_name(),
|
cell.append(Paragraph(candidate.user.get_full_name(),
|
||||||
stylesheet['Ballot_option_name']))
|
stylesheet['Ballot_option_name']))
|
||||||
if candidate.group:
|
if candidate.name_surfix:
|
||||||
cell.append(Paragraph("(%s)" % candidate.group,
|
cell.append(Paragraph("(%s)" % candidate.name_surfix,
|
||||||
stylesheet['Ballot_option_group']))
|
stylesheet['Ballot_option_group']))
|
||||||
else:
|
else:
|
||||||
cell.append(Paragraph(" ",
|
cell.append(Paragraph(" ",
|
||||||
|
@ -67,16 +67,16 @@ 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']
|
u'can_see_application', 'can_see_assignment']
|
||||||
anonymous = Group()
|
anonymous = Group()
|
||||||
anonymous.name = 'Anonymous'
|
anonymous.name = 'Anonymous'
|
||||||
anonymous.save()
|
anonymous.save()
|
||||||
anonymous.permissions = Permission.objects.filter(
|
anonymous.permissions = Permission.objects.filter(
|
||||||
codename__in=default_perms)
|
codename__in=default_perms)
|
||||||
anonymous.save()
|
anonymous.save()
|
||||||
messages.success(self.request,
|
messages.success(self.request,
|
||||||
_('Anonymous access enabled. Please modify the "Anonymous" ' \
|
_('Anonymous access enabled. Please modify the "Anonymous" ' \
|
||||||
'group to fit your required permissions.'))
|
'group to fit your required permissions.'))
|
||||||
else:
|
else:
|
||||||
config['system_enable_anonymous'] = False
|
config['system_enable_anonymous'] = False
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ def _fs2unicode(s):
|
|||||||
|
|
||||||
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
|
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
|
||||||
|
|
||||||
AUTH_PROFILE_MODULE = 'participant.Profile'
|
|
||||||
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',
|
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',
|
||||||
'openslides.utils.auth.AnonymousAuth',)
|
'openslides.utils.auth.AnonymousAuth',)
|
||||||
|
|
||||||
|
@ -15,6 +15,9 @@ import string
|
|||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
from openslides.utils.person import get_person
|
||||||
|
from openslides.participant.models import OpenSlidesUser
|
||||||
|
|
||||||
|
|
||||||
def gen_password():
|
def gen_password():
|
||||||
"""
|
"""
|
||||||
|
@ -15,67 +15,71 @@ from django.contrib.auth.forms import AdminPasswordChangeForm
|
|||||||
from django.contrib.auth.models import User, Group, Permission
|
from django.contrib.auth.models import User, Group, Permission
|
||||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
||||||
|
|
||||||
from openslides.utils.forms import CssClassMixin, LocalizedModelMultipleChoiceField
|
from openslides.utils.forms import (
|
||||||
|
CssClassMixin, LocalizedModelMultipleChoiceField)
|
||||||
|
|
||||||
from openslides.participant.models import Profile
|
from openslides.participant.models import OpenSlidesUser
|
||||||
|
|
||||||
|
|
||||||
USER_APPLICATION_IMPORT_OPTIONS = [
|
USER_APPLICATION_IMPORT_OPTIONS = [
|
||||||
('REASSIGN', _('Keep applications, try to reassign submitter')),
|
('REASSIGN', _('Keep applications, try to reassign submitter')),
|
||||||
('INREVIEW', _('Keep applications, set status to "needs review"')),
|
('INREVIEW', _('Keep applications, set status to "needs review"')),
|
||||||
('DISCARD' , _('Discard applications'))
|
('DISCARD', _('Discard applications'))
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class UserNewForm(forms.ModelForm, CssClassMixin):
|
class UserNewForm(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(queryset=Group.objects.all(),
|
groups = forms.ModelMultipleChoiceField(
|
||||||
label=_("User groups"), required=False)
|
queryset=Group.objects.all(), label=_("User groups"), required=False)
|
||||||
is_active = forms.BooleanField(label=_("Active"), required=False,
|
is_active = forms.BooleanField(
|
||||||
initial=True)
|
label=_("Active"), required=False, initial=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
exclude = ('username', 'password', 'is_staff', 'is_superuser',
|
exclude = ('username', 'password', 'is_staff', 'is_superuser',
|
||||||
'last_login', 'date_joined', 'user_permissions')
|
'last_login', 'date_joined', 'user_permissions')
|
||||||
|
|
||||||
|
|
||||||
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(queryset=Group.objects.all(),
|
groups = forms.ModelMultipleChoiceField(
|
||||||
label=_("User groups"), required=False)
|
queryset=Group.objects.all(), 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',
|
exclude = ('password', 'is_staff', 'is_superuser', 'last_login',
|
||||||
'date_joined', 'user_permissions')
|
'date_joined', 'user_permissions')
|
||||||
|
|
||||||
|
|
||||||
class UsernameForm(forms.ModelForm, CssClassMixin):
|
class UsernameForm(forms.ModelForm, CssClassMixin):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
exclude = ('first_name', 'last_name', 'email', 'is_active',
|
exclude = ('first_name', 'last_name', 'email', 'is_active',
|
||||||
'is_superuser', 'groups', 'password', 'is_staff', 'last_login',
|
'is_superuser', 'groups', 'password', 'is_staff',
|
||||||
'date_joined', 'user_permissions')
|
'last_login', 'date_joined', 'user_permissions')
|
||||||
|
|
||||||
|
|
||||||
class ProfileForm(forms.ModelForm, CssClassMixin):
|
class OpenSlidesUserForm(forms.ModelForm, CssClassMixin):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Profile
|
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"))
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(GroupForm, self).__init__(*args, **kwargs)
|
super(GroupForm, self).__init__(*args, **kwargs)
|
||||||
if kwargs.get('instance', None) is not None:
|
if kwargs.get('instance', None) is not None:
|
||||||
self.fields['permissions'].initial = \
|
self.fields['permissions'].initial = (
|
||||||
[p.pk for p in kwargs['instance'].permissions.all()]
|
[p.pk for p in kwargs['instance'].permissions.all()])
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Group
|
model = Group
|
||||||
@ -87,14 +91,13 @@ 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(
|
application_handling = forms.ChoiceField(
|
||||||
required=True,
|
required=True, choices=USER_APPLICATION_IMPORT_OPTIONS,
|
||||||
choices=USER_APPLICATION_IMPORT_OPTIONS,
|
label=_("For existing applications"))
|
||||||
label=_("For existing applications"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigForm(forms.Form, CssClassMixin):
|
class ConfigForm(forms.Form, CssClassMixin):
|
||||||
@ -102,11 +105,9 @@ class ConfigForm(forms.Form, CssClassMixin):
|
|||||||
widget=forms.TextInput(),
|
widget=forms.TextInput(),
|
||||||
required=False,
|
required=False,
|
||||||
label=_("System URL"),
|
label=_("System URL"),
|
||||||
help_text=_("Printed in PDF of first time passwords only."),
|
help_text=_("Printed in PDF of first time passwords only."))
|
||||||
)
|
|
||||||
participant_pdf_welcometext = forms.CharField(
|
participant_pdf_welcometext = forms.CharField(
|
||||||
widget=forms.Textarea(),
|
widget=forms.Textarea(),
|
||||||
required=False,
|
required=False,
|
||||||
label=_("Welcome text"),
|
label=_("Welcome text"),
|
||||||
help_text=_("Printed in PDF of first time passwords only."),
|
help_text=_("Printed in PDF of first time passwords only."))
|
||||||
)
|
|
||||||
|
@ -10,19 +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
|
from django.contrib.auth.models import User, Group
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q
|
from django.db.models import Q, 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.signals import receiv_persons
|
||||||
|
|
||||||
from openslides.config.signals import default_config_value
|
from openslides.config.signals import default_config_value
|
||||||
|
|
||||||
from openslides.participant.api import gen_password
|
|
||||||
|
|
||||||
|
class OpenSlidesUser(models.Model, PersonMixin):
|
||||||
|
person_prefix = 'openslides_user'
|
||||||
class Profile(models.Model):
|
|
||||||
GENDER_CHOICES = (
|
GENDER_CHOICES = (
|
||||||
('male', _('Male')),
|
('male', _('Male')),
|
||||||
('female', _('Female')),
|
('female', _('Female')),
|
||||||
@ -35,29 +36,36 @@ class Profile(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
user = models.OneToOneField(User, unique=True, editable=False)
|
user = models.OneToOneField(User, unique=True, editable=False)
|
||||||
group = models.CharField(max_length=100, null=True, blank=True,
|
name_surfix = models.CharField(
|
||||||
verbose_name = _("Group"), help_text=_('Shown behind the name.'))
|
max_length=100, null=True, blank=True, verbose_name=_("Name Surfix"),
|
||||||
gender = models.CharField(max_length=50, choices=GENDER_CHOICES, blank=True,
|
help_text=_('Shown behind the name.'))
|
||||||
verbose_name = _("Gender"),
|
gender = models.CharField(
|
||||||
|
max_length=50, choices=GENDER_CHOICES, blank=True,
|
||||||
|
verbose_name=_("Gender"), help_text=_('Only for filter the userlist.'))
|
||||||
|
type = models.CharField(
|
||||||
|
max_length=100, choices=TYPE_CHOICE, blank=True,
|
||||||
|
verbose_name=_("Typ"), help_text=_('Only for filter the userlist.'))
|
||||||
|
committee = models.CharField(
|
||||||
|
max_length=100, null=True, blank=True, verbose_name=_("Committee"),
|
||||||
help_text=_('Only for filter the userlist.'))
|
help_text=_('Only for filter the userlist.'))
|
||||||
type = models.CharField(max_length=100, choices=TYPE_CHOICE, blank=True,
|
comment = models.TextField(
|
||||||
verbose_name = _("Typ"), help_text=_('Only for filter the userlist.'))
|
null=True, blank=True, verbose_name=_('Comment'),
|
||||||
committee = models.CharField(max_length=100, null=True, blank=True,
|
help_text=_('Only for notes.'))
|
||||||
verbose_name = _("Committee"),
|
firstpassword = models.CharField(
|
||||||
help_text=_('Only for filter the userlist.'))
|
max_length=100, null=True, blank=True,
|
||||||
comment = models.TextField(null=True, blank=True,
|
verbose_name=_("First Password"))
|
||||||
verbose_name = _('Comment'), help_text=_('Only for notes.'))
|
|
||||||
firstpassword = models.CharField(max_length=100, null=True, blank=True,
|
|
||||||
verbose_name = _("First Password"))
|
|
||||||
|
|
||||||
|
def reset_password(self, password=None):
|
||||||
def reset_password(self):
|
|
||||||
"""
|
"""
|
||||||
Reset the password for the user to his default-password.
|
Reset the password for the user to his default-password.
|
||||||
"""
|
"""
|
||||||
self.user.set_password(self.firstpassword)
|
if password is None:
|
||||||
|
password = self.firstpassword
|
||||||
|
self.user.set_password(password)
|
||||||
self.user.save()
|
self.user.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'):
|
||||||
@ -74,25 +82,75 @@ class Profile(models.Model):
|
|||||||
return ('user_delete', [str(self.user.id)])
|
return ('user_delete', [str(self.user.id)])
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
if self.group:
|
if self.name_surfix:
|
||||||
return "%s (%s)" % (self.user.get_full_name(), self.group)
|
return "%s (%s)" % (self.user.get_full_name(), self.name_surfix)
|
||||||
return "%s" % self.user.get_full_name()
|
return "%s" % self.user.get_full_name()
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
# Rename permissions
|
||||||
permissions = (
|
permissions = (
|
||||||
('can_see_participant', ugettext_noop("Can see participant")),
|
('can_see_participant', ugettext_noop("Can see participant")),
|
||||||
('can_manage_participant', ugettext_noop("Can manage participant")),
|
('can_manage_participant',
|
||||||
|
ugettext_noop("Can manage participant")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OpenSlidesGroup(models.Model, PersonMixin):
|
||||||
|
person_prefix = 'openslides_group'
|
||||||
|
|
||||||
|
group = models.OneToOneField(Group)
|
||||||
|
group_as_person = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.group)
|
||||||
|
|
||||||
|
|
||||||
|
class OpenSlidesUsersConnecter(object):
|
||||||
|
def __init__(self, person_prefix=None, id=None):
|
||||||
|
self.person_prefix = person_prefix
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
if (not self.person_prefix or
|
||||||
|
self.person_prefix == OpenSlidesUser.person_prefix):
|
||||||
|
if self.id:
|
||||||
|
yield OpenSlidesUser.objects.get(pk=self.id)
|
||||||
|
else:
|
||||||
|
for user in OpenSlidesUser.objects.all():
|
||||||
|
yield user
|
||||||
|
|
||||||
|
if (not self.person_prefix or
|
||||||
|
self.person_prefix == OpenSlidesGroup.person_prefix):
|
||||||
|
if self.id:
|
||||||
|
yield OpenSlidesGroup.objects.get(pk=self.id)
|
||||||
|
else:
|
||||||
|
for group in OpenSlidesGroup.objects.all():
|
||||||
|
yield group
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return OpenSlidesUser.objects.get(pk=key)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(receiv_persons, dispatch_uid="participant")
|
||||||
|
def receiv_persons(sender, **kwargs):
|
||||||
|
return OpenSlidesUsersConnecter(person_prefix=kwargs['person_prefix'],
|
||||||
|
id=kwargs['id'])
|
||||||
|
|
||||||
|
|
||||||
@receiver(default_config_value, dispatch_uid="participant_default_config")
|
@receiver(default_config_value, dispatch_uid="participant_default_config")
|
||||||
def default_config(sender, key, **kwargs):
|
def default_config(sender, key, **kwargs):
|
||||||
"""
|
"""
|
||||||
Default values for the participant app.
|
Default values for the participant app.
|
||||||
"""
|
"""
|
||||||
|
# TODO: Rename config-vars
|
||||||
return {
|
return {
|
||||||
'participant_pdf_system_url': 'http://example.com:8000',
|
'participant_pdf_system_url': 'http://example.com:8000',
|
||||||
'participant_pdf_welcometext': _('Welcome to OpenSlides!'),
|
'participant_pdf_welcometext': _('Welcome to OpenSlides!'),
|
||||||
'admin_password': None,
|
'admin_password': None,
|
||||||
}.get(key)
|
}.get(key)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(signals.post_save, sender=User)
|
||||||
|
def user_post_save(sender, instance, signal, *args, **kwargs):
|
||||||
|
# Creates OpenSlidesUser
|
||||||
|
profile, new = OpenSlidesUser.objects.get_or_create(user=instance)
|
||||||
|
@ -77,11 +77,11 @@
|
|||||||
<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.profile.group }}</td>
|
<td>{{ user.openslidesuser.name_surfix }}</td>
|
||||||
<td>{{ user.profile.get_type_display }}</td>
|
<td>{{ user.openslidesuser.get_type_display }}</td>
|
||||||
<td>{{ user.profile.committee }}</td>
|
<td>{{ user.openslidesuser.committee }}</td>
|
||||||
{% if perms.participant.can_manage_participant %}
|
{% if perms.participant.can_manage_participant %}
|
||||||
<td>{{ user.profile.comment|first_line }}</td>
|
<td>{{ user.openslidesuser.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>
|
||||||
|
72
openslides/participant/tests.py
Normal file
72
openslides/participant/tests.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.participant.tests
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Unit test for the participant app.
|
||||||
|
|
||||||
|
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
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 openslides.utils.person import get_person, Persons
|
||||||
|
from openslides.participant.api import gen_username, gen_password
|
||||||
|
from openslides.participant.models import OpenSlidesUser, OpenSlidesGroup
|
||||||
|
|
||||||
|
|
||||||
|
class OpenSlidesUserTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.user1 = User(first_name=u'Max', last_name=u'Mustermann')
|
||||||
|
self.user1.username = gen_username(self.user1.first_name, self.user1.last_name)
|
||||||
|
self.user1.save()
|
||||||
|
self.openslidesuser1 = self.user1.openslidesuser
|
||||||
|
self.openslidesuser1.firstpassword = gen_password()
|
||||||
|
self.openslidesuser1.save()
|
||||||
|
self.user1 = self.openslidesuser1.user
|
||||||
|
|
||||||
|
def test_participant_user(self):
|
||||||
|
self.assertEqual(self.user1.openslidesuser, self.openslidesuser1)
|
||||||
|
self.assertEqual(self.user1, self.openslidesuser1.user)
|
||||||
|
|
||||||
|
def test_repr(self):
|
||||||
|
self.assertEqual(unicode(self.openslidesuser1), u'Max Mustermann')
|
||||||
|
|
||||||
|
def test_name_surfix(self):
|
||||||
|
self.openslidesuser1.name_surfix = u'München'
|
||||||
|
self.openslidesuser1.save()
|
||||||
|
self.assertEqual(unicode(self.openslidesuser1), u'Max Mustermann (München)')
|
||||||
|
|
||||||
|
def test_reset_password(self):
|
||||||
|
self.assertIsInstance(self.openslidesuser1.firstpassword, basestring)
|
||||||
|
self.assertEqual(len(self.openslidesuser1.firstpassword), 8)
|
||||||
|
self.user1.set_unusable_password()
|
||||||
|
self.assertFalse(self.user1.check_password(self.openslidesuser1.firstpassword))
|
||||||
|
self.openslidesuser1.reset_password()
|
||||||
|
self.assertTrue(self.user1.check_password(self.openslidesuser1.firstpassword))
|
||||||
|
|
||||||
|
def test_person_api(self):
|
||||||
|
self.assertTrue(hasattr(self.openslidesuser1, 'person_id'))
|
||||||
|
self.assertEqual(self.openslidesuser1.person_id, 'openslides_user:1')
|
||||||
|
self.assertEqual(get_person('openslides_user:1'), self.openslidesuser1)
|
||||||
|
self.assertEqual(len(Persons()), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class OpenSlidesGroupTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.group1 = Group.objects.create(name='Test Group')
|
||||||
|
self.openslidesgroup1 = OpenSlidesGroup.objects.create(group=self.group1)
|
||||||
|
|
||||||
|
def test_group_openslidesgroup(self):
|
||||||
|
self.assertEqual(self.openslidesgroup1.group, self.group1)
|
||||||
|
|
||||||
|
def test_person_api(self):
|
||||||
|
self.assertTrue(hasattr(self.openslidesgroup1, 'person_id'))
|
||||||
|
self.assertEqual(self.openslidesgroup1.person_id, 'openslides_group:1')
|
||||||
|
self.assertEqual(get_person('openslides_group:1'), self.openslidesgroup1)
|
@ -18,13 +18,14 @@ from urllib import urlencode
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from urlparse import parse_qs
|
from urlparse import parse_qs
|
||||||
except ImportError: # python <= 2.5 grab it from cgi
|
except ImportError: # python <= 2.5 grab it from cgi
|
||||||
from cgi import parse_qs
|
from cgi import parse_qs
|
||||||
|
|
||||||
from reportlab.lib import colors
|
from reportlab.lib import colors
|
||||||
from reportlab.lib.units import cm
|
from reportlab.lib.units import cm
|
||||||
from reportlab.platypus import (SimpleDocTemplate, PageBreak, Paragraph,
|
from reportlab.platypus import (
|
||||||
LongTable, Spacer, Table, TableStyle)
|
SimpleDocTemplate, PageBreak, Paragraph, LongTable, Spacer, Table,
|
||||||
|
TableStyle)
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
@ -39,18 +40,18 @@ from django.utils.translation import ugettext as _, ungettext, ugettext_lazy
|
|||||||
from openslides.utils import csv_ext
|
from openslides.utils import csv_ext
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
from openslides.utils.template import Tab
|
from openslides.utils.template import Tab
|
||||||
from openslides.utils.utils import (template, permission_required,
|
from openslides.utils.utils import (
|
||||||
gen_confirm_form, ajax_request, decodedict, encodedict,
|
template, permission_required, gen_confirm_form, ajax_request, decodedict,
|
||||||
delete_default_permissions, html_strong)
|
encodedict, delete_default_permissions, html_strong)
|
||||||
from openslides.utils.views import FormView, PDFView
|
from openslides.utils.views import FormView, PDFView
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
|
|
||||||
from openslides.participant.models import Profile
|
from openslides.participant.models import OpenSlidesUser, OpenSlidesGroup
|
||||||
from openslides.participant.api import gen_username, gen_password
|
from openslides.participant.api import gen_username, gen_password
|
||||||
from openslides.participant.forms import (UserNewForm, UserEditForm,
|
from openslides.participant.forms import (
|
||||||
ProfileForm, UsersettingsForm, UserImportForm, GroupForm,
|
UserNewForm, UserEditForm, OpenSlidesUserForm, UsersettingsForm,
|
||||||
AdminPasswordChangeForm, ConfigForm)
|
UserImportForm, GroupForm, AdminPasswordChangeForm, ConfigForm)
|
||||||
|
|
||||||
|
|
||||||
@permission_required('participant.can_see_participant')
|
@permission_required('participant.can_see_participant')
|
||||||
@ -78,53 +79,60 @@ def get_overview(request):
|
|||||||
|
|
||||||
query = User.objects
|
query = User.objects
|
||||||
if 'gender' in sortfilter:
|
if 'gender' in sortfilter:
|
||||||
query = query.filter(profile__gender__iexact=sortfilter['gender'][0])
|
query = query.filter(
|
||||||
|
openslidesuser__gender__iexact=sortfilter['gender'][0])
|
||||||
if 'group' in sortfilter:
|
if 'group' in sortfilter:
|
||||||
query = query.filter(profile__group__iexact=sortfilter['group'][0])
|
query = query.filter(
|
||||||
|
openslidesuser__name_surfix__iexact=sortfilter['group'][0])
|
||||||
if 'type' in sortfilter:
|
if 'type' in sortfilter:
|
||||||
query = query.filter(profile__type__iexact=sortfilter['type'][0])
|
query = query.filter(
|
||||||
|
openslidesuser__type__iexact=sortfilter['type'][0])
|
||||||
if 'committee' in sortfilter:
|
if 'committee' in sortfilter:
|
||||||
query = query. \
|
query = query.filter(
|
||||||
filter(profile__committee__iexact=sortfilter['committee'][0])
|
openslidesuser__committee__iexact=sortfilter['committee'][0])
|
||||||
if 'status' in sortfilter:
|
if 'status' in sortfilter:
|
||||||
query = query.filter(is_active=sortfilter['status'][0])
|
query = query.filter(is_active=sortfilter['status'][0])
|
||||||
if 'sort' in sortfilter:
|
if 'sort' in sortfilter:
|
||||||
if sortfilter['sort'][0] in ['first_name', 'last_name', 'last_login']:
|
if sortfilter['sort'][0] in ['first_name', 'last_name', 'last_login']:
|
||||||
query = query.order_by(sortfilter['sort'][0])
|
query = query.order_by(sortfilter['sort'][0])
|
||||||
elif sortfilter['sort'][0] in ['group', 'type', 'committee', 'comment']:
|
elif (sortfilter['sort'][0] in
|
||||||
query = query.order_by('profile__%s' % sortfilter['sort'][0])
|
['name_surfix', 'type', 'committee', 'comment']):
|
||||||
|
query = query.order_by(
|
||||||
|
'openslidesuser__%s' % sortfilter['sort'][0])
|
||||||
else:
|
else:
|
||||||
query = query.order_by('last_name')
|
query = query.order_by('last_name')
|
||||||
if 'reverse' in sortfilter:
|
if 'reverse' in sortfilter:
|
||||||
query = query.reverse()
|
query = query.reverse()
|
||||||
|
|
||||||
# list of filtered users (with profile)
|
# list of filtered users
|
||||||
userlist = query.all()
|
userlist = query.all()
|
||||||
users = []
|
users = []
|
||||||
for user in userlist:
|
for user in userlist:
|
||||||
try:
|
try:
|
||||||
user.get_profile()
|
user.openslidesuser
|
||||||
users.append(user)
|
except OpenSlidesUser.DoesNotExist:
|
||||||
except Profile.DoesNotExist:
|
|
||||||
pass
|
pass
|
||||||
# list of all existing users (with profile)
|
else:
|
||||||
|
users.append(user)
|
||||||
|
# list of all existing users
|
||||||
allusers = []
|
allusers = []
|
||||||
for user in User.objects.all():
|
for user in User.objects.all():
|
||||||
try:
|
try:
|
||||||
user.get_profile()
|
user.openslidesuser
|
||||||
allusers.append(user)
|
except OpenSlidesUser.DoesNotExist:
|
||||||
except Profile.DoesNotExist:
|
|
||||||
pass
|
pass
|
||||||
|
else:
|
||||||
|
allusers.append(user)
|
||||||
# quotient of selected users and all users
|
# quotient of selected users and all users
|
||||||
if len(allusers) > 0:
|
if len(allusers) > 0:
|
||||||
percent = float(len(users)) * 100 / float(len(allusers))
|
percent = float(len(users)) * 100 / float(len(allusers))
|
||||||
else:
|
else:
|
||||||
percent = 0
|
percent = 0
|
||||||
# list of all existing groups
|
# list of all existing groups
|
||||||
groups = [p['group'] for p in Profile.objects.values('group') \
|
groups = [p['name_surfix'] for p in OpenSlidesUser.objects.values('name_surfix')
|
||||||
.exclude(group='').distinct()]
|
.exclude(name_surfix='').distinct()]
|
||||||
# list of all existing committees
|
# list of all existing committees
|
||||||
committees = [p['committee'] for p in Profile.objects.values('committee') \
|
committees = [p['committee'] for p in OpenSlidesUser.objects.values('committee')
|
||||||
.exclude(committee='').distinct()]
|
.exclude(committee='').distinct()]
|
||||||
return {
|
return {
|
||||||
'users': users,
|
'users': users,
|
||||||
@ -142,7 +150,7 @@ def get_overview(request):
|
|||||||
@template('participant/edit.html')
|
@template('participant/edit.html')
|
||||||
def edit(request, user_id=None):
|
def edit(request, user_id=None):
|
||||||
"""
|
"""
|
||||||
View to create and edit users with profile.
|
View to create and edit users.
|
||||||
"""
|
"""
|
||||||
if user_id is not None:
|
if user_id is not None:
|
||||||
user = User.objects.get(id=user_id)
|
user = User.objects.get(id=user_id)
|
||||||
@ -151,26 +159,31 @@ def edit(request, user_id=None):
|
|||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if user_id is None:
|
if user_id is None:
|
||||||
userform = UserNewForm(request.POST, prefix="user")
|
user_form = UserNewForm(request.POST, prefix="user")
|
||||||
profileform = ProfileForm(request.POST, prefix="profile")
|
openslides_user_form = OpenSlidesUserForm(request.POST, prefix="openslidesuser")
|
||||||
else:
|
else:
|
||||||
userform = UserEditForm(request.POST, instance=user, prefix="user")
|
user_form = UserEditForm(request.POST, instance=user, prefix="user")
|
||||||
profileform = ProfileForm(request.POST, instance=user.profile,
|
openslides_user_form = OpenSlidesUserForm(request.POST, instance=user.openslidesuser,
|
||||||
prefix="profile")
|
prefix="openslidesuser")
|
||||||
|
|
||||||
if userform.is_valid() and profileform.is_valid():
|
if user_form.is_valid() and openslides_user_form.is_valid():
|
||||||
user = userform.save()
|
user = user_form.save(commit=False)
|
||||||
if user_id is None:
|
if user_id is None:
|
||||||
|
# TODO: call first_name and last_name though openslides_user
|
||||||
user.username = gen_username(user.first_name, user.last_name)
|
user.username = gen_username(user.first_name, user.last_name)
|
||||||
user.save()
|
user.save()
|
||||||
profile = profileform.save(commit=False)
|
openslides_user = user.openslidesuser
|
||||||
profile.user = user
|
openslides_user_form = OpenSlidesUserForm(request.POST, instance=openslides_user, prefix="openslidesuser")
|
||||||
|
openslides_user_form.is_valid()
|
||||||
|
openslides_user = openslides_user_form.save(commit=False)
|
||||||
|
openslides_user.user = user
|
||||||
if user_id is None:
|
if user_id is None:
|
||||||
if not profile.firstpassword:
|
if not openslides_user.firstpassword:
|
||||||
profile.firstpassword = gen_password()
|
openslides_user.firstpassword = gen_password()
|
||||||
profile.user.set_password(profile.firstpassword)
|
openslides_user.user.set_password(openslides_user.firstpassword)
|
||||||
profile.user.save()
|
# TODO: Try not to save the user object
|
||||||
profile.save()
|
openslides_user.user.save()
|
||||||
|
openslides_user.save()
|
||||||
if user_id is None:
|
if user_id is None:
|
||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('New participant was successfully created.'))
|
_('New participant was successfully created.'))
|
||||||
@ -185,15 +198,15 @@ def edit(request, user_id=None):
|
|||||||
messages.error(request, _('Please check the form for errors.'))
|
messages.error(request, _('Please check the form for errors.'))
|
||||||
else:
|
else:
|
||||||
if user_id is None:
|
if user_id is None:
|
||||||
userform = UserNewForm(prefix="user")
|
user_form = UserNewForm(prefix="user")
|
||||||
profileform = ProfileForm(prefix="profile")
|
openslides_user_form = OpenSlidesUserForm(prefix="openslidesuser")
|
||||||
else:
|
else:
|
||||||
userform = UserEditForm(instance=user, prefix="user")
|
user_form = UserEditForm(instance=user, prefix="user")
|
||||||
profileform = ProfileForm(instance=user.profile, prefix="profile")
|
openslides_user_form = OpenSlidesUserForm(instance=user.openslidesuser, prefix="openslidesuser")
|
||||||
|
# TODO: rename template vars
|
||||||
return {
|
return {
|
||||||
'userform': userform,
|
'userform': user_form,
|
||||||
'profileform': profileform,
|
'profileform': openslides_user_form,
|
||||||
'edituser': user,
|
'edituser': user,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,6 +282,7 @@ def group_edit(request, group_id=None):
|
|||||||
try:
|
try:
|
||||||
group = Group.objects.get(id=group_id)
|
group = Group.objects.get(id=group_id)
|
||||||
except Group.DoesNotExist:
|
except Group.DoesNotExist:
|
||||||
|
# TODO: return a 404 Object
|
||||||
raise NameError("There is no group %d" % group_id)
|
raise NameError("There is no group %d" % group_id)
|
||||||
else:
|
else:
|
||||||
group = None
|
group = None
|
||||||
@ -277,25 +291,38 @@ def group_edit(request, group_id=None):
|
|||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = GroupForm(request.POST, instance=group)
|
form = GroupForm(request.POST, instance=group)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
# TODO: This can be done inside the form
|
||||||
group_name = form.cleaned_data['name'].lower()
|
group_name = form.cleaned_data['name'].lower()
|
||||||
|
|
||||||
|
# TODO: Why is this code called on any request and not only, if the
|
||||||
|
# anonymous_group is edited?
|
||||||
try:
|
try:
|
||||||
anonymous_group = Group.objects.get(name='Anonymous')
|
anonymous_group = Group.objects.get(name='Anonymous')
|
||||||
except Group.DoesNotExist:
|
except Group.DoesNotExist:
|
||||||
anonymous_group = None
|
anonymous_group = None
|
||||||
|
|
||||||
# special handling for anonymous auth
|
# special handling for anonymous auth
|
||||||
|
# TODO: This code should be a form validator.
|
||||||
if group is None and group_name.strip().lower() == 'anonymous':
|
if group is None and group_name.strip().lower() == 'anonymous':
|
||||||
# don't allow to create this group
|
# don't allow to create this group
|
||||||
messages.error(request,
|
messages.error(request,
|
||||||
_('Group name "%s" is reserved for internal use.')
|
_('Group name "%s" is reserved for internal use.')
|
||||||
% group_name)
|
% group_name)
|
||||||
return {
|
return {
|
||||||
'form' : form,
|
'form': form,
|
||||||
'group': group
|
'group': group
|
||||||
}
|
}
|
||||||
|
|
||||||
group = form.save()
|
group = form.save()
|
||||||
|
try:
|
||||||
|
openslides_group = OpenSlidesGroup.objects.get(group=group)
|
||||||
|
except OpenSlidesGroup.DoesNotExist:
|
||||||
|
django_group = None
|
||||||
|
if form.cleaned_data['as_user'] and django_group is None:
|
||||||
|
OpenSlidesGroup(group=group).save()
|
||||||
|
elif not form.cleaned_data['as_user'] and django_group:
|
||||||
|
django_group.delete()
|
||||||
|
|
||||||
if anonymous_group is not None and \
|
if anonymous_group is not None and \
|
||||||
anonymous_group.id == group.id:
|
anonymous_group.id == group.id:
|
||||||
# prevent name changes -
|
# prevent name changes -
|
||||||
@ -315,7 +342,12 @@ def group_edit(request, group_id=None):
|
|||||||
else:
|
else:
|
||||||
messages.error(request, _('Please check the form for errors.'))
|
messages.error(request, _('Please check the form for errors.'))
|
||||||
else:
|
else:
|
||||||
form = GroupForm(instance=group)
|
if group and OpenSlidesGroup.objects.filter(group=group).exists():
|
||||||
|
initial = {'as_user': True}
|
||||||
|
else:
|
||||||
|
initial = {'as_user': False}
|
||||||
|
|
||||||
|
form = GroupForm(instance=group, initial=initial)
|
||||||
return {
|
return {
|
||||||
'form': form,
|
'form': form,
|
||||||
'group': group,
|
'group': group,
|
||||||
@ -346,7 +378,7 @@ def user_settings(request):
|
|||||||
Edit own user account.
|
Edit own user account.
|
||||||
"""
|
"""
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form_user = UsersettingsForm(request.POST,instance=request.user)
|
form_user = UsersettingsForm(request.POST, instance=request.user)
|
||||||
if form_user.is_valid():
|
if form_user.is_valid():
|
||||||
form_user.save()
|
form_user.save()
|
||||||
messages.success(request, _('User settings successfully saved.'))
|
messages.success(request, _('User settings successfully saved.'))
|
||||||
@ -606,7 +638,7 @@ class ParticipantsListPDF(PDFView):
|
|||||||
document_title = ugettext_lazy('List of Participants')
|
document_title = ugettext_lazy('List of Participants')
|
||||||
|
|
||||||
def append_to_pdf(self, story):
|
def append_to_pdf(self, story):
|
||||||
data= [['#', _('Last Name'), _('First Name'), _('Group'), _('Type'),
|
data = [['#', _('Last Name'), _('First Name'), _('Group'), _('Type'),
|
||||||
_('Committee')]]
|
_('Committee')]]
|
||||||
sort = 'last_name'
|
sort = 'last_name'
|
||||||
counter = 0
|
counter = 0
|
||||||
@ -614,27 +646,27 @@ class ParticipantsListPDF(PDFView):
|
|||||||
try:
|
try:
|
||||||
counter += 1
|
counter += 1
|
||||||
user.get_profile()
|
user.get_profile()
|
||||||
data.append([counter,
|
data.append([
|
||||||
|
counter,
|
||||||
Paragraph(user.last_name, stylesheet['Tablecell']),
|
Paragraph(user.last_name, stylesheet['Tablecell']),
|
||||||
Paragraph(user.first_name, stylesheet['Tablecell']),
|
Paragraph(user.first_name, stylesheet['Tablecell']),
|
||||||
Paragraph(user.profile.group, stylesheet['Tablecell']),
|
Paragraph(user.profile.group, stylesheet['Tablecell']),
|
||||||
Paragraph(user.profile.get_type_display(),
|
Paragraph(user.profile.get_type_display(),
|
||||||
stylesheet['Tablecell']),
|
stylesheet['Tablecell']),
|
||||||
Paragraph(user.profile.committee, stylesheet['Tablecell']),
|
Paragraph(user.profile.committee, stylesheet['Tablecell'])
|
||||||
])
|
])
|
||||||
except Profile.DoesNotExist:
|
except Profile.DoesNotExist:
|
||||||
counter -= 1
|
counter -= 1
|
||||||
pass
|
pass
|
||||||
t = LongTable(data,
|
t = LongTable(data,
|
||||||
style=[
|
style=[
|
||||||
('VALIGN',(0,0),(-1,-1), 'TOP'),
|
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
||||||
('LINEABOVE',(0,0),(-1,0),2,colors.black),
|
('LINEABOVE', (0, 0), (-1, 0), 2, colors.black),
|
||||||
('LINEABOVE',(0,1),(-1,1),1,colors.black),
|
('LINEABOVE', (0, 1), (-1, 1), 1, colors.black),
|
||||||
('LINEBELOW',(0,-1),(-1,-1),2,colors.black),
|
('LINEBELOW', (0, -1), (-1, -1), 2, colors.black),
|
||||||
('ROWBACKGROUNDS', (0, 1), (-1, -1),
|
('ROWBACKGROUNDS', (0, 1), (-1, -1),
|
||||||
(colors.white, (.9, .9, .9))),
|
(colors.white, (.9, .9, .9)))])
|
||||||
])
|
t._argW[0] = 0.75 * cm
|
||||||
t._argW[0]=0.75*cm
|
|
||||||
story.append(t)
|
story.append(t)
|
||||||
|
|
||||||
|
|
||||||
@ -654,48 +686,48 @@ class ParticipantsPasswordsPDF(PDFView):
|
|||||||
pdf_document.build(story)
|
pdf_document.build(story)
|
||||||
|
|
||||||
def append_to_pdf(self, story):
|
def append_to_pdf(self, story):
|
||||||
data= []
|
data = []
|
||||||
participant_pdf_system_url = config["participant_pdf_system_url"]
|
participant_pdf_system_url = config["participant_pdf_system_url"]
|
||||||
participant_pdf_welcometext = config["participant_pdf_welcometext"]
|
participant_pdf_welcometext = config["participant_pdf_welcometext"]
|
||||||
for user in User.objects.all().order_by('last_name'):
|
for user in User.objects.all().order_by('last_name'):
|
||||||
try:
|
try:
|
||||||
user.get_profile()
|
user.get_profile()
|
||||||
cell = []
|
cell = []
|
||||||
cell.append(Spacer(0,0.8*cm))
|
cell.append(Spacer(0, 0.8 * cm))
|
||||||
cell.append(Paragraph(_("Account for OpenSlides"),
|
cell.append(Paragraph(_("Account for OpenSlides"),
|
||||||
stylesheet['Ballot_title']))
|
stylesheet['Ballot_title']))
|
||||||
cell.append(Paragraph(_("for %s") % (user.profile),
|
cell.append(Paragraph(_("for %s") % (user.profile),
|
||||||
stylesheet['Ballot_subtitle']))
|
stylesheet['Ballot_subtitle']))
|
||||||
cell.append(Spacer(0,0.5*cm))
|
cell.append(Spacer(0, 0.5 * cm))
|
||||||
cell.append(Paragraph(_("User: %s") % (user.username),
|
cell.append(Paragraph(_("User: %s") % (user.username),
|
||||||
stylesheet['Monotype']))
|
stylesheet['Monotype']))
|
||||||
cell.append(Paragraph(_("Password: %s")
|
cell.append(Paragraph(_("Password: %s")
|
||||||
% (user.profile.firstpassword), stylesheet['Monotype']))
|
% (user.profile.firstpassword), stylesheet['Monotype']))
|
||||||
cell.append(Spacer(0,0.5*cm))
|
cell.append(Spacer(0, 0.5 * cm))
|
||||||
cell.append(Paragraph(_("URL: %s")
|
cell.append(Paragraph(_("URL: %s")
|
||||||
% (participant_pdf_system_url),
|
% (participant_pdf_system_url),
|
||||||
stylesheet['Ballot_option']))
|
stylesheet['Ballot_option']))
|
||||||
cell.append(Spacer(0,0.5*cm))
|
cell.append(Spacer(0, 0.5 * cm))
|
||||||
cell2 = []
|
cell2 = []
|
||||||
cell2.append(Spacer(0,0.8*cm))
|
cell2.append(Spacer(0, 0.8 * cm))
|
||||||
if participant_pdf_welcometext is not None:
|
if participant_pdf_welcometext is not None:
|
||||||
cell2.append(Paragraph(
|
cell2.append(Paragraph(
|
||||||
participant_pdf_welcometext.replace('\r\n','<br/>'),
|
participant_pdf_welcometext.replace('\r\n', '<br/>'),
|
||||||
stylesheet['Ballot_subtitle']))
|
stylesheet['Ballot_subtitle']))
|
||||||
|
|
||||||
data.append([cell,cell2])
|
data.append([cell, cell2])
|
||||||
except Profile.DoesNotExist:
|
except OpenSlidesUser.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
# add empty table line if no participants available
|
# add empty table line if no participants available
|
||||||
if data == []:
|
if data == []:
|
||||||
data.append(['',''])
|
data.append(['', ''])
|
||||||
# build table
|
# build table
|
||||||
t=Table(data, 10.5*cm, 7.42*cm)
|
t = Table(data, 10.5 * cm, 7.42 * cm)
|
||||||
t.setStyle(TableStyle([
|
t.setStyle(TableStyle([
|
||||||
('LINEBELOW', (0,0), (-1,0), 0.25, colors.grey),
|
('LINEBELOW', (0, 0), (-1, 0), 0.25, colors.grey),
|
||||||
('LINEBELOW', (0,1), (-1,1), 0.25, colors.grey),
|
('LINEBELOW', (0, 1), (-1, 1), 0.25, colors.grey),
|
||||||
('LINEBELOW', (0,1), (-1,-1), 0.25, colors.grey),
|
('LINEBELOW', (0, 1), (-1, -1), 0.25, colors.grey),
|
||||||
('VALIGN', (0,0), (-1,-1), 'TOP'),
|
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
||||||
]))
|
]))
|
||||||
story.append(t)
|
story.append(t)
|
||||||
|
|
||||||
|
22
openslides/utils/person/__init__.py
Normal file
22
openslides/utils/person/__init__.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.utils.person
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Person api for OpenSlides
|
||||||
|
|
||||||
|
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from openslides.utils.person.signals import receiv_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.models import PersonField, PersonMixin
|
||||||
|
|
||||||
|
|
||||||
|
class EmtyPerson(PersonMixin):
|
||||||
|
@property
|
||||||
|
def person_id(self):
|
||||||
|
return 'emtyuser'
|
64
openslides/utils/person/api.py
Normal file
64
openslides/utils/person/api.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.utils.person.api
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Usefull functions for the OpenSlides person api.
|
||||||
|
|
||||||
|
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from openslides.utils.person.signals import receiv_persons
|
||||||
|
|
||||||
|
|
||||||
|
class Persons(object):
|
||||||
|
"""
|
||||||
|
A Storage for a multiplicity of different Person-Objects.
|
||||||
|
"""
|
||||||
|
def __init__(self, person_prefix=None, id=None):
|
||||||
|
self.person_prefix = person_prefix
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
try:
|
||||||
|
return iter(self._cache)
|
||||||
|
except AttributeError:
|
||||||
|
return iter(self.iter_persons())
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(list(self.__iter__()))
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return list(self)[key]
|
||||||
|
|
||||||
|
def iter_persons(self):
|
||||||
|
self._cache = list()
|
||||||
|
for receiver, persons in receiv_persons.send(
|
||||||
|
sender='persons', person_prefix=self.person_prefix, id=self.id):
|
||||||
|
for person in persons:
|
||||||
|
self._cache.append(person)
|
||||||
|
yield person
|
||||||
|
|
||||||
|
|
||||||
|
def generate_person_id(prefix, id):
|
||||||
|
if ':' in prefix:
|
||||||
|
raise ValueError("':' is not allowed in a the 'person_prefix'")
|
||||||
|
return "%s:%d" % (prefix, id)
|
||||||
|
|
||||||
|
|
||||||
|
def split_person_id(person_id):
|
||||||
|
data = person_id.split(':', 1)
|
||||||
|
if len(data) == 2 and data[0] and data[1]:
|
||||||
|
return data
|
||||||
|
raise TypeError("Invalid person_id: '%s'" % person_id)
|
||||||
|
|
||||||
|
|
||||||
|
def get_person(person_id):
|
||||||
|
try:
|
||||||
|
person_prefix, id = split_person_id(person_id)
|
||||||
|
except TypeError:
|
||||||
|
from openslides.utils.person import EmtyPerson
|
||||||
|
return EmtyPerson()
|
||||||
|
return Persons(person_prefix=person_prefix, id=id)[0]
|
77
openslides/utils/person/forms.py
Normal file
77
openslides/utils/person/forms.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.utils.person.forms
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Forms and FormFields for the OpenSlides person api.
|
||||||
|
|
||||||
|
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from openslides.utils.person.api import Persons, get_person
|
||||||
|
|
||||||
|
|
||||||
|
class PersonChoices(object):
|
||||||
|
def __init__(self, field):
|
||||||
|
self.field = field
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
if self.field.empty_label is not None:
|
||||||
|
yield (u"", self.field.empty_label)
|
||||||
|
for person in Persons():
|
||||||
|
yield (person.person_id, person)
|
||||||
|
|
||||||
|
|
||||||
|
class PersonFormField(forms.fields.ChoiceField):
|
||||||
|
def __init__(self, required=True, initial=None, empty_label=u"---------",
|
||||||
|
*args, **kwargs):
|
||||||
|
if required and (initial is not None):
|
||||||
|
self.empty_label = None
|
||||||
|
else:
|
||||||
|
self.empty_label = empty_label
|
||||||
|
forms.fields.Field.__init__(self, required=required, initial=initial,
|
||||||
|
*args, **kwargs)
|
||||||
|
self.widget.choices = self.choices
|
||||||
|
|
||||||
|
def __deepcopy__(self, memo):
|
||||||
|
result = super(forms.fields.ChoiceField, self).__deepcopy__(memo)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _get_choices(self):
|
||||||
|
# If self._choices is set, then somebody must have manually set
|
||||||
|
# the property self.choices. In this case, just return self._choices.
|
||||||
|
if hasattr(self, '_choices'):
|
||||||
|
return self._choices
|
||||||
|
return PersonChoices(self)
|
||||||
|
|
||||||
|
choices = property(_get_choices, forms.fields.ChoiceField._set_choices)
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
return get_person(value)
|
||||||
|
|
||||||
|
def valid_value(self, value):
|
||||||
|
return super(PersonFormField, self).valid_value(value.person_id)
|
||||||
|
|
||||||
|
|
||||||
|
class MultiplePersonFormField(PersonFormField):
|
||||||
|
widget = forms.widgets.SelectMultiple
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(MultiplePersonFormField, self).__init__(empty_label=None,
|
||||||
|
*args, **kwargs)
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
if hasattr(value, '__iter__'):
|
||||||
|
return [super(MultiplePersonFormField, self).to_python(v)
|
||||||
|
for v in value]
|
||||||
|
return super(MultiplePersonFormField, self).to_python(value)
|
||||||
|
|
||||||
|
def valid_value(self, value):
|
||||||
|
if hasattr(value, '__iter__'):
|
||||||
|
return [super(MultiplePersonFormField, self).valid_value(v)
|
||||||
|
for v in value]
|
||||||
|
return super(MultiplePersonFormField, self).valid_value(value)
|
64
openslides/utils/person/models.py
Normal file
64
openslides/utils/person/models.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.utils.person.models
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Models and ModelFields for the OpenSlides person api.
|
||||||
|
|
||||||
|
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
from openslides.utils.person.forms import PersonFormField
|
||||||
|
from openslides.utils.person.api import get_person, generate_person_id
|
||||||
|
|
||||||
|
|
||||||
|
class PersonField(models.fields.Field):
|
||||||
|
__metaclass__ = models.SubfieldBase
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(PersonField, self).__init__(max_length=255, *args, **kwargs)
|
||||||
|
# TODO: Validate the uid
|
||||||
|
|
||||||
|
def get_internal_type(self):
|
||||||
|
return "CharField"
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
"""
|
||||||
|
Convert string value to a User Object.
|
||||||
|
"""
|
||||||
|
if hasattr(value, 'person_id'):
|
||||||
|
person = value
|
||||||
|
else:
|
||||||
|
person = get_person(value)
|
||||||
|
|
||||||
|
person.prepare_database_save = (
|
||||||
|
lambda unused: PersonField().get_prep_value(person))
|
||||||
|
return person
|
||||||
|
|
||||||
|
def get_prep_value(self, value):
|
||||||
|
return value.person_id
|
||||||
|
|
||||||
|
def value_to_string(self, obj):
|
||||||
|
value = self._get_val_from_obj(obj)
|
||||||
|
return self.get_prep_value(value)
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'form_class': PersonFormField}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return super(PersonField, self).formfield(**defaults)
|
||||||
|
|
||||||
|
|
||||||
|
class PersonMixin(object):
|
||||||
|
@property
|
||||||
|
def person_id(self):
|
||||||
|
try:
|
||||||
|
return generate_person_id(self.person_prefix, self.pk)
|
||||||
|
except AttributeError:
|
||||||
|
raise AttributeError("%s has to have a attribute 'user_prefix'"
|
||||||
|
% self)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'Person: %s' % self.person_id
|
15
openslides/utils/person/signals.py
Normal file
15
openslides/utils/person/signals.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.utils.user.signals
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Defines Signals for the user.
|
||||||
|
|
||||||
|
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.dispatch import Signal
|
||||||
|
|
||||||
|
receiv_persons = Signal(providing_args=['person_prefix', 'id'])
|
Loading…
Reference in New Issue
Block a user