kopia lustrzana https://github.com/wagtail/wagtail
Allow workflows to be disabled (#7218)
Introduce a WAGTAIL_WORKFLOW_ENABLED setting; when false, the workflow report and settings menus are hidden, permissions are not registered, moderation-related dashboard panels on the homepage are skipped, workflow actions on add/edit page are hidden, and model methods such as page.current_workflow_state return None / False immediately without any db queries.pull/7222/head
rodzic
4dc68550bc
commit
de9588590b
|
@ -9,6 +9,7 @@ Changelog
|
|||
* Added support for customising group management views (Jan Seifert)
|
||||
* Added `full_url` property to image renditions (Shreyash Srivastava)
|
||||
* Added locale selector when choosing translatable snippets (Karl Hobley)
|
||||
* Added `WAGTAIL_WORKFLOW_ENABLED` setting for enabling / disabling moderation workflows globally (Matt Westcott)
|
||||
* Fix: Invalid filter values for foreign key fields in the API now give an error instead of crashing (Tidjani Dia)
|
||||
* Fix: Ordering specified in `construct_explorer_page_queryset` hook is now taken into account again by the page explorer API (Andre Fonseca)
|
||||
* Fix: Deleting a page from its listing view no longer results in a 404 error (Tidjani Dia)
|
||||
|
|
|
@ -712,6 +712,12 @@ When true, HTML tags in form field help text will be rendered unescaped (default
|
|||
Workflow
|
||||
========
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
WAGTAIL_WORKFLOW_ENABLED = False
|
||||
|
||||
Specifies whether moderation workflows are enabled (default: True). When disabled, editors will no longer be given the option to submit pages to a workflow, and the settings areas for admins to configure workflows and tasks will be unavailable.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
WAGTAIL_WORKFLOW_REQUIRE_REAPPROVAL_ON_EDIT = True
|
||||
|
|
|
@ -17,6 +17,7 @@ Other features
|
|||
* Added support for customising group management views. See :ref:`customising_group_views`. (Jan Seifert)
|
||||
* Added ``full_url`` property to image renditions (Shreyash Srivastava)
|
||||
* Added locale selector when choosing translatable snippets (Karl Hobley)
|
||||
* Added ``WAGTAIL_WORKFLOW_ENABLED`` setting for enabling / disabling moderation workflows globally (Matt Westcott)
|
||||
|
||||
Bug fixes
|
||||
~~~~~~~~~
|
||||
|
|
|
@ -116,6 +116,18 @@ class TestPageCreation(TestCase, WagtailTestUtils):
|
|||
self.assertContains(response, 'testapp/js/siren.js')
|
||||
# test construct_page_action_menu hook
|
||||
self.assertContains(response, '<button type="submit" name="action-relax" value="Relax." class="button">Relax.</button>')
|
||||
# test that workflow actions are shown
|
||||
self.assertContains(
|
||||
response, '<button type="submit" name="action-submit" value="Submit for moderation" class="button">'
|
||||
)
|
||||
|
||||
@override_settings(WAGTAIL_WORKFLOW_ENABLED=False)
|
||||
def test_workflow_buttons_not_shown_when_workflow_disabled(self):
|
||||
response = self.client.get(reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotContains(
|
||||
response, 'value="Submit for moderation"'
|
||||
)
|
||||
|
||||
def test_create_multipart(self):
|
||||
"""
|
||||
|
|
|
@ -103,6 +103,19 @@ class TestPageEdit(TestCase, WagtailTestUtils):
|
|||
self.assertContains(response,
|
||||
'<button type="submit" name="action-relax" value="Relax." class="button">Relax.</button>')
|
||||
|
||||
# test that workflow actions are shown
|
||||
self.assertContains(
|
||||
response, '<button type="submit" name="action-submit" value="Submit to Moderators approval" class="button">'
|
||||
)
|
||||
|
||||
@override_settings(WAGTAIL_WORKFLOW_ENABLED=False)
|
||||
def test_workflow_buttons_not_shown_when_workflow_disabled(self):
|
||||
response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.event_page.id, )))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotContains(
|
||||
response, 'value="Submit to Moderators approval"'
|
||||
)
|
||||
|
||||
def test_edit_draft_page_with_no_revisions(self):
|
||||
# Tests that the edit page loads
|
||||
response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.unpublished_page.id, )))
|
||||
|
|
|
@ -26,6 +26,42 @@ def delete_existing_workflows():
|
|||
WorkflowTask.objects.all().delete()
|
||||
|
||||
|
||||
class TestWorkflowMenus(TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
self.login()
|
||||
|
||||
self.editor = self.create_user(
|
||||
username='editor',
|
||||
email='editor@email.com',
|
||||
password='password',
|
||||
)
|
||||
editors = Group.objects.get(name='Editors')
|
||||
editors.user_set.add(self.editor)
|
||||
|
||||
def test_workflow_settings_and_reports_menus_are_shown_to_admin(self):
|
||||
response = self.client.get('/admin/')
|
||||
self.assertContains(response, 'href="/admin/workflows/list/"')
|
||||
self.assertContains(response, 'href="/admin/workflows/tasks/index/"')
|
||||
self.assertContains(response, 'href="/admin/reports/workflow/"')
|
||||
self.assertContains(response, 'href="/admin/reports/workflow_tasks/"')
|
||||
|
||||
def test_workflow_settings_menus_are_not_shown_to_editor(self):
|
||||
self.login(user=self.editor)
|
||||
response = self.client.get('/admin/')
|
||||
self.assertNotContains(response, 'href="/admin/workflows/list/"')
|
||||
self.assertNotContains(response, 'href="/admin/workflows/tasks/index/"')
|
||||
self.assertContains(response, 'href="/admin/reports/workflow/"')
|
||||
self.assertContains(response, 'href="/admin/reports/workflow_tasks/"')
|
||||
|
||||
@override_settings(WAGTAIL_WORKFLOW_ENABLED=False)
|
||||
def test_workflow_menus_are_hidden_when_workflows_are_disabled(self):
|
||||
response = self.client.get('/admin/')
|
||||
self.assertNotContains(response, 'href="/admin/workflows/list/"')
|
||||
self.assertNotContains(response, 'href="/admin/workflows/tasks/index/"')
|
||||
self.assertNotContains(response, 'href="/admin/reports/workflow/"')
|
||||
self.assertNotContains(response, 'href="/admin/reports/workflow_tasks/"')
|
||||
|
||||
|
||||
class TestWorkflowsIndexView(TestCase, WagtailTestUtils):
|
||||
|
||||
def setUp(self):
|
||||
|
|
|
@ -57,15 +57,18 @@ class UserPagesInWorkflowModerationPanel:
|
|||
|
||||
def __init__(self, request):
|
||||
self.request = request
|
||||
# Find in progress workflow states which are either requested by the user or on pages owned by the user
|
||||
self.workflow_states = (
|
||||
WorkflowState.objects.active()
|
||||
.filter(Q(page__owner=request.user) | Q(requested_by=request.user))
|
||||
.select_related(
|
||||
'page', 'current_task_state', 'current_task_state__task', 'current_task_state__page_revision'
|
||||
if getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
|
||||
# Find in progress workflow states which are either requested by the user or on pages owned by the user
|
||||
self.workflow_states = (
|
||||
WorkflowState.objects.active()
|
||||
.filter(Q(page__owner=request.user) | Q(requested_by=request.user))
|
||||
.select_related(
|
||||
'page', 'current_task_state', 'current_task_state__task', 'current_task_state__page_revision'
|
||||
)
|
||||
.order_by('-current_task_state__started_at')
|
||||
)
|
||||
.order_by('-current_task_state__started_at')
|
||||
)
|
||||
else:
|
||||
self.workflow_states = WorkflowState.objects.none()
|
||||
|
||||
def render(self):
|
||||
return render_to_string('wagtailadmin/home/user_pages_in_workflow_moderation.html', {
|
||||
|
@ -79,15 +82,18 @@ class WorkflowPagesToModeratePanel:
|
|||
|
||||
def __init__(self, request):
|
||||
self.request = request
|
||||
states = (
|
||||
TaskState.objects.reviewable_by(request.user)
|
||||
.select_related('page_revision', 'task', 'page_revision__page')
|
||||
.order_by('-started_at')
|
||||
)
|
||||
self.states = [
|
||||
(state, state.task.specific.get_actions(page=state.page_revision.page, user=request.user), state.workflow_state.all_tasks_with_status())
|
||||
for state in states
|
||||
]
|
||||
if getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
|
||||
states = (
|
||||
TaskState.objects.reviewable_by(request.user)
|
||||
.select_related('page_revision', 'task', 'page_revision__page')
|
||||
.order_by('-started_at')
|
||||
)
|
||||
self.states = [
|
||||
(state, state.task.specific.get_actions(page=state.page_revision.page, user=request.user), state.workflow_state.all_tasks_with_status())
|
||||
for state in states
|
||||
]
|
||||
else:
|
||||
self.states = []
|
||||
|
||||
def render(self):
|
||||
return render_to_string('wagtailadmin/home/workflow_pages_to_moderate.html', {
|
||||
|
|
|
@ -311,8 +311,12 @@ class EditView(TemplateResponseMixin, ContextMixin, HookResponseMixin, View):
|
|||
self.edit_handler = self.edit_handler.bind_to(instance=self.page, request=self.request)
|
||||
self.form_class = self.edit_handler.get_form_class()
|
||||
|
||||
# Retrieve current workflow state if set, default to last workflow state
|
||||
self.workflow_state = self.page.current_workflow_state or self.page.workflow_states.order_by('created_at').last()
|
||||
if getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
|
||||
# Retrieve current workflow state if set, default to last workflow state
|
||||
self.workflow_state = self.page.current_workflow_state or self.page.workflow_states.order_by('created_at').last()
|
||||
else:
|
||||
self.workflow_state = None
|
||||
|
||||
if self.workflow_state:
|
||||
self.workflow_tasks = self.workflow_state.all_tasks_with_status()
|
||||
else:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from django.conf import settings
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.urls import reverse
|
||||
from django.utils.http import urlencode
|
||||
|
@ -116,6 +117,9 @@ def register_collections_menu_item():
|
|||
|
||||
class WorkflowsMenuItem(MenuItem):
|
||||
def is_shown(self, request):
|
||||
if not getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
|
||||
return False
|
||||
|
||||
return workflow_permission_policy.user_has_any_permission(
|
||||
request.user, ['add', 'change', 'delete']
|
||||
)
|
||||
|
@ -123,6 +127,9 @@ class WorkflowsMenuItem(MenuItem):
|
|||
|
||||
class WorkflowTasksMenuItem(MenuItem):
|
||||
def is_shown(self, request):
|
||||
if not getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
|
||||
return False
|
||||
|
||||
return task_permission_policy.user_has_any_permission(
|
||||
request.user, ['add', 'change', 'delete']
|
||||
)
|
||||
|
@ -617,7 +624,7 @@ class LockedPagesMenuItem(MenuItem):
|
|||
|
||||
class WorkflowReportMenuItem(MenuItem):
|
||||
def is_shown(self, request):
|
||||
return True
|
||||
return getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True)
|
||||
|
||||
|
||||
class SiteHistoryReportMenuItem(MenuItem):
|
||||
|
|
|
@ -2835,10 +2835,15 @@ class Page(AbstractPage, index.Indexed, ClusterableModel, metaclass=PageBase):
|
|||
@property
|
||||
def has_workflow(self):
|
||||
"""Returns True if the page or an ancestor has an active workflow assigned, otherwise False"""
|
||||
if not getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
|
||||
return False
|
||||
return self.get_ancestors(inclusive=True).filter(workflowpage__isnull=False).filter(workflowpage__workflow__active=True).exists()
|
||||
|
||||
def get_workflow(self):
|
||||
"""Returns the active workflow assigned to the page or its nearest ancestor"""
|
||||
if not getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
|
||||
return None
|
||||
|
||||
if hasattr(self, 'workflowpage') and self.workflowpage.workflow.active:
|
||||
return self.workflowpage.workflow
|
||||
else:
|
||||
|
@ -2852,11 +2857,15 @@ class Page(AbstractPage, index.Indexed, ClusterableModel, metaclass=PageBase):
|
|||
@property
|
||||
def workflow_in_progress(self):
|
||||
"""Returns True if a workflow is in progress on the current page, otherwise False"""
|
||||
if not getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
|
||||
return False
|
||||
return WorkflowState.objects.filter(page=self, status=WorkflowState.STATUS_IN_PROGRESS).exists()
|
||||
|
||||
@property
|
||||
def current_workflow_state(self):
|
||||
"""Returns the in progress or needs changes workflow state on this page, if it exists"""
|
||||
if not getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
|
||||
return None
|
||||
try:
|
||||
return WorkflowState.objects.active().select_related("current_task_state__task").get(page=self)
|
||||
except WorkflowState.DoesNotExist:
|
||||
|
|
|
@ -102,6 +102,27 @@ class TestWorkflows(TestCase):
|
|||
self.assertTrue(workflow_2.all_pages().filter(id=hello_page.id).exists())
|
||||
self.assertTrue(workflow_2.all_pages().filter(id=goodbye_page.id).exists())
|
||||
|
||||
@override_settings(WAGTAIL_WORKFLOW_ENABLED=False)
|
||||
def test_workflow_methods_generate_no_queries_when_disabled(self):
|
||||
homepage = Page.objects.get(url_path='/home/')
|
||||
with self.assertNumQueries(0):
|
||||
self.assertEqual(homepage.has_workflow, False)
|
||||
|
||||
with self.assertNumQueries(0):
|
||||
self.assertEqual(homepage.get_workflow(), None)
|
||||
|
||||
with self.assertNumQueries(0):
|
||||
self.assertEqual(homepage.workflow_in_progress, False)
|
||||
|
||||
with self.assertNumQueries(0):
|
||||
self.assertEqual(homepage.current_workflow_state, None)
|
||||
|
||||
with self.assertNumQueries(0):
|
||||
self.assertEqual(homepage.current_workflow_task_state, None)
|
||||
|
||||
with self.assertNumQueries(0):
|
||||
self.assertEqual(homepage.current_workflow_task, None)
|
||||
|
||||
@freeze_time("2017-01-01 12:00:00")
|
||||
def test_start_workflow_on_page(self):
|
||||
# test the first WorkflowState and TaskState models are set up correctly when Workflow.start(page) is used.
|
||||
|
|
|
@ -64,20 +64,20 @@ def register_collection_permissions():
|
|||
)
|
||||
|
||||
|
||||
@hooks.register('register_permissions')
|
||||
def register_workflow_permissions():
|
||||
return Permission.objects.filter(
|
||||
content_type__app_label='wagtailcore',
|
||||
codename__in=['add_workflow', 'change_workflow', 'delete_workflow']
|
||||
)
|
||||
if getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
|
||||
@hooks.register('register_permissions')
|
||||
def register_workflow_permissions():
|
||||
return Permission.objects.filter(
|
||||
content_type__app_label='wagtailcore',
|
||||
codename__in=['add_workflow', 'change_workflow', 'delete_workflow']
|
||||
)
|
||||
|
||||
|
||||
@hooks.register('register_permissions')
|
||||
def register_task_permissions():
|
||||
return Permission.objects.filter(
|
||||
content_type__app_label='wagtailcore',
|
||||
codename__in=['add_task', 'change_task', 'delete_task']
|
||||
)
|
||||
@hooks.register('register_permissions')
|
||||
def register_task_permissions():
|
||||
return Permission.objects.filter(
|
||||
content_type__app_label='wagtailcore',
|
||||
codename__in=['add_task', 'change_task', 'delete_task']
|
||||
)
|
||||
|
||||
|
||||
@hooks.register('describe_collection_contents')
|
||||
|
|
Ładowanie…
Reference in New Issue