OpenSlides/openslides/participant/views.py

585 lines
20 KiB
Python
Raw Normal View History

2011-07-31 10:46:29 +02:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.participant.views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Views for the participant app.
:copyright: 20112013 by OpenSlides team, see AUTHORS.
2011-07-31 10:46:29 +02:00
:license: GNU GPL, see LICENSE for more details.
"""
try:
import qrcode
except ImportError:
draw_qrcode = False
else:
draw_qrcode = True
from cStringIO import StringIO
from urllib import urlencode
2012-12-14 14:21:53 +01:00
from urlparse import parse_qs
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, LongTable, Spacer, Table, TableStyle, Image)
2011-07-31 10:46:29 +02:00
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.views import login as django_login
2011-07-31 10:46:29 +02:00
from django.core.urlresolvers import reverse
from django.shortcuts import redirect
from django.utils.translation import ugettext as _, ugettext_lazy, activate
from openslides.utils.pdf import stylesheet
from openslides.utils.template import Tab
from openslides.utils.utils import (
2013-03-15 01:34:17 +01:00
template, delete_default_permissions, html_strong)
from openslides.utils.views import (
FormView, PDFView, CreateView, UpdateView, DeleteView, PermissionMixin,
RedirectView, SingleObjectMixin, ListView, QuestionMixin, DetailView)
from openslides.config.api import config
from openslides.projector.projector import Widget
from openslides.participant.api import gen_username, gen_password, import_users
from openslides.participant.forms import (
UserCreateForm, UserUpdateForm, UsersettingsForm,
UserImportForm, GroupForm)
from openslides.participant.models import User, Group, get_protected_perm
2011-07-31 10:46:29 +02:00
2012-10-27 18:17:22 +02:00
class UserOverview(ListView):
"""
Show all participants (users).
"""
permission_required = 'participant.can_see_participant'
template_name = 'participant/overview.html'
context_object_name = 'users'
def get_queryset(self):
query = User.objects
if config['participant_sort_users_by_first_name']:
query = query.order_by('first_name')
else:
query = query.order_by('last_name')
return query.all()
def get_context_data(self, **kwargs):
2012-10-27 18:17:22 +02:00
context = super(UserOverview, self).get_context_data(**kwargs)
all_users = User.objects.count()
# context vars
context.update({
'allusers': all_users,
'request_user': self.request.user})
return context
2011-07-31 10:46:29 +02:00
2012-10-30 00:07:25 +01:00
class UserDetailView(DetailView, PermissionMixin):
2012-10-27 18:17:22 +02:00
"""
Classed based view to show a specific user in the interface.
"""
2012-10-30 00:07:25 +01:00
permission_required = 'participant.can_see_participant'
2012-10-27 18:17:22 +02:00
model = User
2012-10-30 00:07:25 +01:00
template_name = 'participant/user_detail.html'
context_object_name = 'shown_user'
2012-10-27 18:17:22 +02:00
class UserCreateView(CreateView):
2011-07-31 10:46:29 +02:00
"""
Create a new participant.
2011-07-31 10:46:29 +02:00
"""
permission_required = 'participant.can_manage_participant'
template_name = 'participant/edit.html'
model = User
context_object_name = 'edit_user'
form_class = UserCreateForm
success_url_name = 'user_overview'
def manipulate_object(self, form):
self.object.username = gen_username(
form.cleaned_data['first_name'], form.cleaned_data['last_name'])
2012-08-13 14:37:49 +02:00
if not self.object.default_password:
self.object.default_password = gen_password()
2012-08-15 11:01:38 +02:00
self.object.set_password(self.object.default_password)
def post_save(self, form):
super(UserCreateView, self).post_save(form)
# TODO: find a better solution that makes the following lines obsolete
# Background: motion.models.use_post_save adds already the registerd group
# to new user but super(..).post_save(form) removes it and sets only the
# groups selected in the form (without 'registered')
# workaround: add registered group again manually
from openslides.participant.api import get_registered_group # TODO: Test, if global import is possible
registered = get_registered_group()
self.object.groups.add(registered)
class UserUpdateView(UpdateView):
"""
Update an existing participant.
"""
permission_required = 'participant.can_manage_participant'
template_name = 'participant/edit.html'
model = User
context_object_name = 'edit_user'
form_class = UserUpdateForm
success_url_name = 'user_overview'
2011-07-31 10:46:29 +02:00
def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super(UserUpdateView, self).get_form_kwargs(*args, **kwargs)
form_kwargs.update({'request': self.request})
return form_kwargs
def manipulate_object(self, form):
self.object.username = form.cleaned_data['user_name']
def post_save(self, form):
super(UserUpdateView, self).post_save(form)
# TODO: find a better solution that makes the following lines obsolete
# Background: motion.models.use_post_save adds already the registerd group
# to new user but super(..).post_save(form) removes it and sets only the
# groups selected in the form (without 'registered')
# workaround: add registered group again manually
from openslides.participant.api import get_registered_group # TODO: Test, if global import is possible
registered = get_registered_group()
self.object.groups.add(registered)
class UserDeleteView(DeleteView):
"""
Delete an participant.
"""
permission_required = 'participant.can_manage_participant'
model = User
success_url_name = 'user_overview'
def pre_redirect(self, request, *args, **kwargs):
if self.object == self.request.user:
messages.error(request, _("You can not delete yourself."))
else:
super(UserDeleteView, self).pre_redirect(request, *args, **kwargs)
def pre_post_redirect(self, request, *args, **kwargs):
if self.object == self.request.user:
messages.error(self.request, _("You can not delete yourself."))
else:
super(UserDeleteView, self).pre_post_redirect(request, *args, **kwargs)
class SetUserStatusView(RedirectView, SingleObjectMixin):
"""
Activate or deactivate an user.
"""
permission_required = 'participant.can_manage_participant'
allow_ajax = True
url_name = 'user_overview'
model = User
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':
if self.object.user == self.request.user:
messages.error(request, _("You can not deactivate yourself."))
return
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
2011-07-31 10:46:29 +02:00
class ParticipantsListPDF(PDFView):
"""
Generate the userliste as PDF.
"""
permission_required = 'participant.can_see_participant'
filename = ugettext_lazy("Participant-list")
document_title = ugettext_lazy('List of Participants')
2011-07-31 10:46:29 +02:00
def append_to_pdf(self, story):
data = [['#', _('Title'), _('Last Name'), _('First Name'),
_('Structure level'), _('Group'), _('Committee')]]
if config['participant_sort_users_by_first_name']:
sort = 'first_name'
else:
sort = 'last_name'
counter = 0
for user in User.objects.all().order_by(sort):
counter += 1
groups = ''
for group in user.groups.all():
if group.pk != 2:
groups += "%s<br/>" % unicode(_(group.name))
data.append([
counter,
2013-04-08 21:47:33 +02:00
Paragraph(user.title, stylesheet['Tablecell']),
Paragraph(user.last_name, stylesheet['Tablecell']),
Paragraph(user.first_name, stylesheet['Tablecell']),
Paragraph(user.structure_level, stylesheet['Tablecell']),
Paragraph(groups, 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)
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)
2012-08-03 12:44:34 +02:00
def build_document(self, pdf_document, story):
pdf_document.build(story)
2011-07-31 10:46:29 +02:00
def append_to_pdf(self, story):
data = []
participant_pdf_system_url = config["participant_pdf_system_url"]
participant_pdf_welcometext = config["participant_pdf_welcometext"]
if config['participant_sort_users_by_first_name']:
sort = 'first_name'
else:
sort = 'last_name'
# create qrcode image object from system url
if draw_qrcode:
qrcode_img = qrcode.make(participant_pdf_system_url)
img_stream = StringIO()
qrcode_img.save(img_stream, 'PNG')
img_stream.seek(0)
2013-04-15 10:40:47 +02:00
size = 2 * cm
I = Image(img_stream, width=size, height=size)
for user in User.objects.all().order_by(sort):
cell = []
cell.append(Spacer(0, 0.8 * cm))
cell.append(Paragraph(_("Account for OpenSlides"),
stylesheet['Password_title']))
cell.append(Paragraph(_("for %s") % (user),
stylesheet['Password_subtitle']))
cell.append(Spacer(0, 0.5 * cm))
cell.append(Paragraph(_("User: %s") % (user.username),
stylesheet['Monotype']))
cell.append(
Paragraph(
_("Password: %s")
2012-09-11 19:30:12 +02:00
% (user.default_password), stylesheet['Monotype']))
cell.append(
Paragraph(participant_pdf_system_url, stylesheet['Monotype']))
if draw_qrcode:
cell.append(I)
cell2 = []
cell2.append(Spacer(0, 0.8 * cm))
if participant_pdf_welcometext is not None:
cell2.append(Paragraph(
participant_pdf_welcometext.replace('\r\n', '<br/>'),
stylesheet['Ballot_subtitle']))
data.append([cell, cell2])
# 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([
('LEFTPADDING', (0, 0), (0, -1), 30),
('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)
2011-07-31 10:46:29 +02:00
class UserImportView(FormView):
"""
Import Users via csv.
"""
permission_required = 'participant.can_manage_participant'
template_name = 'participant/import.html'
form_class = UserImportForm
success_url_name = 'user_overview'
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)
2012-10-28 19:59:41 +01:00
class ResetPasswordView(SingleObjectMixin, QuestionMixin, RedirectView):
"""
2012-09-11 19:30:12 +02:00
Set the Passwort for a user to his default password.
"""
permission_required = 'participant.can_manage_participant'
model = User
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])
2012-10-28 19:59:41 +01:00
def case_yes(self):
self.object.reset_password()
2012-10-28 19:59:41 +01:00
def get_success_message(self):
return _('The Password for %s was successfully reset.') % html_strong(self.object)
2012-10-27 18:17:22 +02:00
class GroupOverview(ListView):
"""
Overview over all groups.
"""
permission_required = 'participant.can_manage_participant'
template_name = 'participant/group_overview.html'
context_object_name = 'groups'
model = Group
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'
def get_context_data(self, *args, **kwargs):
context = super(GroupDetailView, self).get_context_data(*args, **kwargs)
query = User.objects
if config['participant_sort_users_by_first_name']:
query = query.order_by('first_name')
else:
query = query.order_by('last_name')
context['group_members'] = query.filter(django_user__groups__in=[context['group']])
return context
2012-08-11 11:36:55 +02:00
class GroupCreateView(CreateView):
"""
Create a new group.
"""
permission_required = 'participant.can_manage_participant'
template_name = 'participant/group_edit.html'
context_object_name = 'group'
model = Group
form_class = GroupForm
success_url_name = 'user_group_overview'
2012-08-11 11:36:55 +02:00
def get(self, request, *args, **kwargs):
delete_default_permissions()
return super(GroupCreateView, self).get(request, *args, **kwargs)
2012-08-11 11:36:55 +02:00
class GroupUpdateView(UpdateView):
"""
Update an existing group.
"""
permission_required = 'participant.can_manage_participant'
template_name = 'participant/group_edit.html'
model = Group
context_object_name = 'group'
form_class = GroupForm
success_url_name = 'user_group_overview'
2012-08-11 11:36:55 +02:00
def get(self, request, *args, **kwargs):
delete_default_permissions()
return super(GroupUpdateView, self).get(request, *args, **kwargs)
def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super(GroupUpdateView, self).get_form_kwargs(*args, **kwargs)
form_kwargs.update({'request': self.request})
return form_kwargs
class GroupDeleteView(DeleteView):
"""
Delete a group.
"""
permission_required = 'participant.can_manage_participant'
model = Group
success_url_name = 'user_group_overview'
def pre_redirect(self, request, *args, **kwargs):
if not self.is_protected_from_deleting():
super(GroupDeleteView, self).pre_redirect(request, *args, **kwargs)
def pre_post_redirect(self, request, *args, **kwargs):
if not self.is_protected_from_deleting():
super(GroupDeleteView, self).pre_post_redirect(request, *args, **kwargs)
def is_protected_from_deleting(self):
"""
Checks whether the group is protected.
"""
if self.object.pk in [1, 2]:
messages.error(request, _('You can not delete this group.'))
return True
if (not self.request.user.is_superuser and
get_protected_perm() in self.object.permissions.all() and
not Group.objects.exclude(pk=self.object.pk).filter(
permissions__in=[get_protected_perm()],
user__pk=self.request.user.pk).exists()):
messages.error(
self.request,
_('You can not delete the last group containing the permission '
'to manage participants you are in.'))
return True
return False
def login(request):
extra_content = {}
try:
admin = User.objects.get(pk=1)
2013-01-04 12:39:42 +01:00
if admin.check_password(admin.default_password):
extra_content['first_time_message'] = _(
"Installation was successfully! Use %(user)s "
"(password: %(password)s) for first login.<br>"
"<strong>Important:</strong> 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),
2012-08-14 14:19:59 +02:00
'password': html_strong(admin.default_password)}
extra_content['next'] = reverse('password_change')
except User.DoesNotExist:
pass
return django_login(request, template_name='participant/login.html', extra_context=extra_content)
2011-07-31 10:46:29 +02:00
@login_required
@template('participant/settings.html')
def user_settings(request):
"""
Edit own user account.
"""
2011-07-31 10:46:29 +02:00
if request.method == 'POST':
form_user = UsersettingsForm(request.POST, instance=request.user)
2012-07-01 16:56:01 +02:00
if form_user.is_valid():
user = form_user.save(commit=False)
user.username = form_user.cleaned_data['user_name']
user.save()
language = request.LANGUAGE_CODE = \
request.session['django_language'] = form_user.cleaned_data['language']
activate(language)
2011-07-31 10:46:29 +02:00
messages.success(request, _('User settings successfully saved.'))
else:
messages.error(request, _('Please check the form for errors.'))
else:
language = request.session.get('django_language', request.LANGUAGE_CODE)
form_user = UsersettingsForm(instance=request.user, initial={'language': language})
2011-07-31 10:46:29 +02:00
return {
'form': form_user,
2011-07-31 10:46:29 +02:00
'edituser': request.user,
}
@login_required
@template('participant/password_change.html')
def user_settings_password(request):
"""
Edit own password.
"""
if request.method == 'POST':
form = PasswordChangeForm(request.user, request.POST)
if form.is_valid():
form.save()
messages.success(request, _('Password successfully changed.'))
return redirect(reverse('dashboard'))
else:
messages.error(request, _('Please check the form for errors.'))
else:
form = PasswordChangeForm(user=request.user)
return {
'form': form,
}
def register_tab(request):
"""
Registers the participant tab.
"""
selected = request.path.startswith('/participant/')
return Tab(
title=_('Participants'),
app='participant',
url=reverse('user_overview'),
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
and a group_widget.
"""
return [get_user_widget(request), get_group_widget(request)]
def get_user_widget(request):
"""
Provides a widget with all users. This is for short activation of
user slides.
"""
return Widget(
2013-06-13 23:38:58 +02:00
request,
name='user',
2012-11-08 09:43:04 +01:00
display_name=_('Participants'),
template='participant/user_widget.html',
context={'users': User.objects.all()},
permission_required='projector.can_manage_projector',
default_column=1,
default_weight=60)
def get_group_widget(request):
"""
Provides a widget with all groups. This is for short activation of
group slides.
"""
return Widget(
2013-06-13 23:38:58 +02:00
request,
name='group',
display_name=_('Groups'),
template='participant/group_widget.html',
context={'groups': Group.objects.all()},
permission_required='projector.can_manage_projector',
default_column=1,
default_weight=70)