diff --git a/mayan/apps/common/apps.py b/mayan/apps/common/apps.py index 92fb2fafbe..2aea598e70 100644 --- a/mayan/apps/common/apps.py +++ b/mayan/apps/common/apps.py @@ -25,11 +25,9 @@ from .handlers import ( ) from .licenses import * # NOQA from .links import ( - link_about, link_check_version, link_current_user_details, - link_current_user_edit, link_current_user_locale_profile_edit, + link_about, link_check_version, link_current_user_locale_profile_edit, link_license, link_object_error_list_clear, link_packages_licenses, - link_setup, link_tools, separator_system, separator_user_label, - text_user_label + link_setup, link_tools, separator_system ) from .literals import DELETE_STALE_UPLOADS_INTERVAL, MESSAGE_SQLITE_WARNING from .menus import ( @@ -132,10 +130,8 @@ class CommonApp(MayanAppConfig): ) menu_user.bind_links( links=( - text_user_label, separator_user_label, - link_current_user_details, link_current_user_edit, link_current_user_locale_profile_edit, - ) + ), position=50 ) menu_about.bind_links( diff --git a/mayan/apps/common/forms.py b/mayan/apps/common/forms.py index 37f566727b..c84cd6063f 100644 --- a/mayan/apps/common/forms.py +++ b/mayan/apps/common/forms.py @@ -159,24 +159,3 @@ class PackagesLicensesForm(FileDisplayForm): self.fields['text'].initial = '\n\n'.join( ['{}\n{}'.format(package.label, package.license_text) for package in Package.get_all()] ) - - -class UserForm(forms.ModelForm): - """ - Form used to edit an user's mininal fields by the user himself - """ - class Meta: - fields = ('username', 'first_name', 'last_name', 'email') - model = get_user_model() - - -class UserForm_view(DetailForm): - """ - Form used to display an user's public details - """ - class Meta: - fields = ( - 'username', 'first_name', 'last_name', 'email', 'last_login', - 'date_joined', 'groups' - ) - model = get_user_model() diff --git a/mayan/apps/common/icons.py b/mayan/apps/common/icons.py index b681f884e6..ca653fbc6c 100644 --- a/mayan/apps/common/icons.py +++ b/mayan/apps/common/icons.py @@ -6,8 +6,6 @@ icon_about = Icon(driver_name='fontawesome', symbol='info') icon_assign_remove_add = Icon(driver_name='fontawesome', symbol='plus') icon_assign_remove_remove = Icon(driver_name='fontawesome', symbol='minus') icon_check_version = Icon(driver_name='fontawesome', symbol='sync') -icon_current_user_details = Icon(driver_name='fontawesome', symbol='user') -icon_current_user_edit = Icon(driver_name='fontawesome', symbol='user') icon_current_user_locale_profile_details = Icon( driver_name='fontawesome', symbol='globe' ) diff --git a/mayan/apps/common/links.py b/mayan/apps/common/links.py index ac6a0473e4..cfadec28e6 100644 --- a/mayan/apps/common/links.py +++ b/mayan/apps/common/links.py @@ -7,14 +7,12 @@ from mayan.apps.navigation import Link from mayan.apps.navigation.classes import Separator, Text from .icons import ( - icon_about, icon_check_version, icon_current_user_details, - icon_current_user_edit, icon_current_user_locale_profile_details, + icon_about, icon_check_version, icon_current_user_locale_profile_details, icon_current_user_locale_profile_edit, icon_documentation, icon_forum, icon_license, icon_object_error_list, icon_packages_licenses, icon_setup, icon_source_code, icon_support, icon_tools ) from .permissions_runtime import permission_error_log_view -from .utils import get_user_label_text def get_kwargs_factory(variable_name): @@ -42,14 +40,6 @@ link_check_version = Link( icon_class=icon_check_version, text=_('Check for updates'), view='common:check_version_view' ) -link_current_user_details = Link( - icon_class=icon_current_user_details, text=_('User details'), - view='common:current_user_details' -) -link_current_user_edit = Link( - icon_class=icon_current_user_edit, text=_('Edit details'), - view='common:current_user_edit' -) link_current_user_locale_profile_details = Link( icon_class=icon_current_user_locale_profile_details, text=_('Locale profile'), @@ -101,5 +91,3 @@ link_tools = Link( icon_class=icon_tools, text=_('Tools'), view='common:tools_list' ) separator_system = Separator() -separator_user_label = Separator() -text_user_label = Text(text=get_user_label_text) diff --git a/mayan/apps/common/urls.py b/mayan/apps/common/urls.py index 747a4684e3..58acf75900 100644 --- a/mayan/apps/common/urls.py +++ b/mayan/apps/common/urls.py @@ -5,10 +5,9 @@ from django.views.i18n import javascript_catalog, set_language from .api_views import APIContentTypeList, APITemplateView from .views import ( - AboutView, CheckVersionView, CurrentUserDetailsView, CurrentUserEditView, - CurrentUserLocaleProfileDetailsView, CurrentUserLocaleProfileEditView, - FaviconRedirectView, HomeView, LicenseView, - ObjectErrorLogEntryListClearView, ObjectErrorLogEntryListView, + AboutView, CheckVersionView, CurrentUserLocaleProfileDetailsView, + CurrentUserLocaleProfileEditView, FaviconRedirectView, HomeView, + LicenseView, ObjectErrorLogEntryListClearView, ObjectErrorLogEntryListView, PackagesLicensesView, RootView, SetupListView, ToolsListView, multi_object_action_view ) @@ -32,14 +31,6 @@ urlpatterns = [ ), url(r'^setup/$', SetupListView.as_view(), name='setup_list'), url(r'^tools/$', ToolsListView.as_view(), name='tools_list'), - url( - r'^user/$', CurrentUserDetailsView.as_view(), - name='current_user_details' - ), - url( - r'^user/edit/$', CurrentUserEditView.as_view(), - name='current_user_edit' - ), url( r'^user/locale/$', CurrentUserLocaleProfileDetailsView.as_view(), name='current_user_locale_profile_details' diff --git a/mayan/apps/common/utils.py b/mayan/apps/common/utils.py index c720f8f3d7..4759bb9732 100644 --- a/mayan/apps/common/utils.py +++ b/mayan/apps/common/utils.py @@ -68,13 +68,6 @@ def encapsulate(function): return lambda: function -def get_user_label_text(context): - if not context['request'].user.is_authenticated: - return _('Anonymous') - else: - return context['request'].user.get_full_name() or context['request'].user - - def fs_cleanup(filename, file_descriptor=None, suppress_exceptions=True): """ Tries to remove the given filename. Ignores non-existent files diff --git a/mayan/apps/common/views.py b/mayan/apps/common/views.py index ae8b2ed825..db0319c234 100644 --- a/mayan/apps/common/views.py +++ b/mayan/apps/common/views.py @@ -19,7 +19,7 @@ from mayan.apps.acls.models import AccessControlList from .exceptions import NotLatestVersion, UnknownLatestVersion from .forms import ( LicenseForm, LocaleProfileForm, LocaleProfileForm_view, - PackagesLicensesForm, UserForm, UserForm_view + PackagesLicensesForm ) from .generics import ( # NOQA AssignRemoveView, ConfirmView, FormView, MultiFormView, @@ -65,28 +65,6 @@ class CheckVersionView(SimpleView): } -class CurrentUserDetailsView(SingleObjectDetailView): - form_class = UserForm_view - - 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('common:current_user_details') - - def get_object(self): - return self.request.user - - class CurrentUserLocaleProfileDetailsView(TemplateView): template_name = 'appearance/generic_form.html' diff --git a/mayan/apps/user_management/apps.py b/mayan/apps/user_management/apps.py index 6bf15dd733..b36eada9e5 100644 --- a/mayan/apps/user_management/apps.py +++ b/mayan/apps/user_management/apps.py @@ -9,22 +9,31 @@ from mayan.apps.acls import ModelPermission from mayan.apps.acls.links import link_acl_list from mayan.apps.acls.permissions import permission_acl_edit, permission_acl_view from mayan.apps.common import ( - menu_list_facet, menu_multi_item, menu_object, menu_secondary, menu_setup + menu_list_facet, menu_multi_item, menu_object, menu_secondary, menu_setup, + menu_user ) from mayan.apps.common.apps import MayanAppConfig from mayan.apps.common.widgets import TwoStateWidget +from mayan.apps.events import ModelEventType +from mayan.apps.events.links import ( + link_events_for_object, link_object_event_types_user_subcriptions_list +) from mayan.apps.metadata import MetadataLookup from mayan.apps.navigation import SourceColumn from mayan.apps.rest_api.fields import DynamicSerializerField +from .events import event_user_edited from .handlers import handler_initialize_new_user_options from .links import ( - link_group_create, link_group_delete, link_group_edit, link_group_list, - link_group_members, link_group_setup, link_user_create, link_user_delete, - link_user_edit, link_user_groups, link_user_list, - link_user_multiple_delete, link_user_multiple_set_password, - link_user_set_options, link_user_set_password, link_user_setup + link_current_user_details, link_current_user_edit, link_group_create, + link_group_delete, link_group_edit, link_group_list, link_group_members, + link_group_setup, link_user_create, link_user_delete, link_user_edit, + link_user_groups, link_user_list, link_user_multiple_delete, + link_user_multiple_set_password, link_user_set_options, + link_user_set_password, link_user_setup, text_user_label, + separator_user_label ) +from .methods import method_get_absolute_url from .permissions import ( permission_group_delete, permission_group_edit, permission_group_view, permission_user_delete, permission_user_edit, @@ -62,6 +71,11 @@ class UserManagementApp(MayanAppConfig): description=_('All the users.'), name='users', value=get_users ) + + ModelEventType.register( + model=User, event_types=(event_user_edited,) + ) + ModelPermission.register( model=Group, permissions=( permission_acl_edit, permission_acl_view, @@ -82,7 +96,10 @@ class UserManagementApp(MayanAppConfig): attribute='user_set.count', label=_('Users'), source=Group ) - SourceColumn(attribute='username', is_identifier=True, source=User) + SourceColumn( + attribute='username', is_absolute_url=True, is_identifier=True, + label=_('Username'), source=User + ) SourceColumn( attribute='get_full_name', label=_('Full name'), source=User ) @@ -99,16 +116,25 @@ class UserManagementApp(MayanAppConfig): source=User, widget=TwoStateWidget ) + User.add_to_class( + name='get_absolute_url', value=method_get_absolute_url + ) + menu_list_facet.bind_links( links=( link_acl_list, link_group_members, ), sources=(Group,) ) + menu_list_facet.bind_links( links=( - link_acl_list, link_user_groups - ), sources=(User,) + link_acl_list, link_events_for_object, + link_object_event_types_user_subcriptions_list, + link_user_groups, + ), + sources=(User,) ) + menu_multi_item.bind_links( links=(link_user_multiple_set_password, link_user_multiple_delete), sources=('user_management:user_list',) @@ -142,6 +168,12 @@ class UserManagementApp(MayanAppConfig): ) ) menu_setup.bind_links(links=(link_user_setup, link_group_setup)) + menu_user.bind_links( + links=( + text_user_label, separator_user_label, + link_current_user_details, link_current_user_edit, + ), position=0 + ) post_save.connect( dispatch_uid='user_management_handler_initialize_new_user_options', diff --git a/mayan/apps/user_management/events.py b/mayan/apps/user_management/events.py new file mode 100644 index 0000000000..d06d007889 --- /dev/null +++ b/mayan/apps/user_management/events.py @@ -0,0 +1,16 @@ +from __future__ import absolute_import, unicode_literals + +from django.utils.translation import ugettext_lazy as _ + +from mayan.apps.events import EventTypeNamespace + +namespace = EventTypeNamespace( + name='user_management', label=_('User management') +) + +event_user_created = namespace.add_event_type( + label=_('User created'), name='created' +) +event_user_edited = namespace.add_event_type( + label=_('User edited'), name='edited' +) diff --git a/mayan/apps/user_management/icons.py b/mayan/apps/user_management/icons.py index d33bc2d7da..94cd2623e3 100644 --- a/mayan/apps/user_management/icons.py +++ b/mayan/apps/user_management/icons.py @@ -2,6 +2,8 @@ from __future__ import absolute_import, unicode_literals from mayan.apps.appearance.classes import Icon +icon_current_user_details = Icon(driver_name='fontawesome', symbol='user') +icon_current_user_edit = Icon(driver_name='fontawesome', symbol='user') icon_group = Icon(driver_name='fontawesome', symbol='users') icon_group_create = Icon(driver_name='fontawesome', symbol='plus') icon_group_members = Icon(driver_name='fontawesome', symbol='user') diff --git a/mayan/apps/user_management/links.py b/mayan/apps/user_management/links.py index 449846fccf..36c794db7d 100644 --- a/mayan/apps/user_management/links.py +++ b/mayan/apps/user_management/links.py @@ -2,17 +2,19 @@ from __future__ import unicode_literals from django.utils.translation import ugettext_lazy as _ -from mayan.apps.navigation import Link +from mayan.apps.navigation import Link, Separator, Text from .icons import ( - icon_group, icon_group_create, icon_group_members, icon_group_setup, - icon_user_create, icon_user_setup + icon_current_user_details, icon_current_user_edit, icon_group, + icon_group_create, icon_group_members, icon_group_setup, icon_user_create, + icon_user_setup ) 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 ) +from .utils import get_user_label_text def condition_is_not_superuser(context): @@ -20,6 +22,14 @@ def condition_is_not_superuser(context): return not user.is_superuser and not user.is_staff +link_current_user_details = Link( + icon_class=icon_current_user_details, text=_('User details'), + view='user_management:current_user_details' +) +link_current_user_edit = Link( + icon_class=icon_current_user_edit, text=_('Edit details'), + view='user_management:current_user_edit' +) link_group_create = Link( icon_class=icon_group_create, permissions=(permission_group_create,), text=_('Create new group'), view='user_management:group_create' @@ -86,3 +96,6 @@ link_user_setup = Link( icon_class=icon_user_setup, permissions=(permission_user_view,), text=_('Users'), view='user_management:user_list' ) + +separator_user_label = Separator() +text_user_label = Text(text=get_user_label_text) diff --git a/mayan/apps/user_management/methods.py b/mayan/apps/user_management/methods.py new file mode 100644 index 0000000000..1bfa84c1b7 --- /dev/null +++ b/mayan/apps/user_management/methods.py @@ -0,0 +1,8 @@ +from __future__ import unicode_literals + +from django.apps import apps +from django.shortcuts import reverse + + +def method_get_absolute_url(self): + return reverse(viewname='user_management:user_details', args=(self.pk,)) diff --git a/mayan/apps/user_management/urls.py b/mayan/apps/user_management/urls.py index ca47b5d69b..20a94f1d7b 100644 --- a/mayan/apps/user_management/urls.py +++ b/mayan/apps/user_management/urls.py @@ -7,52 +7,65 @@ from .api_views import ( APIUserListView, APIUserView ) from .views import ( - GroupCreateView, GroupDeleteView, GroupEditView, GroupListView, - GroupMembersView, UserCreateView, UserDeleteView, UserEditView, + CurrentUserDetailsView, CurrentUserEditView, GroupCreateView, + GroupDeleteView, GroupEditView, GroupListView, GroupMembersView, + UserCreateView, UserDeleteView, UserDetailsView, UserEditView, UserGroupsView, UserListView, UserOptionsEditView, UserSetPasswordView ) urlpatterns = [ - url(r'^group/list/$', GroupListView.as_view(), name='group_list'), - url(r'^group/create/$', GroupCreateView.as_view(), name='group_create'), + url(r'^groups/list/$', GroupListView.as_view(), name='group_list'), + url(r'^groups/create/$', GroupCreateView.as_view(), name='group_create'), url( - r'^group/(?P\d+)/edit/$', GroupEditView.as_view(), + r'^groups/(?P\d+)/edit/$', GroupEditView.as_view(), name='group_edit' ), url( - r'^group/(?P\d+)/delete/$', GroupDeleteView.as_view(), + r'^groups/(?P\d+)/delete/$', GroupDeleteView.as_view(), name='group_delete' ), url( - r'^group/(?P\d+)/members/$', GroupMembersView.as_view(), + r'^groups/(?P\d+)/members/$', GroupMembersView.as_view(), name='group_members' ), - url(r'^user/list/$', UserListView.as_view(), name='user_list'), - url(r'^user/create/$', UserCreateView.as_view(), name='user_create'), - url(r'^user/(?P\d+)/edit/$', UserEditView.as_view(), name='user_edit'), url( - r'^user/(?P\d+)/delete/$', UserDeleteView.as_view(), - name='user_delete' + r'^users/current/$', CurrentUserDetailsView.as_view(), + name='current_user_details' ), url( - r'^user/multiple/delete/$', UserDeleteView.as_view(), + r'^users/current/edit/$', CurrentUserEditView.as_view(), + name='current_user_edit' + ), + url(r'^users/list/$', UserListView.as_view(), name='user_list'), + url(r'^users/create/$', UserCreateView.as_view(), name='user_create'), + url( + r'^users/(?P\d+)/delete/$', UserDeleteView.as_view(), + name='user_delete' + ), + url(r'^users/(?P\d+)/edit/$', UserEditView.as_view(), name='user_edit'), + url( + r'^users/(?P\d+)/$', UserDetailsView.as_view(), + name='user_details' + ), + url( + r'^users/multiple/delete/$', UserDeleteView.as_view(), name='user_multiple_delete' ), url( - r'^user/(?P\d+)/set_password/$', UserSetPasswordView.as_view(), + r'^users/(?P\d+)/set_password/$', UserSetPasswordView.as_view(), name='user_set_password' ), url( - r'^user/multiple/set_password/$', UserSetPasswordView.as_view(), + r'^users/multiple/set_password/$', UserSetPasswordView.as_view(), name='user_multiple_set_password' ), url( - r'^user/(?P\d+)/groups/$', UserGroupsView.as_view(), + r'^users/(?P\d+)/groups/$', UserGroupsView.as_view(), name='user_groups' ), url( - r'^user/(?P\d+)/options/$', + r'^users/(?P\d+)/options/$', UserOptionsEditView.as_view(), name='user_options' ), diff --git a/mayan/apps/user_management/utils.py b/mayan/apps/user_management/utils.py index 6ea7893fa9..d33f9a1d4c 100644 --- a/mayan/apps/user_management/utils.py +++ b/mayan/apps/user_management/utils.py @@ -16,3 +16,10 @@ def get_users(): for user in get_user_model().objects.all() ] ) + + +def get_user_label_text(context): + if not context['request'].user.is_authenticated: + return _('Anonymous') + else: + return context['request'].user.get_full_name() or context['request'].user diff --git a/mayan/apps/user_management/views.py b/mayan/apps/user_management/views.py index 3285b47969..51d38102cd 100644 --- a/mayan/apps/user_management/views.py +++ b/mayan/apps/user_management/views.py @@ -15,10 +15,12 @@ from django.utils.translation import ungettext, ugettext_lazy as _ from mayan.apps.common.views import ( AssignRemoveView, MultipleObjectConfirmActionView, MultipleObjectFormActionView, SingleObjectCreateView, - SingleObjectDeleteView, SingleObjectEditView, SingleObjectListView + SingleObjectDeleteView, SingleObjectDetailView, SingleObjectEditView, + SingleObjectListView ) -from .forms import UserForm +from .events import event_user_created, event_user_edited +from .forms import UserForm, UserForm_view from .icons import icon_group_setup, icon_user_setup from .links import link_group_create, link_user_create from .permissions import ( @@ -28,6 +30,31 @@ from .permissions import ( ) +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('user_management:current_user_details') + + def get_object(self): + return self.request.user + + class GroupCreateView(SingleObjectCreateView): extra_context = {'title': _('Create new group')} fields = ('name',) @@ -141,6 +168,11 @@ class UserCreateView(SingleObjectCreateView): user = form.save(commit=False) user.set_unusable_password() user.save() + + event_user_created.commit( + actor=self.request.user, target=user + ) + messages.success( self.request, _('User "%s" created successfully.') % user ) @@ -205,6 +237,23 @@ class UserDeleteView(MultipleObjectConfirmActionView): ) +class UserDetailsView(SingleObjectDetailView): + fields = ( + 'username', 'first_name', 'last_name', 'email', 'last_login', + 'date_joined', 'groups', + ) + object_permission = permission_user_view + 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 @@ -213,6 +262,12 @@ class UserEditView(SingleObjectEditView): is_superuser=False, is_staff=False ) + def form_valid(self, form): + event_user_edited.commit( + actor=self.request.user, target=self.get_object() + ) + return super(UserEditView, self).form_valid(form=form) + def get_extra_context(self): return { 'object': self.get_object(),