diff --git a/docs/reference/hooks.rst b/docs/reference/hooks.rst index ca449a610..5e1f5c58d 100644 --- a/docs/reference/hooks.rst +++ b/docs/reference/hooks.rst @@ -224,6 +224,22 @@ Hooks for building new areas of the admin interface (alongside pages, images, do As ``construct_main_menu``, but modifies the 'Settings' sub-menu rather than the top-level menu. +.. _register_reports_menu_item: + +``register_reports_menu_item`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + As ``register_admin_menu_item``, but registers menu items into the 'Reports' sub-menu rather than the top-level menu. + + +.. _construct_reports_menu: + +``construct_reports_menu`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + As ``construct_main_menu``, but modifies the 'Reports' sub-menu rather than the top-level menu. + + .. _register_admin_search_area: ``register_admin_search_area`` diff --git a/docs/reference/pages/model_reference.rst b/docs/reference/pages/model_reference.rst index efc18fc30..3533ae209 100644 --- a/docs/reference/pages/model_reference.rst +++ b/docs/reference/pages/model_reference.rst @@ -115,7 +115,7 @@ Database fields The user who has currently locked the page. Only this user can edit the page. - If this is ``None`` when ``locked`` is ``False``, nobody can edit the page. + If this is ``None`` when ``locked`` is ``True``, nobody can edit the page. .. attribute:: locked_at diff --git a/wagtail/admin/templates/wagtailadmin/home/locked_pages.html b/wagtail/admin/templates/wagtailadmin/home/locked_pages.html index 5e649d1cf..9f579cd21 100644 --- a/wagtail/admin/templates/wagtailadmin/home/locked_pages.html +++ b/wagtail/admin/templates/wagtailadmin/home/locked_pages.html @@ -3,7 +3,9 @@
{# TODO try moving these classes onto the section tag #}

{% trans "Your locked pages" %}

- {% trans "See all locked pages" %} + {% if can_remove_locks %} + {% trans "See all locked pages" %} + {% endif %} diff --git a/wagtail/admin/templates/wagtailadmin/reports/listing/_list_unlock.html b/wagtail/admin/templates/wagtailadmin/reports/listing/_list_unlock.html index f1cc01d0e..4e386f1b4 100644 --- a/wagtail/admin/templates/wagtailadmin/reports/listing/_list_unlock.html +++ b/wagtail/admin/templates/wagtailadmin/reports/listing/_list_unlock.html @@ -2,13 +2,32 @@ {% load i18n wagtailadmin_tags %} + {% block page_navigation %}
{% page_permissions page as perms %}

- Locked - {% if page.locked_by %}by {% if page.locked_by_id == request.user.pk %}you{% else %}{{ page.locked_by }}{% endif %}{% endif %} - {% if page.locked_at %}at {{ page.locked_at|date:"d M Y H:i" }}{% endif %} + {% if page.locked_at %} + {% with page.locked_at|date:"d M Y H:i" as locking_date %} + {% if page.locked_by %} + {% if page.locked_by_id == request.user.pk %} + {% blocktrans %} + Locked by you at {{ locking_date }} + {% endblocktrans %} + {% else %} + {% blocktrans with locked_by=page.locked_by %} + Locked by {{ locked_by }} at {{ locking_date }} + {% endblocktrans %} + {% endif %} + {% else %} + {% blocktrans %} + Locked at {{ locking_date }} + {% endblocktrans %} + {% endif %} + {% endwith %} + {% else %} + {% trans 'Locked' %} + {% endif %}

{% if perms.can_unlock %}
diff --git a/wagtail/admin/tests/test_reports_views.py b/wagtail/admin/tests/test_reports_views.py new file mode 100644 index 000000000..be8e3470c --- /dev/null +++ b/wagtail/admin/tests/test_reports_views.py @@ -0,0 +1,35 @@ +from django.test import TestCase +from django.urls import reverse +from django.utils import timezone + +from wagtail.core.models import Page +from wagtail.tests.utils import WagtailTestUtils + + +class TestLockedPagesView(TestCase, WagtailTestUtils): + def setUp(self): + self.user = self.login() + + def get(self, params={}): + return self.client.get(reverse('wagtailadmin_reports:locked_pages'), params) + + def test_simple(self): + response = self.get() + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'wagtailadmin/reports/locked_pages.html') + + # Initially there should be no locked pages + self.assertContains(response, "No locked pages found.") + + self.page = Page.objects.first() + self.page.locked = True + self.page.locked_by = self.user + self.page.locked_at = timezone.now() + self.page.save() + + # Now the listing should contain our locked page + response = self.get() + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'wagtailadmin/reports/locked_pages.html') + self.assertNotContains(response, "No locked pages found.") + self.assertContains(response, self.page.title) diff --git a/wagtail/admin/views/home.py b/wagtail/admin/views/home.py index cd9edc0d8..80ad25fc4 100644 --- a/wagtail/admin/views/home.py +++ b/wagtail/admin/views/home.py @@ -59,7 +59,8 @@ class LockedPagesPanel: 'locked_pages': Page.objects.filter( locked=True, locked_by=self.request.user, - ) + ), + 'can_remove_locks': UserPagePermissionsProxy(self.request.user).can_remove_locks() }, request=self.request) diff --git a/wagtail/admin/views/reports.py b/wagtail/admin/views/reports.py index 2b75f716e..9ab2ca8bd 100644 --- a/wagtail/admin/views/reports.py +++ b/wagtail/admin/views/reports.py @@ -2,6 +2,7 @@ from django.utils.translation import ugettext_lazy as _ from django.views.generic.base import TemplateResponseMixin from django.views.generic.list import BaseListView +from wagtail.admin.auth import permission_denied from wagtail.core.models import UserPagePermissionsProxy @@ -28,3 +29,8 @@ class LockedPagesView(ReportView): pages = UserPagePermissionsProxy(self.request.user).editable_pages().filter(locked=True) self.queryset = pages return super().get_queryset() + + def dispatch(self, request, *args, **kwargs): + if not UserPagePermissionsProxy(request.user).can_remove_locks(): + return permission_denied(request) + return super().dispatch(request, *args, **kwargs) diff --git a/wagtail/admin/wagtail_hooks.py b/wagtail/admin/wagtail_hooks.py index 3e3f553b7..d7489d655 100644 --- a/wagtail/admin/wagtail_hooks.py +++ b/wagtail/admin/wagtail_hooks.py @@ -619,9 +619,14 @@ class ReportsMenuItem(SubmenuMenuItem): template = 'wagtailadmin/shared/menu_submenu_item.html' +class LockedPagesMenuItem(MenuItem): + def is_shown(self, request): + return UserPagePermissionsProxy(request.user).can_remove_locks() + + @hooks.register('register_reports_menu_item') def register_locked_pages_menu_item(): - return MenuItem(_('Locked Pages'), reverse('wagtailadmin_reports:locked_pages'), classnames='icon icon-locked', order=700) + return LockedPagesMenuItem(_('Locked Pages'), reverse('wagtailadmin_reports:locked_pages'), classnames='icon icon-locked', order=700) @hooks.register('register_admin_menu_item') diff --git a/wagtail/core/models.py b/wagtail/core/models.py index 737e36d34..cf6d16d22 100644 --- a/wagtail/core/models.py +++ b/wagtail/core/models.py @@ -1838,6 +1838,15 @@ class UserPagePermissionsProxy: """Return True if the user has permission to publish any pages""" return self.publishable_pages().exists() + def can_remove_locks(self): + """Returns True if the user has permission to unlock pages they have not locked""" + if self.user.is_superuser: + return True + if not self.user.is_active: + return False + else: + return self.permissions.filter(permission_type='unlock').exists() + class PagePermissionTester: def __init__(self, user_perms, page):