from django.contrib import messages
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.hashers import make_password
from django.contrib.auth.views import login as django_login
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _, ugettext_lazy, activate
from openslides.config.api import config
from openslides.utils.utils import delete_default_permissions, html_strong
from openslides.utils.views import (
CreateView, CSVImportView, DeleteView, DetailView, FormView, ListView,
PDFView, PermissionMixin, QuestionView, RedirectView, SingleObjectMixin,
UpdateView, LoginMixin)
from openslides.utils.exceptions import OpenSlidesError
from .api import gen_password, gen_username, get_protected_perm
from .csv_import import import_users
from .forms import (GroupForm, UserCreateForm, UserMultipleCreateForm,
UsersettingsForm, UserUpdateForm)
from .models import Group, User
from .pdf import users_to_pdf, users_passwords_to_pdf
class UserListView(ListView):
"""
Show all users.
"""
required_permission = 'users.can_see'
context_object_name = 'users'
def get_queryset(self):
query = User.objects
if config['users_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):
context = super().get_context_data(**kwargs)
all_users = User.objects.count()
# context vars
context.update({
'allusers': all_users,
'request_user': self.request.user})
return context
class UserDetailView(DetailView, PermissionMixin):
"""
Classed based view to show a specific user in the interface.
"""
required_permission = 'users.can_see'
model = User
context_object_name = 'shown_user'
class UserCreateView(CreateView):
"""
Create a new user.
"""
required_permission = 'users.can_manage'
model = User
context_object_name = 'edit_user'
form_class = UserCreateForm
success_url_name = 'user_list'
url_name_args = []
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)
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.users.api import get_registered_group # TODO: Test, if global import is possible
registered = get_registered_group()
self.object.groups.add(registered)
class UserMultipleCreateView(FormView):
"""
View to create multiple users at once using a big text field.
Sets the password with md5. It is the same password as in the
default_password field in cleartext. A stronger password hasher is used,
when the password is changed by the user.
"""
required_permission = 'users.can_manage'
template_name = 'users/user_form_multiple.html'
form_class = UserMultipleCreateForm
success_url_name = 'user_list'
def form_valid(self, form):
# TODO: Use bulk_create
for number, line in enumerate(form.cleaned_data['users_block'].splitlines()):
names_list = line.split()
first_name = ' '.join(names_list[:-1])
last_name = names_list[-1]
username = gen_username(first_name, last_name)
default_password = gen_password()
User.objects.create(
username=username,
first_name=first_name,
last_name=last_name,
default_password=default_password,
password=make_password(default_password, '', 'md5'))
messages.success(self.request, _('%(number)d users successfully created.') % {'number': number + 1})
return super(UserMultipleCreateView, self).form_valid(form)
class UserUpdateView(UpdateView):
"""
Update an existing users.
"""
required_permission = 'users.can_manage'
model = User
context_object_name = 'edit_user'
form_class = UserUpdateForm
success_url_name = 'user_list'
url_name_args = []
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 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.users.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 a user.
"""
required_permission = 'users.can_manage'
model = User
success_url_name = 'user_list'
url_name_args = []
def pre_redirect(self, request, *args, **kwargs):
if self.get_object() == self.request.user:
messages.error(request, _("You can not delete yourself."))
else:
super().pre_redirect(request, *args, **kwargs)
def pre_post_redirect(self, request, *args, **kwargs):
if self.get_object() == self.request.user:
messages.error(self.request, _("You can not delete yourself."))
else:
super().pre_post_redirect(request, *args, **kwargs)
class SetUserStatusView(SingleObjectMixin, RedirectView):
"""
Activate or deactivate an user.
"""
required_permission = 'users.can_manage'
allow_ajax = True
url_name = 'user_list'
url_name_args = []
model = User
def pre_redirect(self, request, *args, **kwargs):
action = kwargs['action']
if action == 'activate':
self.get_object().is_active = True
elif action == 'deactivate':
if self.get_object().user == self.request.user:
messages.error(request, _("You can not deactivate yourself."))
else:
self.get_object().is_active = False
self.get_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.get_object().is_active
return context
class UsersListPDF(PDFView):
"""
Generate the userliste as PDF.
"""
required_permission = 'users.can_see'
filename = ugettext_lazy("user-list")
document_title = ugettext_lazy('List of Users')
def append_to_pdf(self, pdf):
"""
Append PDF objects.
"""
users_to_pdf(pdf)
class UsersPasswordsPDF(PDFView):
"""
Generate the access data welcome paper for all users as PDF.
"""
required_permission = 'users.can_manage'
filename = ugettext_lazy("User-access-data")
top_space = 0
def build_document(self, pdf_document, story):
pdf_document.build(story)
def append_to_pdf(self, pdf):
"""
Append PDF objects.
"""
users_passwords_to_pdf(pdf)
class UserCSVImportView(CSVImportView):
"""
Import users via CSV.
"""
import_function = staticmethod(import_users)
required_permission = 'users.can_manage'
success_url_name = 'user_list'
template_name = 'users/user_form_csv_import.html'
class ResetPasswordView(SingleObjectMixin, QuestionView):
"""
Set the Passwort for a user to his default password.
"""
required_permission = 'users.can_manage'
model = User
allow_ajax = True
question_message = ugettext_lazy('Do you really want to reset the password?')
def get_redirect_url(self, **kwargs):
return reverse('user_edit', args=[self.get_object().id])
def on_clicked_yes(self):
self.get_object().reset_password()
self.get_object().save()
def get_final_message(self):
return _('The Password for %s was successfully reset.') % html_strong(self.get_object())
class GroupListView(ListView):
"""
Overview over all groups.
"""
required_permission = 'users.can_manage'
template_name = 'users/group_list.html'
context_object_name = 'groups'
model = Group
class GroupDetailView(DetailView, PermissionMixin):
"""
Classed based view to show a specific group in the interface.
"""
required_permission = 'users.can_manage'
model = Group
template_name = 'users/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['users_sort_users_by_first_name']:
query = query.order_by('first_name')
else:
query = query.order_by('last_name')
context['group_members'] = query.filter(groups__in=[context['group']])
return context
class GroupCreateView(CreateView):
"""
Create a new group.
"""
required_permission = 'users.can_manage'
template_name = 'users/group_form.html'
context_object_name = 'group'
model = Group
form_class = GroupForm
success_url_name = 'group_list'
url_name_args = []
def get(self, request, *args, **kwargs):
delete_default_permissions()
return super(GroupCreateView, self).get(request, *args, **kwargs)
def get_apply_url(self):
"""
Returns the url when the user clicks on 'apply'.
"""
return self.get_url('group_update', args=[self.object.pk])
class GroupUpdateView(UpdateView):
"""
Update an existing group.
"""
required_permission = 'users.can_manage'
template_name = 'users/group_form.html'
model = Group
context_object_name = 'group'
form_class = GroupForm
url_name_args = []
success_url_name = 'group_list'
def get(self, request, *args, **kwargs):
delete_default_permissions()
return super().get(request, *args, **kwargs)
def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super().get_form_kwargs(*args, **kwargs)
form_kwargs.update({'request': self.request})
return form_kwargs
def get_apply_url(self):
"""
Returns the url when the user clicks on 'apply'.
"""
return self.get_url('group_update', args=[self.object.pk])
class GroupDeleteView(DeleteView):
"""
Delete a group.
"""
required_permission = 'users.can_manage'
model = Group
success_url_name = 'group_list'
question_url_name = 'group_detail'
url_name_args = []
def pre_redirect(self, request, *args, **kwargs):
if not self.is_protected_from_deleting():
super().pre_redirect(request, *args, **kwargs)
def pre_post_redirect(self, request, *args, **kwargs):
if not self.is_protected_from_deleting():
super().pre_post_redirect(request, *args, **kwargs)
def is_protected_from_deleting(self):
"""
Checks whether the group is protected.
"""
if self.get_object().pk in [1, 2]:
messages.error(self.request, _('You can not delete this group.'))
return True
if (not self.request.user.is_superuser and
get_protected_perm() in self.get_object().permissions.all() and
not Group.objects.exclude(pk=self.get_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 users you are in.'))
return True
return False
def get_url_name_args(self):
try:
answer = self.get_answer()
except OpenSlidesError:
answer = 'no'
if self.request.method == 'POST' and answer != 'no':
return []
else:
return [self.object.pk]
def login(request):
extra_content = {}
try:
admin = User.objects.get(pk=1)
if admin.check_password(admin.default_password):
user_data = {
'user': html_strong(admin.username),
'password': html_strong(admin.default_password)}
extra_content['first_time_message'] = _(
"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_data
extra_content['next'] = reverse('password_change')
except User.DoesNotExist:
pass
return django_login(request, template_name='users/login.html', extra_context=extra_content)
class UserSettingsView(LoginMixin, UpdateView):
model = User
form_class = UsersettingsForm
success_url_name = 'user_settings'
url_name_args = []
template_name = 'users/settings.html'
def get_initial(self):
initial = super().get_initial()
initial['language'] = self.request.session.get('django_language', self.request.LANGUAGE_CODE)
return initial
def form_valid(self, form):
self.request.LANGUAGE_CODE = self.request.session['django_language'] = form.cleaned_data['language']
activate(self.request.LANGUAGE_CODE)
return super().form_valid(form)
def get_object(self):
return self.request.user
class UserPasswordSettingsView(LoginMixin, FormView):
form_class = PasswordChangeForm
success_url_name = 'core_dashboard'
template_name = 'users/password_change.html'
def form_valid(self, form):
form.save()
messages.success(self.request, _('Password successfully changed.'))
return super().form_valid(form)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs