Use Task.get_template_for_action() in workflow modal views (#12926)

pull/12649/merge
Sage Abdullah 2025-02-26 13:01:01 +00:00 zatwierdzone przez Matt Westcott
rodzic e86fdb1899
commit 32e1366534
8 zmienionych plików z 145 dodań i 2 usunięć

Wyświetl plik

@ -16,6 +16,7 @@ Changelog
* Add better support and documentation for overriding or extending icons used in the in the userbar (Sébastien Corbin)
* List the comments action, if comments are enabled, within the admin keyboard shortcuts dialog (Dhruvi Patel)
* Add better support and documentation for overriding the default field widgets used within form pages (Baptiste Mispelon)
* Allow workflow tasks to specify a template for the action modal via `get_template_for_action` (Sage Abdullah)
* Fix: Take preferred language into account for translatable strings in client-side code (Bernhard Bliem, Sage Abdullah)
* Fix: Do not show the content type column as sortable when searching pages (Srishti Jaiswal, Sage Abdullah)
* Fix: Support simple subqueries for `in` and `exact` lookup on Elasticsearch (Sage Abdullah)

Wyświetl plik

@ -30,6 +30,7 @@ This version adds formal support for Django 5.2.
* Add better support and documentation for overriding or extending [icons used in the in the userbar](custom_icons_userbar) (Sébastien Corbin)
* List the comments action, if comments are enabled, within the admin keyboard shortcuts dialog (Dhruvi Patel)
* Add better support and documentation for [overriding the default field widgets](custom_form_field_type_widgets) used within form pages (Baptiste Mispelon)
* Allow workflow tasks to specify a template for the action modal via `get_template_for_action` (Sage Abdullah)
### Bug fixes

Wyświetl plik

@ -46,6 +46,7 @@ from wagtail.test.testapp.models import (
MultiPreviewModesPage,
SimplePage,
SimpleTask,
UserApprovalTask,
)
from wagtail.test.utils import WagtailTestUtils
from wagtail.test.utils.template_tests import AdminTemplateTestUtils
@ -2705,6 +2706,62 @@ class TestApproveRejectPageWorkflow(BasePageWorkflowTests):
)
self.assertIn("Comment", html)
def test_workflow_action_get_custom_template(self):
"""
https://github.com/wagtail/wagtail/issues/12222
Custom tasks can override Task.get_template_for_action() to use a custom
template for the workflow action modal.
"""
# Add a custom task to the workflow
custom_task = UserApprovalTask.objects.create(
name="user_approval_1",
user=self.moderator,
)
WorkflowTask.objects.create(
workflow=self.workflow,
task=custom_task,
sort_order=2,
)
self.approve() # Approve the GroupApprovalTask
# Refresh from DB
self.object = self.object_class.objects.get(pk=self.object.pk)
response = self.client.get(
self.get_url(
"workflow_action",
args=(
quote(self.object.pk),
"approve",
self.object.current_workflow_task_state.id,
),
),
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "tests/workflows/approve_with_style.html")
self.assertTemplateNotUsed(
response, "wagtailadmin/shared/workflow_action_modal.html"
)
html = json.loads(response.content)["html"]
soup = self.get_soup(html)
form = soup.select_one("form")
self.assertIsNotNone(form)
self.assertEqual(
form["action"],
self.get_url(
"workflow_action",
args=(
quote(self.object.pk),
"approve",
self.object.current_workflow_task_state.id,
),
),
)
submit = form.select_one("button[type=submit]")
self.assertIsNotNone(submit)
self.assertEqual(submit.text.strip(), "Ship it!")
self.assertNotIn("Comment", html)
def test_workflow_action_view_bad_id(self):
"""
This tests that the workflow action view handles invalid object ids correctly
@ -2935,6 +2992,62 @@ class TestApproveRejectPageWorkflow(BasePageWorkflowTests):
)
self.assertIn("Comment", html)
def test_collect_workflow_action_data_get_custom_template(self):
"""
https://github.com/wagtail/wagtail/issues/12222
Custom tasks can override Task.get_template_for_action() to use a custom
template for the workflow action modal.
"""
# Add a custom task to the workflow
custom_task = UserApprovalTask.objects.create(
name="user_approval_1",
user=self.moderator,
)
WorkflowTask.objects.create(
workflow=self.workflow,
task=custom_task,
sort_order=2,
)
self.approve() # Approve the GroupApprovalTask
# Refresh from DB
self.object = self.object_class.objects.get(pk=self.object.pk)
response = self.client.get(
self.get_url(
"collect_workflow_action_data",
args=(
quote(self.object.pk),
"approve",
self.object.current_workflow_task_state.id,
),
),
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "tests/workflows/approve_with_style.html")
self.assertTemplateNotUsed(
response, "wagtailadmin/shared/workflow_action_modal.html"
)
html = json.loads(response.content)["html"]
soup = self.get_soup(html)
form = soup.select_one("form")
self.assertIsNotNone(form)
self.assertEqual(
form["action"],
self.get_url(
"collect_workflow_action_data",
args=(
quote(self.object.pk),
"approve",
self.object.current_workflow_task_state.id,
),
),
)
submit = form.select_one("button[type=submit]")
self.assertIsNotNone(submit)
self.assertEqual(submit.text.strip(), "Ship it!")
self.assertNotIn("Comment", html)
def test_collect_workflow_action_data_post(self):
"""
This tests that a POST request to the collect_workflow_action_data view (for the approve action) returns a modal response with the validated data

Wyświetl plik

@ -47,6 +47,11 @@ class BaseWorkflowFormView(BaseObjectMixin, View):
def get_form_class(self):
return self.task.get_form_for_action(self.action_name)
def get_template_names(self):
if template := self.task.get_template_for_action(self.action_name):
return [template]
return [self.template_name]
def add_not_in_moderation_error(self):
messages.error(
self.request,
@ -103,7 +108,7 @@ class BaseWorkflowFormView(BaseObjectMixin, View):
def render_modal_form(self, request, form):
return render_modal_workflow(
request,
self.template_name,
self.get_template_names(),
None,
self.get_context_data(form=form),
json_data={"step": "action"},
@ -195,6 +200,11 @@ class ConfirmWorkflowCancellation(BaseObjectMixin, View):
json_data={"step": "no_confirmation_needed"},
)
# This confirmation step is specific to the
# `WAGTAIL_WORKFLOW_CANCEL_ON_PUBLISH` setting that happens when a user
# publishes a page with a workflow in progress, which is different from
# a "cancel" action on the task. So, we use `self.template_name`
# directly and not make it customisable.
return render_modal_workflow(
request,
self.template_name,

Wyświetl plik

@ -780,6 +780,9 @@ class Task(SpecificMixin, models.Model):
return TaskStateCommentForm
def get_template_for_action(self, action):
"""
Specifies a template for the workflow action modal.
"""
return ""
def get_task_states_user_can_moderate(self, user, **kwargs):

Wyświetl plik

@ -8,7 +8,7 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("tests", "0047_advertwithcustomuuidprimarykey_page"),
("tests", "0050_headcountrelatedmodelusingpk_related_page"),
("wagtailcore", "0094_alter_page_locale"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

Wyświetl plik

@ -2375,12 +2375,21 @@ class UserApprovalTask(Task):
if user == self.user:
return [
("approve", "Approve", False),
("approve", "Approve with style", True),
("reject", "Reject", False),
("cancel", "Cancel", False),
]
else:
return []
def get_template_for_action(self, action):
# https://github.com/wagtail/wagtail/issues/12222
# This will be used for "Approve with style" which has the third value
# (action_requires_additional_data_from_modal) set to True.
if action == "approve":
return "tests/workflows/approve_with_style.html"
return super().get_template_for_action(action)
def on_action(self, task_state, user, action_name, **kwargs):
if action_name == "cancel":
return task_state.workflow_state.cancel(user=user)

Wyświetl plik

@ -0,0 +1,6 @@
{% include "wagtailadmin/shared/header.html" with title=action_verbose icon="clipboard-list" %}
<form class="nice-padding" action="{{ submit_url }}" method="POST" novalidate>
{% csrf_token %}
<button class="button" type="submit">Ship it!</button>
</form>