From 153482f32dd8aa1ceb7594874354225450f31fa3 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 10 Aug 2012 11:14:11 +0200 Subject: [PATCH 01/18] first_name, last_name and name_surfix as OpenSlidesUser attributes --- openslides/participant/models.py | 39 +++++++++++++++++++++++++++++++- openslides/participant/tests.py | 12 +++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/openslides/participant/models.py b/openslides/participant/models.py index f7685994e..ae8a33f40 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -36,7 +36,7 @@ class OpenSlidesUser(models.Model, PersonMixin): ) user = models.OneToOneField(User, unique=True, editable=False) - name_surfix = models.CharField( + category = models.CharField( max_length=100, null=True, blank=True, verbose_name=_("Name Surfix"), help_text=_('Shown behind the name.')) gender = models.CharField( @@ -55,6 +55,34 @@ class OpenSlidesUser(models.Model, PersonMixin): max_length=100, null=True, blank=True, verbose_name=_("First Password")) + save_user_object = False + + def get_name_surfix(self): + return self.category + + def set_name_surfix(self, value): + self.category = value + + name_surfix = property(get_name_surfix, set_name_surfix) + + def get_first_name(self): + return self.user.first_name + + def set_first_name(self, first_name): + self.user.first_name = first_name + self.save_user_object = True + + first_name = property(get_first_name, set_first_name) + + def get_last_name(self): + return self.user.last_name + + def set_last_name(self, last_name): + self.user.last_name = last_name + self.save_user_object = True + + last_name = property(get_last_name, set_last_name) + def reset_password(self, password=None): """ Reset the password for the user to his default-password. @@ -67,6 +95,9 @@ class OpenSlidesUser(models.Model, PersonMixin): def has_perm(self, perm): return self.user.has_perm(perm) + def save_in_user_object(self, attribute, value): + self.save_in_user_object_dict[attribute] = value + @models.permalink def get_absolute_url(self, link='edit'): """ @@ -86,6 +117,12 @@ class OpenSlidesUser(models.Model, PersonMixin): return "%s (%s)" % (self.user.get_full_name(), self.name_surfix) return "%s" % self.user.get_full_name() + def save(self, *args, **kwargs): + if self.save_user_object: + self.user.save() + self.save_user_object = False + super(OpenSlidesUser, self).save(*args, **kwargs) + class Meta: # Rename permissions permissions = ( diff --git a/openslides/participant/tests.py b/openslides/participant/tests.py index c7dbadf46..a64f4ea54 100644 --- a/openslides/participant/tests.py +++ b/openslides/participant/tests.py @@ -39,7 +39,7 @@ class OpenSlidesUserTest(TestCase): self.assertEqual(unicode(self.openslidesuser1), u'Max Mustermann') def test_name_surfix(self): - self.openslidesuser1.name_surfix = u'München' + self.openslidesuser1.category = u'München' self.openslidesuser1.save() self.assertEqual(unicode(self.openslidesuser1), u'Max Mustermann (München)') @@ -57,6 +57,16 @@ class OpenSlidesUserTest(TestCase): self.assertEqual(get_person('openslides_user:1'), self.openslidesuser1) self.assertEqual(len(Persons()), 1) + def test_save_name(self): + self.assertEqual(self.openslidesuser1.first_name, self.user1.first_name) + self.assertEqual(self.openslidesuser1.last_name, self.user1.last_name) + self.openslidesuser1.first_name = 'foo' + self.openslidesuser1.last_name = 'bar' + self.openslidesuser1.save() + user1 = User.objects.get(pk=1) + self.assertEqual(user1.first_name, 'foo') + self.assertEqual(user1.last_name, 'bar') + class OpenSlidesGroupTest(TestCase): def setUp(self): From f008a4eb0a9948ad6eda0c61ca7d7ca8c8960028 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 10 Aug 2012 11:18:01 +0200 Subject: [PATCH 02/18] use OpenSlides.get_full_name instead of OpenSlides.user.get_full_name --- openslides/participant/models.py | 7 +++++-- openslides/participant/tests.py | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/openslides/participant/models.py b/openslides/participant/models.py index ae8a33f40..39b0da215 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -74,6 +74,9 @@ class OpenSlidesUser(models.Model, PersonMixin): first_name = property(get_first_name, set_first_name) + def get_full_name(self): + return "%s %s" % self.first_name, self.last_name + def get_last_name(self): return self.user.last_name @@ -114,8 +117,8 @@ class OpenSlidesUser(models.Model, PersonMixin): def __unicode__(self): if self.name_surfix: - return "%s (%s)" % (self.user.get_full_name(), self.name_surfix) - return "%s" % self.user.get_full_name() + return u"%s (%s)" % (self.get_full_name(), self.name_surfix) + return u"%s" % self.get_full_name() def save(self, *args, **kwargs): if self.save_user_object: diff --git a/openslides/participant/tests.py b/openslides/participant/tests.py index a64f4ea54..e8fcff2b7 100644 --- a/openslides/participant/tests.py +++ b/openslides/participant/tests.py @@ -66,6 +66,7 @@ class OpenSlidesUserTest(TestCase): user1 = User.objects.get(pk=1) self.assertEqual(user1.first_name, 'foo') self.assertEqual(user1.last_name, 'bar') + self.assertEqual(user1.get_full_name(), 'foo bar') class OpenSlidesGroupTest(TestCase): From 29049119f73db6adf7256b54c0505483ee68ccac Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 10 Aug 2012 11:51:45 +0200 Subject: [PATCH 03/18] rewrote participant overview as class based view --- openslides/participant/models.py | 11 +- .../templates/participant/overview.html | 156 +++++++++-------- openslides/participant/urls.py | 6 +- openslides/participant/views.py | 163 +++++++++--------- 4 files changed, 171 insertions(+), 165 deletions(-) diff --git a/openslides/participant/models.py b/openslides/participant/models.py index 39b0da215..f833f3bd7 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -37,7 +37,7 @@ class OpenSlidesUser(models.Model, PersonMixin): user = models.OneToOneField(User, unique=True, editable=False) category = models.CharField( - max_length=100, null=True, blank=True, verbose_name=_("Name Surfix"), + max_length=100, null=True, blank=True, verbose_name=_("Category"), help_text=_('Shown behind the name.')) gender = models.CharField( max_length=50, choices=GENDER_CHOICES, blank=True, @@ -140,6 +140,7 @@ class OpenSlidesGroup(models.Model, PersonMixin): group = models.OneToOneField(Group) group_as_person = models.BooleanField(default=False) + description = models.TextField(blank=True) def __unicode__(self): return unicode(self.group) @@ -193,4 +194,10 @@ def default_config(sender, key, **kwargs): @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) + openslidesuser, new = OpenSlidesUser.objects.get_or_create(user=instance) + + +@receiver(signals.post_save, sender=Group) +def group_post_save(sender, instance, signal, *args, **kwargs): + # Creates OpenSlidesGroup + openslidesgroup, new = OpenSlidesGroup.objects.get_or_create(group=instance) diff --git a/openslides/participant/templates/participant/overview.html b/openslides/participant/templates/participant/overview.html index f068dd69c..3cd529af4 100644 --- a/openslides/participant/templates/participant/overview.html +++ b/openslides/participant/templates/participant/overview.html @@ -8,99 +8,105 @@ {% block header %} {% if perms.agenda.can_manage_agenda %} - - + + {% endif %} {% endblock %} + {% block content %}

{% trans "Participants" %}

-

- {% csrf_token %} - {% trans "Filter" %}: - - - - - +

+ + {% trans "Filter" %}: + + + + +

- {% if users|length == allusers|length %} - {{ users|length }} - {% blocktrans count counter=users|length %}participant{% plural %}participants{% endblocktrans %} + {% if users.count == allusers %} + {{ users.count }} + {% blocktrans count counter=users.count %}participant{% plural %}participants{% endblocktrans %} {% else %} - {{ users|length }} {% trans "of" %} {{ allusers|length }} {% trans "Participants" %} (= {{ percent }} %) + {{ users.count }} {% trans "of" %} {{ allusers }} {% trans "Participants" %} (= {{ percent }} %) {% endif %} - + {% if perms.participant.can_manage_participant %} - - - + + + {% endif %} - {% for user in users %} - - - - - - - {% if perms.participant.can_manage_participant %} - - - - {% endif %} - - {% empty %} - - - - {% endfor %} + {% for user in users %} + + + + + + + {% if perms.participant.can_manage_participant %} + + + + {% endif %} + + {% empty %} + + + + {% endfor %}
{% trans "First Name" %} {% trans "Last Name" %}{% trans "Group" %}{% trans "Category" %} {% trans "Type" %} {% trans "Committee" %}{% trans "Comment" %}{% trans "Last Login" %}{% trans "Actions" %}{% trans "Comment" %}{% trans "Last Login" %}{% trans "Actions" %}
{{ user.first_name }}{{ user.last_name }}{{ user.openslidesuser.name_surfix }}{{ user.openslidesuser.get_type_display }}{{ user.openslidesuser.committee }}{{ user.openslidesuser.comment|first_line }}{% if user.last_login > user.date_joined %} - {{ user.last_login }} - {% endif %} - - - - - - -
{% trans "No participants available." %}
{{ user.first_name }}{{ user.last_name }}{{ user.openslidesuser.category }}{{ user.openslidesuser.get_type_display }}{{ user.openslidesuser.committee }}{{ user.openslidesuser.comment|first_line }} + {% if user.last_login > user.date_joined %} + {{ user.last_login }} + {% endif %} + + + + + + + + + + + + +
{% trans "No participants available." %}
{% endblock %} diff --git a/openslides/participant/urls.py b/openslides/participant/urls.py index e0245d575..2786161bb 100644 --- a/openslides/participant/urls.py +++ b/openslides/participant/urls.py @@ -13,12 +13,12 @@ from django.conf.urls.defaults import url, patterns from django.core.urlresolvers import reverse -from openslides.participant.views import (ParticipantsListPDF, - ParticipantsPasswordsPDF) +from openslides.participant.views import ( + ParticipantsListPDF, ParticipantsPasswordsPDF, Overview) urlpatterns = patterns('openslides.participant.views', url(r'^$', - 'get_overview', + Overview.as_view(), name='user_overview', ), diff --git a/openslides/participant/views.py b/openslides/participant/views.py index 3fd5f6a55..7299398c2 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -43,7 +43,7 @@ from openslides.utils.template import Tab from openslides.utils.utils import ( template, permission_required, gen_confirm_form, ajax_request, decodedict, encodedict, delete_default_permissions, html_strong) -from openslides.utils.views import FormView, PDFView +from openslides.utils.views import FormView, PDFView, TemplateView from openslides.config.models import config @@ -54,96 +54,89 @@ from openslides.participant.forms import ( UserImportForm, GroupForm, AdminPasswordChangeForm, ConfigForm) -@permission_required('participant.can_see_participant') -@template('participant/overview.html') -def get_overview(request): +class Overview(TemplateView): """ - Show all users. + Show all participants. """ - try: - sortfilter = encodedict(parse_qs( - request.COOKIES['participant_sortfilter'])) - except KeyError: - sortfilter = {} + permission_required = 'participant.can_see_participant' + template_name = 'participant/overview.html' - for value in [u'gender', u'group', u'type', u'committee', u'status', - u'sort', u'reverse']: - if value in request.REQUEST: - if request.REQUEST[value] == '---': - try: - del sortfilter[value] - except KeyError: - pass - else: - sortfilter[value] = [request.REQUEST[value]] - - query = User.objects - if 'gender' in sortfilter: - query = query.filter( - openslidesuser__gender__iexact=sortfilter['gender'][0]) - if 'group' in sortfilter: - query = query.filter( - openslidesuser__name_surfix__iexact=sortfilter['group'][0]) - if 'type' in sortfilter: - query = query.filter( - openslidesuser__type__iexact=sortfilter['type'][0]) - if 'committee' in sortfilter: - query = query.filter( - openslidesuser__committee__iexact=sortfilter['committee'][0]) - if 'status' in sortfilter: - query = query.filter(is_active=sortfilter['status'][0]) - if 'sort' in sortfilter: - if sortfilter['sort'][0] in ['first_name', 'last_name', 'last_login']: - query = query.order_by(sortfilter['sort'][0]) - elif (sortfilter['sort'][0] in - ['name_surfix', 'type', 'committee', 'comment']): - query = query.order_by( - 'openslidesuser__%s' % sortfilter['sort'][0]) - else: - query = query.order_by('last_name') - if 'reverse' in sortfilter: - query = query.reverse() - - # list of filtered users - userlist = query.all() - users = [] - for user in userlist: + def get_context_data(self, **kwargs): + context = super(Overview, self).get_context_data(**kwargs) try: - user.openslidesuser - except OpenSlidesUser.DoesNotExist: - pass + sortfilter = encodedict(parse_qs( + self.request.COOKIES['participant_sortfilter'])) + except KeyError: + sortfilter = {} + + for value in [u'gender', u'category', u'type', u'committee', u'status', + u'sort', u'reverse']: + if value in self.request.REQUEST: + if self.request.REQUEST[value] == '---': + try: + del sortfilter[value] + except KeyError: + pass + else: + sortfilter[value] = [self.request.REQUEST[value]] + + query = User.objects + if 'gender' in sortfilter: + query = query.filter( + openslidesuser__gender__iexact=sortfilter['gender'][0]) + if 'category' in sortfilter: + query = query.filter( + openslidesuser__category__iexact=sortfilter['category'][0]) + if 'type' in sortfilter: + query = query.filter( + openslidesuser__type__iexact=sortfilter['type'][0]) + if 'committee' in sortfilter: + query = query.filter( + openslidesuser__committee__iexact=sortfilter['committee'][0]) + if 'status' in sortfilter: + query = query.filter(is_active=sortfilter['status'][0]) + if 'sort' in sortfilter: + if sortfilter['sort'][0] in ['first_name', 'last_name', 'last_login']: + query = query.order_by(sortfilter['sort'][0]) + elif (sortfilter['sort'][0] in + ['category', 'type', 'committee', 'comment']): + query = query.order_by( + 'openslidesuser__%s' % sortfilter['sort'][0]) else: - users.append(user) - # list of all existing users - allusers = [] - for user in User.objects.all(): - try: - user.openslidesuser - except OpenSlidesUser.DoesNotExist: - pass + query = query.order_by('last_name') + + if 'reverse' in sortfilter: + query = query.reverse() + + # list of filtered users + users = query.all() + + # list of all existing users + all_users = User.objects.count() + + # quotient of selected users and all users + if all_users > 0: + percent = float(len(users)) * 100 / float(all_users) else: - allusers.append(user) - # quotient of selected users and all users - if len(allusers) > 0: - percent = float(len(users)) * 100 / float(len(allusers)) - else: - percent = 0 - # list of all existing groups - groups = [p['name_surfix'] for p in OpenSlidesUser.objects.values('name_surfix') - .exclude(name_surfix='').distinct()] - # list of all existing committees - committees = [p['committee'] for p in OpenSlidesUser.objects.values('committee') - .exclude(committee='').distinct()] - return { - 'users': users, - 'allusers': allusers, - 'percent': round(percent, 1), - 'groups': groups, - 'committees': committees, - 'cookie': ['participant_sortfilter', urlencode(decodedict(sortfilter), - doseq=True)], - 'sortfilter': sortfilter, - } + percent = 0 + + # list of all existing categories + categories = [p['category'] for p in OpenSlidesUser.objects.values('category') + .exclude(category='').distinct()] + + # list of all existing committees + committees = [p['committee'] for p in OpenSlidesUser.objects.values('committee') + .exclude(committee='').distinct()] + context.update({ + 'users': users, + 'allusers': all_users, + 'percent': round(percent, 1), + 'categories': categories, + 'committees': committees, + 'cookie': ['participant_sortfilter', urlencode(decodedict(sortfilter), + doseq=True)], + 'sortfilter': sortfilter}) + return context @permission_required('participant.can_manage_participant') From 1832a0c125eae33acf35192eeba47e0f5e09f68b Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 10 Aug 2012 13:11:10 +0200 Subject: [PATCH 04/18] make the openslides user a child model of the django user model --- openslides/participant/models.py | 45 +++++--------------------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/openslides/participant/models.py b/openslides/participant/models.py index f833f3bd7..850b23806 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -22,7 +22,7 @@ from openslides.utils.person.signals import receiv_persons from openslides.config.signals import default_config_value -class OpenSlidesUser(models.Model, PersonMixin): +class OpenSlidesUser(User, PersonMixin): person_prefix = 'openslides_user' GENDER_CHOICES = ( ('male', _('Male')), @@ -35,10 +35,10 @@ class OpenSlidesUser(models.Model, PersonMixin): ('guest', _('Guest')), ) - user = models.OneToOneField(User, unique=True, editable=False) + user = models.OneToOneField(User, unique=True, editable=False, parent_link=True) category = models.CharField( max_length=100, null=True, blank=True, verbose_name=_("Category"), - help_text=_('Shown behind the name.')) + help_text=_('Will be shown behind the name.')) gender = models.CharField( max_length=50, choices=GENDER_CHOICES, blank=True, verbose_name=_("Gender"), help_text=_('Only for filter the userlist.')) @@ -65,27 +65,6 @@ class OpenSlidesUser(models.Model, PersonMixin): name_surfix = property(get_name_surfix, set_name_surfix) - def get_first_name(self): - return self.user.first_name - - def set_first_name(self, first_name): - self.user.first_name = first_name - self.save_user_object = True - - first_name = property(get_first_name, set_first_name) - - def get_full_name(self): - return "%s %s" % self.first_name, self.last_name - - def get_last_name(self): - return self.user.last_name - - def set_last_name(self, last_name): - self.user.last_name = last_name - self.save_user_object = True - - last_name = property(get_last_name, set_last_name) - def reset_password(self, password=None): """ Reset the password for the user to his default-password. @@ -95,12 +74,6 @@ class OpenSlidesUser(models.Model, PersonMixin): self.user.set_password(password) self.user.save() - def has_perm(self, perm): - return self.user.has_perm(perm) - - def save_in_user_object(self, attribute, value): - self.save_in_user_object_dict[attribute] = value - @models.permalink def get_absolute_url(self, link='edit'): """ @@ -120,12 +93,6 @@ class OpenSlidesUser(models.Model, PersonMixin): return u"%s (%s)" % (self.get_full_name(), self.name_surfix) return u"%s" % self.get_full_name() - def save(self, *args, **kwargs): - if self.save_user_object: - self.user.save() - self.save_user_object = False - super(OpenSlidesUser, self).save(*args, **kwargs) - class Meta: # Rename permissions permissions = ( @@ -193,8 +160,10 @@ def default_config(sender, key, **kwargs): @receiver(signals.post_save, sender=User) def user_post_save(sender, instance, signal, *args, **kwargs): - # Creates OpenSlidesUser - openslidesuser, new = OpenSlidesUser.objects.get_or_create(user=instance) + try: + instance.openslidesuser + except OpenSlidesUser.DoesNotExist: + OpenSlidesUser(user=instance).save_base(raw=True) @receiver(signals.post_save, sender=Group) From ddb5756868f78c97d505374973f3512dc893c786 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 10 Aug 2012 13:22:09 +0200 Subject: [PATCH 05/18] rewrote the participant edit view as class based view --- openslides/participant/forms.py | 23 ++--- .../templates/participant/edit.html | 35 ++++---- openslides/participant/urls.py | 8 +- openslides/participant/views.py | 84 +++++-------------- openslides/utils/views.py | 9 ++ 5 files changed, 63 insertions(+), 96 deletions(-) diff --git a/openslides/participant/forms.py b/openslides/participant/forms.py index f39db40fb..ebd267bc0 100644 --- a/openslides/participant/forms.py +++ b/openslides/participant/forms.py @@ -28,7 +28,7 @@ USER_APPLICATION_IMPORT_OPTIONS = [ ] -class UserNewForm(forms.ModelForm, CssClassMixin): +class UserCreateForm(forms.ModelForm, CssClassMixin): first_name = forms.CharField(label=_("First name")) last_name = forms.CharField(label=_("Last name")) groups = forms.ModelMultipleChoiceField( @@ -37,22 +37,17 @@ class UserNewForm(forms.ModelForm, CssClassMixin): label=_("Active"), required=False, initial=True) class Meta: - model = User - exclude = ('username', 'password', 'is_staff', 'is_superuser', - 'last_login', 'date_joined', 'user_permissions') + model = OpenSlidesUser + fields = ('first_name', 'last_name', 'is_active', 'groups', 'category', + 'gender', 'type', 'committee', 'comment', 'firstpassword') -class UserEditForm(forms.ModelForm, CssClassMixin): - first_name = forms.CharField(label=_("First name")) - last_name = forms.CharField(label=_("Last name")) - groups = forms.ModelMultipleChoiceField( - queryset=Group.objects.all(), label=_("User groups"), required=False) - is_active = forms.BooleanField(label=_("Active"), required=False) - +class UserUpdateForm(UserCreateForm): class Meta: - model = User - exclude = ('password', 'is_staff', 'is_superuser', 'last_login', - 'date_joined', 'user_permissions') + model = OpenSlidesUser + fields = ('username', 'first_name', 'last_name', 'is_active', 'groups', + 'category', 'gender', 'type', 'committee', 'comment', + 'firstpassword') class UsernameForm(forms.ModelForm, CssClassMixin): diff --git a/openslides/participant/templates/participant/edit.html b/openslides/participant/templates/participant/edit.html index ca7c2b649..da910b19e 100644 --- a/openslides/participant/templates/participant/edit.html +++ b/openslides/participant/templates/participant/edit.html @@ -4,7 +4,7 @@ {% block title %} {{ block.super }} – - {% if edituser %} + {% if edit_user %} {% trans "Edit participant" %} {% else %} {% trans "New participant" %} @@ -13,30 +13,31 @@ {% block content %} - {% if edituser %} + {% if edit_user %}

{% trans "Edit participant" %}

{% else %}

{% trans "New participant" %}

{% endif %}
{% csrf_token %} - {{ userform.as_p }} - {{ profileform.as_p }} - {% if edituser %} -

{% trans 'Reset to First Password' %}

+ {{ form.as_p }} + {% if edit_user %} +

+ {% trans 'Reset to First Password' %} +

{% endif %}

- - - - - + + + + +

* {% trans "required" %}
diff --git a/openslides/participant/urls.py b/openslides/participant/urls.py index 2786161bb..c6e57e98c 100644 --- a/openslides/participant/urls.py +++ b/openslides/participant/urls.py @@ -14,7 +14,7 @@ from django.conf.urls.defaults import url, patterns from django.core.urlresolvers import reverse from openslides.participant.views import ( - ParticipantsListPDF, ParticipantsPasswordsPDF, Overview) + ParticipantsListPDF, ParticipantsPasswordsPDF, Overview, UserCreateView, UserUpdateView) urlpatterns = patterns('openslides.participant.views', url(r'^$', @@ -23,12 +23,12 @@ urlpatterns = patterns('openslides.participant.views', ), url(r'^new/$', - 'edit', + UserCreateView.as_view(), name='user_new', ), - url(r'^(?P\d+)/edit/$', - 'edit', + url(r'^(?P\d+)/edit/$', + UserUpdateView.as_view(), name='user_edit', ), diff --git a/openslides/participant/views.py b/openslides/participant/views.py index 7299398c2..e62d6c248 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -43,14 +43,15 @@ from openslides.utils.template import Tab from openslides.utils.utils import ( template, permission_required, gen_confirm_form, ajax_request, decodedict, encodedict, delete_default_permissions, html_strong) -from openslides.utils.views import FormView, PDFView, TemplateView +from openslides.utils.views import ( + FormView, PDFView, TemplateView, CreateView, UpdateView) from openslides.config.models import config from openslides.participant.models import OpenSlidesUser, OpenSlidesGroup from openslides.participant.api import gen_username, gen_password from openslides.participant.forms import ( - UserNewForm, UserEditForm, OpenSlidesUserForm, UsersettingsForm, + UserCreateForm, UserUpdateForm, OpenSlidesUserForm, UsersettingsForm, UserImportForm, GroupForm, AdminPasswordChangeForm, ConfigForm) @@ -139,69 +140,30 @@ class Overview(TemplateView): return context -@permission_required('participant.can_manage_participant') -@template('participant/edit.html') -def edit(request, user_id=None): +class UserCreateView(CreateView): """ - View to create and edit users. + Create a new participant. """ - if user_id is not None: - user = User.objects.get(id=user_id) - else: - user = None + permission_required = 'participant.can_manage_participant' + template_name = 'participant/edit.html' + model = OpenSlidesUser + context_object_name = 'edit_user' + form_class = UserCreateForm + success_url = 'user_overview' + apply_url = 'participant_edit' - if request.method == 'POST': - if user_id is None: - user_form = UserNewForm(request.POST, prefix="user") - openslides_user_form = OpenSlidesUserForm(request.POST, prefix="openslidesuser") - else: - user_form = UserEditForm(request.POST, instance=user, prefix="user") - openslides_user_form = OpenSlidesUserForm(request.POST, instance=user.openslidesuser, - prefix="openslidesuser") + def manipulate_object(self, form): + self.object.username = gen_username(form.cleaned_data['first_name'], form.cleaned_data['last_name']) - if user_form.is_valid() and openslides_user_form.is_valid(): - user = user_form.save(commit=False) - 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.save() - openslides_user = user.openslidesuser - 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 not openslides_user.firstpassword: - openslides_user.firstpassword = gen_password() - openslides_user.user.set_password(openslides_user.firstpassword) - # TODO: Try not to save the user object - openslides_user.user.save() - openslides_user.save() - if user_id is None: - messages.success(request, - _('New participant was successfully created.')) - else: - messages.success(request, - _('Participant was successfully modified.')) - if not 'apply' in request.POST: - return redirect(reverse('user_overview')) - if user_id is None: - return redirect(reverse('user_edit', args=[user.id])) - else: - messages.error(request, _('Please check the form for errors.')) - else: - if user_id is None: - user_form = UserNewForm(prefix="user") - openslides_user_form = OpenSlidesUserForm(prefix="openslidesuser") - else: - user_form = UserEditForm(instance=user, prefix="user") - openslides_user_form = OpenSlidesUserForm(instance=user.openslidesuser, prefix="openslidesuser") - # TODO: rename template vars - return { - 'userform': user_form, - 'profileform': openslides_user_form, - 'edituser': user, - } + +class UserUpdateView(UpdateView): + permission_required = 'participant.can_manage_participant' + template_name = 'participant/edit.html' + model = OpenSlidesUser + context_object_name = 'edit_user' + form_class = UserUpdateForm + success_url = 'user_overview' + apply_url = 'participant_edit' @permission_required('participant.can_manage_participant') diff --git a/openslides/utils/views.py b/openslides/utils/views.py index 0c3667025..5dab27f3c 100644 --- a/openslides/utils/views.py +++ b/openslides/utils/views.py @@ -208,9 +208,18 @@ class CreateView(PermissionMixin, _CreateView): messages.error(self.request, _('Please check the form for errors.')) return super(CreateView, self).form_invalid(form) + def form_valid(self, form): + self.object = form.save(commit=False) + self.manipulate_object(form) + self.object.save() + return HttpResponseRedirect(self.get_success_url()) + def get_success_message(self): return _('%s was successfully created.') % html_strong(self.object) + def manipulate_object(self, form): + pass + class DeleteView(RedirectView, SingleObjectMixin): def get_confirm_question(self): From c029fe2f9506c8705a84d3bd84d95c159eb1569c Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 10 Aug 2012 13:26:03 +0200 Subject: [PATCH 06/18] ix participant tests --- openslides/participant/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openslides/participant/tests.py b/openslides/participant/tests.py index e8fcff2b7..fc76ae0f3 100644 --- a/openslides/participant/tests.py +++ b/openslides/participant/tests.py @@ -55,7 +55,7 @@ class OpenSlidesUserTest(TestCase): 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) + self.assertEqual(len(Persons(person_prefix='openslides_user')), 1) def test_save_name(self): self.assertEqual(self.openslidesuser1.first_name, self.user1.first_name) From 90fddba63b958f87c3483cab2097c9838c370c0a Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 10 Aug 2012 13:29:46 +0200 Subject: [PATCH 07/18] rewrote delete participant view as class based view --- openslides/participant/urls.py | 7 ++++--- openslides/participant/views.py | 14 +++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/openslides/participant/urls.py b/openslides/participant/urls.py index c6e57e98c..17efe5aac 100644 --- a/openslides/participant/urls.py +++ b/openslides/participant/urls.py @@ -14,7 +14,8 @@ from django.conf.urls.defaults import url, patterns from django.core.urlresolvers import reverse from openslides.participant.views import ( - ParticipantsListPDF, ParticipantsPasswordsPDF, Overview, UserCreateView, UserUpdateView) + ParticipantsListPDF, ParticipantsPasswordsPDF, Overview, UserCreateView, + UserUpdateView, UserDeleteView) urlpatterns = patterns('openslides.participant.views', url(r'^$', @@ -37,8 +38,8 @@ urlpatterns = patterns('openslides.participant.views', name='user_print', ), - url(r'^(?P\d+)/del/$', - 'user_delete', + url(r'^(?P\d+)/del/$', + UserDeleteView.as_view(), name='user_delete', ), diff --git a/openslides/participant/views.py b/openslides/participant/views.py index e62d6c248..48771d6bc 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -44,7 +44,7 @@ from openslides.utils.utils import ( template, permission_required, gen_confirm_form, ajax_request, decodedict, encodedict, delete_default_permissions, html_strong) from openslides.utils.views import ( - FormView, PDFView, TemplateView, CreateView, UpdateView) + FormView, PDFView, TemplateView, CreateView, UpdateView, DeleteView) from openslides.config.models import config @@ -157,6 +157,9 @@ class UserCreateView(CreateView): class UserUpdateView(UpdateView): + """ + Update an existing participant. + """ permission_required = 'participant.can_manage_participant' template_name = 'participant/edit.html' model = OpenSlidesUser @@ -166,6 +169,15 @@ class UserUpdateView(UpdateView): apply_url = 'participant_edit' +class UserDeleteView(DeleteView): + """ + Delete an participant. + """ + permission_required = 'participant.can_manage_participant' + model = OpenSlidesUser + url = 'user_overview' + + @permission_required('participant.can_manage_participant') @template('confirm.html') def user_delete(request, user_id): From bb00eb3eb48cb3fd8a0fc7b8b2cef707052e11e7 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 10 Aug 2012 19:19:41 +0200 Subject: [PATCH 08/18] rewrote participant status view as class based view --- .../static/javascript/participant.js | 9 +- .../participant/static/styles/participant.css | 9 +- .../templates/participant/overview.html | 21 +- openslides/participant/urls.py | 21 +- openslides/participant/views.py | 509 +++++++++--------- 5 files changed, 284 insertions(+), 285 deletions(-) diff --git a/openslides/participant/static/javascript/participant.js b/openslides/participant/static/javascript/participant.js index f02e6c3f6..bf17d5202 100644 --- a/openslides/participant/static/javascript/participant.js +++ b/openslides/participant/static/javascript/participant.js @@ -9,17 +9,20 @@ $(function() { $('.status_link').click(function(event) { event.preventDefault(); link = $(this); + group = $(this).parent(); $.ajax({ type: 'GET', url: link.attr('href'), dataType: 'json', success: function(data) { if (data.active) { - link.addClass('active'); + group.children('.status_link.deactivate').show(); + group.children('.status_link.activate').hide(); } else { - link.removeClass('active'); + group.children('.status_link.deactivate').hide(); + group.children('.status_link.activate').show(); } } }); }); -}); \ No newline at end of file +}); diff --git a/openslides/participant/static/styles/participant.css b/openslides/participant/static/styles/participant.css index 3d2820f49..0d656c9c8 100644 --- a/openslides/participant/static/styles/participant.css +++ b/openslides/participant/static/styles/participant.css @@ -4,14 +4,19 @@ * :copyright: 2011, 2012 by OpenSlides team, see AUTHORS. * :license: GNU GPL, see LICENSE for more details. */ + a.status_link span { - background-image: url(../images/icons/off.png); background-repeat: no-repeat; background-position: center; width: 16px; height: 16px; display: inline-block; } -a.status_link.active span { + +a.status_link.deactivate span { background-image: url(../images/icons/on.png); } + +a.status_link.activate span { + background-image: url(../images/icons/off.png); +} diff --git a/openslides/participant/templates/participant/overview.html b/openslides/participant/templates/participant/overview.html index 3cd529af4..4641d615a 100644 --- a/openslides/participant/templates/participant/overview.html +++ b/openslides/participant/templates/participant/overview.html @@ -90,15 +90,18 @@ - - - - - - - - - + + + + + + + + {% endif %} diff --git a/openslides/participant/urls.py b/openslides/participant/urls.py index 17efe5aac..61751402c 100644 --- a/openslides/participant/urls.py +++ b/openslides/participant/urls.py @@ -15,7 +15,7 @@ from django.core.urlresolvers import reverse from openslides.participant.views import ( ParticipantsListPDF, ParticipantsPasswordsPDF, Overview, UserCreateView, - UserUpdateView, UserDeleteView) + UserUpdateView, UserDeleteView, SetUserStatusView) urlpatterns = patterns('openslides.participant.views', url(r'^$', @@ -43,9 +43,22 @@ urlpatterns = patterns('openslides.participant.views', name='user_delete', ), - url(r'^(?P\d+)/status/$', - 'user_set_status', - name='user_status', + url(r'^(?P\d+)/status/toggle/$', + SetUserStatusView.as_view(), + {'action': 'toggle'}, + name='user_status_toggle', + ), + + url(r'^(?P\d+)/status/activate/$', + SetUserStatusView.as_view(), + {'action': 'activate'}, + name='user_status_activate', + ), + + url(r'^(?P\d+)/status/deactivate/$', + SetUserStatusView.as_view(), + {'action': 'deactivate'}, + name='user_status_deactivate', ), url(r'^import/$', diff --git a/openslides/participant/views.py b/openslides/participant/views.py index 48771d6bc..e0169ae5c 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -44,7 +44,8 @@ from openslides.utils.utils import ( template, permission_required, gen_confirm_form, ajax_request, decodedict, encodedict, delete_default_permissions, html_strong) from openslides.utils.views import ( - FormView, PDFView, TemplateView, CreateView, UpdateView, DeleteView) + FormView, PDFView, TemplateView, CreateView, UpdateView, DeleteView, + RedirectView, SingleObjectMixin, ListView) from openslides.config.models import config @@ -55,15 +56,15 @@ from openslides.participant.forms import ( UserImportForm, GroupForm, AdminPasswordChangeForm, ConfigForm) -class Overview(TemplateView): +class Overview(ListView): """ Show all participants. """ permission_required = 'participant.can_see_participant' template_name = 'participant/overview.html' + context_object_name = 'users' - def get_context_data(self, **kwargs): - context = super(Overview, self).get_context_data(**kwargs) + def get_queryset(self): try: sortfilter = encodedict(parse_qs( self.request.COOKIES['participant_sortfilter'])) @@ -81,19 +82,15 @@ class Overview(TemplateView): else: sortfilter[value] = [self.request.REQUEST[value]] - query = User.objects + query = OpenSlidesUser.objects if 'gender' in sortfilter: - query = query.filter( - openslidesuser__gender__iexact=sortfilter['gender'][0]) + query = query.filter(gender__iexact=sortfilter['gender'][0]) if 'category' in sortfilter: - query = query.filter( - openslidesuser__category__iexact=sortfilter['category'][0]) + query = query.filter(category__iexact=sortfilter['category'][0]) if 'type' in sortfilter: - query = query.filter( - openslidesuser__type__iexact=sortfilter['type'][0]) + query = query.filter(type__iexact=sortfilter['type'][0]) if 'committee' in sortfilter: - query = query.filter( - openslidesuser__committee__iexact=sortfilter['committee'][0]) + query = query.filter(committee__iexact=sortfilter['committee'][0]) if 'status' in sortfilter: query = query.filter(is_active=sortfilter['status'][0]) if 'sort' in sortfilter: @@ -109,15 +106,18 @@ class Overview(TemplateView): if 'reverse' in sortfilter: query = query.reverse() - # list of filtered users - users = query.all() + self.sortfilter = sortfilter + + return query.all() + + def get_context_data(self, **kwargs): + context = super(Overview, self).get_context_data(**kwargs) - # list of all existing users all_users = User.objects.count() # quotient of selected users and all users if all_users > 0: - percent = float(len(users)) * 100 / float(all_users) + percent = self.object_list.count() * 100 / float(all_users) else: percent = 0 @@ -129,14 +129,13 @@ class Overview(TemplateView): committees = [p['committee'] for p in OpenSlidesUser.objects.values('committee') .exclude(committee='').distinct()] context.update({ - 'users': users, 'allusers': all_users, 'percent': round(percent, 1), 'categories': categories, 'committees': committees, - 'cookie': ['participant_sortfilter', urlencode(decodedict(sortfilter), + 'cookie': ['participant_sortfilter', urlencode(decodedict(self.sortfilter), doseq=True)], - 'sortfilter': sortfilter}) + 'sortfilter': self.sortfilter}) return context @@ -154,6 +153,8 @@ class UserCreateView(CreateView): def manipulate_object(self, form): self.object.username = gen_username(form.cleaned_data['first_name'], form.cleaned_data['last_name']) + if not self.object.firstpassword: + self.object.firstpassword = gen_password() class UserUpdateView(UpdateView): @@ -178,164 +179,125 @@ class UserDeleteView(DeleteView): url = 'user_overview' -@permission_required('participant.can_manage_participant') -@template('confirm.html') -def user_delete(request, user_id): +class SetUserStatusView(RedirectView, SingleObjectMixin): """ - Delete an user. + Activate or deactivate an user. """ - user = User.objects.get(pk=user_id) - if request.method == 'POST': - user.delete() - messages.success(request, - _('Participant %s was successfully deleted.') % user) - else: - gen_confirm_form(request, - _('Do you really want to delete %s?') % user, - reverse('user_delete', args=[user_id])) - return redirect(reverse('user_overview')) + permission_required = 'participant.can_manage_participant' + allow_ajax = True + url = 'user_overview' + model = OpenSlidesUser + + def pre_redirect(self, request, *args, **kwargs): + self.object = self.get_object() + action = kwargs['action'] + if action == 'activate': + self.object.is_active = True + elif action == 'deactivate': + self.object.is_active = False + elif action == 'toggle': + self.object.is_active = not self.object.is_active + self.object.save() + return super(SetUserStatusView, self).pre_redirect(request, *args, **kwargs) + + def get_ajax_context(self, **kwargs): + context = super(SetUserStatusView, self).get_ajax_context(**kwargs) + context['active'] = self.object.is_active + return context -@permission_required('participant.can_manage_participant') -@template('confirm.html') -def user_set_status(request, user_id): +class ParticipantsListPDF(PDFView): """ - Set the status of an user. + Generate the userliste as PDF. """ - try: - user = User.objects.get(pk=user_id) - if user.is_active: - user.is_active = False - else: - user.is_active = True - user.save() - except User.DoesNotExist: - messages.error(request, - _('Participant ID %d does not exist.') % int(user_id)) - return redirect(reverse('user_overview')) + permission_required = 'participant.can_see_participant' + filename = ugettext_lazy("Participant-list") + document_title = ugettext_lazy('List of Participants') - if request.is_ajax(): - return ajax_request({'active': user.is_active}) - # set success messages for page reload only (= not ajax request) - if user.is_active: - messages.success(request, _('%s is now present.') % user) - else: - messages.success(request, _('%s is now absent.') % user) - return redirect(reverse('user_overview')) + def append_to_pdf(self, story): + data = [['#', _('Last Name'), _('First Name'), _('Group'), _('Type'), + _('Committee')]] + sort = 'last_name' + counter = 0 + for user in OpenSlidesUser.objects.all().order_by(sort): + counter += 1 + data.append([ + counter, + Paragraph(user.last_name, stylesheet['Tablecell']), + Paragraph(user.first_name, stylesheet['Tablecell']), + Paragraph(user.category, stylesheet['Tablecell']), + Paragraph(user.type, stylesheet['Tablecell']), + Paragraph(user.committee, stylesheet['Tablecell']) + ]) + t = LongTable(data, + style=[ + ('VALIGN', (0, 0), (-1, -1), 'TOP'), + ('LINEABOVE', (0, 0), (-1, 0), 2, colors.black), + ('LINEABOVE', (0, 1), (-1, 1), 1, colors.black), + ('LINEBELOW', (0, -1), (-1, -1), 2, colors.black), + ('ROWBACKGROUNDS', (0, 1), (-1, -1), + (colors.white, (.9, .9, .9)))]) + t._argW[0] = 0.75 * cm + story.append(t) -@permission_required('participant.can_manage_participant') -@template('participant/group_overview.html') -def get_group_overview(request): +class ParticipantsPasswordsPDF(PDFView): """ - Show all groups. + Generate the Welcomepaper for the users. """ - if config['system_enable_anonymous']: - groups = Group.objects.all() - else: - groups = Group.objects.exclude(name='Anonymous') - return { - 'groups': groups, - } + permission_required = 'participant.can_manage_participant' + filename = ugettext_lazy("Participant-passwords") + top_space = 0 + def get_template(self, buffer): + return SimpleDocTemplate(buffer, topMargin=-6, bottomMargin=-6, + leftMargin=0, rightMargin=0, showBoundary=False) -@permission_required('participant.can_manage_participant') -@template('participant/group_edit.html') -def group_edit(request, group_id=None): - """ - Edit a group. - """ - if group_id is not None: - try: - group = Group.objects.get(id=group_id) - except Group.DoesNotExist: - # TODO: return a 404 Object - raise NameError("There is no group %d" % group_id) - else: - group = None - delete_default_permissions() + def build_document(self, pdf_document, story): + pdf_document.build(story) - if request.method == 'POST': - form = GroupForm(request.POST, instance=group) - if form.is_valid(): - # TODO: This can be done inside the form - group_name = form.cleaned_data['name'].lower() + def append_to_pdf(self, story): + data = [] + participant_pdf_system_url = config["participant_pdf_system_url"] + participant_pdf_welcometext = config["participant_pdf_welcometext"] + for user in OpenSlidesUser.objects.all().order_by('last_name'): + cell = [] + cell.append(Spacer(0, 0.8 * cm)) + cell.append(Paragraph(_("Account for OpenSlides"), + stylesheet['Ballot_title'])) + cell.append(Paragraph(_("for %s") % (user), + stylesheet['Ballot_subtitle'])) + cell.append(Spacer(0, 0.5 * cm)) + cell.append(Paragraph(_("User: %s") % (user.username), + stylesheet['Monotype'])) + cell.append(Paragraph(_("Password: %s") + % (user.firstpassword), stylesheet['Monotype'])) + cell.append(Spacer(0, 0.5 * cm)) + cell.append(Paragraph(_("URL: %s") + % (participant_pdf_system_url), + stylesheet['Ballot_option'])) + cell.append(Spacer(0, 0.5 * cm)) + cell2 = [] + cell2.append(Spacer(0, 0.8 * cm)) + if participant_pdf_welcometext is not None: + cell2.append(Paragraph( + participant_pdf_welcometext.replace('\r\n', '
'), + stylesheet['Ballot_subtitle'])) - # TODO: Why is this code called on any request and not only, if the - # anonymous_group is edited? - try: - anonymous_group = Group.objects.get(name='Anonymous') - except Group.DoesNotExist: - anonymous_group = None + data.append([cell, cell2]) - # special handling for anonymous auth - # TODO: This code should be a form validator. - if group is None and group_name.strip().lower() == 'anonymous': - # don't allow to create this group - messages.error(request, - _('Group name "%s" is reserved for internal use.') - % group_name) - return { - 'form': form, - 'group': group - } - - 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 \ - anonymous_group.id == group.id: - # prevent name changes - - # XXX: I'm sure this could be done as *one* group.save() - group.name = 'Anonymous' - group.save() - - if group_id is None: - messages.success(request, - _('New group was successfully created.')) - else: - messages.success(request, _('Group was successfully modified.')) - if not 'apply' in request.POST: - return redirect(reverse('user_group_overview')) - if group_id is None: - return redirect(reverse('user_group_edit', args=[group.id])) - else: - messages.error(request, _('Please check the form for errors.')) - else: - 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 { - 'form': form, - 'group': group, - } - - -@permission_required('participant.can_manage_participant') -def group_delete(request, group_id): - """ - Delete a group. - """ - group = Group.objects.get(pk=group_id) - if request.method == 'POST': - group.delete() - messages.success(request, - _('Group %s was successfully deleted.') % group) - else: - gen_confirm_form(request, - _('Do you really want to delete %s?') % group, - reverse('user_group_delete', args=[group_id])) - return redirect(reverse('user_group_overview')) + # add empty table line if no participants available + if not data: + data.append(['', '']) + # build table + t = Table(data, 10.5 * cm, 7.42 * cm) + t.setStyle(TableStyle([ + ('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), + ('VALIGN', (0, 0), (-1, -1), 'TOP'), + ])) + story.append(t) @login_required @@ -582,121 +544,120 @@ def login(request): return django_login(request, template_name='participant/login.html', extra_context=extra_content) -def register_tab(request): - """ - Register the participant tab. - """ - selected = request.path.startswith('/participant/') - return Tab( - title=_('Participants'), - url=reverse('user_overview'), - permission=request.user.has_perm('participant.can_see_participant') - or request.user.has_perm('participant.can_manage_participant'), - selected=selected, - ) -class ParticipantsListPDF(PDFView): +@permission_required('participant.can_manage_participant') +@template('participant/group_overview.html') +def get_group_overview(request): """ - Generate the userliste as PDF. + Show all groups. """ - permission_required = 'participant.can_see_participant' - filename = ugettext_lazy("Participant-list") - document_title = ugettext_lazy('List of Participants') + if config['system_enable_anonymous']: + groups = Group.objects.all() + else: + groups = Group.objects.exclude(name='Anonymous') + return { + 'groups': groups, + } - def append_to_pdf(self, story): - data = [['#', _('Last Name'), _('First Name'), _('Group'), _('Type'), - _('Committee')]] - sort = 'last_name' - counter = 0 - for user in User.objects.all().order_by(sort): + +@permission_required('participant.can_manage_participant') +@template('participant/group_edit.html') +def group_edit(request, group_id=None): + """ + Edit a group. + """ + if group_id is not None: + try: + group = Group.objects.get(id=group_id) + except Group.DoesNotExist: + # TODO: return a 404 Object + raise NameError("There is no group %d" % group_id) + else: + group = None + delete_default_permissions() + + if request.method == 'POST': + form = GroupForm(request.POST, instance=group) + if form.is_valid(): + # TODO: This can be done inside the form + 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: - counter += 1 - user.get_profile() - data.append([ - counter, - Paragraph(user.last_name, stylesheet['Tablecell']), - Paragraph(user.first_name, stylesheet['Tablecell']), - Paragraph(user.profile.group, stylesheet['Tablecell']), - Paragraph(user.profile.get_type_display(), - stylesheet['Tablecell']), - Paragraph(user.profile.committee, stylesheet['Tablecell']) - ]) - except Profile.DoesNotExist: - counter -= 1 - pass - t = LongTable(data, - style=[ - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ('LINEABOVE', (0, 0), (-1, 0), 2, colors.black), - ('LINEABOVE', (0, 1), (-1, 1), 1, colors.black), - ('LINEBELOW', (0, -1), (-1, -1), 2, colors.black), - ('ROWBACKGROUNDS', (0, 1), (-1, -1), - (colors.white, (.9, .9, .9)))]) - t._argW[0] = 0.75 * cm - story.append(t) + anonymous_group = Group.objects.get(name='Anonymous') + except Group.DoesNotExist: + anonymous_group = None + # special handling for anonymous auth + # TODO: This code should be a form validator. + if group is None and group_name.strip().lower() == 'anonymous': + # don't allow to create this group + messages.error(request, + _('Group name "%s" is reserved for internal use.') + % group_name) + return { + 'form': form, + 'group': group + } -class ParticipantsPasswordsPDF(PDFView): - """ - Generate the Welcomepaper for the users. - """ - permission_required = 'participant.can_manage_participant' - filename = ugettext_lazy("Participant-passwords") - top_space = 0 - - def get_template(self, buffer): - return SimpleDocTemplate(buffer, topMargin=-6, bottomMargin=-6, - leftMargin=0, rightMargin=0, showBoundary=False) - - def build_document(self, pdf_document, story): - pdf_document.build(story) - - def append_to_pdf(self, story): - data = [] - participant_pdf_system_url = config["participant_pdf_system_url"] - participant_pdf_welcometext = config["participant_pdf_welcometext"] - for user in User.objects.all().order_by('last_name'): + group = form.save() try: - user.get_profile() - cell = [] - cell.append(Spacer(0, 0.8 * cm)) - cell.append(Paragraph(_("Account for OpenSlides"), - stylesheet['Ballot_title'])) - cell.append(Paragraph(_("for %s") % (user.profile), - stylesheet['Ballot_subtitle'])) - cell.append(Spacer(0, 0.5 * cm)) - cell.append(Paragraph(_("User: %s") % (user.username), - stylesheet['Monotype'])) - cell.append(Paragraph(_("Password: %s") - % (user.profile.firstpassword), stylesheet['Monotype'])) - cell.append(Spacer(0, 0.5 * cm)) - cell.append(Paragraph(_("URL: %s") - % (participant_pdf_system_url), - stylesheet['Ballot_option'])) - cell.append(Spacer(0, 0.5 * cm)) - cell2 = [] - cell2.append(Spacer(0, 0.8 * cm)) - if participant_pdf_welcometext is not None: - cell2.append(Paragraph( - participant_pdf_welcometext.replace('\r\n', '
'), - stylesheet['Ballot_subtitle'])) + 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() - data.append([cell, cell2]) - except OpenSlidesUser.DoesNotExist: - pass - # add empty table line if no participants available - if data == []: - data.append(['', '']) - # build table - t = Table(data, 10.5 * cm, 7.42 * cm) - t.setStyle(TableStyle([ - ('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), - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ])) - story.append(t) + if anonymous_group is not None and \ + anonymous_group.id == group.id: + # prevent name changes - + # XXX: I'm sure this could be done as *one* group.save() + group.name = 'Anonymous' + group.save() + + if group_id is None: + messages.success(request, + _('New group was successfully created.')) + else: + messages.success(request, _('Group was successfully modified.')) + if not 'apply' in request.POST: + return redirect(reverse('user_group_overview')) + if group_id is None: + return redirect(reverse('user_group_edit', args=[group.id])) + else: + messages.error(request, _('Please check the form for errors.')) + else: + 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 { + 'form': form, + 'group': group, + } + + +@permission_required('participant.can_manage_participant') +def group_delete(request, group_id): + """ + Delete a group. + """ + group = Group.objects.get(pk=group_id) + if request.method == 'POST': + group.delete() + messages.success(request, + _('Group %s was successfully deleted.') % group) + else: + gen_confirm_form(request, + _('Do you really want to delete %s?') % group, + reverse('user_group_delete', args=[group_id])) + return redirect(reverse('user_group_overview')) class Config(FormView): @@ -721,3 +682,17 @@ class Config(FormView): messages.success(self.request, _('Participants settings successfully saved.')) return super(Config, self).form_valid(form) + + +def register_tab(request): + """ + Register the participant tab. + """ + selected = request.path.startswith('/participant/') + return Tab( + title=_('Participants'), + url=reverse('user_overview'), + permission=request.user.has_perm('participant.can_see_participant') + or request.user.has_perm('participant.can_manage_participant'), + selected=selected, + ) From 1f2f7cc73a1cc12e04a15f37dc0a622270abf566 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 10 Aug 2012 19:49:46 +0200 Subject: [PATCH 09/18] rewrote user csv import The import does not delete the existing users anymore, but append the users as new users. --- openslides/participant/api.py | 46 +++++++ openslides/participant/forms.py | 3 - openslides/participant/urls.py | 4 +- openslides/participant/views.py | 217 ++++++-------------------------- 4 files changed, 84 insertions(+), 186 deletions(-) diff --git a/openslides/participant/api.py b/openslides/participant/api.py index d2919821b..84077a0e4 100644 --- a/openslides/participant/api.py +++ b/openslides/participant/api.py @@ -10,12 +10,19 @@ :license: GNU GPL, see LICENSE for more details. """ +# for python 2.5 support +from __future__ import with_statement + from random import choice import string +import csv from django.contrib.auth.models import User +from django.db import transaction +from openslides.utils import csv_ext from openslides.utils.person import get_person + from openslides.participant.models import OpenSlidesUser @@ -47,3 +54,42 @@ def gen_username(first_name, last_name): User.objects.get(username=testname) except User.DoesNotExist: return testname + +def import_users(csv_file): + error_messages = [] + count_success = 0 + try: + # check for valid encoding (will raise UnicodeDecodeError if not) + csv_file.read().decode('utf-8') + csv_file.seek(0) + + with transaction.commit_on_success(): + dialect = csv.Sniffer().sniff(csv_file.readline()) + dialect = csv_ext.patchup(dialect) + csv_file.seek(0) + + for (line_no, line) in enumerate(csv.reader(csv_file, dialect=dialect)): + if line_no: + try: + (first_name, last_name, gender, category, type, committee, comment) = line[:7] + except ValueError: + error_messages.append(_('Ignoring malformed line %d in import file.') % line_no + 1) + continue + user = OpenSlidesUser() + user.last_name = last_name + user.first_name = first_name + user.username = gen_username(first_name, last_name) + user.gender = gender + user.category = category + user.type = type + user.committee = committee + user.comment = comment + user.firstpassword = gen_password() + user.save() + user.reset_password() + count_success += 1 + except csv.Error: + error_messages.appen(_('Import aborted because of severe errors in the input file.')) + except UnicodeDecodeError: + error_messages.appen(_('Import file has wrong character encoding, only UTF-8 is supported!')) + return (count_success, error_messages) diff --git a/openslides/participant/forms.py b/openslides/participant/forms.py index ebd267bc0..7da694a58 100644 --- a/openslides/participant/forms.py +++ b/openslides/participant/forms.py @@ -90,9 +90,6 @@ class UsersettingsForm(forms.ModelForm, CssClassMixin): class UserImportForm(forms.Form, CssClassMixin): csvfile = forms.FileField(widget=forms.FileInput(attrs={'size': '50'}), label=_("CSV File")) - application_handling = forms.ChoiceField( - required=True, choices=USER_APPLICATION_IMPORT_OPTIONS, - label=_("For existing applications")) class ConfigForm(forms.Form, CssClassMixin): diff --git a/openslides/participant/urls.py b/openslides/participant/urls.py index 61751402c..51ae2e301 100644 --- a/openslides/participant/urls.py +++ b/openslides/participant/urls.py @@ -15,7 +15,7 @@ from django.core.urlresolvers import reverse from openslides.participant.views import ( ParticipantsListPDF, ParticipantsPasswordsPDF, Overview, UserCreateView, - UserUpdateView, UserDeleteView, SetUserStatusView) + UserUpdateView, UserDeleteView, SetUserStatusView, UserImportView) urlpatterns = patterns('openslides.participant.views', url(r'^$', @@ -62,7 +62,7 @@ urlpatterns = patterns('openslides.participant.views', ), url(r'^import/$', - 'user_import', + UserImportView.as_view(), name='user_import', ), diff --git a/openslides/participant/views.py b/openslides/participant/views.py index e0169ae5c..a0879c002 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -50,7 +50,7 @@ from openslides.utils.views import ( from openslides.config.models import config 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, import_users from openslides.participant.forms import ( UserCreateForm, UserUpdateForm, OpenSlidesUserForm, UsersettingsForm, UserImportForm, GroupForm, AdminPasswordChangeForm, ConfigForm) @@ -300,6 +300,41 @@ class ParticipantsPasswordsPDF(PDFView): story.append(t) +class UserImportView(FormView): + """ + Import Users via csv. + """ + permission_required = 'participant.can_manage_participant' + template_name = 'participant/import.html' + form_class = UserImportForm + + def form_valid(self, form): + # check for valid encoding (will raise UnicodeDecodeError if not) + success, error_messages = import_users(self.request.FILES['csvfile']) + for message in error_messages: + messages.error(self.request, message) + if success: + messages.success(self.request, _('%d new participants were successfully imported.') % success) + return super(UserImportView, self).form_valid(form) + + +@permission_required('participant.can_manage_participant') +def reset_password(request, user_id): + """ + Reset the Password. + """ + user = User.objects.get(pk=user_id) + if request.method == 'POST': + user.profile.reset_password() + messages.success(request, + _('The Password for %s was successfully reset.') % user) + else: + gen_confirm_form(request, + _('Do you really want to reset the password for %s?') % user, + reverse('user_reset_password', args=[user_id])) + return redirect(reverse('user_edit', args=[user_id])) + + @login_required @template('participant/settings.html') def user_settings(request): @@ -344,186 +379,6 @@ def user_settings_password(request): } -@permission_required('participant.can_manage_participant') -@template('participant/import.html') -def user_import(request): - """ - Import Users via csv. - """ - from openslides.application.models import Application - try: - request.user.profile - messages.error(request, _('The import function is available for the admin (without user profile) only.')) - return redirect(reverse('user_overview')) - except Profile.DoesNotExist: - pass - except AttributeError: - # AnonymousUser - pass - - if request.method == 'POST': - form = UserImportForm(request.POST, request.FILES) - if form.is_valid(): - try: - # check for valid encoding (will raise UnicodeDecodeError if not) - request.FILES['csvfile'].read().decode('utf-8') - request.FILES['csvfile'].seek(0) - - with transaction.commit_on_success(): - - old_users = {} - applications_mapped = 0 - applications_review = 0 - applications_removed = 0 - - try: - janitor = User.objects.get(username='__system__.janitor') - except User.DoesNotExist: - janitor = User() - janitor.first_name = '' - janitor.last_name = '' - janitor.username = '__system__.janitor' - janitor.save() - - applications = Application.objects.all() - for application in applications: - if form.cleaned_data['application_handling'] == 'DISCARD': - # need to do this explicit since some applications may belong - # to __system__.janitor which is a permanent user - application.delete(force=True) - applications_removed += 1 - else: - # collect all applications and map them to their submitters - submitter = application.submitter - skey = '%s_%s' % (submitter.first_name, submitter.last_name) - - if not skey in old_users: - old_users[skey] = [] - old_users[skey].append(application.id) - - application.submitter = janitor - application.save() - - if application.supporter.all(): - application.writelog(_('Supporters removed after user import.'), user=request.user) - - profiles = Profile.objects.all() - for profile in profiles: - profile.user.delete() - profile.delete() - i = -1 - dialect = csv.Sniffer().sniff(request.FILES['csvfile'].readline()) - dialect = csv_ext.patchup(dialect) - request.FILES['csvfile'].seek(0) - - for (lno, line) in enumerate(csv.reader(request.FILES['csvfile'], dialect=dialect)): - i += 1 - if i > 0: - try: - (first_name, last_name, gender, group, type, committee, comment) = line[:7] - except ValueError: - messages.error(request, _('Ignoring malformed line %d in import file.') % (lno + 1)) - i -= 1 - continue - user = User() - user.last_name = last_name - user.first_name = first_name - user.username = gen_username(first_name, last_name) - #user.email = email - user.save() - profile = Profile() - profile.user = user - profile.gender = gender - profile.group = group - profile.type = type - profile.committee = committee - profile.comment = comment - profile.firstpassword = gen_password() - profile.user.set_password(profile.firstpassword) - profile.user.save() - profile.save() - - if type == 'delegate': - delegate = Group.objects.get(name='Delegierter') - user.groups.add(delegate) - else: - observer = Group.objects.get(name='Beobachter') - user.groups.add(observer) - - if form.cleaned_data['application_handling'] == 'REASSIGN': - # live remap - skey = '%s_%s' % (user.first_name, user.last_name) - if skey in old_users: - for appid in old_users[skey]: - try: - application = Application.objects.get(id=appid) - application.submitter = user - application.save() - application.writelog(_('Reassigned to "%s" after (re)importing users.') % ("%s %s" % (user.first_name, user.last_name)), user=request.user) - applications_mapped += 1 - except Application.DoesNotExist: - messages.error(request, _('Could not reassing application %d - object not found!') % appid) - del old_users[skey] - - if old_users: - # mark all applications without a valid user as 'needs review' - # this will account for *all* applications if application_mode == 'INREVIEW' - for skey in old_users: - for appid in old_users[skey]: - try: - application = Application.objects.get(id=appid) - if application.status != 'rev': - application.set_status(user=request.user, status='rev', force=True) - applications_review += 1 - except Application.DoesNotExist: - messages.error(request, _('Could not reassing application %d - object not found!') % appid) - - if applications_review: - messages.warning(request, ungettext('%d application could not be reassigned and needs a review!', - '%d applications could not be reassigned and need a review!', applications_review) % applications_review) - if applications_mapped: - messages.success(request, ungettext('%d application was successfully reassigned.', - '%d applications were successfully reassigned.', applications_mapped) % applications_mapped) - if applications_removed: - messages.warning(request, ungettext('%d application was discarded.', - '%d applications were discarded.', applications_removed) % applications_removed) - - if i > 0: - messages.success(request, _('%d new participants were successfully imported.') % i) - return redirect(reverse('user_overview')) - except csv.Error: - message.error(request, _('Import aborted because of severe errors in the input file.')) - except UnicodeDecodeError: - messages.error(request, _('Import file has wrong character encoding, only UTF-8 is supported!')) - else: - messages.error(request, _('Please check the form for errors.')) - else: - messages.warning(request, _("Attention: All existing participants will be removed if you import new participants.")) - if Application.objects.all(): - messages.warning(request, _("Attention: Supporters from all existing applications will be removed.")) - messages.warning(request, _("Attention: Applications which can't be mapped to new users will be set to 'Needs Review'.")) - form = UserImportForm() - return { - 'form': form, - } - - -@permission_required('participant.can_manage_participant') -def reset_password(request, user_id): - """ - Reset the Password. - """ - user = User.objects.get(pk=user_id) - if request.method == 'POST': - user.profile.reset_password() - messages.success(request, - _('The Password for %s was successfully reset.') % user) - else: - gen_confirm_form(request, - _('Do you really want to reset the password for %s?') % user, - reverse('user_reset_password', args=[user_id])) - return redirect(reverse('user_edit', args=[user_id])) - def login(request): extra_content = {} From 87af568eeb38368e9115ff01cad08dd4ae095103 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 10 Aug 2012 21:00:13 +0200 Subject: [PATCH 10/18] redesign the DeleteView Append a QuestionMixin to send a question via the django message api --- openslides/agenda/views.py | 61 ++++-------------------------- openslides/utils/views.py | 76 ++++++++++++++++++++++++++++---------- 2 files changed, 65 insertions(+), 72 deletions(-) diff --git a/openslides/agenda/views.py b/openslides/agenda/views.py index 9e5f69ac3..24a21f1c8 100644 --- a/openslides/agenda/views.py +++ b/openslides/agenda/views.py @@ -167,69 +167,24 @@ class ItemDelete(DeleteView): model = Item url = 'item_overview' - def pre_post_redirect(self, request, *args, **kwargs): - self.object = self.get_object() + def get_answer_options(self): + if self.object.children.exists(): + return [('all', _("Yes, with all child items."))] + self.answer_options + else: + return self.answer_options - if 'all' in request.POST: + def pre_post_redirect(self, request, *args, **kwargs): + if self.get_answer() == 'all': self.object.delete(with_children=True) messages.success(request, _("Item %s and his children were successfully deleted.") \ % html_strong(self.object)) - else: + elif self.get_answer() == 'yes': self.object.delete(with_children=False) messages.success(request, _("Item %s was successfully deleted.") \ % html_strong(self.object)) - def gen_confirm_form(self, request, message, url, singleitem=False): - if singleitem: - messages.warning( - request, - """ - %s -
- - - -
- """ - % (message, url, csrf(request)['csrf_token'], _("Yes"), - _("No")) - ) - else: - messages.warning( - request, - """ - %s -
- - - - -
- """ - % (message, url, csrf(request)['csrf_token'], _("Yes"), - _("Yes, with all child items."), _("No")) - ) - - def confirm_form(self, request, object, item=None): - if item is None: - item = object - if item.get_children(): - self.gen_confirm_form( - request, - _('Do you really want to delete %s?') % html_strong(item), - item.get_absolute_url('delete'), - False, - ) - else: - self.gen_confirm_form( - request, - _('Do you really want to delete %s?') % html_strong(item), - item.get_absolute_url('delete'), - True, - ) - class AgendaPDF(PDFView): """ diff --git a/openslides/utils/views.py b/openslides/utils/views.py index 5dab27f3c..c7e398481 100644 --- a/openslides/utils/views.py +++ b/openslides/utils/views.py @@ -34,7 +34,7 @@ from django.conf import settings from django.dispatch import receiver from django.http import HttpResponseServerError, HttpResponse, HttpResponseRedirect from django.utils.decorators import method_decorator -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext as _, ugettext_noop, ugettext_lazy from django.utils.importlib import import_module from django.template import loader, RequestContext from django.template.loader import render_to_string @@ -105,6 +105,55 @@ class AjaxMixin(object): return HttpResponse(json.dumps(self.get_ajax_context(**kwargs))) +class QuestionMixin(object): + question = ugettext_lazy('Are you sure?') + answer_options = [('yes', ugettext_lazy("Yes")), ('no', ugettext_lazy("No"))] + + def get_answer_options(self): + return self.answer_options + + def get_confirm_question(self): + return self.questions + + def get_success_message(self, option=None): + if option is None: + return _('Invalid answer') + return _('You choose %s') % option + + def get_answer(self): + for option in self.get_answer_options(): + if option[0] in self.request.POST: + return option[0] + return None + + def get_answer_url(self): + return self.answer_url + + def confirm_form(self): + option_fields = "\n".join([ + '' % (option[0], unicode(option[1])) + for option in self.get_answer_options()]) + messages.warning(self.request, + """ + %(message)s +
+ + %(option_fields)s +
+ """ % { + 'message': self.get_confirm_question(), + 'url': self.get_answer_url(), + 'csrf': csrf(self.request)['csrf_token'], + 'option_fields': option_fields}) + + def pre_redirect(self, request, *args, **kwargs): + self.confirm_form(request, self.object) + + def pre_post_redirect(self, request, *args, **kwargs): + option = self.get_answer() + messages.success(request, self.get_success_message(option)) + + class TemplateView(PermissionMixin, _TemplateView): def get_context_data(self, **kwargs): context = super(TemplateView, self).get_context_data(**kwargs) @@ -221,7 +270,7 @@ class CreateView(PermissionMixin, _CreateView): pass -class DeleteView(RedirectView, SingleObjectMixin): +class DeleteView(RedirectView, SingleObjectMixin, QuestionMixin): def get_confirm_question(self): return _('Do you really want to delete %s?') % html_strong(self.object) @@ -229,30 +278,19 @@ class DeleteView(RedirectView, SingleObjectMixin): return _('%s was successfully deleted.') % html_strong(self.object) def pre_redirect(self, request, *args, **kwargs): - self.confirm_form(request, self.object) + self.confirm_form() def pre_post_redirect(self, request, *args, **kwargs): - self.object.delete() - messages.success(request, self.get_success_message()) + if self.get_answer().lower() == 'yes': + self.object.delete() + messages.success(request, self.get_success_message()) def get(self, request, *args, **kwargs): self.object = self.get_object() return super(DeleteView, self).get(request, *args, **kwargs) - def confirm_form(self, request, object): - self.gen_confirm_form(request, self.get_confirm_question(), - object.get_absolute_url('delete')) - - def gen_confirm_form(self, request, message, url): - messages.warning(request, - """ - %s -
- - - -
- """ % (message, url, csrf(request)['csrf_token'], _("Yes"), _("No"))) + def get_answer_url(self): + return self.object.get_absolute_url('delete') class DetailView(TemplateView, SingleObjectMixin): From 9b3bec69d1bf91cd2aa838a611d79e64d1379f0b Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 10 Aug 2012 21:24:26 +0200 Subject: [PATCH 11/18] rewrote the passwort reset view as class base view --- openslides/participant/models.py | 1 + openslides/participant/urls.py | 23 +++++++++--------- openslides/participant/views.py | 40 +++++++++++++++++++++----------- openslides/utils/views.py | 17 +++++--------- 4 files changed, 45 insertions(+), 36 deletions(-) diff --git a/openslides/participant/models.py b/openslides/participant/models.py index 850b23806..09d2adbdf 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -51,6 +51,7 @@ class OpenSlidesUser(User, PersonMixin): comment = models.TextField( null=True, blank=True, verbose_name=_('Comment'), help_text=_('Only for notes.')) + # TODO: Rename this fild to default_password firstpassword = models.CharField( max_length=100, null=True, blank=True, verbose_name=_("First Password")) diff --git a/openslides/participant/urls.py b/openslides/participant/urls.py index 51ae2e301..584f880e7 100644 --- a/openslides/participant/urls.py +++ b/openslides/participant/urls.py @@ -15,7 +15,8 @@ from django.core.urlresolvers import reverse from openslides.participant.views import ( ParticipantsListPDF, ParticipantsPasswordsPDF, Overview, UserCreateView, - UserUpdateView, UserDeleteView, SetUserStatusView, UserImportView) + UserUpdateView, UserDeleteView, SetUserStatusView, UserImportView, + ResetPasswordView) urlpatterns = patterns('openslides.participant.views', url(r'^$', @@ -33,16 +34,21 @@ urlpatterns = patterns('openslides.participant.views', name='user_edit', ), - url(r'^print/$', - ParticipantsListPDF.as_view(), - name='user_print', - ), - url(r'^(?P\d+)/del/$', UserDeleteView.as_view(), name='user_delete', ), + url(r'^(?P\d+)/reset_password/$', + ResetPasswordView.as_view(), + name='user_reset_password', + ), + + url(r'^print/$', + ParticipantsListPDF.as_view(), + name='user_print', + ), + url(r'^(?P\d+)/status/toggle/$', SetUserStatusView.as_view(), {'action': 'toggle'}, @@ -86,11 +92,6 @@ urlpatterns = patterns('openslides.participant.views', name='user_group_delete', ), - url(r'^resetpassword/(?P\d+)/$', - 'reset_password', - name='user_reset_password', - ), - url(r'^passwords/print/$', ParticipantsPasswordsPDF.as_view(), name='print_passwords', diff --git a/openslides/participant/views.py b/openslides/participant/views.py index a0879c002..15e4e9eba 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -45,7 +45,7 @@ from openslides.utils.utils import ( encodedict, delete_default_permissions, html_strong) from openslides.utils.views import ( FormView, PDFView, TemplateView, CreateView, UpdateView, DeleteView, - RedirectView, SingleObjectMixin, ListView) + RedirectView, SingleObjectMixin, ListView, QuestionMixin) from openslides.config.models import config @@ -318,21 +318,33 @@ class UserImportView(FormView): return super(UserImportView, self).form_valid(form) -@permission_required('participant.can_manage_participant') -def reset_password(request, user_id): +class ResetPasswordView(RedirectView, SingleObjectMixin, QuestionMixin): """ - Reset the Password. + Set the Passwort for a user to his firstpassword. """ - user = User.objects.get(pk=user_id) - if request.method == 'POST': - user.profile.reset_password() - messages.success(request, - _('The Password for %s was successfully reset.') % user) - else: - gen_confirm_form(request, - _('Do you really want to reset the password for %s?') % user, - reverse('user_reset_password', args=[user_id])) - return redirect(reverse('user_edit', args=[user_id])) + permission_required = 'participant.can_manage_participant' + model = OpenSlidesUser + allow_ajax = True + question = ugettext_lazy('Do you really want to reset the password?') + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + return super(ResetPasswordView, self).get(request, *args, **kwargs) + + def get_redirect_url(self, **kwargs): + return reverse('user_edit', args=[self.object.id]) + + def pre_redirect(self, request, *args, **kwargs): + self.confirm_form() + + def pre_post_redirect(self, request, *args, **kwargs): + if self.get_answer().lower() == 'yes': + self.object.reset_password() + messages.success(request, + _('The Password for %s was successfully reset.') % self.object) + + def get_answer_url(self): + return reverse('user_reset_password', args=[self.object.id]) @login_required diff --git a/openslides/utils/views.py b/openslides/utils/views.py index c7e398481..45a2b0e9d 100644 --- a/openslides/utils/views.py +++ b/openslides/utils/views.py @@ -107,18 +107,14 @@ class AjaxMixin(object): class QuestionMixin(object): question = ugettext_lazy('Are you sure?') + success_message = ugettext_lazy('Thank you for your answer') answer_options = [('yes', ugettext_lazy("Yes")), ('no', ugettext_lazy("No"))] def get_answer_options(self): return self.answer_options - def get_confirm_question(self): - return self.questions - - def get_success_message(self, option=None): - if option is None: - return _('Invalid answer') - return _('You choose %s') % option + def get_question(self): + return unicode(self.question) def get_answer(self): for option in self.get_answer_options(): @@ -141,7 +137,7 @@ class QuestionMixin(object): %(option_fields)s """ % { - 'message': self.get_confirm_question(), + 'message': self.get_question(), 'url': self.get_answer_url(), 'csrf': csrf(self.request)['csrf_token'], 'option_fields': option_fields}) @@ -150,8 +146,7 @@ class QuestionMixin(object): self.confirm_form(request, self.object) def pre_post_redirect(self, request, *args, **kwargs): - option = self.get_answer() - messages.success(request, self.get_success_message(option)) + messages.success(request) class TemplateView(PermissionMixin, _TemplateView): @@ -271,7 +266,7 @@ class CreateView(PermissionMixin, _CreateView): class DeleteView(RedirectView, SingleObjectMixin, QuestionMixin): - def get_confirm_question(self): + def get_question(self): return _('Do you really want to delete %s?') % html_strong(self.object) def get_success_message(self): From bc3e7fdaab429270d9e308ccaedb1dafa82e42c3 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 11 Aug 2012 10:09:54 +0200 Subject: [PATCH 12/18] rewrote the group views as class based views --- openslides/participant/forms.py | 45 +++---- openslides/participant/models.py | 30 ++++- openslides/participant/urls.py | 25 ++-- openslides/participant/views.py | 223 +++++++++++-------------------- 4 files changed, 134 insertions(+), 189 deletions(-) diff --git a/openslides/participant/forms.py b/openslides/participant/forms.py index 7da694a58..f766c247b 100644 --- a/openslides/participant/forms.py +++ b/openslides/participant/forms.py @@ -18,14 +18,7 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop from openslides.utils.forms import ( CssClassMixin, LocalizedModelMultipleChoiceField) -from openslides.participant.models import OpenSlidesUser - - -USER_APPLICATION_IMPORT_OPTIONS = [ - ('REASSIGN', _('Keep applications, try to reassign submitter')), - ('INREVIEW', _('Keep applications, set status to "needs review"')), - ('DISCARD', _('Discard applications')) -] +from openslides.participant.models import OpenSlidesUser, OpenSlidesGroup class UserCreateForm(forms.ModelForm, CssClassMixin): @@ -50,25 +43,10 @@ class UserUpdateForm(UserCreateForm): 'firstpassword') -class UsernameForm(forms.ModelForm, CssClassMixin): - class Meta: - model = User - exclude = ('first_name', 'last_name', 'email', 'is_active', - 'is_superuser', 'groups', 'password', 'is_staff', - 'last_login', 'date_joined', 'user_permissions') - - -class OpenSlidesUserForm(forms.ModelForm, CssClassMixin): - class Meta: - model = OpenSlidesUser - - 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( - queryset=Permission.objects.all(), label=_("Persmissions")) + queryset=Permission.objects.all(), label=_("Persmissions"), + required=False) def __init__(self, *args, **kwargs): super(GroupForm, self).__init__(*args, **kwargs) @@ -76,9 +54,22 @@ class GroupForm(forms.ModelForm, CssClassMixin): self.fields['permissions'].initial = ( [p.pk for p in kwargs['instance'].permissions.all()]) + def clean_name(self): + data = self.cleaned_data['name'] + if self.instance.name.lower() == 'anonymous': + # Editing the anonymous-user + if self.instance.name.lower() != data.lower(): + raise forms.ValidationError( + _('You can not edit the name for the anonymous user')) + else: + if data.lower() == 'anonymous': + raise forms.ValidationError( + _('Group name "%s" is reserved for internal use.') % data) + return data + + class Meta: - model = Group - exclude = ('permissions',) + model = OpenSlidesGroup class UsersettingsForm(forms.ModelForm, CssClassMixin): diff --git a/openslides/participant/models.py b/openslides/participant/models.py index 09d2adbdf..625a82ec9 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -35,7 +35,7 @@ class OpenSlidesUser(User, PersonMixin): ('guest', _('Guest')), ) - user = models.OneToOneField(User, unique=True, editable=False, parent_link=True) + user = models.OneToOneField(User, editable=False, parent_link=True) category = models.CharField( max_length=100, null=True, blank=True, verbose_name=_("Category"), help_text=_('Will be shown behind the name.')) @@ -85,9 +85,9 @@ class OpenSlidesUser(User, PersonMixin): * delete """ if link == 'edit': - return ('user_edit', [str(self.user.id)]) + return ('user_edit', [str(self.id)]) if link == 'delete': - return ('user_delete', [str(self.user.id)]) + return ('user_delete', [str(self.id)]) def __unicode__(self): if self.name_surfix: @@ -103,13 +103,27 @@ class OpenSlidesUser(User, PersonMixin): ) -class OpenSlidesGroup(models.Model, PersonMixin): +class OpenSlidesGroup(Group, PersonMixin): person_prefix = 'openslides_group' - group = models.OneToOneField(Group) + group = models.OneToOneField(Group, editable=False, parent_link=True) group_as_person = models.BooleanField(default=False) description = models.TextField(blank=True) + @models.permalink + def get_absolute_url(self, link='edit'): + """ + Return the URL to this user. + + link can be: + * edit + * delete + """ + if link == 'edit': + return ('user_group_edit', [str(self.id)]) + if link == 'delete': + return ('user_group_delete', [str(self.id)]) + def __unicode__(self): return unicode(self.group) @@ -169,5 +183,7 @@ def user_post_save(sender, instance, signal, *args, **kwargs): @receiver(signals.post_save, sender=Group) def group_post_save(sender, instance, signal, *args, **kwargs): - # Creates OpenSlidesGroup - openslidesgroup, new = OpenSlidesGroup.objects.get_or_create(group=instance) + try: + instance.openslidesgroup + except OpenSlidesGroup.DoesNotExist: + OpenSlidesGroup(group=instance).save_base(raw=True) diff --git a/openslides/participant/urls.py b/openslides/participant/urls.py index 584f880e7..f55c30756 100644 --- a/openslides/participant/urls.py +++ b/openslides/participant/urls.py @@ -16,7 +16,8 @@ from django.core.urlresolvers import reverse from openslides.participant.views import ( ParticipantsListPDF, ParticipantsPasswordsPDF, Overview, UserCreateView, UserUpdateView, UserDeleteView, SetUserStatusView, UserImportView, - ResetPasswordView) + ResetPasswordView, GroupOverviewView, GroupCreateView, GroupUpdateView, + GroupDeleteView) urlpatterns = patterns('openslides.participant.views', url(r'^$', @@ -44,11 +45,6 @@ urlpatterns = patterns('openslides.participant.views', name='user_reset_password', ), - url(r'^print/$', - ParticipantsListPDF.as_view(), - name='user_print', - ), - url(r'^(?P\d+)/status/toggle/$', SetUserStatusView.as_view(), {'action': 'toggle'}, @@ -73,25 +69,30 @@ urlpatterns = patterns('openslides.participant.views', ), url(r'^group/$', - 'get_group_overview', + GroupOverviewView.as_view(), name='user_group_overview', ), url(r'^group/new/$', - 'group_edit', + GroupCreateView.as_view(), name='user_group_new', ), - url(r'^group/(?P\d+)/edit/$', - 'group_edit', + url(r'^group/(?P\d+)/edit/$', + GroupUpdateView.as_view(), name='user_group_edit', ), - url(r'^group/(?P\d+)/del/$', - 'group_delete', + url(r'^group/(?P\d+)/del/$', + GroupDeleteView.as_view(), name='user_group_delete', ), + url(r'^print/$', + ParticipantsListPDF.as_view(), + name='user_print', + ), + url(r'^passwords/print/$', ParticipantsPasswordsPDF.as_view(), name='print_passwords', diff --git a/openslides/participant/views.py b/openslides/participant/views.py index 15e4e9eba..4854c29af 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -52,7 +52,7 @@ from openslides.config.models import config from openslides.participant.models import OpenSlidesUser, OpenSlidesGroup from openslides.participant.api import gen_username, gen_password, import_users from openslides.participant.forms import ( - UserCreateForm, UserUpdateForm, OpenSlidesUserForm, UsersettingsForm, + UserCreateForm, UserUpdateForm, UsersettingsForm, UserImportForm, GroupForm, AdminPasswordChangeForm, ConfigForm) @@ -113,7 +113,7 @@ class Overview(ListView): def get_context_data(self, **kwargs): context = super(Overview, self).get_context_data(**kwargs) - all_users = User.objects.count() + all_users = OpenSlidesUser.objects.count() # quotient of selected users and all users if all_users > 0: @@ -347,6 +347,84 @@ class ResetPasswordView(RedirectView, SingleObjectMixin, QuestionMixin): return reverse('user_reset_password', args=[self.object.id]) +class GroupOverviewView(ListView): + """ + Overview over all groups. + """ + permission_required = 'participant.can_manage_participant' + template_name = 'participant/group_overview.html' + context_object_name = 'groups' + model = OpenSlidesGroup + + +class GroupEditMixin(object): + """ + Methodes for the GroupCreateView and GroupUpdateView. + """ + def get_form_class(self): + delete_default_permissions() + return self.form_class + + +class GroupCreateView(CreateView, GroupEditMixin): + """ + Create a new group. + """ + permission_required = 'participant.can_manage_participant' + template_name = 'participant/group_edit.html' + context_object_name = 'group' + model = OpenSlidesGroup + form_class = GroupForm + success_url = 'user_group_overview' + apply_url = 'user_group_edit' + + +class GroupUpdateView(UpdateView, GroupEditMixin): + """ + Update an existing group. + """ + permission_required = 'participant.can_manage_participant' + template_name = 'participant/group_edit.html' + model = OpenSlidesGroup + context_object_name = 'group' + form_class = GroupForm + success_url = 'user_group_overview' + apply_url = 'user_group_edit' + + +class GroupDeleteView(DeleteView): + """ + Delete a Group. + """ + permission_required = 'participant.can_manage_participant' + model = OpenSlidesGroup + url = 'user_group_overview' + + +class Config(FormView): + """ + Config page for the participant app. + """ + permission_required = 'config.can_manage_config' + form_class = ConfigForm + template_name = 'participant/config.html' + + def get_initial(self): + return { + 'participant_pdf_system_url': config['participant_pdf_system_url'], + 'participant_pdf_welcometext': config['participant_pdf_welcometext'] + } + + def form_valid(self, form): + config['participant_pdf_system_url'] = \ + form.cleaned_data['participant_pdf_system_url'] + config['participant_pdf_welcometext'] = \ + form.cleaned_data['participant_pdf_welcometext'] + messages.success(self.request, + _('Participants settings successfully saved.')) + return super(Config, self).form_valid(form) + + @login_required @template('participant/settings.html') def user_settings(request): @@ -391,7 +469,6 @@ def user_settings_password(request): } - def login(request): extra_content = {} try: @@ -411,146 +488,6 @@ def login(request): return django_login(request, template_name='participant/login.html', extra_context=extra_content) - - -@permission_required('participant.can_manage_participant') -@template('participant/group_overview.html') -def get_group_overview(request): - """ - Show all groups. - """ - if config['system_enable_anonymous']: - groups = Group.objects.all() - else: - groups = Group.objects.exclude(name='Anonymous') - return { - 'groups': groups, - } - - -@permission_required('participant.can_manage_participant') -@template('participant/group_edit.html') -def group_edit(request, group_id=None): - """ - Edit a group. - """ - if group_id is not None: - try: - group = Group.objects.get(id=group_id) - except Group.DoesNotExist: - # TODO: return a 404 Object - raise NameError("There is no group %d" % group_id) - else: - group = None - delete_default_permissions() - - if request.method == 'POST': - form = GroupForm(request.POST, instance=group) - if form.is_valid(): - # TODO: This can be done inside the form - 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: - anonymous_group = Group.objects.get(name='Anonymous') - except Group.DoesNotExist: - anonymous_group = None - - # special handling for anonymous auth - # TODO: This code should be a form validator. - if group is None and group_name.strip().lower() == 'anonymous': - # don't allow to create this group - messages.error(request, - _('Group name "%s" is reserved for internal use.') - % group_name) - return { - 'form': form, - 'group': group - } - - 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 \ - anonymous_group.id == group.id: - # prevent name changes - - # XXX: I'm sure this could be done as *one* group.save() - group.name = 'Anonymous' - group.save() - - if group_id is None: - messages.success(request, - _('New group was successfully created.')) - else: - messages.success(request, _('Group was successfully modified.')) - if not 'apply' in request.POST: - return redirect(reverse('user_group_overview')) - if group_id is None: - return redirect(reverse('user_group_edit', args=[group.id])) - else: - messages.error(request, _('Please check the form for errors.')) - else: - 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 { - 'form': form, - 'group': group, - } - - -@permission_required('participant.can_manage_participant') -def group_delete(request, group_id): - """ - Delete a group. - """ - group = Group.objects.get(pk=group_id) - if request.method == 'POST': - group.delete() - messages.success(request, - _('Group %s was successfully deleted.') % group) - else: - gen_confirm_form(request, - _('Do you really want to delete %s?') % group, - reverse('user_group_delete', args=[group_id])) - return redirect(reverse('user_group_overview')) - - -class Config(FormView): - """ - Config page for the participant app. - """ - permission_required = 'config.can_manage_config' - form_class = ConfigForm - template_name = 'participant/config.html' - - def get_initial(self): - return { - 'participant_pdf_system_url': config['participant_pdf_system_url'], - 'participant_pdf_welcometext': config['participant_pdf_welcometext'] - } - - def form_valid(self, form): - config['participant_pdf_system_url'] = \ - form.cleaned_data['participant_pdf_system_url'] - config['participant_pdf_welcometext'] = \ - form.cleaned_data['participant_pdf_welcometext'] - messages.success(self.request, - _('Participants settings successfully saved.')) - return super(Config, self).form_valid(form) - - def register_tab(request): """ Register the participant tab. From f01fe5a8942861ad1b87b0aecd076bddd190e444 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 11 Aug 2012 10:51:52 +0200 Subject: [PATCH 13/18] removed unused imports from participant and did some pep8 changes --- openslides/participant/api.py | 5 +- openslides/participant/forms.py | 11 ++-- openslides/participant/models.py | 2 +- openslides/participant/tests.py | 31 ++++------- openslides/participant/views.py | 92 ++++++++++++++++---------------- 5 files changed, 64 insertions(+), 77 deletions(-) diff --git a/openslides/participant/api.py b/openslides/participant/api.py index 84077a0e4..9fdccc47b 100644 --- a/openslides/participant/api.py +++ b/openslides/participant/api.py @@ -21,7 +21,6 @@ from django.contrib.auth.models import User from django.db import transaction from openslides.utils import csv_ext -from openslides.utils.person import get_person from openslides.participant.models import OpenSlidesUser @@ -55,6 +54,7 @@ def gen_username(first_name, last_name): except User.DoesNotExist: return testname + def import_users(csv_file): error_messages = [] count_success = 0 @@ -68,7 +68,8 @@ def import_users(csv_file): dialect = csv_ext.patchup(dialect) csv_file.seek(0) - for (line_no, line) in enumerate(csv.reader(csv_file, dialect=dialect)): + for (line_no, line) in enumerate(csv.reader(csv_file, + dialect=dialect)): if line_no: try: (first_name, last_name, gender, category, type, committee, comment) = line[:7] diff --git a/openslides/participant/forms.py b/openslides/participant/forms.py index f766c247b..f37a28420 100644 --- a/openslides/participant/forms.py +++ b/openslides/participant/forms.py @@ -11,9 +11,8 @@ """ from django import forms -from django.contrib.auth.forms import AdminPasswordChangeForm -from django.contrib.auth.models import User, Group, Permission -from django.utils.translation import ugettext_lazy as _, ugettext_noop +from django.contrib.auth.models import Permission +from django.utils.translation import ugettext_lazy as _ from openslides.utils.forms import ( CssClassMixin, LocalizedModelMultipleChoiceField) @@ -25,7 +24,8 @@ class UserCreateForm(forms.ModelForm, CssClassMixin): first_name = forms.CharField(label=_("First name")) last_name = forms.CharField(label=_("Last name")) groups = forms.ModelMultipleChoiceField( - queryset=Group.objects.all(), label=_("User groups"), required=False) + queryset=OpenSlidesGroup.objects.all(), label=_("User groups"), + required=False) is_active = forms.BooleanField( label=_("Active"), required=False, initial=True) @@ -67,14 +67,13 @@ class GroupForm(forms.ModelForm, CssClassMixin): _('Group name "%s" is reserved for internal use.') % data) return data - class Meta: model = OpenSlidesGroup class UsersettingsForm(forms.ModelForm, CssClassMixin): class Meta: - model = User + model = OpenSlidesUser fields = ('username', 'first_name', 'last_name', 'email') diff --git a/openslides/participant/models.py b/openslides/participant/models.py index 625a82ec9..3bc597ad8 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -12,7 +12,7 @@ from django.contrib.auth.models import User, Group from django.db import models -from django.db.models import Q, signals +from django.db.models import signals from django.dispatch import receiver from django.utils.translation import ugettext_lazy as _, ugettext_noop diff --git a/openslides/participant/tests.py b/openslides/participant/tests.py index fc76ae0f3..99cfcb009 100644 --- a/openslides/participant/tests.py +++ b/openslides/participant/tests.py @@ -12,8 +12,6 @@ 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 @@ -23,10 +21,11 @@ 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 = OpenSlidesUser() + self.openslidesuser1.first_name = u'Max' + self.openslidesuser1.last_name = u'Mustermann' + self.openslidesuser1.username = gen_username( + self.openslidesuser1.first_name, self.openslidesuser1.last_name) self.openslidesuser1.firstpassword = gen_password() self.openslidesuser1.save() self.user1 = self.openslidesuser1.user @@ -57,27 +56,17 @@ class OpenSlidesUserTest(TestCase): self.assertEqual(get_person('openslides_user:1'), self.openslidesuser1) self.assertEqual(len(Persons(person_prefix='openslides_user')), 1) - def test_save_name(self): - self.assertEqual(self.openslidesuser1.first_name, self.user1.first_name) - self.assertEqual(self.openslidesuser1.last_name, self.user1.last_name) - self.openslidesuser1.first_name = 'foo' - self.openslidesuser1.last_name = 'bar' - self.openslidesuser1.save() - user1 = User.objects.get(pk=1) - self.assertEqual(user1.first_name, 'foo') - self.assertEqual(user1.last_name, 'bar') - self.assertEqual(user1.get_full_name(), 'foo bar') - class OpenSlidesGroupTest(TestCase): def setUp(self): - self.group1 = Group.objects.create(name='Test Group') - self.openslidesgroup1 = OpenSlidesGroup.objects.create(group=self.group1) + self.openslidesgroup1 = OpenSlidesGroup.objects.create(name='Test Group') + self.group1 = self.openslidesgroup1.group 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) + person_id = "openslides_group:%d" % self.openslidesgroup1.id + self.assertEqual(self.openslidesgroup1.person_id, person_id) + self.assertEqual(get_person(person_id), self.openslidesgroup1) diff --git a/openslides/participant/views.py b/openslides/participant/views.py index 4854c29af..4ee452681 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -13,7 +13,6 @@ # for python 2.5 support from __future__ import with_statement -import csv from urllib import urlencode try: @@ -24,36 +23,31 @@ except ImportError: # python <= 2.5 grab it from cgi from reportlab.lib import colors from reportlab.lib.units import cm from reportlab.platypus import ( - SimpleDocTemplate, PageBreak, Paragraph, LongTable, Spacer, Table, - TableStyle) + SimpleDocTemplate, Paragraph, LongTable, Spacer, Table, TableStyle) -from django.db import transaction from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User, Group from django.contrib.auth.forms import PasswordChangeForm from django.contrib.auth.views import login as django_login from django.core.urlresolvers import reverse from django.shortcuts import redirect -from django.utils.translation import ugettext as _, ungettext, ugettext_lazy +from django.utils.translation import ugettext as _, ugettext_lazy -from openslides.utils import csv_ext from openslides.utils.pdf import stylesheet from openslides.utils.template import Tab from openslides.utils.utils import ( - template, permission_required, gen_confirm_form, ajax_request, decodedict, - encodedict, delete_default_permissions, html_strong) + template, decodedict, encodedict, delete_default_permissions, html_strong) from openslides.utils.views import ( - FormView, PDFView, TemplateView, CreateView, UpdateView, DeleteView, + FormView, PDFView, CreateView, UpdateView, DeleteView, RedirectView, SingleObjectMixin, ListView, QuestionMixin) from openslides.config.models import config -from openslides.participant.models import OpenSlidesUser, OpenSlidesGroup from openslides.participant.api import gen_username, gen_password, import_users from openslides.participant.forms import ( UserCreateForm, UserUpdateForm, UsersettingsForm, - UserImportForm, GroupForm, AdminPasswordChangeForm, ConfigForm) + UserImportForm, GroupForm, ConfigForm) +from openslides.participant.models import OpenSlidesUser, OpenSlidesGroup class Overview(ListView): @@ -152,7 +146,8 @@ class UserCreateView(CreateView): apply_url = 'participant_edit' def manipulate_object(self, form): - self.object.username = gen_username(form.cleaned_data['first_name'], form.cleaned_data['last_name']) + self.object.username = gen_username(form.cleaned_data['first_name'], + form.cleaned_data['last_name']) if not self.object.firstpassword: self.object.firstpassword = gen_password() @@ -216,7 +211,7 @@ class ParticipantsListPDF(PDFView): def append_to_pdf(self, story): data = [['#', _('Last Name'), _('First Name'), _('Group'), _('Type'), - _('Committee')]] + _('Committee')]] sort = 'last_name' counter = 0 for user in OpenSlidesUser.objects.all().order_by(sort): @@ -227,16 +222,14 @@ class ParticipantsListPDF(PDFView): Paragraph(user.first_name, stylesheet['Tablecell']), Paragraph(user.category, stylesheet['Tablecell']), Paragraph(user.type, stylesheet['Tablecell']), - Paragraph(user.committee, stylesheet['Tablecell']) - ]) - t = LongTable(data, - style=[ - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ('LINEABOVE', (0, 0), (-1, 0), 2, colors.black), - ('LINEABOVE', (0, 1), (-1, 1), 1, colors.black), - ('LINEBELOW', (0, -1), (-1, -1), 2, colors.black), - ('ROWBACKGROUNDS', (0, 1), (-1, -1), - (colors.white, (.9, .9, .9)))]) + Paragraph(user.committee, stylesheet['Tablecell'])]) + t = LongTable(data, style=[ + ('VALIGN', (0, 0), (-1, -1), 'TOP'), + ('LINEABOVE', (0, 0), (-1, 0), 2, colors.black), + ('LINEABOVE', (0, 1), (-1, 1), 1, colors.black), + ('LINEBELOW', (0, -1), (-1, -1), 2, colors.black), + ('ROWBACKGROUNDS', (0, 1), (-1, -1), + (colors.white, (.9, .9, .9)))]) t._argW[0] = 0.75 * cm story.append(t) @@ -250,7 +243,8 @@ class ParticipantsPasswordsPDF(PDFView): top_space = 0 def get_template(self, buffer): - return SimpleDocTemplate(buffer, topMargin=-6, bottomMargin=-6, + return SimpleDocTemplate( + buffer, topMargin=-6, bottomMargin=-6, leftMargin=0, rightMargin=0, showBoundary=False) def build_document(self, pdf_document, story): @@ -270,12 +264,15 @@ class ParticipantsPasswordsPDF(PDFView): cell.append(Spacer(0, 0.5 * cm)) cell.append(Paragraph(_("User: %s") % (user.username), stylesheet['Monotype'])) - cell.append(Paragraph(_("Password: %s") - % (user.firstpassword), stylesheet['Monotype'])) + cell.append( + Paragraph( + _("Password: %s") + % (user.firstpassword), stylesheet['Monotype'])) cell.append(Spacer(0, 0.5 * cm)) - cell.append(Paragraph(_("URL: %s") - % (participant_pdf_system_url), - stylesheet['Ballot_option'])) + cell.append( + Paragraph( + _("URL: %s") % (participant_pdf_system_url), + stylesheet['Ballot_option'])) cell.append(Spacer(0, 0.5 * cm)) cell2 = [] cell2.append(Spacer(0, 0.8 * cm)) @@ -314,7 +311,9 @@ class UserImportView(FormView): for message in error_messages: messages.error(self.request, message) if success: - messages.success(self.request, _('%d new participants were successfully imported.') % success) + messages.success( + self.request, + _('%d new participants were successfully imported.') % success) return super(UserImportView, self).form_valid(form) @@ -341,7 +340,7 @@ class ResetPasswordView(RedirectView, SingleObjectMixin, QuestionMixin): if self.get_answer().lower() == 'yes': self.object.reset_password() messages.success(request, - _('The Password for %s was successfully reset.') % self.object) + _('The Password for %s was successfully reset.') % html_strong(self.object)) def get_answer_url(self): return reverse('user_reset_password', args=[self.object.id]) @@ -412,15 +411,15 @@ class Config(FormView): def get_initial(self): return { 'participant_pdf_system_url': config['participant_pdf_system_url'], - 'participant_pdf_welcometext': config['participant_pdf_welcometext'] - } + 'participant_pdf_welcometext': config['participant_pdf_welcometext']} def form_valid(self, form): - config['participant_pdf_system_url'] = \ - form.cleaned_data['participant_pdf_system_url'] - config['participant_pdf_welcometext'] = \ - form.cleaned_data['participant_pdf_welcometext'] - messages.success(self.request, + config['participant_pdf_system_url'] = ( + form.cleaned_data['participant_pdf_system_url']) + config['participant_pdf_welcometext'] = ( + form.cleaned_data['participant_pdf_welcometext']) + messages.success( + self.request, _('Participants settings successfully saved.')) return super(Config, self).form_valid(form) @@ -478,10 +477,10 @@ def login(request): "Installation was successfully! Use %(user)s " "(password: %(password)s) for first login.
" "Important: Please change the password after " - "first login! Otherwise this message still appears for everyone " - "and could be a security risk.") % { - 'user': html_strong(admin.username), - 'password': html_strong(config['admin_password'])} + "first login! Otherwise this message still appears for " + "everyone and could be a security risk.") % { + 'user': html_strong(admin.username), + 'password': html_strong(config['admin_password'])} extra_content['next'] = reverse('password_change') except User.DoesNotExist: pass @@ -496,7 +495,6 @@ def register_tab(request): return Tab( title=_('Participants'), url=reverse('user_overview'), - permission=request.user.has_perm('participant.can_see_participant') - or request.user.has_perm('participant.can_manage_participant'), - selected=selected, - ) + permission=request.user.has_perm('participant.can_see_participant') or + request.user.has_perm('participant.can_manage_participant'), + selected=selected) From cdf160dfc3d220ec879fd7a4fa648144a96cc987 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 11 Aug 2012 11:09:20 +0200 Subject: [PATCH 14/18] fix some problems with the creation of new users --- openslides/participant/forms.py | 4 ++-- openslides/participant/views.py | 3 ++- openslides/utils/views.py | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/openslides/participant/forms.py b/openslides/participant/forms.py index f37a28420..a6245e94c 100644 --- a/openslides/participant/forms.py +++ b/openslides/participant/forms.py @@ -24,8 +24,8 @@ class UserCreateForm(forms.ModelForm, CssClassMixin): first_name = forms.CharField(label=_("First name")) last_name = forms.CharField(label=_("Last name")) groups = forms.ModelMultipleChoiceField( - queryset=OpenSlidesGroup.objects.all(), label=_("User groups"), - required=False) + queryset=OpenSlidesGroup.objects.exclude(name__iexact='anonymous'), + label=_("User groups"), required=False) is_active = forms.BooleanField( label=_("Active"), required=False, initial=True) diff --git a/openslides/participant/views.py b/openslides/participant/views.py index 4ee452681..67684ba58 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -28,6 +28,7 @@ from reportlab.platypus import ( from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.auth.forms import PasswordChangeForm +from django.contrib.auth.models import User from django.contrib.auth.views import login as django_login from django.core.urlresolvers import reverse from django.shortcuts import redirect @@ -143,7 +144,7 @@ class UserCreateView(CreateView): context_object_name = 'edit_user' form_class = UserCreateForm success_url = 'user_overview' - apply_url = 'participant_edit' + apply_url = 'user_edit' def manipulate_object(self, form): self.object.username = gen_username(form.cleaned_data['first_name'], diff --git a/openslides/utils/views.py b/openslides/utils/views.py index 45a2b0e9d..becdbfc8b 100644 --- a/openslides/utils/views.py +++ b/openslides/utils/views.py @@ -256,6 +256,7 @@ class CreateView(PermissionMixin, _CreateView): self.object = form.save(commit=False) self.manipulate_object(form) self.object.save() + form.save_m2m() return HttpResponseRedirect(self.get_success_url()) def get_success_message(self): From 6795ccd25b73ea92be149aea6b79f27a8761cdab Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 11 Aug 2012 11:36:55 +0200 Subject: [PATCH 15/18] change the users in the groupform --- openslides/participant/forms.py | 31 ++++++++++++++++++++++++++++--- openslides/participant/models.py | 5 +++-- openslides/participant/views.py | 21 ++++++++++----------- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/openslides/participant/forms.py b/openslides/participant/forms.py index a6245e94c..d420ba55f 100644 --- a/openslides/participant/forms.py +++ b/openslides/participant/forms.py @@ -47,14 +47,39 @@ class GroupForm(forms.ModelForm, CssClassMixin): permissions = LocalizedModelMultipleChoiceField( queryset=Permission.objects.all(), label=_("Persmissions"), required=False) + users = forms.ModelMultipleChoiceField( + queryset=OpenSlidesUser.objects.all(), + label=_("Users"), required=False) def __init__(self, *args, **kwargs): - super(GroupForm, self).__init__(*args, **kwargs) + # Initial users if kwargs.get('instance', None) is not None: - self.fields['permissions'].initial = ( - [p.pk for p in kwargs['instance'].permissions.all()]) + initial = kwargs.setdefault('initial', {}) + initial['users'] = [user.pk for user in kwargs['instance'].user_set.all()] + + super(GroupForm, self).__init__(*args, **kwargs) + + def save(self, commit=True): + instance = forms.ModelForm.save(self, False) + + old_save_m2m = self.save_m2m + def save_m2m(): + old_save_m2m() + + instance.user_set.clear() + for user in self.cleaned_data['users']: + instance.user_set.add(user) + self.save_m2m = save_m2m + + if commit: + instance.save() + self.save_m2m() + + return instance def clean_name(self): + # Do not allow to change the name "anonymous" or give another group + # this name data = self.cleaned_data['name'] if self.instance.name.lower() == 'anonymous': # Editing the anonymous-user diff --git a/openslides/participant/models.py b/openslides/participant/models.py index 3bc597ad8..8924d4d4b 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -90,9 +90,10 @@ class OpenSlidesUser(User, PersonMixin): return ('user_delete', [str(self.id)]) def __unicode__(self): + name = self.get_full_name() or _("No Name yet") if self.name_surfix: - return u"%s (%s)" % (self.get_full_name(), self.name_surfix) - return u"%s" % self.get_full_name() + return u"%s (%s)" % (name, self.name_surfix) + return u"%s" % name class Meta: # Rename permissions diff --git a/openslides/participant/views.py b/openslides/participant/views.py index 67684ba58..67b854ff5 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -357,16 +357,7 @@ class GroupOverviewView(ListView): model = OpenSlidesGroup -class GroupEditMixin(object): - """ - Methodes for the GroupCreateView and GroupUpdateView. - """ - def get_form_class(self): - delete_default_permissions() - return self.form_class - - -class GroupCreateView(CreateView, GroupEditMixin): +class GroupCreateView(CreateView): """ Create a new group. """ @@ -378,8 +369,12 @@ class GroupCreateView(CreateView, GroupEditMixin): success_url = 'user_group_overview' apply_url = 'user_group_edit' + def get(self, request, *args, **kwargs): + delete_default_permissions() + return super(GroupCreateView, self).get(request, *args, **kwargs) -class GroupUpdateView(UpdateView, GroupEditMixin): + +class GroupUpdateView(UpdateView): """ Update an existing group. """ @@ -391,6 +386,10 @@ class GroupUpdateView(UpdateView, GroupEditMixin): success_url = 'user_group_overview' apply_url = 'user_group_edit' + def get(self, request, *args, **kwargs): + delete_default_permissions() + return super(GroupUpdateView, self).get(request, *args, **kwargs) + class GroupDeleteView(DeleteView): """ From 0a8ee9cbad80550d1deba4d8c1a4928ce08906dc Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sun, 12 Aug 2012 10:12:26 +0200 Subject: [PATCH 16/18] changed request.user to be the openslidesuser --- openslides/openslides_settings.py | 2 +- openslides/participant/middleware.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 openslides/participant/middleware.py diff --git a/openslides/openslides_settings.py b/openslides/openslides_settings.py index b3f14315f..b14ff673e 100755 --- a/openslides/openslides_settings.py +++ b/openslides/openslides_settings.py @@ -101,7 +101,7 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'openslides.participant.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.locale.LocaleMiddleware', ) diff --git a/openslides/participant/middleware.py b/openslides/participant/middleware.py new file mode 100644 index 000000000..0e86e8ad2 --- /dev/null +++ b/openslides/participant/middleware.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + openslides.utils.middleware + ~~~~~~~~~~~~~~~~~~~~~~ + + Additional definitions for OpenSlides forms. + + :copyright: 2011, 2012 by OpenSlides team, see AUTHORS. + :license: GNU GPL, see LICENSE for more details. +""" + +from django.contrib.auth.middleware import AuthenticationMiddleware as _AuthenticationMiddleware +from django.contrib.auth.models import AnonymousUser + + +class AuthenticationMiddleware(_AuthenticationMiddleware): + def process_request(self, request): + super(AuthenticationMiddleware, self).process_request(request) + + if not isinstance(request.user, AnonymousUser): + request.user = request.user.openslidesuser From df40de67b03e2e59c1e63b6aa80585e94d217e27 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sun, 12 Aug 2012 12:52:38 +0200 Subject: [PATCH 17/18] rename openslidesuser to user and openslidesgroup to group --- initial_data.json | 20 +++--- openslides/application/models.py | 22 ++---- openslides/application/tests.py | 10 +-- openslides/application/views.py | 54 ++++++--------- openslides/assignment/views.py | 9 ++- openslides/participant/api.py | 4 +- openslides/participant/forms.py | 17 +++-- openslides/participant/middleware.py | 4 +- openslides/participant/models.py | 54 +++++++-------- .../templates/participant/overview.html | 8 +-- openslides/participant/tests.py | 69 ++++++++++--------- openslides/participant/views.py | 34 ++++----- openslides/urls.py | 1 - openslides/utils/person/api.py | 2 + openslides/utils/person/models.py | 2 +- 15 files changed, 144 insertions(+), 166 deletions(-) diff --git a/initial_data.json b/initial_data.json index ad17d7a56..5f7c8e30c 100644 --- a/initial_data.json +++ b/initial_data.json @@ -1,6 +1,6 @@ [ { - "pk": 2, + "pk": 1, "model": "auth.group", "fields": { "name": "Beobachter", @@ -38,7 +38,7 @@ [ "can_see_participant", "participant", - "openslidesuser" + "user" ], [ "can_see_projector", @@ -49,7 +49,7 @@ } }, { - "pk": 3, + "pk": 2, "model": "auth.group", "fields": { "name": "Delegierter", @@ -92,7 +92,7 @@ [ "can_see_participant", "participant", - "openslidesuser" + "user" ], [ "can_see_projector", @@ -103,7 +103,7 @@ } }, { - "pk": 4, + "pk": 3, "model": "auth.group", "fields": { "name": "Versammlungsleitung", @@ -161,12 +161,12 @@ [ "can_manage_participant", "participant", - "openslidesuser" + "user" ], [ "can_see_participant", "participant", - "openslidesuser" + "user" ], [ "can_manage_projector", @@ -182,7 +182,7 @@ } }, { - "pk": 5, + "pk": 4, "model": "auth.group", "fields": { "name": "Teilnehmerverwaltung", @@ -195,12 +195,12 @@ [ "can_manage_participant", "participant", - "openslidesuser" + "user" ], [ "can_see_participant", "participant", - "openslidesuser" + "user" ], [ "can_see_projector", diff --git a/openslides/application/models.py b/openslides/application/models.py index 78ebd55b6..82d1099c8 100644 --- a/openslides/application/models.py +++ b/openslides/application/models.py @@ -12,7 +12,6 @@ from datetime import datetime -from django.contrib.auth.models import User from django.core.urlresolvers import reverse from django.db import models from django.db.models import Max @@ -26,8 +25,6 @@ from openslides.utils.person import PersonField from openslides.config.models import config from openslides.config.signals import default_config_value -from openslides.participant.models import OpenSlidesUser - from openslides.poll.models import (BaseOption, BasePoll, CountVotesCast, CountInvalid, BaseVote) @@ -360,15 +357,6 @@ class Application(models.Model, SlideMixin): Return a list of all the allowed status. """ actions = [] - is_admin = False - if user: - try: - user = user.openslidesuser - except OpenSlidesUser.DoesNotExist: - is_admin = True - except AttributeError: - # For the anonymous-user - pass # check if user allowed to withdraw an application if ((self.status == "pub" @@ -405,11 +393,10 @@ class Application(models.Model, SlideMixin): # Check if the user can delete the application (admin, manager, owner) # reworked as requiered in #100 - if is_admin \ - or (user.has_perm("application.can_manage_application") \ - and (self.status == "pub" or self.number is None)) \ - or (self.submitter == user \ - and (self.status == "pub" or self.number is None)): + if (user.has_perm("applicatoin.can_delete_all_applications") or + (user.has_perm("application.can_manage_application") and + self.number is None) or + (self.submitter == user and self.number is None)): actions.append("delete") #For the rest, all actions need the manage permission @@ -543,6 +530,7 @@ class Application(models.Model, SlideMixin): ('can_create_application', ugettext_noop("Can create application")), ('can_support_application', ugettext_noop("Can support application")), ('can_manage_application', ugettext_noop("Can manage application")), + ('can_delete_all_applications', ugettext_noop("Can delete all applications")), ) diff --git a/openslides/application/tests.py b/openslides/application/tests.py index 2f5e65e09..bd3ecb73a 100644 --- a/openslides/application/tests.py +++ b/openslides/application/tests.py @@ -12,15 +12,17 @@ from django.test import TestCase from django.test.client import Client -from django.contrib.auth.models import User +from openslides.participant.models import User from openslides.application.models import Application, AVersion class ApplicationTest(TestCase): def setUp(self): - self.admin = User.objects.create_user('testadmin', '', 'default') - self.anonym = User.objects.create_user('testanoym', '', 'default') - self.app1 = Application(submitter=self.admin.openslidesuser) + self.admin = User(username='testadmin') + self.admin.save() + self.anonym = User(username='testanoym') + self.anonym.save() + self.app1 = Application(submitter=self.admin) self.app1.save() def refresh(self): diff --git a/openslides/application/views.py b/openslides/application/views.py index d531b0cab..1163d6ea2 100644 --- a/openslides/application/views.py +++ b/openslides/application/views.py @@ -51,7 +51,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.models import OpenSlidesUser +from openslides.participant.models import User from openslides.agenda.models import Item @@ -124,7 +124,7 @@ def overview(request): for (i, application) in enumerate(applications): try: applications[i] = { - 'actions' : application.get_allowed_actions(request.user.openslidesuser), + 'actions' : application.get_allowed_actions(request.user), 'application' : application } except: @@ -152,8 +152,7 @@ def view(request, application_id, newest=False): else: version = application.public_version revisions = application.versions - user = request.user.openslidesuser - actions = application.get_allowed_actions(user=user) + actions = application.get_allowed_actions(user=request.user) return { 'application': application, @@ -183,11 +182,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.openslidesuser == application.submitter.user) \ + not 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.openslidesuser) + actions = application.get_allowed_actions(user=request.user) else: application = None actions = None @@ -221,7 +220,7 @@ def edit(request, application_id=None): original_supporters = [] application = managerform.save(commit=False) elif application_id is None: - application = Application(submitter=request.user.openslidesuser) + application = Application(submitter=request.user) application.title = dataform.cleaned_data['title'] application.text = dataform.cleaned_data['text'] application.reason = dataform.cleaned_data['reason'] @@ -231,7 +230,7 @@ def edit(request, application_id=None): and dataform.cleaned_data['trivial_change'] except KeyError: trivial_change = False - application.save(request.user.openslidesuser, trivial_change=trivial_change) + application.save(request.user, trivial_change=trivial_change) if is_manager: try: new_supporters = set(managerform.cleaned_data['supporter']) @@ -273,7 +272,7 @@ def edit(request, application_id=None): dataform = formclass(initial=initial, prefix="data") if is_manager: if application_id is None: - initial = {'submitter': request.user.openslidesuser.person_id} + initial = {'submitter': request.user.person_id} else: initial = {'submitter': application.submitter.person_id, 'supporter': [supporter.person_id for supporter in application.supporters]} @@ -296,7 +295,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.openslidesuser) + Application.objects.get(pk=application_id).set_number(user=request.user) messages.success(request, _("Application number was successfully set.")) except Application.DoesNotExist: pass @@ -312,7 +311,7 @@ def permit(request, application_id): permit an application. """ try: - Application.objects.get(pk=application_id).permit(user=request.user.openslidesuser) + Application.objects.get(pk=application_id).permit(user=request.user) messages.success(request, _("Application was successfully permitted.")) except Application.DoesNotExist: pass @@ -327,7 +326,7 @@ def notpermit(request, application_id): reject (not permit) an application. """ try: - Application.objects.get(pk=application_id).notpermit(user=request.user.openslidesuser) + Application.objects.get(pk=application_id).notpermit(user=request.user) messages.success(request, _("Application was successfully rejected.")) except Application.DoesNotExist: pass @@ -343,7 +342,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.openslidesuser, status=status) + application.set_status(user=request.user, status=status) messages.success(request, _("Application status was set to: %s.") % application.get_status_display()) except Application.DoesNotExist: pass @@ -387,7 +386,7 @@ def unsupport(request, application_id): unsupport an application. """ try: - Application.objects.get(pk=application_id).unsupport(user=request.user.openslidesuser) + Application.objects.get(pk=application_id).unsupport(user=request.user) messages.success(request, _("You have unsupport the application successfully.") ) except Application.DoesNotExist: pass @@ -401,7 +400,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.openslidesuser) + poll = Application.objects.get(pk=application_id).gen_poll(user=request.user) messages.success(request, _("New vote was successfully created.") ) except Application.DoesNotExist: pass # TODO: do not call poll after this excaption @@ -418,7 +417,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.openslidesuser) + application.writelog(_("Poll deleted"), request.user) messages.success(request, _('Poll was successfully deleted.')) else: del_confirm_form(request, poll, name=_("the %s. poll") % count, delete_link=reverse('application_poll_delete', args=[poll_id])) @@ -458,7 +457,7 @@ class ApplicationDelete(DeleteView): if len(self.applications): for application in self.applications: - if not 'delete' in application.get_allowed_actions(user=request.user.openslidesuser): + if not 'delete' in application.get_allowed_actions(user=request.user): messages.error(request, _("You can not delete application %s.") % application) continue @@ -467,7 +466,7 @@ 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.openslidesuser): + 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 @@ -508,12 +507,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.openslidesuser) + context['actions'] = self.application.get_allowed_actions(user=self.request.user) return context def get_modelform_class(self): cls = super(ViewPoll, self).get_modelform_class() - user = self.request.user.openslidesuser + user = self.request.user class ViewPollFormClass(cls): def save(self, commit = True): @@ -535,7 +534,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.openslidesuser) + application.accept_version(aversion, user=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])) @@ -547,7 +546,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.openslidesuser): + if application.reject_version(aversion, user=request.user): messages.success(request, _("Version %s rejected.") % (aversion.aid)) else: messages.error(request, _("ERROR by rejecting the version.") ) @@ -559,17 +558,6 @@ def reject_version(request, aversion_id): @permission_required('application.can_manage_application') @template('application/import.html') def application_import(request): - try: - request.user.openslidesuser - except OpenSlidesUser.DoesNotExist: - pass - except AttributeError: - # AnonymousUser - pass - else: - messages.error(request, _('The import function is available for the admin (without user profile) only.')) - return redirect(reverse('application_overview')) - if request.method == 'POST': form = ApplicationImportForm(request.POST, request.FILES) if form.is_valid(): diff --git a/openslides/assignment/views.py b/openslides/assignment/views.py index 96a37bb14..56dc26448 100644 --- a/openslides/assignment/views.py +++ b/openslides/assignment/views.py @@ -34,7 +34,7 @@ from openslides.utils.person import get_person from openslides.config.models import config -from openslides.participant.models import OpenSlidesUser +from openslides.participant.models import User from openslides.projector.projector import Widget @@ -97,13 +97,12 @@ def view(request, assignment_id=None): polls = assignment.poll_set.all() vote_results = assignment.vote_results(only_published=False) - user = request.user.openslidesuser return { 'assignment': assignment, 'form': form, 'vote_results': vote_results, 'polls': polls, - 'user_is_candidate': assignment.is_candidate(user) + 'user_is_candidate': assignment.is_candidate(request.user) } @@ -173,7 +172,7 @@ def set_status(request, assignment_id=None, status=None): def run(request, assignment_id): assignment = Assignment.objects.get(pk=assignment_id) try: - assignment.run(request.user.openslidesuser, request.user) + assignment.run(request.user, request.user) messages.success(request, _('You have set your candidature successfully.') ) except NameError, e: messages.error(request, e) @@ -185,7 +184,7 @@ def delrun(request, assignment_id): assignment = Assignment.objects.get(pk=assignment_id) try: if assignment.status == 'sea' or user.has_perm("assignment.can_manage_assignment"): - assignment.delrun(request.user.openslidesuser) + assignment.delrun(request.user) else: messages.error(request, _('The candidate list is already closed.')) except Exception, e: diff --git a/openslides/participant/api.py b/openslides/participant/api.py index 9fdccc47b..6728016cd 100644 --- a/openslides/participant/api.py +++ b/openslides/participant/api.py @@ -22,7 +22,7 @@ from django.db import transaction from openslides.utils import csv_ext -from openslides.participant.models import OpenSlidesUser +from openslides.participant.models import User def gen_password(): @@ -76,7 +76,7 @@ def import_users(csv_file): except ValueError: error_messages.append(_('Ignoring malformed line %d in import file.') % line_no + 1) continue - user = OpenSlidesUser() + user = User() user.last_name = last_name user.first_name = first_name user.username = gen_username(first_name, last_name) diff --git a/openslides/participant/forms.py b/openslides/participant/forms.py index d420ba55f..93e6b22df 100644 --- a/openslides/participant/forms.py +++ b/openslides/participant/forms.py @@ -17,27 +17,27 @@ from django.utils.translation import ugettext_lazy as _ from openslides.utils.forms import ( CssClassMixin, LocalizedModelMultipleChoiceField) -from openslides.participant.models import OpenSlidesUser, OpenSlidesGroup +from openslides.participant.models import User, Group class UserCreateForm(forms.ModelForm, CssClassMixin): first_name = forms.CharField(label=_("First name")) last_name = forms.CharField(label=_("Last name")) groups = forms.ModelMultipleChoiceField( - queryset=OpenSlidesGroup.objects.exclude(name__iexact='anonymous'), + queryset=Group.objects.exclude(name__iexact='anonymous'), label=_("User groups"), required=False) is_active = forms.BooleanField( label=_("Active"), required=False, initial=True) class Meta: - model = OpenSlidesUser + model = User fields = ('first_name', 'last_name', 'is_active', 'groups', 'category', 'gender', 'type', 'committee', 'comment', 'firstpassword') class UserUpdateForm(UserCreateForm): class Meta: - model = OpenSlidesUser + model = User fields = ('username', 'first_name', 'last_name', 'is_active', 'groups', 'category', 'gender', 'type', 'committee', 'comment', 'firstpassword') @@ -48,14 +48,13 @@ class GroupForm(forms.ModelForm, CssClassMixin): queryset=Permission.objects.all(), label=_("Persmissions"), required=False) users = forms.ModelMultipleChoiceField( - queryset=OpenSlidesUser.objects.all(), - label=_("Users"), required=False) + queryset=User.objects.all(), label=_("Users"), required=False) def __init__(self, *args, **kwargs): # Initial users if kwargs.get('instance', None) is not None: initial = kwargs.setdefault('initial', {}) - initial['users'] = [user.pk for user in kwargs['instance'].user_set.all()] + initial['users'] = [django_user.user.pk for django_user in kwargs['instance'].user_set.all()] super(GroupForm, self).__init__(*args, **kwargs) @@ -93,12 +92,12 @@ class GroupForm(forms.ModelForm, CssClassMixin): return data class Meta: - model = OpenSlidesGroup + model = Group class UsersettingsForm(forms.ModelForm, CssClassMixin): class Meta: - model = OpenSlidesUser + model = User fields = ('username', 'first_name', 'last_name', 'email') diff --git a/openslides/participant/middleware.py b/openslides/participant/middleware.py index 0e86e8ad2..b2341f00f 100644 --- a/openslides/participant/middleware.py +++ b/openslides/participant/middleware.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ openslides.utils.middleware - ~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Additional definitions for OpenSlides forms. @@ -19,4 +19,4 @@ class AuthenticationMiddleware(_AuthenticationMiddleware): super(AuthenticationMiddleware, self).process_request(request) if not isinstance(request.user, AnonymousUser): - request.user = request.user.openslidesuser + request.user = request.user.user diff --git a/openslides/participant/models.py b/openslides/participant/models.py index 8924d4d4b..83b484432 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -10,7 +10,7 @@ :license: GNU GPL, see LICENSE for more details. """ -from django.contrib.auth.models import User, Group +from django.contrib.auth.models import User as DjangoUser, Group as DjangoGroup from django.db import models from django.db.models import signals from django.dispatch import receiver @@ -22,8 +22,8 @@ from openslides.utils.person.signals import receiv_persons from openslides.config.signals import default_config_value -class OpenSlidesUser(User, PersonMixin): - person_prefix = 'openslides_user' +class User(DjangoUser, PersonMixin): + person_prefix = 'user' GENDER_CHOICES = ( ('male', _('Male')), ('female', _('Female')), @@ -35,7 +35,7 @@ class OpenSlidesUser(User, PersonMixin): ('guest', _('Guest')), ) - user = models.OneToOneField(User, editable=False, parent_link=True) + django_user = models.OneToOneField(DjangoUser, editable=False, parent_link=True) category = models.CharField( max_length=100, null=True, blank=True, verbose_name=_("Category"), help_text=_('Will be shown behind the name.')) @@ -72,8 +72,8 @@ class OpenSlidesUser(User, PersonMixin): """ if password is None: password = self.firstpassword - self.user.set_password(password) - self.user.save() + self.set_password(password) + self.save() @models.permalink def get_absolute_url(self, link='edit'): @@ -104,10 +104,10 @@ class OpenSlidesUser(User, PersonMixin): ) -class OpenSlidesGroup(Group, PersonMixin): - person_prefix = 'openslides_group' +class Group(DjangoGroup, PersonMixin): + person_prefix = 'group' - group = models.OneToOneField(Group, editable=False, parent_link=True) + django_group = models.OneToOneField(DjangoGroup, editable=False, parent_link=True) group_as_person = models.BooleanField(default=False) description = models.TextField(blank=True) @@ -126,38 +126,38 @@ class OpenSlidesGroup(Group, PersonMixin): return ('user_group_delete', [str(self.id)]) def __unicode__(self): - return unicode(self.group) + return unicode(self.name) -class OpenSlidesUsersConnecter(object): +class UsersConnecter(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): + self.person_prefix == User.person_prefix): if self.id: - yield OpenSlidesUser.objects.get(pk=self.id) + yield User.objects.get(pk=self.id) else: - for user in OpenSlidesUser.objects.all(): + for user in User.objects.all(): yield user if (not self.person_prefix or - self.person_prefix == OpenSlidesGroup.person_prefix): + self.person_prefix == Group.person_prefix): if self.id: - yield OpenSlidesGroup.objects.get(pk=self.id) + yield Group.objects.filter(group_as_person=True).get(pk=self.id) else: - for group in OpenSlidesGroup.objects.all(): + for group in Group.objects.filter(group_as_person=True): yield group def __getitem__(self, key): - return OpenSlidesUser.objects.get(pk=key) + return User.objects.get(pk=key) @receiver(receiv_persons, dispatch_uid="participant") def receiv_persons(sender, **kwargs): - return OpenSlidesUsersConnecter(person_prefix=kwargs['person_prefix'], + return UsersConnecter(person_prefix=kwargs['person_prefix'], id=kwargs['id']) @@ -174,17 +174,17 @@ def default_config(sender, key, **kwargs): }.get(key) -@receiver(signals.post_save, sender=User) +@receiver(signals.post_save, sender=DjangoUser) def user_post_save(sender, instance, signal, *args, **kwargs): try: - instance.openslidesuser - except OpenSlidesUser.DoesNotExist: - OpenSlidesUser(user=instance).save_base(raw=True) + instance.user + except User.DoesNotExist: + User(django_user=instance).save_base(raw=True) -@receiver(signals.post_save, sender=Group) +@receiver(signals.post_save, sender=DjangoGroup) def group_post_save(sender, instance, signal, *args, **kwargs): try: - instance.openslidesgroup - except OpenSlidesGroup.DoesNotExist: - OpenSlidesGroup(group=instance).save_base(raw=True) + instance.group + except Group.DoesNotExist: + Group(django_group=instance).save_base(raw=True) diff --git a/openslides/participant/templates/participant/overview.html b/openslides/participant/templates/participant/overview.html index 4641d615a..3e2762a37 100644 --- a/openslides/participant/templates/participant/overview.html +++ b/openslides/participant/templates/participant/overview.html @@ -78,11 +78,11 @@ {{ user.first_name }} {{ user.last_name }} - {{ user.openslidesuser.category }} - {{ user.openslidesuser.get_type_display }} - {{ user.openslidesuser.committee }} + {{ user.category }} + {{ user.get_type_display }} + {{ user.committee }} {% if perms.participant.can_manage_participant %} - {{ user.openslidesuser.comment|first_line }} + {{ user.comment|first_line }} {% if user.last_login > user.date_joined %} {{ user.last_login }} diff --git a/openslides/participant/tests.py b/openslides/participant/tests.py index 99cfcb009..bbcda6cf7 100644 --- a/openslides/participant/tests.py +++ b/openslides/participant/tests.py @@ -16,57 +16,58 @@ 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 +from openslides.participant.models import User, Group -class OpenSlidesUserTest(TestCase): +class UserTest(TestCase): def setUp(self): - self.openslidesuser1 = OpenSlidesUser() - self.openslidesuser1.first_name = u'Max' - self.openslidesuser1.last_name = u'Mustermann' - self.openslidesuser1.username = gen_username( - self.openslidesuser1.first_name, self.openslidesuser1.last_name) - self.openslidesuser1.firstpassword = gen_password() - self.openslidesuser1.save() - self.user1 = self.openslidesuser1.user + self.user1 = User() + self.user1.first_name = u'Max' + self.user1.last_name = u'Mustermann' + self.user1.username = gen_username( + self.user1.first_name, self.user1.last_name) + self.user1.firstpassword = gen_password() + self.user1.save() + self.django_user1 = self.user1.django_user def test_participant_user(self): - self.assertEqual(self.user1.openslidesuser, self.openslidesuser1) - self.assertEqual(self.user1, self.openslidesuser1.user) + self.assertEqual(self.django_user1.user, self.user1) + self.assertEqual(self.django_user1, self.user1.django_user) def test_repr(self): - self.assertEqual(unicode(self.openslidesuser1), u'Max Mustermann') + self.assertEqual(unicode(self.user1), u'Max Mustermann') def test_name_surfix(self): - self.openslidesuser1.category = u'München' - self.openslidesuser1.save() - self.assertEqual(unicode(self.openslidesuser1), u'Max Mustermann (München)') + self.user1.category = u'München' + self.user1.save() + self.assertEqual(unicode(self.user1), u'Max Mustermann (München)') def test_reset_password(self): - self.assertIsInstance(self.openslidesuser1.firstpassword, basestring) - self.assertEqual(len(self.openslidesuser1.firstpassword), 8) + self.assertIsInstance(self.user1.firstpassword, basestring) + self.assertEqual(len(self.user1.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)) + self.assertFalse(self.user1.check_password(self.user1.firstpassword)) + self.user1.reset_password() + self.assertTrue(self.user1.check_password(self.user1.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(person_prefix='openslides_user')), 1) + self.assertTrue(hasattr(self.user1, 'person_id')) + self.assertEqual(self.user1.person_id, 'user:1') + self.assertEqual(get_person('user:1'), self.user1) + self.assertEqual(len(Persons(person_prefix='user')), 1) -class OpenSlidesGroupTest(TestCase): +class GroupTest(TestCase): def setUp(self): - self.openslidesgroup1 = OpenSlidesGroup.objects.create(name='Test Group') - self.group1 = self.openslidesgroup1.group + self.group1 = Group.objects.create(name='Test Group') + self.django_group1 = self.group1.django_group - def test_group_openslidesgroup(self): - self.assertEqual(self.openslidesgroup1.group, self.group1) + def test_group_group(self): + self.assertEqual(self.group1.django_group, self.django_group1) + self.assertEqual(self.group1, self.django_group1.group) def test_person_api(self): - self.assertTrue(hasattr(self.openslidesgroup1, 'person_id')) - person_id = "openslides_group:%d" % self.openslidesgroup1.id - self.assertEqual(self.openslidesgroup1.person_id, person_id) - self.assertEqual(get_person(person_id), self.openslidesgroup1) + self.assertTrue(hasattr(self.group1, 'person_id')) + person_id = "group:%d" % self.group1.id + self.assertEqual(self.group1.person_id, person_id) + self.assertEqual(get_person(person_id), self.group1) diff --git a/openslides/participant/views.py b/openslides/participant/views.py index 67b854ff5..27abb541e 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -48,7 +48,7 @@ from openslides.participant.api import gen_username, gen_password, import_users from openslides.participant.forms import ( UserCreateForm, UserUpdateForm, UsersettingsForm, UserImportForm, GroupForm, ConfigForm) -from openslides.participant.models import OpenSlidesUser, OpenSlidesGroup +from openslides.participant.models import User, Group class Overview(ListView): @@ -77,7 +77,7 @@ class Overview(ListView): else: sortfilter[value] = [self.request.REQUEST[value]] - query = OpenSlidesUser.objects + query = User.objects if 'gender' in sortfilter: query = query.filter(gender__iexact=sortfilter['gender'][0]) if 'category' in sortfilter: @@ -94,7 +94,7 @@ class Overview(ListView): elif (sortfilter['sort'][0] in ['category', 'type', 'committee', 'comment']): query = query.order_by( - 'openslidesuser__%s' % sortfilter['sort'][0]) + '%s' % sortfilter['sort'][0]) else: query = query.order_by('last_name') @@ -108,7 +108,7 @@ class Overview(ListView): def get_context_data(self, **kwargs): context = super(Overview, self).get_context_data(**kwargs) - all_users = OpenSlidesUser.objects.count() + all_users = User.objects.count() # quotient of selected users and all users if all_users > 0: @@ -117,11 +117,11 @@ class Overview(ListView): percent = 0 # list of all existing categories - categories = [p['category'] for p in OpenSlidesUser.objects.values('category') + categories = [p['category'] for p in User.objects.values('category') .exclude(category='').distinct()] # list of all existing committees - committees = [p['committee'] for p in OpenSlidesUser.objects.values('committee') + committees = [p['committee'] for p in User.objects.values('committee') .exclude(committee='').distinct()] context.update({ 'allusers': all_users, @@ -140,7 +140,7 @@ class UserCreateView(CreateView): """ permission_required = 'participant.can_manage_participant' template_name = 'participant/edit.html' - model = OpenSlidesUser + model = User context_object_name = 'edit_user' form_class = UserCreateForm success_url = 'user_overview' @@ -159,7 +159,7 @@ class UserUpdateView(UpdateView): """ permission_required = 'participant.can_manage_participant' template_name = 'participant/edit.html' - model = OpenSlidesUser + model = User context_object_name = 'edit_user' form_class = UserUpdateForm success_url = 'user_overview' @@ -171,7 +171,7 @@ class UserDeleteView(DeleteView): Delete an participant. """ permission_required = 'participant.can_manage_participant' - model = OpenSlidesUser + model = User url = 'user_overview' @@ -182,7 +182,7 @@ class SetUserStatusView(RedirectView, SingleObjectMixin): permission_required = 'participant.can_manage_participant' allow_ajax = True url = 'user_overview' - model = OpenSlidesUser + model = User def pre_redirect(self, request, *args, **kwargs): self.object = self.get_object() @@ -215,7 +215,7 @@ class ParticipantsListPDF(PDFView): _('Committee')]] sort = 'last_name' counter = 0 - for user in OpenSlidesUser.objects.all().order_by(sort): + for user in User.objects.all().order_by(sort): counter += 1 data.append([ counter, @@ -255,7 +255,7 @@ class ParticipantsPasswordsPDF(PDFView): data = [] participant_pdf_system_url = config["participant_pdf_system_url"] participant_pdf_welcometext = config["participant_pdf_welcometext"] - for user in OpenSlidesUser.objects.all().order_by('last_name'): + for user in User.objects.all().order_by('last_name'): cell = [] cell.append(Spacer(0, 0.8 * cm)) cell.append(Paragraph(_("Account for OpenSlides"), @@ -323,7 +323,7 @@ class ResetPasswordView(RedirectView, SingleObjectMixin, QuestionMixin): Set the Passwort for a user to his firstpassword. """ permission_required = 'participant.can_manage_participant' - model = OpenSlidesUser + model = User allow_ajax = True question = ugettext_lazy('Do you really want to reset the password?') @@ -354,7 +354,7 @@ class GroupOverviewView(ListView): permission_required = 'participant.can_manage_participant' template_name = 'participant/group_overview.html' context_object_name = 'groups' - model = OpenSlidesGroup + model = Group class GroupCreateView(CreateView): @@ -364,7 +364,7 @@ class GroupCreateView(CreateView): permission_required = 'participant.can_manage_participant' template_name = 'participant/group_edit.html' context_object_name = 'group' - model = OpenSlidesGroup + model = Group form_class = GroupForm success_url = 'user_group_overview' apply_url = 'user_group_edit' @@ -380,7 +380,7 @@ class GroupUpdateView(UpdateView): """ permission_required = 'participant.can_manage_participant' template_name = 'participant/group_edit.html' - model = OpenSlidesGroup + model = Group context_object_name = 'group' form_class = GroupForm success_url = 'user_group_overview' @@ -396,7 +396,7 @@ class GroupDeleteView(DeleteView): Delete a Group. """ permission_required = 'participant.can_manage_participant' - model = OpenSlidesGroup + model = Group url = 'user_group_overview' diff --git a/openslides/urls.py b/openslides/urls.py index d3f0e9d91..0a21c503e 100644 --- a/openslides/urls.py +++ b/openslides/urls.py @@ -55,7 +55,6 @@ for plugin in settings.INSTALLED_PLUGINS: urlpatterns += patterns('', - (r'^500/$', 'openslides.utils.views.server_error'), (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), url(r'^login/$', diff --git a/openslides/utils/person/api.py b/openslides/utils/person/api.py index 59e7d7a1a..2fe48a4ee 100644 --- a/openslides/utils/person/api.py +++ b/openslides/utils/person/api.py @@ -43,6 +43,8 @@ class Persons(object): def generate_person_id(prefix, id): + assert prefix is not None + assert id is not None if ':' in prefix: raise ValueError("':' is not allowed in a the 'person_prefix'") return "%s:%d" % (prefix, id) diff --git a/openslides/utils/person/models.py b/openslides/utils/person/models.py index 3df620dfd..cf16f7286 100644 --- a/openslides/utils/person/models.py +++ b/openslides/utils/person/models.py @@ -57,7 +57,7 @@ class PersonMixin(object): try: return generate_person_id(self.person_prefix, self.pk) except AttributeError: - raise AttributeError("%s has to have a attribute 'user_prefix'" + raise AttributeError("%s has to have a attribute 'person_prefix'" % self) def __repr__(self): From 31ca01563ebe0aee5eea41568314097a004a5fbc Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Mon, 13 Aug 2012 14:37:49 +0200 Subject: [PATCH 18/18] some corrective actions --- openslides/participant/forms.py | 8 +--- openslides/participant/models.py | 61 ++++++++++++++--------------- openslides/participant/tests.py | 3 ++ openslides/participant/views.py | 4 +- openslides/utils/person/__init__.py | 2 +- openslides/utils/person/api.py | 14 +++---- openslides/utils/person/signals.py | 2 +- 7 files changed, 46 insertions(+), 48 deletions(-) diff --git a/openslides/participant/forms.py b/openslides/participant/forms.py index 93e6b22df..4fb13a570 100644 --- a/openslides/participant/forms.py +++ b/openslides/participant/forms.py @@ -21,18 +21,14 @@ from openslides.participant.models import User, Group class UserCreateForm(forms.ModelForm, CssClassMixin): - first_name = forms.CharField(label=_("First name")) - last_name = forms.CharField(label=_("Last name")) groups = forms.ModelMultipleChoiceField( queryset=Group.objects.exclude(name__iexact='anonymous'), label=_("User groups"), required=False) - is_active = forms.BooleanField( - label=_("Active"), required=False, initial=True) class Meta: model = User fields = ('first_name', 'last_name', 'is_active', 'groups', 'category', - 'gender', 'type', 'committee', 'comment', 'firstpassword') + 'gender', 'type', 'committee', 'comment', 'default_password') class UserUpdateForm(UserCreateForm): @@ -40,7 +36,7 @@ class UserUpdateForm(UserCreateForm): model = User fields = ('username', 'first_name', 'last_name', 'is_active', 'groups', 'category', 'gender', 'type', 'committee', 'comment', - 'firstpassword') + 'default_password') class GroupForm(forms.ModelForm, CssClassMixin): diff --git a/openslides/participant/models.py b/openslides/participant/models.py index 83b484432..8daf6fcdc 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -17,7 +17,7 @@ from django.dispatch import receiver from django.utils.translation import ugettext_lazy as _, ugettext_noop from openslides.utils.person import PersonMixin -from openslides.utils.person.signals import receiv_persons +from openslides.utils.person.signals import receive_persons from openslides.config.signals import default_config_value @@ -51,27 +51,24 @@ class User(DjangoUser, PersonMixin): comment = models.TextField( null=True, blank=True, verbose_name=_('Comment'), help_text=_('Only for notes.')) - # TODO: Rename this fild to default_password - firstpassword = models.CharField( + default_password = models.CharField( max_length=100, null=True, blank=True, - verbose_name=_("First Password")) + verbose_name=_("Default password")) - save_user_object = False - - def get_name_surfix(self): + def get_name_suffix(self): return self.category - def set_name_surfix(self, value): + def set_name_suffix(self, value): self.category = value - name_surfix = property(get_name_surfix, set_name_surfix) + name_suffix = property(get_name_suffix, set_name_suffix) def reset_password(self, password=None): """ Reset the password for the user to his default-password. """ if password is None: - password = self.firstpassword + password = self.default_password self.set_password(password) self.save() @@ -90,9 +87,9 @@ class User(DjangoUser, PersonMixin): return ('user_delete', [str(self.id)]) def __unicode__(self): - name = self.get_full_name() or _("No Name yet") - if self.name_surfix: - return u"%s (%s)" % (name, self.name_surfix) + name = self.get_full_name() or self.username + if self.name_suffix: + return u"%s (%s)" % (name, self.name_suffix) return u"%s" % name class Meta: @@ -129,36 +126,38 @@ class Group(DjangoGroup, PersonMixin): return unicode(self.name) -class UsersConnecter(object): - def __init__(self, person_prefix=None, id=None): - self.person_prefix = person_prefix - self.id = id +class UsersConnector(object): + def __init__(self, person_prefix_filter=None, id_filter=None): + self.person_prefix_filter = person_prefix_filter + self.id_filter = id_filter + self.users = User.objects.all() + self.groups = Group.objects.filter(group_as_person=True) def __iter__(self): - if (not self.person_prefix or - self.person_prefix == User.person_prefix): - if self.id: - yield User.objects.get(pk=self.id) + if (not self.person_prefix_filter or + self.person_prefix_filter == User.person_prefix): + if self.id_filter: + yield users.get(pk=self.id_filter) else: - for user in User.objects.all(): + for user in self.users: yield user - if (not self.person_prefix or - self.person_prefix == Group.person_prefix): - if self.id: - yield Group.objects.filter(group_as_person=True).get(pk=self.id) + if (not self.person_prefix_filter or + self.person_prefix_filter == Group.person_prefix): + if self.id_filter: + yield groups.get(pk=self.id_filter) else: - for group in Group.objects.filter(group_as_person=True): + for group in self.groups: yield group def __getitem__(self, key): return User.objects.get(pk=key) -@receiver(receiv_persons, dispatch_uid="participant") -def receiv_persons(sender, **kwargs): - return UsersConnecter(person_prefix=kwargs['person_prefix'], - id=kwargs['id']) +@receiver(receive_persons, dispatch_uid="participant") +def receive_persons(sender, **kwargs): + return UsersConnecter(person_prefix_filter=kwargs['person_prefix_filter'], + id=kwargs['id_filter']) @receiver(default_config_value, dispatch_uid="participant_default_config") diff --git a/openslides/participant/tests.py b/openslides/participant/tests.py index bbcda6cf7..4bba1168d 100644 --- a/openslides/participant/tests.py +++ b/openslides/participant/tests.py @@ -70,4 +70,7 @@ class GroupTest(TestCase): self.assertTrue(hasattr(self.group1, 'person_id')) person_id = "group:%d" % self.group1.id self.assertEqual(self.group1.person_id, person_id) + self.assertRaises(Group.DoesNotExist) + self.group1.group_as_person = True + self.group1.save() self.assertEqual(get_person(person_id), self.group1) diff --git a/openslides/participant/views.py b/openslides/participant/views.py index 27abb541e..64538db49 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -149,8 +149,8 @@ class UserCreateView(CreateView): def manipulate_object(self, form): self.object.username = gen_username(form.cleaned_data['first_name'], form.cleaned_data['last_name']) - if not self.object.firstpassword: - self.object.firstpassword = gen_password() + if not self.object.default_password: + self.object.default_password = gen_password() class UserUpdateView(UpdateView): diff --git a/openslides/utils/person/__init__.py b/openslides/utils/person/__init__.py index e54e6258a..da896616f 100644 --- a/openslides/utils/person/__init__.py +++ b/openslides/utils/person/__init__.py @@ -10,7 +10,7 @@ :license: GNU GPL, see LICENSE for more details. """ -from openslides.utils.person.signals import receiv_persons +from openslides.utils.person.signals import receive_persons from openslides.utils.person.api import generate_person_id, get_person, Persons from openslides.utils.person.forms import PersonFormField, MultiplePersonFormField from openslides.utils.person.models import PersonField, PersonMixin diff --git a/openslides/utils/person/api.py b/openslides/utils/person/api.py index 2fe48a4ee..55b4691a8 100644 --- a/openslides/utils/person/api.py +++ b/openslides/utils/person/api.py @@ -10,16 +10,16 @@ :license: GNU GPL, see LICENSE for more details. """ -from openslides.utils.person.signals import receiv_persons +from openslides.utils.person.signals import receive_persons class Persons(object): """ 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 __init__(self, person_prefix_filter=None, id_filter=None): + self.person_prefix_filter = person_prefix_filter + self.id_filter = id_filter def __iter__(self): try: @@ -35,8 +35,8 @@ class Persons(object): 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 receiver, persons in receive_persons.send( + sender='persons', person_prefix_filter=self.person_prefix_filter, id_filter=self.id_filter): for person in persons: self._cache.append(person) yield person @@ -63,4 +63,4 @@ def get_person(person_id): except TypeError: from openslides.utils.person import EmtyPerson return EmtyPerson() - return Persons(person_prefix=person_prefix, id=id)[0] + return Persons(person_prefix_filter=person_prefix, id_filter=id)[0] diff --git a/openslides/utils/person/signals.py b/openslides/utils/person/signals.py index 5d8dba88b..32d5879b4 100644 --- a/openslides/utils/person/signals.py +++ b/openslides/utils/person/signals.py @@ -12,4 +12,4 @@ from django.dispatch import Signal -receiv_persons = Signal(providing_args=['person_prefix', 'id']) +receive_persons = Signal(providing_args=['person_prefix_filter', 'id_filter'])