diff --git a/wagtail/wagtailusers/forms.py b/wagtail/wagtailusers/forms.py index 7c8d0b1b6..efb0ca016 100644 --- a/wagtail/wagtailusers/forms.py +++ b/wagtail/wagtailusers/forms.py @@ -2,6 +2,7 @@ from django import forms from django.contrib.auth.forms import UserCreationForm as BaseUserCreationForm from django.utils.translation import ugettext_lazy as _ from django.contrib.auth import get_user_model +from django.contrib.auth.models import Group from wagtail.wagtailusers.models import UserProfile from wagtail.wagtailcore.models import UserPagePermissionsProxy @@ -136,6 +137,34 @@ class UserEditForm(forms.ModelForm): return user +class GroupForm(forms.ModelForm): + required_css_class = "required" + + error_messages = { + 'duplicate_name': _("A group with that name already exists."), + } + + is_superuser = forms.BooleanField( + label=_("Administrator"), + required=False, + help_text=_("Administrators have the ability to manage user accounts.") + ) + + class Meta: + model = Group + fields = ("name", "permissions", ) + + def clean_name(self): + # Since Group.name is unique, this check is redundant, + # but it sets a nicer error message than the ORM. See #13147. + name = self.cleaned_data["name"] + try: + Group._default_manager.exclude(id=self.instance.id).get(name=name) + except Group.DoesNotExist: + return name + raise forms.ValidationError(self.error_messages['duplicate_name']) + + class NotificationPreferencesForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(NotificationPreferencesForm, self).__init__(*args, **kwargs) diff --git a/wagtail/wagtailusers/templates/wagtailusers/groups/create.html b/wagtail/wagtailusers/templates/wagtailusers/groups/create.html new file mode 100644 index 000000000..dd9f34806 --- /dev/null +++ b/wagtail/wagtailusers/templates/wagtailusers/groups/create.html @@ -0,0 +1,21 @@ +{% extends "wagtailadmin/base.html" %} +{% load image_tags %} +{% load i18n %} +{% block titletag %}{% trans "Add group" %}{% endblock %} +{% block bodyclass %}menu-groups{% endblock %} +{% block content %} + + {% trans "Add group" as add_group_str %} + {% include "wagtailadmin/shared/header.html" with title=add_group_str icon="group" %} + +
+ {% csrf_token %} +
+ +
+
+{% endblock %} diff --git a/wagtail/wagtailusers/templates/wagtailusers/groups/edit.html b/wagtail/wagtailusers/templates/wagtailusers/groups/edit.html new file mode 100644 index 000000000..541a803f5 --- /dev/null +++ b/wagtail/wagtailusers/templates/wagtailusers/groups/edit.html @@ -0,0 +1,21 @@ +{% extends "wagtailadmin/base.html" %} +{% load image_tags %} +{% load i18n %} +{% block titletag %}{% trans "Editing" %} {{ group.name }}{% endblock %} +{% block bodyclass %}menu-groups{% endblock %} +{% block content %} + + {% trans "Editing" as editing_str %} + {% include "wagtailadmin/shared/header.html" with title=editing_str subtitle=group.name icon="group" %} + +
+ {% csrf_token %} +
+ +
+
+{% endblock %} diff --git a/wagtail/wagtailusers/templates/wagtailusers/groups/index.html b/wagtail/wagtailusers/templates/wagtailusers/groups/index.html new file mode 100644 index 000000000..45934be39 --- /dev/null +++ b/wagtail/wagtailusers/templates/wagtailusers/groups/index.html @@ -0,0 +1,26 @@ +{% extends "wagtailadmin/base.html" %} +{% load i18n %} +{% load gravatar %} +{% block titletag %}{% trans "groups" %}{% endblock %} +{% block bodyclass %}menu-groups{% endblock %} +{% block extra_js %} + +{% endblock %} + +{% block content %} + {% trans "groups" as groups_str %} + {% trans "Add a group" as add_a_group_str %} + {% include "wagtailadmin/shared/header.html" with title=groups_str add_link="wagtailusers_groups_create" add_text=add_a_group_str icon="group" search_url="wagtailusers_groups_index" %} + +
+
+ {% include "wagtailusers/groups/results.html" %} +
+
+{% endblock %} diff --git a/wagtail/wagtailusers/templates/wagtailusers/groups/list.html b/wagtail/wagtailusers/templates/wagtailusers/groups/list.html new file mode 100644 index 000000000..33ccd001a --- /dev/null +++ b/wagtail/wagtailusers/templates/wagtailusers/groups/list.html @@ -0,0 +1,27 @@ +{% load i18n %} + + + + + + + + {% for group in groups %} + + + + {% endfor %} + +
+ {% trans "Name" %} + {% if ordering == "name" %} + + {% else %} + + {% endif %} +
+

+ + {{ group.name }} +

+
diff --git a/wagtail/wagtailusers/templates/wagtailusers/groups/results.html b/wagtail/wagtailusers/templates/wagtailusers/groups/results.html new file mode 100644 index 000000000..ea3cc5650 --- /dev/null +++ b/wagtail/wagtailusers/templates/wagtailusers/groups/results.html @@ -0,0 +1,10 @@ +{% load i18n %} +{% if groups %} + + {% include "wagtailusers/groups/list.html" %} + + {% include "wagtailadmin/shared/pagination_nav.html" with items=groups is_searching=is_searching linkurl="wagtailusers_groups_index" %} +{% else %} + {% url 'wagtailusers_groups_create' as wagtailusers_create_group_url %} +

{% blocktrans %}There are no groups configured. Why not add some?{% endblocktrans %}

+{% endif %} diff --git a/wagtail/wagtailusers/urls/groups.py b/wagtail/wagtailusers/urls/groups.py new file mode 100644 index 000000000..4899eea12 --- /dev/null +++ b/wagtail/wagtailusers/urls/groups.py @@ -0,0 +1,8 @@ +from django.conf.urls import url +from wagtail.wagtailusers.views import groups + +urlpatterns = [ + url(r'^$', groups.index, name='wagtailusers_groups_index'), + url(r'^new/$', groups.create, name='wagtailusers_groups_create'), + url(r'^(\d+)/$', groups.edit, name='wagtailusers_groups_edit'), +] diff --git a/wagtail/wagtailusers/views/groups.py b/wagtail/wagtailusers/views/groups.py new file mode 100644 index 000000000..15994f39e --- /dev/null +++ b/wagtail/wagtailusers/views/groups.py @@ -0,0 +1,114 @@ +from django.shortcuts import render, redirect, get_object_or_404 +from django.contrib.auth import get_user_model +from django.contrib.auth.models import Group +from django.contrib.auth.decorators import permission_required +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger +from django.contrib import messages +from django.db.models import Q +from django.utils.translation import ugettext as _ +from django.views.decorators.vary import vary_on_headers + +from wagtail.wagtailadmin.forms import SearchForm +from wagtail.wagtailusers.forms import GroupForm +from wagtail.wagtailcore.compat import AUTH_USER_APP_LABEL, AUTH_USER_MODEL_NAME + +User = get_user_model() + +# Typically we would check the permission 'auth.change_user' for user +# management actions, but this may vary according to the AUTH_USER_MODEL +# setting +change_user_perm = "{0}.change_{1}".format(AUTH_USER_APP_LABEL, AUTH_USER_MODEL_NAME.lower()) + + +@permission_required(change_user_perm) +@vary_on_headers('X-Requested-With') +def index(request): + q = None + p = request.GET.get("p", 1) + is_searching = False + + if 'q' in request.GET: + form = SearchForm(request.GET, placeholder=_("Search groups")) + if form.is_valid(): + q = form.cleaned_data['q'] + + is_searching = True + groups = Group.objects.filter(name__icontains=q) + else: + form = SearchForm(placeholder=_("Search groups")) + + if not is_searching: + groups = Group.objects + + groups = groups.order_by('name') + + if 'ordering' in request.GET: + ordering = request.GET['ordering'] + + if ordering in ['name', 'username']: + if ordering != 'name': + users = users.order_by(ordering) + else: + ordering = 'name' + + paginator = Paginator(groups, 20) + + try: + groups = paginator.page(p) + except PageNotAnInteger: + groups = paginator.page(1) + except EmptyPage: + groups = paginator.page(paginator.num_pages) + + if request.is_ajax(): + return render(request, "wagtailusers/groups/results.html", { + 'groups': groups, + 'is_searching': is_searching, + 'query_string': q, + 'ordering': ordering, + }) + else: + return render(request, "wagtailusers/groups/index.html", { + 'search_form': form, + 'groups': groups, + 'is_searching': is_searching, + 'ordering': ordering, + 'query_string': q, + }) + +@permission_required(change_user_perm) +def create(request): + if request.POST: + form = GroupForm(request.POST) + if form.is_valid(): + group = form.save() + messages.success(request, _("Group '{0}' created.").format(group)) + return redirect('wagtailusers_groups_index') + else: + messages.error(request, _("The group could not be created due to errors.") ) + else: + form = GroupForm() + + return render(request, 'wagtailusers/groups/create.html', { + 'form': form, + }) + + +@permission_required(change_user_perm) +def edit(request, group_id): + group = get_object_or_404(Group, id=group_id) + if request.POST: + form = GroupForm(request.POST, instance=group) + if form.is_valid(): + group = form.save() + messages.success(request, _("Group '{0}' updated.").format(group)) + return redirect('wagtailusers_groups_index') + else: + messages.error(request, _("The group could not be saved due to errors.")) + else: + form = GroupForm(instance=group) + + return render(request, 'wagtailusers/groups/edit.html', { + 'group': group, + 'form': form, + }) diff --git a/wagtail/wagtailusers/wagtail_hooks.py b/wagtail/wagtailusers/wagtail_hooks.py index 6c03614b8..5a8195fa0 100644 --- a/wagtail/wagtailusers/wagtail_hooks.py +++ b/wagtail/wagtailusers/wagtail_hooks.py @@ -5,12 +5,13 @@ from django.utils.translation import ugettext_lazy as _ from wagtail.wagtailcore import hooks from wagtail.wagtailadmin.menu import MenuItem -from wagtail.wagtailusers.urls import users +from wagtail.wagtailusers.urls import users, groups def register_admin_urls(): return [ url(r'^users/', include(users)), + url(r'^groups/', include(groups)), ] hooks.register('register_admin_urls', register_admin_urls) @@ -20,4 +21,7 @@ def construct_main_menu(request, menu_items): menu_items.append( MenuItem(_('Users'), urlresolvers.reverse('wagtailusers_users_index'), classnames='icon icon-user', order=600) ) + menu_items.append( + MenuItem(_('Groups'), urlresolvers.reverse('wagtailusers_groups_index'), classnames='icon icon-group', order=601) + ) hooks.register('construct_main_menu', construct_main_menu)