Add admin interface for setting collection view restrictions

This commit is contained in:
Matt Westcott 2017-06-06 12:17:33 +01:00
parent 58d7112e5f
commit 83c8db1ab5
12 changed files with 206 additions and 8 deletions

View file

@ -21,7 +21,9 @@ from taggit.managers import TaggableManager
from wagtail.wagtailadmin import widgets
from wagtail.wagtailcore.models import (
Collection, GroupCollectionPermission, Page, PageViewRestriction
BaseViewRestriction,
Collection, CollectionViewRestriction, GroupCollectionPermission,
Page, PageViewRestriction
)
@ -192,29 +194,43 @@ class CopyForm(forms.Form):
return cleaned_data
class PageViewRestrictionForm(forms.ModelForm):
class BaseViewRestrictionForm(forms.ModelForm):
restriction_type = forms.ChoiceField(
label=ugettext_lazy("Visibility"), choices=PageViewRestriction.RESTRICTION_CHOICES,
label=ugettext_lazy("Visibility"), choices=BaseViewRestriction.RESTRICTION_CHOICES,
widget=forms.RadioSelect)
def __init__(self, *args, **kwargs):
super(PageViewRestrictionForm, self).__init__(*args, **kwargs)
super(BaseViewRestrictionForm, self).__init__(*args, **kwargs)
self.fields['groups'].widget = forms.CheckboxSelectMultiple()
self.fields['groups'].queryset = Group.objects.all()
def clean_password(self):
password = self.cleaned_data.get('password')
if self.cleaned_data.get('restriction_type') == PageViewRestriction.PASSWORD and not password:
if self.cleaned_data.get('restriction_type') == BaseViewRestriction.PASSWORD and not password:
raise forms.ValidationError(_("This field is required."), code='invalid')
return password
def clean_groups(self):
groups = self.cleaned_data.get('groups')
if self.cleaned_data.get('restriction_type') == PageViewRestriction.GROUPS and not groups:
if self.cleaned_data.get('restriction_type') == BaseViewRestriction.GROUPS and not groups:
raise forms.ValidationError(_("Please select at least one group."), code='invalid')
return groups
class Meta:
model = BaseViewRestriction
fields = ('restriction_type', 'password', 'groups')
class CollectionViewRestrictionForm(BaseViewRestrictionForm):
class Meta:
model = CollectionViewRestriction
fields = ('restriction_type', 'password', 'groups')
class PageViewRestrictionForm(BaseViewRestrictionForm):
class Meta:
model = PageViewRestriction
fields = ('restriction_type', 'password', 'groups')

View file

@ -0,0 +1,8 @@
{% load i18n %}
{% trans "Collection privacy" as title_str %}
{% include "wagtailadmin/shared/header.html" with title=title_str icon="no-view" %}
<div class="nice-padding">
<p>{% trans "This collection has been made private by a parent collection." %}</p>
<p>{% trans "You can edit the privacy settings on:" %} <a href="{% url 'wagtailadmin_collections:edit' collection_with_restriction.id %}">{{ collection_with_restriction.name }}</a>
</div>

View file

@ -0,0 +1,19 @@
{% load i18n %}
{% trans "Collection privacy" as title_str %}
{% include "wagtailadmin/shared/header.html" with title=title_str icon="no-view" %}
<div class="nice-padding">
<p class="help-block help-info">{% trans "Privacy settings determine who is able to view documents in this collection." %}</p>
<form action="{% url 'wagtailadmin_collections:set_privacy' collection.id %}" method="POST" novalidate>
{% csrf_token %}
<ul class="fields">
{% include "wagtailadmin/shared/field_as_li.html" with field=form.restriction_type %}
{% include "wagtailadmin/shared/field_as_li.html" with field=form.password li_classes="password-field" %}
</ul>
<ul class="fields" id="groups-fields">
{% include "wagtailadmin/shared/field_as_li.html" with field=form.groups %}
</ul>
<input type="submit" value="{% trans "Save" %}" class="button" />
</form>
</div>

View file

@ -0,0 +1,27 @@
function(modal) {
$('form', modal.body).submit(function() {
modal.postForm(this.action, $(this).serialize());
return false;
});
var restrictionTypePasswordField = $("input[name='restriction_type'][value='password']", modal.body);
var restrictionTypeGroupsField = $("input[name='restriction_type'][value='groups']", modal.body);
var passwordField = $(".password-field", modal.body);
var groupsFields = $('#groups-fields', modal.body);
function refreshFormFields() {
if (restrictionTypePasswordField.is(':checked')) {
passwordField.show();
groupsFields.hide();
} else if (restrictionTypeGroupsField.is(':checked')){
passwordField.hide();
groupsFields.show();
} else {
passwordField.hide();
groupsFields.hide();
}
}
refreshFormFields();
$("input[name='restriction_type']", modal.body).change(refreshFormFields);
}

View file

@ -0,0 +1,4 @@
function(modal) {
modal.respond('setPermission', {% if is_public %}true{% else %}false{% endif %});
modal.close();
}

View file

@ -0,0 +1,14 @@
{% load i18n wagtailadmin_tags %}
{% test_collection_is_public collection as is_public %}
{% if not collection.is_root %}
<div class="privacy-indicator {% if is_public %}public{% else %}private{% endif %}">
{% trans "Privacy" %}
<a href="{% url 'wagtailadmin_collections:set_privacy' collection.id %}" class="status-tag primary action-set-privacy">
{# labels are shown/hidden in CSS according to the 'private' / 'public' class on view-permission-indicator #}
<span class="label-public icon icon-view">{% trans 'Public' %}</span>
<span class="label-private icon icon-no-view">{% trans 'Private' %}</span>
</a>
</div>
{% endif %}

View file

@ -0,0 +1,12 @@
{% extends "wagtailadmin/generic/edit.html" %}
{% load static %}
{% block before_form %}
{% include "wagtailadmin/collections/_privacy_switch.html" with collection=object collection_perms=collection_perms only %}
{% endblock %}
{% block extra_js %}
{{ block.super }}
<script src="{% static 'wagtailadmin/js/modal-workflow.js' %}"></script>
<script src="{% static 'wagtailadmin/js/privacy-switch.js' %}"></script>
{% endblock %}

View file

@ -8,6 +8,7 @@
{% include "wagtailadmin/shared/header.html" with title=view.page_title subtitle=view.get_page_subtitle icon=view.header_icon %}
<div class="nice-padding">
{% block before_form %}{% endblock %}
<form action="{{ view.get_edit_url }}" method="POST" novalidate{% if form.is_multipart %} enctype="multipart/form-data"{% endif %}>
{% csrf_token %}

View file

@ -17,7 +17,11 @@ from wagtail.wagtailadmin.menu import admin_menu
from wagtail.wagtailadmin.navigation import get_explorable_root_page
from wagtail.wagtailadmin.search import admin_search_areas
from wagtail.wagtailcore import hooks
from wagtail.wagtailcore.models import Page, PageViewRestriction, UserPagePermissionsProxy
from wagtail.wagtailcore.models import (
CollectionViewRestriction,
Page, PageViewRestriction,
UserPagePermissionsProxy
)
from wagtail.wagtailcore.utils import cautious_slugify as _cautious_slugify
from wagtail.wagtailcore.utils import camelcase_to_underscore, escape_script
@ -133,6 +137,25 @@ def page_permissions(context, page):
return context['user_page_permissions'].for_page(page)
@assignment_tag(takes_context=True)
def test_collection_is_public(context, collection):
"""
Usage: {% test_collection_is_public collection as is_public %}
Sets 'is_public' to True iff there are no collection view restrictions in place
on this collection.
Caches the list of collection view restrictions in the context, to avoid repeated
DB queries on repeated calls.
"""
if 'all_collection_view_restrictions' not in context:
context['all_collection_view_restrictions'] = CollectionViewRestriction.objects.select_related('collection').values_list(
'collection__name', flat=True
)
is_private = collection.name in context['all_collection_view_restrictions']
return not is_private
@assignment_tag(takes_context=True)
def test_page_is_public(context, page):
"""

View file

@ -2,11 +2,12 @@ from __future__ import absolute_import, unicode_literals
from django.conf.urls import url
from wagtail.wagtailadmin.views import collections
from wagtail.wagtailadmin.views import collection_privacy, collections
urlpatterns = [
url(r'^$', collections.Index.as_view(), name='index'),
url(r'^add/$', collections.Create.as_view(), name='add'),
url(r'^(\d+)/$', collections.Edit.as_view(), name='edit'),
url(r'^(\d+)/delete/$', collections.Delete.as_view(), name='delete'),
url(r'^(\d+)/privacy/$', collection_privacy.set_privacy, name='set_privacy'),
]

View file

@ -0,0 +1,72 @@
from __future__ import absolute_import, unicode_literals
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from wagtail.wagtailadmin.forms import CollectionViewRestrictionForm
from wagtail.wagtailadmin.modal_workflow import render_modal_workflow
from wagtail.wagtailcore.models import Collection, CollectionViewRestriction
from wagtail.wagtailcore.permissions import collection_permission_policy
def set_privacy(request, collection_id):
collection = get_object_or_404(Collection, id=collection_id)
if not collection_permission_policy.user_has_permission(request.user, 'change'):
raise PermissionDenied
# fetch restriction records in depth order so that ancestors appear first
restrictions = collection.get_view_restrictions().order_by('collection__depth')
if restrictions:
restriction = restrictions[0]
restriction_exists_on_ancestor = (restriction.collection != collection)
else:
restriction = None
restriction_exists_on_ancestor = False
if request.method == 'POST':
form = CollectionViewRestrictionForm(request.POST, instance=restriction)
if form.is_valid() and not restriction_exists_on_ancestor:
if form.cleaned_data['restriction_type'] == CollectionViewRestriction.NONE:
# remove any existing restriction
if restriction:
restriction.delete()
else:
restriction = form.save(commit=False)
restriction.collection = collection
form.save()
return render_modal_workflow(
request, None, 'wagtailadmin/collection_privacy/set_privacy_done.js', {
'is_public': (form.cleaned_data['restriction_type'] == 'none')
}
)
else: # request is a GET
if not restriction_exists_on_ancestor:
if restriction:
form = CollectionViewRestrictionForm(instance=restriction)
else:
# no current view restrictions on this collection
form = CollectionViewRestrictionForm(initial={
'restriction_type': 'none'
})
if restriction_exists_on_ancestor:
# display a message indicating that there is a restriction at ancestor level -
# do not provide the form for setting up new restrictions
return render_modal_workflow(
request, 'wagtailadmin/collection_privacy/ancestor_privacy.html', None,
{
'collection_with_restriction': restriction.collection,
}
)
else:
# no restriction set at ancestor level - can set restrictions here
return render_modal_workflow(
request,
'wagtailadmin/collection_privacy/set_privacy.html',
'wagtailadmin/collection_privacy/set_privacy.js', {
'collection': collection,
'form': form,
}
)

View file

@ -49,6 +49,7 @@ class Edit(EditView):
permission_policy = collection_permission_policy
model = Collection
form_class = CollectionForm
template_name = 'wagtailadmin/collections/edit.html'
success_message = ugettext_lazy("Collection '{0}' updated.")
error_message = ugettext_lazy("The collection could not be saved due to errors.")
delete_item_label = ugettext_lazy("Delete collection")