diff --git a/openslides/application/forms.py b/openslides/application/forms.py
index 9da1f3a83..34a7ef9b9 100644
--- a/openslides/application/forms.py
+++ b/openslides/application/forms.py
@@ -15,7 +15,7 @@ from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _, ugettext_noop
from openslides.utils.forms import CssClassMixin
-from openslides.utils.user import UserFormField
+from openslides.utils.user import UserFormField, MultipleUserFormField
from openslides.application.models import Application
@@ -59,11 +59,8 @@ class ApplicationManagerForm(forms.ModelForm, CssClassMixin):
class ApplicationManagerFormSupporter(ApplicationManagerForm):
- supporter = UserModelMultipleChoiceField(
- queryset=User.objects.all().exclude(profile=None).
- order_by("first_name"),
- required=False, label=_("Supporters"),
- )
+ # TODO: Do not show the submitter in the user-list
+ supporter = MultipleUserFormField(required=False, label=_("Supporters"))
class ApplicationImportForm(forms.Form, CssClassMixin):
diff --git a/openslides/application/models.py b/openslides/application/models.py
index e0d8b738b..4b45a5ed8 100644
--- a/openslides/application/models.py
+++ b/openslides/application/models.py
@@ -37,6 +37,11 @@ from openslides.projector.models import SlideMixin
from openslides.agenda.models import Item
+class ApplicationSupporter(models.Model):
+ application = models.ForeignKey("Application")
+ user = UserField()
+
+
class Application(models.Model, SlideMixin):
prefix = "application"
STATUS = (
@@ -62,8 +67,6 @@ class Application(models.Model, SlideMixin):
)
submitter = UserField(verbose_name=_("Submitter"))
- supporter = models.ManyToManyField(User, related_name='supporter', \
- null=True, blank=True, verbose_name=_("Supporters"))
number = models.PositiveSmallIntegerField(blank=True, null=True,
unique=True)
status = models.CharField(max_length=3, choices=STATUS, default='pub')
@@ -158,6 +161,13 @@ class Application(models.Model, SlideMixin):
else:
return False
+ @property
+ def supporters(self):
+ return [object.user for object in self.applicationsupporter_set.all()]
+
+ def is_supporter(self, user):
+ return self.applicationsupporter_set.filter(user=user).exists()
+
@property
def enough_supporters(self):
"""
@@ -165,17 +175,20 @@ class Application(models.Model, SlideMixin):
"""
min_supporters = int(config['application_min_supporters'])
if self.status == "pub":
- return self.supporter.count() >= min_supporters
+ return self.count_supporters() >= min_supporters
else:
return True
+ def count_supporters(self):
+ return self.applicationsupporter_set.count()
+
@property
def missing_supporters(self):
"""
Return number of missing supporters
"""
min_supporters = int(config['application_min_supporters'])
- delta = min_supporters - self.supporter.count()
+ delta = min_supporters - self.count_supporters()
if delta > 0:
return delta
else:
@@ -222,10 +235,11 @@ class Application(models.Model, SlideMixin):
except AttributeError:
is_manager = False
+ supporters = self.applicationsupporter_set.all()
if (self.status == "pub"
- and self.supporter.exists()
+ and supporters
and not is_manager):
- self.supporter.clear()
+ supporters.delete()
self.writelog(_("Supporters removed"), user)
def reset(self, user):
@@ -242,29 +256,36 @@ class Application(models.Model, SlideMixin):
Add a Supporter to the list of supporters of the application.
"""
if user == self.submitter:
+ # TODO: Use own Exception
raise NameError('Supporter can not be the submitter of a ' \
'application.')
if self.permitted is not None:
+ # TODO: Use own Exception
raise NameError('This application is already permitted.')
- if user not in self.supporter.all():
- self.supporter.add(user)
- self.writelog(_("Supporter: +%s") % (user))
+ if not self.is_supporter(user):
+ ApplicationSupporter(application=self, user=user).save()
+ self.writelog(_("Supporter: +%s") % (user))
def unsupport(self, user):
"""
remove a supporter from the list of supporters of the application
"""
if self.permitted is not None:
+ # TODO: Use own Exception
raise NameError('This application is already permitted.')
- if user in self.supporter.all():
- self.supporter.remove(user)
- self.writelog(_("Supporter: -%s") % (user))
+ try:
+ object = self.applicationsupporter_set.get(user=user).delete()
+ except ApplicationSupporter.DoesNotExist:
+ pass
+ else:
+ self.writelog(_("Supporter: -%s") % (user))
def set_number(self, number=None, user=None):
"""
Set a number for ths application.
"""
if self.number is not None:
+ # TODO: Use own Exception
raise NameError('This application has already a number.')
if number is None:
try:
@@ -333,7 +354,7 @@ class Application(models.Model, SlideMixin):
self.writelog(_("Status modified")+": %s -> %s" \
% (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.
"""
@@ -367,16 +388,12 @@ class Application(models.Model, SlideMixin):
actions.append("pub")
# Check if the user can support and unspoort the application
- try:
- if (self.status == "pub"
- and user != self.submitter
- and user not in self.supporter.all()
- and getattr(user, 'profile', None)):
- actions.append("support")
- except Profile.DoesNotExist:
- pass
+ if (self.status == "pub"
+ and user != self.submitter
+ and not self.is_supporter(user)):
+ actions.append("support")
- if self.status == "pub" and user in self.supporter.all():
+ if self.status == "pub" and self.is_supporter(user):
actions.append("unsupport")
#Check if the user can edit the application
diff --git a/openslides/application/templates/application/view.html b/openslides/application/templates/application/view.html
index dd9ad9c69..183f76b50 100644
--- a/openslides/application/templates/application/view.html
+++ b/openslides/application/templates/application/view.html
@@ -22,16 +22,16 @@
{% endif %}
{% if min_supporters > 0 %}
-
{% trans "Supporters" %}: *
- {% if application.supporter.count == 0 %}
- -
- {% else %}
-
- {% for supporter in application.supporter.all %}
- - {{ supporter.profile }}
- {% endfor %}
-
- {% endif %}
+ {% trans "Supporters" %}: *
+ {% if not application.supporters %}
+ -
+ {% else %}
+
+ {% for supporter in application.supporters %}
+ - {{ supporter }}
+ {% endfor %}
+
+ {% endif %}
{% endif %}
{% trans "Status" %}:
diff --git a/openslides/application/views.py b/openslides/application/views.py
index f9a92a5f5..f4d06cab9 100644
--- a/openslides/application/views.py
+++ b/openslides/application/views.py
@@ -42,6 +42,7 @@ from openslides.utils.template import Tab
from openslides.utils.utils import (template, permission_required,
del_confirm_form, gen_confirm_form)
from openslides.utils.views import PDFView, RedirectView, DeleteView, FormView
+from openslides.utils.user import get_user
from openslides.config.models import config
@@ -49,7 +50,7 @@ from openslides.projector.projector import Widget
from openslides.poll.views import PollFormView
-from openslides.participant.api import gen_username, gen_password
+from openslides.participant.api import gen_username, gen_password, user2djangouser
from openslides.participant.models import Profile
from openslides.agenda.models import Item
@@ -123,7 +124,7 @@ def overview(request):
for (i, application) in enumerate(applications):
try:
applications[i] = {
- 'actions' : application.get_allowed_actions(request.user),
+ 'actions' : application.get_allowed_actions(user2djangouser(request.user)),
'application' : application
}
except:
@@ -151,7 +152,8 @@ def view(request, application_id, newest=False):
else:
version = application.public_version
revisions = application.versions
- actions = application.get_allowed_actions(user=request.user)
+ user = user2djangouser(request.user)
+ actions = application.get_allowed_actions(user=user)
return {
'application': application,
@@ -181,11 +183,11 @@ def edit(request, application_id=None):
if application_id is not None:
application = Application.objects.get(id=application_id)
if (not hasattr(application.submitter, 'user') or
- not request.user == application.submitter.user) \
+ not user2djangouser(request.user) == application.submitter.user) \
and not is_manager:
messages.error(request, _("You can not edit this application. You are not the submitter."))
return redirect(reverse('application_view', args=[application.id]))
- actions = application.get_allowed_actions(user=request.user)
+ actions = application.get_allowed_actions(user=user2djangouser(request.user))
else:
application = None
actions = None
@@ -212,14 +214,14 @@ def edit(request, application_id=None):
if valid:
del_supporters = True
- original_supporters = []
if is_manager:
if application: # Edit application
- for s in application.supporter.all():
- original_supporters.append(s)
+ original_supporters = list(application.supporters)
+ else:
+ original_supporters = []
application = managerform.save(commit=False)
elif application_id is None:
- application = Application(submitter=request.user.profile)
+ application = Application(submitter=user2djangouser(request.user))
application.title = dataform.cleaned_data['title']
application.text = dataform.cleaned_data['text']
application.reason = dataform.cleaned_data['reason']
@@ -229,30 +231,17 @@ def edit(request, application_id=None):
and dataform.cleaned_data['trivial_change']
except KeyError:
trivial_change = False
- application.save(request.user, trivial_change=trivial_change)
+ application.save(user2djangouser(request.user), trivial_change=trivial_change)
if is_manager:
- # log added supporters
- supporters_added = []
- for s in application.supporter.all():
- if s not in original_supporters:
- try:
- supporters_added.append(unicode(s.profile))
- except Profile.DoesNotExist:
- pass
- if len(supporters_added) > 0:
- log_added = ", ".join(supporters_added)
- application.writelog(_("Supporter: +%s") % log_added, request.user)
- # log removed supporters
- supporters_removed = []
- 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)
+ # TODO: Deal with the case, that the form has no field 'supporters'
+ old_supporters = set(application.supporters)
+ new_supporters = set(managerform.cleaned_data['supporter'])
+ # add new supporters
+ for supporter in new_supporters.difference(old_supporters):
+ application.support(supporter)
+ # remove old supporters
+ for supporter in old_supporters.difference(new_supporters):
+ application.unsupport(supporter)
if application_id is None:
messages.success(request, _('New application was successfully created.'))
else:
@@ -268,11 +257,11 @@ def edit(request, application_id=None):
if application_id is None:
initial = {'text': config['application_preamble']}
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'):
messages.warning(request, _("Attention: Do you really want to edit this application? The supporters will not be removed automatically because you can manage applications. Please check if the supports are valid after your changing!"))
else:
- messages.warning(request, _("Attention: Do you really want to edit this application? All %s 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 %s supporters will be removed! Try to convince the supporters again.") % len(application.supporters) )
initial = {'title': application.title,
'text': application.text,
'reason': application.reason}
@@ -280,12 +269,10 @@ def edit(request, application_id=None):
dataform = formclass(initial=initial, prefix="data")
if is_manager:
if application_id is None:
- try:
- initial = {'submitter': request.user.profile.uid}
- except Profile.DoesNotExist:
- initial = {}
+ initial = {'submitter': user2djangouser(request.user).uid}
else:
initial = {'submitter': application.submitter.uid}
+ initial['supporter'] = [supporter.uid for supporter in application.supporters]
managerform = managerformclass(initial=initial,
instance=application, prefix="manager")
else:
@@ -305,7 +292,7 @@ def set_number(request, application_id):
set a number for an application.
"""
try:
- Application.objects.get(pk=application_id).set_number(user=request.user)
+ Application.objects.get(pk=application_id).set_number(user=user2djangouser(request.user))
messages.success(request, _("Application number was successfully set."))
except Application.DoesNotExist:
pass
@@ -321,7 +308,7 @@ def permit(request, application_id):
permit an application.
"""
try:
- Application.objects.get(pk=application_id).permit(user=request.user)
+ Application.objects.get(pk=application_id).permit(user=user2djangouser(request.user))
messages.success(request, _("Application was successfully permitted."))
except Application.DoesNotExist:
pass
@@ -336,7 +323,7 @@ def notpermit(request, application_id):
reject (not permit) an application.
"""
try:
- Application.objects.get(pk=application_id).notpermit(user=request.user)
+ Application.objects.get(pk=application_id).notpermit(user=user2djangouser(request.user))
messages.success(request, _("Application was successfully rejected."))
except Application.DoesNotExist:
pass
@@ -352,7 +339,7 @@ def set_status(request, application_id=None, status=None):
try:
if status is not None:
application = Application.objects.get(pk=application_id)
- application.set_status(user=request.user, status=status)
+ application.set_status(user=user2djangouser(request.user), status=status)
messages.success(request, _("Application status was set to: %s.") % application.get_status_display())
except Application.DoesNotExist:
pass
@@ -368,7 +355,7 @@ def reset(request, application_id):
reset an application.
"""
try:
- Application.objects.get(pk=application_id).reset(user=request.user)
+ Application.objects.get(pk=application_id).reset(user=user2djangouser(request.user))
messages.success(request, _("Application status was reset.") )
except Application.DoesNotExist:
pass
@@ -382,7 +369,7 @@ def support(request, application_id):
support an application.
"""
try:
- Application.objects.get(pk=application_id).support(user=request.user)
+ Application.objects.get(pk=application_id).support(user=user2djangouser(request.user))
messages.success(request, _("You have support the application successfully.") )
except Application.DoesNotExist:
pass
@@ -396,7 +383,7 @@ def unsupport(request, application_id):
unsupport an application.
"""
try:
- Application.objects.get(pk=application_id).unsupport(user=request.user)
+ Application.objects.get(pk=application_id).unsupport(user=user2djangouser(request.user))
messages.success(request, _("You have unsupport the application successfully.") )
except Application.DoesNotExist:
pass
@@ -410,7 +397,7 @@ def gen_poll(request, application_id):
gen a poll for this application.
"""
try:
- poll = Application.objects.get(pk=application_id).gen_poll(user=request.user)
+ poll = Application.objects.get(pk=application_id).gen_poll(user=user2djangouser(request.user))
messages.success(request, _("New vote was successfully created.") )
except Application.DoesNotExist:
pass # TODO: do not call poll after this excaption
@@ -427,7 +414,7 @@ def delete_poll(request, poll_id):
count = application.polls.filter(id__lte=poll_id).count()
if request.method == 'POST':
poll.delete()
- application.writelog(_("Poll deleted"), request.user)
+ application.writelog(_("Poll deleted"), user2djangouser(request.user))
messages.success(request, _('Poll was successfully deleted.'))
else:
del_confirm_form(request, poll, name=_("the %s. poll") % count, delete_link=reverse('application_poll_delete', args=[poll_id]))
@@ -467,7 +454,7 @@ class ApplicationDelete(DeleteView):
if len(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=user2djangouser(request.user)):
messages.error(request, _("You can not delete application %s.") % application)
continue
@@ -476,12 +463,12 @@ class ApplicationDelete(DeleteView):
messages.success(request, _("Application %s was successfully deleted.") % title)
elif self.object:
- if not 'delete' in self.object.get_allowed_actions(user=request.user):
- messages.error(request, _("You can not delete application %s.") % self.object)
- else:
- title = self.object.title
- self.object.delete(force=True)
- messages.success(request, _("Application %s was successfully deleted.") % title)
+ if not 'delete' in self.object.get_allowed_actions(user=user2djangouser(request.user)):
+ messages.error(request, _("You can not delete application %s.") % self.object)
+ else:
+ title = self.object.title
+ self.object.delete(force=True)
+ messages.success(request, _("Application %s was successfully deleted.") % title)
else:
messages.error(request, _("Invalid request"))
@@ -517,12 +504,12 @@ class ViewPoll(PollFormView):
self.application = self.poll.get_application()
context['application'] = self.application
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=user2djangouser(self.request.user))
return context
def get_modelform_class(self):
cls = super(ViewPoll, self).get_modelform_class()
- user = self.request.user
+ user = user2djangouser(self.request.user)
class ViewPollFormClass(cls):
def save(self, commit = True):
@@ -544,7 +531,7 @@ def permit_version(request, aversion_id):
aversion = AVersion.objects.get(pk=aversion_id)
application = aversion.application
if request.method == 'POST':
- application.accept_version(aversion, user = request.user)
+ application.accept_version(aversion, user=user2djangouser(request.user))
messages.success(request, _("Version %s accepted.") % (aversion.aid))
else:
gen_confirm_form(request, _('Do you really want to permit version %s?') % aversion.aid, reverse('application_version_permit', args=[aversion.id]))
@@ -556,7 +543,7 @@ def reject_version(request, aversion_id):
aversion = AVersion.objects.get(pk=aversion_id)
application = aversion.application
if request.method == 'POST':
- if application.reject_version(aversion, user = request.user):
+ if application.reject_version(aversion, user=user2djangouser(request.user)):
messages.success(request, _("Version %s rejected.") % (aversion.aid))
else:
messages.error(request, _("ERROR by rejecting the version.") )
@@ -570,13 +557,14 @@ def reject_version(request, aversion_id):
def application_import(request):
try:
request.user.profile
- messages.error(request, _('The import function is available for the admin (without user profile) only.'))
- return redirect(reverse('application_overview'))
except Profile.DoesNotExist:
pass
except AttributeError:
# AnonymousUser
pass
+ else:
+ messages.error(request, _('The import function is available for the admin (without user profile) only.'))
+ return redirect(reverse('application_overview'))
if request.method == 'POST':
form = ApplicationImportForm(request.POST, request.FILES)
@@ -771,7 +759,7 @@ class ApplicationPDF(PDFView):
cell2a.append(Paragraph("%s:" % _("Supporters"), stylesheet['Heading4']))
- for s in application.supporter.all():
+ for supporter in application.supporters:
cell2b.append(Paragraph(". %s" % unicode(s.profile), stylesheet['Signaturefield']))
if application.status == "pub":
for x in range(0,application.missing_supporters):
diff --git a/openslides/participant/api.py b/openslides/participant/api.py
index 3dde39c14..77104b98b 100644
--- a/openslides/participant/api.py
+++ b/openslides/participant/api.py
@@ -15,6 +15,9 @@ import string
from django.contrib.auth.models import User
+from openslides.utils.user import get_user
+from openslides.participant.models import Profile
+
def gen_password():
"""
@@ -44,3 +47,11 @@ def gen_username(first_name, last_name):
User.objects.get(username=testname)
except User.DoesNotExist:
return testname
+
+
+def user2djangouser(user):
+ u = get_user('djangouser:%d' % user.id)
+ try:
+ return u.profile
+ except Profile.DoesNotExist:
+ return u
diff --git a/openslides/participant/forms.py b/openslides/participant/forms.py
index 2d33bb960..610e17b2b 100644
--- a/openslides/participant/forms.py
+++ b/openslides/participant/forms.py
@@ -90,6 +90,7 @@ class UsersettingsForm(forms.ModelForm, CssClassMixin):
model = User
fields = ('username', 'first_name', 'last_name', 'email')
+
class UserImportForm(forms.Form, CssClassMixin):
csvfile = forms.FileField(widget=forms.FileInput(attrs={'size':'50'}),
label=_("CSV File"))
diff --git a/openslides/participant/models.py b/openslides/participant/models.py
index fe19ff9de..919e03b95 100644
--- a/openslides/participant/models.py
+++ b/openslides/participant/models.py
@@ -21,8 +21,6 @@ from openslides.utils.user.signals import receiv_users
from openslides.config.signals import default_config_value
-from openslides.participant.api import gen_password
-
class Profile(models.Model, UserMixin):
user_prefix = 'participant'
@@ -53,7 +51,6 @@ class Profile(models.Model, UserMixin):
firstpassword = models.CharField(max_length=100, null=True, blank=True,
verbose_name = _("First Password"))
-
def reset_password(self):
"""
Reset the password for the user to his default-password.
@@ -61,7 +58,6 @@ class Profile(models.Model, UserMixin):
self.user.set_password(self.firstpassword)
self.user.save()
-
@models.permalink
def get_absolute_url(self, link='edit'):
"""
@@ -98,6 +94,17 @@ class DjangoGroup(models.Model, UserMixin):
return unicode(self.group)
+class DjangoUser(User, UserMixin):
+ user_prefix = 'djangouser'
+
+ def has_no_profile(self):
+ # TODO: Make ths with a Manager, so it does manipulate the sql query
+ return not hasattr(self, 'profile')
+
+ class Meta:
+ proxy = True
+
+
class ParticipantUsers(object):
def __init__(self, user_prefix=None, id=None):
self.user_prefix = user_prefix
@@ -118,6 +125,17 @@ class ParticipantUsers(object):
for group in DjangoGroup.objects.all():
yield group
+ if not self.user_prefix or self.user_prefix == DjangoUser.user_prefix:
+ if self.id:
+ yield DjangoUser.objects.get(pk=self.id)
+ else:
+ for user in DjangoUser.objects.all():
+ if user.has_no_profile():
+ yield user
+ elif self.user_prefix:
+ # If only users where requested, return the profile object.
+ yield user.profile
+
def __getitem__(self, key):
return Profile.objects.get(pk=key)
diff --git a/openslides/utils/user/__init__.py b/openslides/utils/user/__init__.py
index 3a77a827d..df711f6c6 100644
--- a/openslides/utils/user/__init__.py
+++ b/openslides/utils/user/__init__.py
@@ -57,6 +57,23 @@ class UserFormField(forms.fields.ChoiceField):
return super(UserFormField, self).valid_value(value.uid)
+class MultipleUserFormField(UserFormField):
+ widget = forms.widgets.SelectMultiple
+
+ def __init__(self, *args, **kwargs):
+ super(MultipleUserFormField, self).__init__(empty_label=None, *args, **kwargs)
+
+ def to_python(self, value):
+ if hasattr(value, '__iter__'):
+ return [super(MultipleUserFormField, self).to_python(v) for v in value]
+ return super(MultipleUserFormField, self).to_python(value)
+
+ def valid_value(self, value):
+ if hasattr(value, '__iter__'):
+ return [super(MultipleUserFormField, self).valid_value(v) for v in value]
+ return super(MultipleUserFormField, self).valid_value(value)
+
+
class UserField(models.fields.Field):
__metaclass__ = models.SubfieldBase