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" %}
+
+ {% for person in assignment.elected %}
+ -
+ {{ person }}
+ {% if perms.assignment.can_manage_assignment %}
+ {% if assignment.status == "sea" or assignment.status == "vot" %}
+
+ {% endif %}
+ {% endif %}
+
+ {% empty %}
+ - {% trans "There are no elected candidates available." %}
+ {% endfor %}
+
+
{% if perms.assignment.can_manage_assignments %}
{% trans "Blocked Candidates" %}
- {% for candidate in blocked_candidates %}
+ {% for person in blocked_candidates %}
-
- {{ candidate }}
+ {{ person }}
{% empty %}
- - {% trans "There are no blocked candidates." %}
+ - {% trans "There are no blocked candidates available." %}
{% endfor %}
{% 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 %}
+
+
+{% for group in groups %}
+ {% if group.name != 'Anonymous' %}
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ group }}
+
+ {% endif %}
+{% empty %}
+ - {% trans 'No groups available.' %}
+{% endfor %}
+
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 %}
+
+
+ {% trans "You submitted the following motions:" %}
+ {% for motion in submitted_motions %}
+ -
+ {{ motion.public_version.title }}
+ ({% trans "motion" %}
+ {% if motion.number %}
+ {{ motion.number }})
+ {% else %}
+ [{% trans "no number" %}])
+ {% endif %}
+
+ {% empty %}
+ - {% trans "Currently none" %}
+ {% endfor %}
+
+
+{% if config_motion_min_supporters %}
+
+
+ {% trans "You support the following motions:" %}
+ {% for motion in supported_motions %}
+ -
+ {{ motion.public_version.title }}
+ ({% trans "motion" %}
+ {% if motion.number %}
+ {{ motion.number }})
+ {% else %}
+ [{% trans "no number" %}])
+ {% endif %}
+
+ {% empty %}
+ - {% trans "Currently none" %}
+ {% endfor %}
+
+{% endif %}
+
+
+
+ {% trans "You are candidate in the following assignments:" %}
+ {% for assignment in assignments %}
+ - {{ assignment }}
+ {% empty %}
+ - {% trans "Currently none" %}
+ {% endfor %}
+
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 %}
+
+
+{% for user in users %}
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ user }}
+
+{% empty %}
+ - {% trans 'No users available.' %}
+{% endfor %}
+
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 }}