Browse Source

Move inline script for activating workflow actions to workflow-action.js

Also deprecate the `window.ActivateWorkflowActionsForDashboard` and
`window.ActivateWorkflowActionsForEditView` functions as they no longer
need to be globally-accessible
Sage Abdullah 9 months ago
parent
commit
063c52e1f2

+ 53 - 1
client/src/entrypoints/admin/workflow-action.js

@@ -1,4 +1,5 @@
 import $ from 'jquery';
+import { WAGTAIL_CONFIG } from '../../config/wagtailConfig';
 
 function addHiddenInput(form, name, val) {
   const element = document.createElement('input');
@@ -11,10 +12,11 @@ function addHiddenInput(form, name, val) {
 window._addHiddenInput = addHiddenInput;
 
 /* When a workflow action button is clicked, either show a modal or make a POST request to the workflow action view */
-function ActivateWorkflowActionsForDashboard(csrfToken) {
+function ActivateWorkflowActionsForDashboard() {
   const workflowActionElements = document.querySelectorAll(
     '[data-workflow-action-url]',
   );
+  const csrfToken = WAGTAIL_CONFIG.CSRF_TOKEN;
 
   workflowActionElements.forEach((buttonElement) => {
     buttonElement.addEventListener(
@@ -59,6 +61,7 @@ function ActivateWorkflowActionsForDashboard(csrfToken) {
     );
   });
 }
+/** @deprecated RemovedInWagtail70 - Remove global.ActivateWorkflowActionsForDashboard usage  */
 window.ActivateWorkflowActionsForDashboard =
   ActivateWorkflowActionsForDashboard;
 
@@ -121,4 +124,53 @@ function ActivateWorkflowActionsForEditView(formSelector) {
     );
   });
 }
+/** @deprecated RemovedInWagtail70 - Remove global.ActivateWorkflowActionsForEditView usage  */
 window.ActivateWorkflowActionsForEditView = ActivateWorkflowActionsForEditView;
+
+const currentScript = document.currentScript;
+const activateTarget = currentScript.dataset.activate;
+const cancellationUrl = currentScript.dataset.confirmCancellationUrl;
+
+document.addEventListener('DOMContentLoaded', () => {
+  if (activateTarget === 'dashboard') {
+    ActivateWorkflowActionsForDashboard();
+  } else if (activateTarget === 'editor') {
+    ActivateWorkflowActionsForEditView('[data-edit-form]');
+  }
+
+  if (cancellationUrl) {
+    /* Make user confirm before publishing the object if it will cancel an ongoing workflow */
+    let cancellationConfirmed = false;
+    $('[name=action-publish]').click((e) => {
+      if (!cancellationConfirmed) {
+        e.stopImmediatePropagation();
+        e.preventDefault();
+        window.ModalWorkflow({
+          url: cancellationUrl,
+          onload: {
+            // eslint-disable-next-line @typescript-eslint/no-unused-vars
+            confirm(modal, jsonData) {
+              // eslint-disable-next-line @typescript-eslint/no-unused-vars
+              $('[data-confirm-cancellation]', modal.body).click((event) => {
+                cancellationConfirmed = true;
+                modal.close();
+                e.currentTarget.click();
+              });
+              // eslint-disable-next-line @typescript-eslint/no-unused-vars
+              $('[data-cancel-dialog]', modal.body).click((event) => {
+                modal.close();
+              });
+            },
+            // eslint-disable-next-line @typescript-eslint/no-unused-vars
+            no_confirmation_needed(modal, jsonData) {
+              modal.close();
+              cancellationConfirmed = true;
+              e.currentTarget.click();
+            },
+          },
+          triggerElement: e.currentTarget,
+        });
+      }
+    });
+  }
+});

+ 4 - 0
docs/extending/custom_tasks.md

@@ -1,3 +1,5 @@
+(custom_tasks)=
+
 # Adding new Task types
 
 The Workflow system allows users to create tasks, which represent stages of moderation.
@@ -125,6 +127,8 @@ class UserApprovalTask(Task):
     task_state_class = UserApprovalTaskState
 ```
 
+(custom_tasks_behavior)=
+
 ## Customising behaviour
 
 Both `Task` and `TaskState` have a number of methods that can be overridden to implement custom behavior. Here are some of the most useful:

+ 8 - 0
docs/releases/6.2.md

@@ -391,3 +391,11 @@ If you need to completely customize the view's template, you can still override
 If you override `template_name`, it is still necessary to set `results_template_name` to a template that extends `wagtailadmin/reports/base_report_results.html` (or `wagtailadmin/reports/base_page_report_results.html` for page reports), so the view can correctly update the listing and show the active filters as you apply or remove any filters.
 
 ## Upgrade considerations - changes to undocumented internals
+
+### Deprecation of `window.ActivateWorkflowActionsForDashboard` and `window.ActivateWorkflowActionsForEditView`
+
+The undocumented usage of the JavaScript `window.ActivateWorkflowActionsForDashboard` and `window.ActivateWorkflowActionsForEditView` functions will be removed in a future release.
+
+These functions are only used by Wagtail to initialize event listeners to workflow action buttons via inline scripts and are never intended to be used in custom code. The inline scripts have been removed in favour of initializing the event listeners directly in the included `workflow-action.js` script. As a result, the functions no longer need to be globally-accessible.
+
+Any custom workflow actions should be done using the [documented approach for customising the behavior of custom task types](custom_tasks_behavior), such as by overriding `Task.get_actions()`.

+ 1 - 4
wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html

@@ -89,8 +89,5 @@
         </table>
     {% endpanel %}
 
-    <script src="{% versioned_static 'wagtailadmin/js/workflow-action.js' %}"></script>
-    <script>
-        document.addEventListener('DOMContentLoaded', () => ActivateWorkflowActionsForDashboard('{{ csrf_token|escapejs }}'));
-    </script>
+    <script src="{% versioned_static 'wagtailadmin/js/workflow-action.js' %}" data-activate="dashboard"></script>
 {% endif %}

+ 0 - 1
wagtail/admin/templates/wagtailadmin/pages/_editor_js.html

@@ -9,5 +9,4 @@
 <script src="{% versioned_static 'wagtailadmin/js/expanding-formset.js' %}"></script>
 <script src="{% versioned_static 'wagtailadmin/js/preview-panel.js' %}"></script>
 <script src="{% versioned_static 'wagtailadmin/js/privacy-switch.js' %}"></script>
-<script src="{% versioned_static 'wagtailadmin/js/workflow-action.js' %}"></script>
 {% hook_output 'insert_editor_js' %}

+ 8 - 36
wagtail/admin/templates/wagtailadmin/shared/_workflow_init.html

@@ -1,37 +1,9 @@
-<script>
-    $(function() {
-        ActivateWorkflowActionsForEditView('[data-edit-form]');
-
-        {% if publishing_will_cancel_workflow %}
-            /* Make user confirm before publishing the object if it will cancel an ongoing workflow */
-            let cancellationConfirmed = false;
-            $('[name=action-publish]').click((e) => {
-                if (!cancellationConfirmed) {
-                    e.stopImmediatePropagation();
-                    e.preventDefault();
-                    ModalWorkflow({
-                        'url': "{{ confirm_workflow_cancellation_url }}",
-                        'onload': {
-                            'confirm': function(modal, jsonData) {
-                                $('[data-confirm-cancellation]', modal.body).click((event) => {
-                                    cancellationConfirmed = true;
-                                    modal.close();
-                                    e.currentTarget.click();
-                                })
-                                $('[data-cancel-dialog]', modal.body).click((event) => {
-                                    modal.close();
-                                })
-                            },
-                            'no_confirmation_needed': function(modal, jsonData) {
-                                modal.close();
-                                cancellationConfirmed = true;
-                                e.currentTarget.click();
-                            }
-                        },
-                        'triggerElement': e.currentTarget,
-                    });
-                }
-            });
-        {% endif %}
-    });
+{% load wagtailadmin_tags %}
+<script
+    src="{% versioned_static 'wagtailadmin/js/workflow-action.js' %}"
+    data-activate="editor"
+    {% if publishing_will_cancel_workflow %}
+        data-confirm-cancellation-url="{{ confirm_workflow_cancellation_url }}"
+    {% endif %}
+>
 </script>

+ 47 - 4
wagtail/admin/tests/test_workflows.py

@@ -20,6 +20,7 @@ from wagtail.admin.mail import (
     WorkflowStateApprovalEmailNotifier,
     WorkflowStateRejectionEmailNotifier,
 )
+from wagtail.admin.staticfiles import versioned_static
 from wagtail.admin.utils import (
     get_admin_base_url,
     get_latest_str,
@@ -2508,11 +2509,53 @@ class TestApproveRejectPageWorkflow(BasePageWorkflowTests):
     def test_workflow_dashboard_panel(self):
         response = self.client.get(reverse("wagtailadmin_home"))
         self.assertContains(response, "Awaiting your review")
-        # check that ActivateWorkflowActionsForDashboard is present and passes a valid csrf token
-        self.assertRegex(
-            response.content.decode("utf-8"),
-            r"ActivateWorkflowActionsForDashboard\(\'\w+\'\)",
+        soup = self.get_soup(response.content)
+        # check that the workflow-action script is present with the correct data-activate attribute
+        workflow_action_js = versioned_static("wagtailadmin/js/workflow-action.js")
+        scripts = soup.select(f"script[src='{workflow_action_js}']")
+        self.assertEqual(len(scripts), 1)
+        script = scripts[0]
+        self.assertIsNotNone(script)
+        self.assertEqual(script.get("data-activate"), "dashboard")
+        # Should no longer contain inline JS for activating the workflow actions
+        self.assertNotContains(response, "ActivateWorkflowActionsForDashboard")
+
+    def test_workflow_action_script_included(self):
+        response = self.client.get(self.get_url("edit"))
+        self.assertEqual(response.status_code, 200)
+        soup = self.get_soup(response.content)
+        # check that the workflow-action script is present with the correct
+        # data-activate and data-confirm-cancellation-url attributes
+        workflow_action_js = versioned_static("wagtailadmin/js/workflow-action.js")
+        scripts = soup.select(f"script[src='{workflow_action_js}']")
+        self.assertEqual(len(scripts), 1)
+        script = scripts[0]
+        self.assertIsNotNone(script)
+        self.assertEqual(script.get("data-activate"), "editor")
+        self.assertEqual(
+            script.get("data-confirm-cancellation-url"),
+            self.get_url("confirm_workflow_cancellation"),
         )
+        # Should no longer contain inline JS for activating the workflow actions
+        self.assertNotContains(response, "ActivateWorkflowActionsForEditView")
+
+    @override_settings(WAGTAIL_WORKFLOW_CANCEL_ON_PUBLISH=False)
+    def test_workflow_action_script_included_without_cancel_confirmation(self):
+        response = self.client.get(self.get_url("edit"))
+        self.assertEqual(response.status_code, 200)
+        soup = self.get_soup(response.content)
+        # check that the workflow-action script is present with the correct data-activate attribute
+        workflow_action_js = versioned_static("wagtailadmin/js/workflow-action.js")
+        scripts = soup.select(f"script[src='{workflow_action_js}']")
+        self.assertEqual(len(scripts), 1)
+        script = scripts[0]
+        self.assertIsNotNone(script)
+        self.assertEqual(script.get("data-activate"), "editor")
+        # data-confirm-cancellation-url attribute should not be present as
+        # WAGTAIL_WORKFLOW_CANCEL_ON_PUBLISH is set to False
+        self.assertIsNone(script.get("data-confirm-cancellation-url"))
+        # Should no longer contain inline JS for activating the workflow actions
+        self.assertNotContains(response, "ActivateWorkflowActionsForEditView")
 
     def test_workflow_action_get(self):
         """