Add keyword arguments. Update view resolutions and URL parameters to the '_id' form. Remove code from create and edit subclasses and user the super class error checking. Cache the view object instead of using .get_object() every time. Movernize tests. Update views to comply with MERCs 5 and 6. Split UserTestMixin into mixins for Groups and Users tests. Add super delete and detail tests. Remove redundant superuser filtering from views. Add transactions to views that also commit events. Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
468 lines
15 KiB
Python
468 lines
15 KiB
Python
from __future__ import absolute_import, unicode_literals
|
|
|
|
from django.contrib import messages
|
|
from django.contrib.auth import get_user_model
|
|
from django.contrib.auth.forms import SetPasswordForm
|
|
from django.contrib.auth.models import Group
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.db import transaction
|
|
from django.http import HttpResponseRedirect
|
|
from django.shortcuts import get_object_or_404
|
|
from django.template import RequestContext
|
|
from django.urls import reverse, reverse_lazy
|
|
from django.utils.translation import ungettext, ugettext_lazy as _
|
|
|
|
from mayan.apps.acls.models import AccessControlList
|
|
from mayan.apps.common.generics import (
|
|
AssignRemoveView, MultipleObjectConfirmActionView,
|
|
MultipleObjectFormActionView, SingleObjectCreateView,
|
|
SingleObjectDeleteView, SingleObjectDetailView, SingleObjectEditView,
|
|
SingleObjectListView
|
|
)
|
|
from mayan.apps.common.mixins import ExternalObjectMixin
|
|
|
|
from .events import (
|
|
event_group_created, event_group_edited, event_user_created,
|
|
event_user_edited
|
|
)
|
|
from .forms import UserForm
|
|
from .icons import icon_group_setup, icon_user_setup
|
|
from .links import link_group_create, link_user_create
|
|
from .permissions import (
|
|
permission_group_create, permission_group_delete, permission_group_edit,
|
|
permission_group_view, permission_user_create, permission_user_delete,
|
|
permission_user_edit, permission_user_view
|
|
)
|
|
|
|
|
|
class CurrentUserDetailsView(SingleObjectDetailView):
|
|
fields = (
|
|
'username', 'first_name', 'last_name', 'email', 'last_login',
|
|
'date_joined', 'groups'
|
|
)
|
|
|
|
def get_object(self):
|
|
return self.request.user
|
|
|
|
def get_extra_context(self, **kwargs):
|
|
return {
|
|
'object': None,
|
|
'title': _('Current user details'),
|
|
}
|
|
|
|
|
|
class CurrentUserEditView(SingleObjectEditView):
|
|
extra_context = {'object': None, 'title': _('Edit current user details')}
|
|
form_class = UserForm
|
|
post_action_redirect = reverse_lazy(
|
|
viewname='user_management:current_user_details'
|
|
)
|
|
|
|
def get_object(self):
|
|
return self.request.user
|
|
|
|
|
|
class GroupCreateView(SingleObjectCreateView):
|
|
extra_context = {'title': _('Create new group')}
|
|
fields = ('name',)
|
|
model = Group
|
|
post_action_redirect = reverse_lazy(viewname='user_management:group_list')
|
|
view_permission = permission_group_create
|
|
|
|
def form_valid(self, form):
|
|
with transaction.atomic():
|
|
result = super(GroupCreateView, self).form_valid(form=form)
|
|
event_group_created.commit(
|
|
actor=self.request.user, target=self.object
|
|
)
|
|
return result
|
|
|
|
|
|
class GroupDeleteView(SingleObjectDeleteView):
|
|
model = Group
|
|
object_permission = permission_group_delete
|
|
pk_url_kwarg = 'group_id'
|
|
post_action_redirect = reverse_lazy(viewname='user_management:group_list')
|
|
|
|
def get_extra_context(self):
|
|
return {
|
|
'object': self.object,
|
|
'title': _('Delete the group: %s?') % self.object,
|
|
}
|
|
|
|
|
|
class GroupEditView(SingleObjectEditView):
|
|
fields = ('name',)
|
|
model = Group
|
|
object_permission = permission_group_edit
|
|
pk_url_kwarg = 'group_id'
|
|
post_action_redirect = reverse_lazy(viewname='user_management:group_list')
|
|
|
|
def form_valid(self, form):
|
|
with transaction.atomic():
|
|
result = super(GroupEditView, self).form_valid(form=form)
|
|
event_group_edited.commit(
|
|
actor=self.request.user, target=self.object
|
|
)
|
|
return result
|
|
|
|
def get_extra_context(self):
|
|
return {
|
|
'object': self.object,
|
|
'title': _('Edit group: %s') % self.object,
|
|
}
|
|
|
|
|
|
class GroupListView(SingleObjectListView):
|
|
model = Group
|
|
object_permission = permission_group_view
|
|
|
|
def get_extra_context(self):
|
|
return {
|
|
'hide_object': True,
|
|
'no_results_icon': icon_group_setup,
|
|
'no_results_main_link': link_group_create.resolve(
|
|
context=RequestContext(request=self.request)
|
|
),
|
|
'no_results_text': _(
|
|
'User groups are organizational units. They should '
|
|
'mirror the organizational units of your organization. '
|
|
'Groups can\'t be used for access control. Use roles '
|
|
'for permissions and access control, add groups to '
|
|
'them.'
|
|
),
|
|
'no_results_title': _('There are no user groups'),
|
|
'title': _('Groups'),
|
|
}
|
|
|
|
|
|
class GroupMembersView(ExternalObjectMixin, AssignRemoveView):
|
|
decode_content_type = True
|
|
external_object_class = Group
|
|
external_object_permission = permission_group_edit
|
|
external_object_pk_url_kwarg = 'group_id'
|
|
left_list_title = _('Available users')
|
|
object_permission = permission_group_edit
|
|
right_list_title = _('Users in group')
|
|
|
|
@staticmethod
|
|
def generate_choices(choices):
|
|
results = []
|
|
for choice in choices:
|
|
ct = ContentType.objects.get_for_model(model=choice)
|
|
label = choice.get_full_name() if choice.get_full_name() else choice
|
|
|
|
results.append(('%s,%s' % (ct.model, choice.pk), '%s' % (label)))
|
|
|
|
# Sort results by the label not the key value
|
|
return sorted(results, key=lambda x: x[1])
|
|
|
|
def add(self, item):
|
|
self.object.user_set.add(item)
|
|
|
|
def dispatch(self, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
return super(GroupMembersView, self).dispatch(*args, **kwargs)
|
|
|
|
def get_extra_context(self):
|
|
return {
|
|
'object': self.object,
|
|
'title': _('Users of group: %s') % self.object
|
|
}
|
|
|
|
def get_object(self):
|
|
return self.get_external_object()
|
|
|
|
def left_list(self):
|
|
queryset = AccessControlList.objects.restrict_queryset(
|
|
permission=permission_user_edit,
|
|
queryset=get_user_model().objects.exclude(
|
|
groups=self.object
|
|
).exclude(is_staff=True).exclude(is_superuser=True),
|
|
user=self.request.user
|
|
)
|
|
|
|
return GroupMembersView.generate_choices(choices=queryset)
|
|
|
|
def right_list(self):
|
|
queryset = AccessControlList.objects.restrict_queryset(
|
|
permission=permission_user_edit,
|
|
queryset=self.object.user_set.all(),
|
|
user=self.request.user
|
|
)
|
|
|
|
return GroupMembersView.generate_choices(choices=queryset)
|
|
|
|
def remove(self, item):
|
|
self.object.user_set.remove(item)
|
|
|
|
|
|
class UserCreateView(SingleObjectCreateView):
|
|
extra_context = {
|
|
'title': _('Create new user'),
|
|
}
|
|
form_class = UserForm
|
|
view_permission = permission_user_create
|
|
|
|
def form_valid(self, form):
|
|
with transaction.atomic():
|
|
super(UserCreateView, self).form_valid(form=form)
|
|
event_user_created.commit(
|
|
actor=self.request.user, target=self.object
|
|
)
|
|
|
|
return HttpResponseRedirect(
|
|
reverse(
|
|
viewname='user_management:user_set_password',
|
|
kwargs={'user_id': self.object.pk}
|
|
)
|
|
)
|
|
|
|
|
|
class UserDeleteView(MultipleObjectConfirmActionView):
|
|
object_permission = permission_user_delete
|
|
pk_url_kwarg = 'user_id'
|
|
source_queryset = get_user_model().objects.filter(
|
|
is_superuser=False, is_staff=False
|
|
)
|
|
success_message = _('User delete request performed on %(count)d user')
|
|
success_message_plural = _(
|
|
'User delete request performed on %(count)d users'
|
|
)
|
|
|
|
def get_extra_context(self):
|
|
queryset = self.get_object_list()
|
|
|
|
result = {
|
|
'title': ungettext(
|
|
singular='Delete user',
|
|
plural='Delete users',
|
|
number=queryset.count()
|
|
)
|
|
}
|
|
|
|
if queryset.count() == 1:
|
|
result.update(
|
|
{
|
|
'object': queryset.first(),
|
|
'title': _('Delete user: %s') % queryset.first()
|
|
}
|
|
)
|
|
|
|
return result
|
|
|
|
def object_action(self, form, instance):
|
|
try:
|
|
instance.delete()
|
|
messages.success(
|
|
message=_(
|
|
'User "%s" deleted successfully.'
|
|
) % instance, request=self.request
|
|
)
|
|
except Exception as exception:
|
|
messages.error(
|
|
message=_(
|
|
'Error deleting user "%(user)s": %(error)s'
|
|
) % {'user': instance, 'error': exception},
|
|
request=self.request
|
|
)
|
|
|
|
|
|
class UserDetailsView(SingleObjectDetailView):
|
|
fields = (
|
|
'username', 'first_name', 'last_name', 'email', 'last_login',
|
|
'date_joined', 'groups',
|
|
)
|
|
object_permission = permission_user_view
|
|
pk_url_kwarg = 'user_id'
|
|
source_queryset = get_user_model().objects.filter(
|
|
is_superuser=False, is_staff=False
|
|
)
|
|
|
|
def get_extra_context(self, **kwargs):
|
|
return {
|
|
'object': self.get_object(),
|
|
'title': _('Details of user: %s') % self.get_object()
|
|
}
|
|
|
|
|
|
class UserEditView(SingleObjectEditView):
|
|
fields = ('username', 'first_name', 'last_name', 'email', 'is_active',)
|
|
object_permission = permission_user_edit
|
|
pk_url_kwarg = 'user_id'
|
|
post_action_redirect = reverse_lazy(viewname='user_management:user_list')
|
|
source_queryset = get_user_model().objects.filter(
|
|
is_superuser=False, is_staff=False
|
|
)
|
|
|
|
def form_valid(self, form):
|
|
with transaction.atomic():
|
|
result = super(UserEditView, self).form_valid(form=form)
|
|
event_user_edited.commit(
|
|
actor=self.request.user, target=self.object
|
|
)
|
|
|
|
return result
|
|
|
|
def get_extra_context(self):
|
|
return {
|
|
'object': self.object,
|
|
'title': _('Edit user: %s') % self.object,
|
|
}
|
|
|
|
|
|
class UserGroupsView(ExternalObjectMixin, AssignRemoveView):
|
|
decode_content_type = True
|
|
external_object_queryset = get_user_model().objects.filter(
|
|
is_staff=False, is_superuser=False
|
|
)
|
|
external_object_permission = permission_user_edit
|
|
external_object_pk_url_kwarg = 'user_id'
|
|
left_list_title = _('Available groups')
|
|
right_list_title = _('Groups joined')
|
|
|
|
def add(self, item):
|
|
item.user_set.add(self.object)
|
|
|
|
def dispatch(self, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
return super(UserGroupsView, self).dispatch(*args, **kwargs)
|
|
|
|
def get_extra_context(self):
|
|
return {
|
|
'object': self.object,
|
|
'title': _('Groups of user: %s') % self.object
|
|
}
|
|
|
|
def get_object(self):
|
|
return self.get_external_object()
|
|
|
|
def left_list(self):
|
|
queryset = AccessControlList.objects.restrict_queryset(
|
|
permission=permission_group_edit,
|
|
queryset=Group.objects.exclude(user=self.object),
|
|
user=self.request.user
|
|
)
|
|
return AssignRemoveView.generate_choices(choices=queryset)
|
|
|
|
def right_list(self):
|
|
queryset = AccessControlList.objects.restrict_queryset(
|
|
permission=permission_group_edit,
|
|
queryset=Group.objects.filter(user=self.object),
|
|
user=self.request.user
|
|
)
|
|
return AssignRemoveView.generate_choices(choices=queryset)
|
|
|
|
def remove(self, item):
|
|
item.user_set.remove(self.object)
|
|
|
|
|
|
class UserListView(SingleObjectListView):
|
|
object_permission = permission_user_view
|
|
|
|
def get_extra_context(self):
|
|
return {
|
|
'hide_object': True,
|
|
'no_results_icon': icon_user_setup,
|
|
'no_results_main_link': link_user_create.resolve(
|
|
context=RequestContext(request=self.request)
|
|
),
|
|
'no_results_text': _(
|
|
'User accounts can be create from this view. After creating '
|
|
'an user account you will prompted to set a password for it. '
|
|
),
|
|
'no_results_title': _('There are no user accounts'),
|
|
'title': _('Users'),
|
|
}
|
|
|
|
def get_source_queryset(self):
|
|
return get_user_model().objects.exclude(
|
|
is_superuser=True
|
|
).exclude(is_staff=True).order_by('last_name', 'first_name')
|
|
|
|
|
|
class UserOptionsEditView(SingleObjectEditView):
|
|
fields = ('block_password_change',)
|
|
object_permission = permission_user_edit
|
|
|
|
def get_extra_context(self):
|
|
return {
|
|
'object': self.get_user(),
|
|
'title': _(
|
|
'Edit options for user: %s'
|
|
) % self.get_user()
|
|
}
|
|
|
|
def get_object(self, queryset=None):
|
|
return self.get_user().user_options
|
|
|
|
def get_post_action_redirect(self):
|
|
return reverse(viewname='user_management:user_list')
|
|
|
|
def get_user(self):
|
|
return get_object_or_404(
|
|
klass=get_user_model().objects.filter(
|
|
is_superuser=False, is_staff=False
|
|
), pk=self.kwargs['user_id']
|
|
)
|
|
|
|
|
|
class UserSetPasswordView(MultipleObjectFormActionView):
|
|
form_class = SetPasswordForm
|
|
model = get_user_model()
|
|
object_permission = permission_user_edit
|
|
pk_url_kwarg = 'user_id'
|
|
source_queryset = get_user_model().objects.filter(
|
|
is_superuser=False, is_staff=False
|
|
)
|
|
success_message = _('Password change request performed on %(count)d user')
|
|
success_message_plural = _(
|
|
'Password change request performed on %(count)d users'
|
|
)
|
|
|
|
def get_extra_context(self):
|
|
queryset = self.get_object_list()
|
|
|
|
result = {
|
|
'submit_label': _('Submit'),
|
|
'title': ungettext(
|
|
singular='Change the password of the %(count)d selected user',
|
|
plural='Change the password of the %(count)d selected users',
|
|
number=queryset.count()
|
|
) % {'count': queryset.count()}
|
|
}
|
|
|
|
if queryset.count() == 1:
|
|
result.update(
|
|
{
|
|
'object': queryset.first(),
|
|
'title': _(
|
|
'Change the password of user: %s'
|
|
) % queryset.first()
|
|
}
|
|
)
|
|
|
|
return result
|
|
|
|
def get_form_extra_kwargs(self):
|
|
queryset = self.get_object_list()
|
|
return {'user': queryset.first()}
|
|
|
|
def object_action(self, form, instance):
|
|
try:
|
|
instance.set_password(form.cleaned_data['new_password1'])
|
|
instance.save()
|
|
messages.success(
|
|
message=_(
|
|
'Successful password reset for user: %s.'
|
|
) % instance, request=self.request
|
|
)
|
|
except Exception as exception:
|
|
messages.error(
|
|
message=_(
|
|
'Error reseting password for user "%(user)s": %(error)s'
|
|
) % {
|
|
'user': instance, 'error': exception
|
|
}, request=self.request
|
|
)
|