diff --git a/openslides/assignment/models.py b/openslides/assignment/models.py index af67411a6..4b86b75cf 100644 --- a/openslides/assignment/models.py +++ b/openslides/assignment/models.py @@ -227,10 +227,12 @@ class Assignment(models.Model, SlideMixin): """ return the slide dict """ + polls = self.poll_set data = super(Assignment, self).slide() data['assignment'] = self data['title'] = self.name - data['polls'] = self.poll_set.filter(published=True) + data['some_polls_available'] = polls.exists() + data['polls'] = polls.filter(published=True) data['vote_results'] = self.vote_results(only_published=True) data['assignment_publish_winner_results_only'] = \ config['assignment_publish_winner_results_only'] diff --git a/openslides/assignment/templates/assignment/overview.html b/openslides/assignment/templates/assignment/overview.html index 2d9e3b8e0..717ff1538 100644 --- a/openslides/assignment/templates/assignment/overview.html +++ b/openslides/assignment/templates/assignment/overview.html @@ -31,7 +31,12 @@ {{ assignment }} - {{ assignment.candidates|length }} / {{ assignment.posts }} + + {{ assignment.posts }} {% trans "posts" %} / {{ assignment.elected|length }} {% trans "elected" %} + {% if assignment.status != 'fin' %} + / {{ assignment.candidates|length }} {% trans "candidates" %} + {% endif %} + {{ assignment.get_status_display }} diff --git a/openslides/assignment/templates/assignment/view.html b/openslides/assignment/templates/assignment/view.html index 7616f6a63..2f46fa81f 100644 --- a/openslides/assignment/templates/assignment/view.html +++ b/openslides/assignment/templates/assignment/view.html @@ -88,18 +88,36 @@ {% endif %} {% endif %} + +

{% trans "Elected Candidates" %}

+ + {% if perms.assignment.can_manage_assignments %}

{% trans "Blocked Candidates" %}

{% endif %} +

{% trans "Election results" %}

{% if polls.exists %} diff --git a/openslides/assignment/templates/projector/Assignment.html b/openslides/assignment/templates/projector/Assignment.html index 8a5c50b0e..8cf59860c 100644 --- a/openslides/assignment/templates/projector/Assignment.html +++ b/openslides/assignment/templates/projector/Assignment.html @@ -122,6 +122,8 @@ + {% elif some_polls_available %} + {% trans "Vote results are not published yet." %} {% elif assignment.candidates %} {% trans "No ballots available." %} {% endif %} diff --git a/openslides/assignment/views.py b/openslides/assignment/views.py index 0c1e1f969..1db7fa849 100644 --- a/openslides/assignment/views.py +++ b/openslides/assignment/views.py @@ -542,7 +542,8 @@ class AssignmentPollPDF(PDFView): ballot_string = _("%d. ballot") % self.poll.get_ballot() candidate_string = ungettext("%d candidate", "%d candidates", len(options)) % len(options) - available_posts_string = _("%d available posts") % self.poll.assignment.posts + available_posts_string = ungettext("%d available post", "%d available posts", + self.poll.assignment.posts) % self.poll.assignment.posts cell.append(Paragraph("%s, %s, %s" % (ballot_string, candidate_string, available_posts_string), stylesheet['Ballot_description'])) cell.append(Spacer(0, 0.4 * cm)) diff --git a/openslides/participant/models.py b/openslides/participant/models.py index 851be38d0..36260055a 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -78,14 +78,17 @@ class User(DjangoUser, PersonMixin, Person): self.save() @models.permalink - def get_absolute_url(self, link='edit'): + def get_absolute_url(self, link='view'): """ Return the URL to this user. link can be: + * view * edit * delete """ + if link == 'view': + return ('user_view', [str(self.id)]) if link == 'edit': return ('user_edit', [str(self.id)]) if link == 'delete': @@ -114,14 +117,17 @@ class Group(DjangoGroup, PersonMixin, Person): description = models.TextField(blank=True, verbose_name=_("Description")) @models.permalink - def get_absolute_url(self, link='edit'): + def get_absolute_url(self, link='view'): """ Return the URL to this user. link can be: + * view * edit * delete """ + if link == 'view': + return ('user_group_view', [str(self.id)]) if link == 'edit': return ('user_group_edit', [str(self.id)]) if link == 'delete': diff --git a/openslides/participant/templates/participant/base_participant.html b/openslides/participant/templates/participant/base_participant.html index bb726731d..d9fc3adeb 100644 --- a/openslides/participant/templates/participant/base_participant.html +++ b/openslides/participant/templates/participant/base_participant.html @@ -27,4 +27,51 @@
  • {% trans 'First time passwords as PDF' %}
  • {% endif %} + + {# second submenu #} + {% if shown_user %} +
    +

    {{ shown_user.clean_name }}

    + + {% elif group %} +
    +

    {{ group.name }}

    + + {% endif %} + {% endblock %} diff --git a/openslides/participant/templates/participant/group_detail.html b/openslides/participant/templates/participant/group_detail.html new file mode 100644 index 000000000..e4896ae17 --- /dev/null +++ b/openslides/participant/templates/participant/group_detail.html @@ -0,0 +1,22 @@ +{% extends "participant/base_participant.html" %} + +{% load i18n %} +{% load tags %} + +{% block title %}{{ block.super }} – {{ group }}{% endblock %} + +{% block content %} + +

    {{ group }}

    + +

    {{ group.description }}

    + +

    {% trans "Members" %}

    + +{% for member in group.user_set.all %} +

    {{ member }}

    +{% empty %} +

    {% trans "This group has not any members." %}

    +{% endfor %} + +{% endblock %} diff --git a/openslides/participant/templates/participant/group_overview.html b/openslides/participant/templates/participant/group_overview.html index 3745308fb..c226f7ec1 100644 --- a/openslides/participant/templates/participant/group_overview.html +++ b/openslides/participant/templates/participant/group_overview.html @@ -2,6 +2,7 @@ {% load i18n %} {% load staticfiles %} +{% load tags %} {% block title %}{{ block.super }} – {% trans "User groups" %}{% endblock %} @@ -14,7 +15,7 @@ {% for group in groups %} - {{ group.name }} + {{ group.name }} {% if group.name != 'Anonymous' %} diff --git a/openslides/participant/templates/participant/group_widget.html b/openslides/participant/templates/participant/group_widget.html new file mode 100644 index 000000000..50cd7e6b6 --- /dev/null +++ b/openslides/participant/templates/participant/group_widget.html @@ -0,0 +1,26 @@ +{% load i18n %} +{% load tags %} + + diff --git a/openslides/participant/templates/participant/overview.html b/openslides/participant/templates/participant/overview.html index f950bc302..0066b1dbe 100644 --- a/openslides/participant/templates/participant/overview.html +++ b/openslides/participant/templates/participant/overview.html @@ -76,7 +76,7 @@ {% for user in users %} - {{ user.first_name }} + {{ user.first_name }} {{ user.last_name }} {{ user.detail }} {{ user.get_type_display }} diff --git a/openslides/participant/templates/participant/personal_info_widget.html b/openslides/participant/templates/participant/personal_info_widget.html new file mode 100644 index 000000000..5b300a599 --- /dev/null +++ b/openslides/participant/templates/participant/personal_info_widget.html @@ -0,0 +1,49 @@ +{% load i18n %} +{% load tags %} + + + +{% if config_motion_min_supporters %} +
    + +{% endif %} + +
    + diff --git a/openslides/participant/templates/participant/user_detail.html b/openslides/participant/templates/participant/user_detail.html new file mode 100644 index 000000000..b7a0e50ff --- /dev/null +++ b/openslides/participant/templates/participant/user_detail.html @@ -0,0 +1,52 @@ +{% extends "participant/base_participant.html" %} + +{% load i18n %} +{% load tags %} + +{% block title %}{{ block.super }} – {{ shown_user }}{% endblock %} + +{% block content %} + +

    {{ shown_user }}

    + +

    {{ shown_user.email }}

    + +

    {% trans "Groups" %}

    +

    + {% for group in shown_user.groups.all %} + {{ group }}, + {% empty %} + {% trans "The participant is not member of any group." %} + {% endfor %} +

    + +{% if shown_user.get_gender_display %} +

    {% trans "Gender" %}

    +

    {{ shown_user.get_gender_display }}

    +{% endif %} + +{% if shown_user.get_type_display %} +

    {% trans "Type" %}

    +

    {{ shown_user.get_type_display }}

    +{% endif %} + +{% if shown_user.committee %} +

    {% trans "Committee" %}

    +

    {{ shown_user.committee }}

    +{% endif %} + +{% if perms.participant.can_manage_participant %} + {% if shown_user.comment %} +

    {% trans "Comment" %}

    +

    {{ shown_user.comment }}

    + {% endif %} + +

    {% trans "Last Login" %}

    + {% if shown_user.last_login > shown_user.date_joined %} +

    {{ shown_user.last_login }}

    + {% else %} +

    {% trans "The participant was not logged in yet." %}

    + {% endif %} +{% endif %} + +{% endblock %} diff --git a/openslides/participant/templates/participant/user_widget.html b/openslides/participant/templates/participant/user_widget.html new file mode 100644 index 000000000..e8657cb17 --- /dev/null +++ b/openslides/participant/templates/participant/user_widget.html @@ -0,0 +1,24 @@ +{% load i18n %} +{% load tags %} + + diff --git a/openslides/participant/urls.py b/openslides/participant/urls.py index f55c30756..e3ac2e553 100644 --- a/openslides/participant/urls.py +++ b/openslides/participant/urls.py @@ -14,14 +14,16 @@ from django.conf.urls.defaults import url, patterns from django.core.urlresolvers import reverse from openslides.participant.views import ( - ParticipantsListPDF, ParticipantsPasswordsPDF, Overview, UserCreateView, - UserUpdateView, UserDeleteView, SetUserStatusView, UserImportView, - ResetPasswordView, GroupOverviewView, GroupCreateView, GroupUpdateView, - GroupDeleteView) + UserOverview, UserCreateView, UserDetailView, UserUpdateView, + UserDeleteView, ResetPasswordView, SetUserStatusView, UserImportView, + GroupOverview, GroupCreateView, GroupDetailView, GroupUpdateView, GroupDeleteView, + ParticipantsListPDF, ParticipantsPasswordsPDF) -urlpatterns = patterns('openslides.participant.views', +urlpatterns = patterns('', + + # User url(r'^$', - Overview.as_view(), + UserOverview.as_view(), name='user_overview', ), @@ -30,6 +32,11 @@ urlpatterns = patterns('openslides.participant.views', name='user_new', ), + url(r'^(?P\d+)/$', + UserDetailView.as_view(), + name='user_view', + ), + url(r'^(?P\d+)/edit/$', UserUpdateView.as_view(), name='user_edit', @@ -45,12 +52,6 @@ urlpatterns = patterns('openslides.participant.views', name='user_reset_password', ), - 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'}, @@ -63,13 +64,20 @@ urlpatterns = patterns('openslides.participant.views', name='user_status_deactivate', ), + url(r'^(?P\d+)/status/toggle/$', + SetUserStatusView.as_view(), + {'action': 'toggle'}, + name='user_status_toggle', + ), + url(r'^import/$', UserImportView.as_view(), name='user_import', ), + # Group url(r'^group/$', - GroupOverviewView.as_view(), + GroupOverview.as_view(), name='user_group_overview', ), @@ -78,6 +86,11 @@ urlpatterns = patterns('openslides.participant.views', name='user_group_new', ), + url(r'^group/(?P\d+)/$', + GroupDetailView.as_view(), + name='user_group_view', + ), + url(r'^group/(?P\d+)/edit/$', GroupUpdateView.as_view(), name='user_group_edit', @@ -88,6 +101,7 @@ urlpatterns = patterns('openslides.participant.views', name='user_group_delete', ), + # PDF url(r'^print/$', ParticipantsListPDF.as_view(), name='user_print', diff --git a/openslides/participant/views.py b/openslides/participant/views.py index 99665a469..d47271f1a 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -44,6 +44,11 @@ from openslides.utils.views import ( from openslides.config.models import config +from openslides.projector.projector import Widget + +from openslides.motion.models import Motion +from openslides.assignment.models import Assignment + from openslides.participant.api import gen_username, gen_password, import_users from openslides.participant.forms import ( UserCreateForm, UserUpdateForm, UsersettingsForm, @@ -51,7 +56,7 @@ from openslides.participant.forms import ( from openslides.participant.models import User, Group -class Overview(ListView): +class UserOverview(ListView): """ Show all participants (users). """ @@ -109,7 +114,7 @@ class Overview(ListView): return query.all() def get_context_data(self, **kwargs): - context = super(Overview, self).get_context_data(**kwargs) + context = super(UserOverview, self).get_context_data(**kwargs) all_users = User.objects.count() @@ -137,6 +142,27 @@ class Overview(ListView): return context +from openslides.utils.views import DetailView, PermissionMixin +class UserDetailView(DetailView, PermissionMixin): + """ + Classed based view to show a specific user in the interface. + """ + permission_required = 'participant.can_see_participant' + model = User + template_name = 'participant/user_detail.html' + context_object_name = 'shown_user' + + +class GroupDetailView(DetailView, PermissionMixin): + """ + Classed based view to show a specific group in the interface. + """ + permission_required = 'participant.can_manage_participant' + model = Group + template_name = 'participant/group_detail.html' + context_object_name = 'group' + + class UserCreateView(CreateView): """ Create a new participant. @@ -352,7 +378,7 @@ class ResetPasswordView(SingleObjectMixin, QuestionMixin, RedirectView): return _('The Password for %s was successfully reset.') % html_strong(self.object) -class GroupOverviewView(ListView): +class GroupOverview(ListView): """ Overview over all groups. """ @@ -506,3 +532,63 @@ def register_tab(request): permission=request.user.has_perm('participant.can_see_participant') or request.user.has_perm('participant.can_manage_participant'), selected=selected) + + +def get_widgets(request): + """ + Returns all widgets of the participant app. This is a user_widget, a + group_widget and a personal_info_widget. + """ + return [ + get_personal_info_widget(request), + get_user_widget(request), + get_group_widget(request)] + + +def get_personal_info_widget(request): + """ + Provides a widget for personal info. It shows your submitted motions + and where you are supporter or candidate. + """ + personal_info_context = { + 'submitted_motions': Motion.objects.filter(submitter=request.user), + 'config_motion_min_supporters': config['motion_min_supporters'], + 'supported_motions': Motion.objects.filter(motionsupporter=request.user), + 'assignments': Assignment.objects.filter( + assignmentcandidate__person=request.user, + assignmentcandidate__blocked=False),} + return Widget( + name='personal_info', + display_name=_('On You'), + template='participant/personal_info_widget.html', + context=personal_info_context, + permission_required=None, + default_column=1) + + +def get_user_widget(request): + """ + Provides a widget with all users. This is for short activation of + user slides. + """ + return Widget( + name='user', + display_name=_('Users'), + template='participant/user_widget.html', + context={'users': User.objects.all(),}, + permission_required='projector.can_manage_projector', + default_column=1) + + +def get_group_widget(request): + """ + Provides a widget with all groups. This is for short activation of + group slides. + """ + return Widget( + name='group', + display_name=_('Groups'), + template='participant/group_widget.html', + context={'groups': Group.objects.all(),}, + permission_required='projector.can_manage_projector', + default_column=1) diff --git a/openslides/projector/templates/projector/static_info_widget.html b/openslides/projector/templates/projector/static_info_widget.html new file mode 100644 index 000000000..b99a5cbd9 --- /dev/null +++ b/openslides/projector/templates/projector/static_info_widget.html @@ -0,0 +1 @@ +{{ welcometext|safe|linebreaks }}