OpenSlides/openslides/participant/views.py
2012-11-03 08:59:34 +01:00

595 lines
20 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.participant.views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Views for the participant app.
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
# for python 2.5 support
from __future__ import with_statement
from urllib import urlencode
try:
from urlparse import parse_qs
except ImportError: # python <= 2.5 grab it from cgi
from cgi import parse_qs
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, LongTable, Spacer, Table, TableStyle)
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
from django.utils.translation import ugettext as _, ugettext_lazy
from openslides.utils.pdf import stylesheet
from openslides.utils.template import Tab
from openslides.utils.utils import (
template, decodedict, encodedict, delete_default_permissions, html_strong)
from openslides.utils.views import (
FormView, PDFView, CreateView, UpdateView, DeleteView,
RedirectView, SingleObjectMixin, ListView, QuestionMixin)
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,
UserImportForm, GroupForm, ConfigForm)
from openslides.participant.models import User, Group
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):
try:
sortfilter = encodedict(parse_qs(
self.request.COOKIES['participant_sortfilter']))
except KeyError:
sortfilter = {}
for value in ['gender', 'detail', 'type', 'committee', 'status',
'sort', '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(gender__iexact=sortfilter['gender'][0])
if 'detail' in sortfilter:
query = query.filter(detail__iexact=sortfilter['detail'][0])
if 'type' in sortfilter:
query = query.filter(type__iexact=sortfilter['type'][0])
if 'committee' in sortfilter:
query = query.filter(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
['detail', 'type', 'committee', 'comment']):
query = query.order_by(
'%s' % sortfilter['sort'][0])
else:
if config['participant_sort_users_by_first_name']:
query = query.order_by('first_name')
else:
query = query.order_by('last_name')
if 'reverse' in sortfilter:
query = query.reverse()
self.sortfilter = sortfilter
return query.all()
def get_context_data(self, **kwargs):
context = super(UserOverview, self).get_context_data(**kwargs)
all_users = User.objects.count()
# quotient of selected users and all users
if all_users > 0:
percent = self.object_list.count() * 100 / float(all_users)
else:
percent = 0
# list of all existing categories
details = [p['detail'] for p in User.objects.values('detail')
.exclude(detail='').distinct()]
# list of all existing committees
committees = [p['committee'] for p in User.objects.values('committee')
.exclude(committee='').distinct()]
context.update({
'allusers': all_users,
'percent': round(percent, 1),
'details': details,
'committees': committees,
'cookie': ['participant_sortfilter', urlencode(decodedict(self.sortfilter),
doseq=True)],
'sortfilter': self.sortfilter})
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.
"""
permission_required = 'participant.can_manage_participant'
template_name = 'participant/edit.html'
model = User
context_object_name = 'edit_user'
form_class = UserCreateForm
success_url = 'user_overview'
apply_url = 'user_edit'
def manipulate_object(self, form):
self.object.username = gen_username(form.cleaned_data['first_name'],
form.cleaned_data['last_name'])
if not self.object.default_password:
self.object.default_password = gen_password()
self.object.set_password(self.object.default_password)
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 = 'user_overview'
apply_url = 'participant_edit'
class UserDeleteView(DeleteView):
"""
Delete an participant.
"""
permission_required = 'participant.can_manage_participant'
model = User
url = 'user_overview'
class SetUserStatusView(RedirectView, SingleObjectMixin):
"""
Activate or deactivate an user.
"""
permission_required = 'participant.can_manage_participant'
allow_ajax = True
url = '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':
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
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')
def append_to_pdf(self, story):
data = [['#', _('Last Name'), _('First Name'), _('Group'), _('Type'),
_('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
data.append([
counter,
Paragraph(user.last_name, stylesheet['Tablecell']),
Paragraph(user.first_name, stylesheet['Tablecell']),
Paragraph(user.detail, 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)
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"]
if config['participant_sort_users_by_first_name']:
sort = 'first_name'
else:
sort = 'last_name'
for user in User.objects.all().order_by(sort):
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.default_password), 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', '<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([
('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)
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)
class ResetPasswordView(SingleObjectMixin, QuestionMixin, RedirectView):
"""
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])
def case_yes(self):
self.object.reset_password()
def get_success_message(self):
return _('The Password for %s was successfully reset.') % html_strong(self.object)
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 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 = '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):
"""
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 = '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):
"""
Delete a Group.
"""
permission_required = 'participant.can_manage_participant'
model = Group
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'],
'participant_sort_users_by_first_name': config['participant_sort_users_by_first_name']}
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'])
config['participant_sort_users_by_first_name'] = (
form.cleaned_data['participant_sort_users_by_first_name'])
messages.success(
self.request,
_('Participants settings successfully saved.'))
return super(Config, self).form_valid(form)
def login(request):
extra_content = {}
try:
admin = User.objects.get(pk=1)
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),
'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)
@login_required
@template('participant/settings.html')
def user_settings(request):
"""
Edit own user account.
"""
if request.method == 'POST':
form_user = UsersettingsForm(request.POST, instance=request.user)
if form_user.is_valid():
form_user.save()
messages.success(request, _('User settings successfully saved.'))
else:
messages.error(request, _('Please check the form for errors.'))
else:
form_user = UsersettingsForm(instance=request.user)
return {
'form_user': form_user,
'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('user_settings'))
else:
messages.error(request, _('Please check the form for errors.'))
else:
form = PasswordChangeForm(user=request.user)
return {
'form': 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)
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=_('My motions and elections'),
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)