ソースを参照

Migrate lock/unlock actions to w-action controller

- Revise 'redirect' from a string to a 'continue' boolean that defaults to false
- Use 'continue=true' for cases where we do not want to create a next param on submit that takes the user back to the current page
- Fixes #9815
Lovelyfin00 2 年 前
コミット
b929694203

+ 1 - 1
CHANGELOG.txt

@@ -119,7 +119,7 @@ Changelog
  * Maintenance: Refactor userbar styles to use the same stylesheet as other components (Thibaud Colas)
  * Maintenance: Add deprecation warnings for `wagtail.core` and other imports deprecated in Wagtail 3.0 (Matt Westcott)
  * Maintenance: Migrate admin upgrade notification message implementation to a Stimulus controller (Loveth Omokaro)
- * Maintenance: Migrate workflow and workflow tasks enable action to a Stimulus controller (Loveth Omokaro)
+ * Maintenance: Migrate workflow and workflow tasks enable action and lock/unlock actions to a Stimulus controller (Loveth Omokaro)
  * Maintenance: Pull out icon sprite setup function from inline script to its own TypeScript file & add unit tests (Loveth Omokaro)
 
 

+ 39 - 0
client/src/controllers/ActionController.stories.js

@@ -0,0 +1,39 @@
+import React from 'react';
+
+import { StimulusWrapper } from '../../storybook/StimulusWrapper';
+import { ActionController } from './ActionController';
+
+export default {
+  title: 'Shared / ActionController',
+  argTypes: {
+    debug: {
+      control: 'boolean',
+      defaultValue: false,
+    },
+  },
+};
+
+const definitions = [
+  {
+    identifier: 'w-action',
+    controllerConstructor: ActionController,
+  },
+];
+
+const Template = ({ debug = false }) => (
+  <StimulusWrapper debug={debug} definitions={definitions}>
+    <button
+      type="button"
+      className="button button-small button-secondary"
+      data-controller="w-action"
+      data-action="w-action#post"
+      data-w-action-url-value={window.location.href}
+    >
+      Lock
+    </button>
+
+    <p>Click to lock post and redirect</p>
+  </StimulusWrapper>
+);
+
+export const Base = Template.bind({});

+ 2 - 2
client/src/controllers/ActionController.test.js

@@ -16,17 +16,17 @@ describe('ActionController', () => {
     Application.start().register('w-action', ActionController);
   });
 
-  it('it should enable the workflow on click', () => {
+  it('it should enable the workflow, lock and Unlock button', () => {
     const btn = document.querySelector('[data-controller="w-action"]');
     const submitMock = jest.fn();
     window.HTMLFormElement.prototype.submit = submitMock;
 
     btn.click();
-
     const form = document.querySelector('form');
 
     expect(submitMock).toHaveBeenCalled();
     expect(form.action).toBe('https://www.github.com/');
     expect(new FormData(form).get('csrfmiddlewaretoken')).toBe('potato');
+    expect(new FormData(form).get('next')).toBe('http://localhost/');
   });
 });

+ 17 - 8
client/src/controllers/ActionController.ts

@@ -2,20 +2,26 @@ import { Controller } from '@hotwired/stimulus';
 import { WAGTAIL_CONFIG } from '../config/wagtailConfig';
 
 /**
- * <button type="submit" class="button no"
- * data-controller="w-action"
- * data-action="click->w-action#post"
- * data-w-action-redirect-value="true"
- * data-w-action-url-value = '{{ view.get_enable_url }}'>Enable</button>
+ *
+ * @example
+ * <button
+ *  type="submit"
+ *  class="button no"
+ *  data-controller="w-action"
+ *  data-action="w-action#post"
+ *  data-w-action-url-value='url/to/post/to'
+ * >
+ *  Enable
+ * </button>
  */
 export class ActionController extends Controller {
   static values = {
-    redirect: String,
+    continue: { type: Boolean, default: false },
     url: String,
   };
 
+  continueValue: boolean;
   urlValue: string;
-  redirectValue: any;
 
   post(event: Event) {
     event.preventDefault();
@@ -32,7 +38,10 @@ export class ActionController extends Controller {
     csrftokenElement.value = WAGTAIL_CONFIG.CSRF_TOKEN;
     formElement.appendChild(csrftokenElement);
 
-    if (this.redirectValue) {
+    /** If continue is false, pass the current URL as the next param
+     * so that the user is redirected back to the current page instead
+     * of continuing to the submitted page */
+    if (!this.continueValue) {
       const nextElement = document.createElement('input');
       nextElement.type = 'hidden';
       nextElement.name = 'next';

+ 1 - 1
client/src/controllers/index.ts

@@ -10,7 +10,7 @@ import { UpgradeController } from './UpgradeController';
  * Important: Only add default core controllers that should load with the base admin JS bundle.
  */
 export const coreControllerDefinitions: Definition[] = [
-  // Keep this list alphabetized.
+  // Keep this list in alphabetical order
   { controllerConstructor: ActionController, identifier: 'w-action' },
   { controllerConstructor: AutoFieldController, identifier: 'w-auto-field' },
   { controllerConstructor: SkipLinkController, identifier: 'w-skip-link' },

+ 0 - 37
client/src/entrypoints/admin/lock-unlock-action.js

@@ -1,37 +0,0 @@
-/* When a lock/unlock action button is clicked, make a POST request to the relevant view */
-
-function LockUnlockAction(csrfToken, next) {
-  const actionElements = document.querySelectorAll('[data-action-lock-unlock]');
-  actionElements.forEach((buttonElement) => {
-    buttonElement.addEventListener(
-      'click',
-      (e) => {
-        e.stopPropagation();
-
-        const formElement = document.createElement('form');
-
-        formElement.action = buttonElement.dataset.url;
-        formElement.method = 'POST';
-
-        const csrftokenElement = document.createElement('input');
-        csrftokenElement.type = 'hidden';
-        csrftokenElement.name = 'csrfmiddlewaretoken';
-        csrftokenElement.value = csrfToken;
-        formElement.appendChild(csrftokenElement);
-
-        if (typeof next !== 'undefined') {
-          const nextElement = document.createElement('input');
-          nextElement.type = 'hidden';
-          nextElement.name = 'next';
-          nextElement.value = next;
-          formElement.appendChild(nextElement);
-        }
-
-        document.body.appendChild(formElement);
-        formElement.submit();
-      },
-      { capture: true },
-    );
-  });
-}
-window.LockUnlockAction = LockUnlockAction;

+ 0 - 1
client/webpack.config.js

@@ -41,7 +41,6 @@ module.exports = function exports(env, argv) {
       'expanding-formset',
       'filtered-select',
       'icons',
-      'lock-unlock-action',
       'modal-workflow',
       'page-chooser-modal',
       'page-chooser',

+ 1 - 1
docs/releases/4.2.md

@@ -158,7 +158,7 @@ Thank you to all who provided feedback, participants to our usability testing se
  * Refactor userbar styles to use the same stylesheet as other components (Thibaud Colas)
  * Add deprecation warnings for `wagtail.core` and other imports deprecated in Wagtail 3.0 (Matt Westcott)
  * Migrate admin upgrade notification message implementation to a Stimulus controller (Loveth Omokaro)
- * Migrate workflow and workflow tasks enable action to a Stimulus controller (Loveth Omokaro)
+ * Migrate workflow and workflow tasks enable action and lock/unlock actions to a Stimulus controller (Loveth Omokaro)
  * Pull out icon sprite setup function from inline script to its own TypeScript file & add unit tests (Loveth Omokaro)
 
 ## Upgrade considerations

+ 11 - 5
wagtail/admin/templates/wagtailadmin/home/locked_pages.html

@@ -31,7 +31,17 @@
                             </div>
                             <ul class="actions">
                                 {% if can_remove_locks %}
-                                    <li><button data-action-lock-unlock data-url="{% url 'wagtailadmin_pages:unlock' page.id %}" class="button button-small button-secondary">{% trans "Unlock" %}</button></li>
+                                    <li>
+                                        <button
+                                            class="button button-small button-secondary"
+                                            data-controller="w-action"
+                                            data-action="w-action#post"
+                                            data-w-action-next-value="{% url 'wagtailadmin_home' %}"
+                                            data-w-action-url-value="{% url 'wagtailadmin_pages:unlock' page.id %}"
+                                        >
+                                            {% trans "Unlock" %}
+                                        </button>
+                                    </li>
                                 {% endif %}
                                 <li><a href="{% url 'wagtailadmin_pages:edit' page.id %}" class="button button-small button-secondary">{% trans "Edit" %}</a></li>
                                 {% if page.has_unpublished_changes and page.is_previewable %}
@@ -53,8 +63,4 @@
             </tbody>
         </table>
     {% endpanel %}
-    <script src="{% versioned_static 'wagtailadmin/js/lock-unlock-action.js' %}"></script>
-    <script>
-        document.addEventListener('DOMContentLoaded', () => LockUnlockAction('{{ csrf_token|escapejs }}', '{% url 'wagtailadmin_home' %}'));
-    </script>
 {% endif %}

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

@@ -28,6 +28,5 @@
 <script src="{% versioned_static 'wagtailadmin/js/vendor/urlify.js' %}"></script>
 <script src="{% versioned_static 'wagtailadmin/js/workflow-action.js' %}"></script>
 <script src="{% versioned_static 'wagtailadmin/js/workflow-status.js' %}"></script>
-<script src="{% versioned_static 'wagtailadmin/js/lock-unlock-action.js' %}"></script>
 <script src="{% versioned_static 'wagtailadmin/js/vendor/bootstrap-tooltip.js' %}"></script>
 {% hook_output 'insert_editor_js' %}

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

@@ -130,7 +130,6 @@
             });
 
             ActivateWorkflowActionsForEditView('#page-edit-form');
-            LockUnlockAction('{{ csrf_token|escapejs }}', '{% url 'wagtailadmin_pages:edit' page.id %}');
 
             {% get_comments_enabled as comments_enabled %}
             {% if comments_enabled %}

+ 0 - 4
wagtail/admin/templates/wagtailadmin/pages/index.html

@@ -25,10 +25,6 @@
 
 {% block extra_js %}
     {{ block.super }}
-    <script src="{% versioned_static 'wagtailadmin/js/lock-unlock-action.js' %}"></script>
-    <script>
-        document.addEventListener('DOMContentLoaded', () => LockUnlockAction('{{ csrf_token|escapejs }}', '{% url 'wagtailadmin_home' %}'));
-    </script>
 
     {% comment %} modal-workflow is required by the view restrictions interface {% endcomment %}
     <script src="{% versioned_static 'wagtailadmin/js/modal-workflow.js' %}"></script>

+ 1 - 1
wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/side_panel_button.html

@@ -9,7 +9,7 @@
     attr - custom attributes for the button
 {% endcomment %}
 {% if text %}
-    <button type="button" class="{{ classname }} w-bg-transparent w-text-14 w-p-0 w-text-secondary hover:w-text-secondary-600 w-inline-flex w-justify-center w-transition" {% if data_url %}data-url="{{ data_url }}" {% endif %}{% if has_toggle %}data-button-with-dropdown-toggle {% endif %}{{ attr }}>
+    <button type="button" class="{{ classname }} w-bg-transparent w-text-14 w-p-0 w-text-secondary hover:w-text-secondary-600 w-inline-flex w-justify-center w-transition" {% if data_url %}data-controller="w-action" data-action="w-action#post" data-w-action-url-value="{{ data_url }}" {% endif %}{% if has_toggle %}data-button-with-dropdown-toggle {% endif %}{{ attr }}>
         {{ text }}
         {% if has_toggle %}
             {% icon name='arrow-down' class_name='w-w-4 w-h-4 w-transition motion-reduce:w-transition-none' %}

+ 2 - 2
wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/locked.html

@@ -78,10 +78,10 @@
 {% block action %}
     {% if user_can_unlock and lock %}
         {% trans 'Unlock' as unlock_text %}
-        {% include 'wagtailadmin/shared/side_panels/includes/side_panel_button.html' with attr='data-action-lock-unlock' data_url=unlock_url text=unlock_text %}
+        {% include 'wagtailadmin/shared/side_panels/includes/side_panel_button.html' with data_url=unlock_url text=unlock_text %}
     {% endif %}
     {% if user_can_lock and not lock %}
         {% trans 'Lock' as lock_text %}
-        {% include 'wagtailadmin/shared/side_panels/includes/side_panel_button.html' with attr='data-action-lock-unlock' data_url=lock_url text=lock_text %}
+        {% include 'wagtailadmin/shared/side_panels/includes/side_panel_button.html' with data_url=lock_url text=lock_text %}
     {% endif %}
 {% endblock %}

+ 8 - 4
wagtail/admin/templates/wagtailadmin/workflows/edit.html

@@ -58,11 +58,15 @@
                                 <ul>
                                     {% if can_enable %}
                                         <li>
-                                            <button class="button no"
-                                                data-action="w-action#post"
+                                            <button
+                                                class="button no"
                                                 data-controller="w-action"
-                                                data-w-action-url-value="{{ view.get_enable_url }}"
-                                                type="submit">{{ view.enable_item_label }}
+                                                data-action="w-action#post"
+                                                data-w-action-continue-value="true"
+                                                data-w-action-url-value='{{ view.get_enable_url }}'
+                                                type="submit"
+                                            >
+                                                {{ view.enable_item_label }}
                                             </button>
                                         </li>
                                     {% elif can_disable %}

+ 8 - 4
wagtail/admin/templates/wagtailadmin/workflows/edit_task.html

@@ -49,11 +49,15 @@
                                 <ul>
                                     {% if can_enable %}
                                         <li>
-                                            <button class="button no"
-                                                data-action="w-action#post"
+                                            <button
+                                                class="button no"
                                                 data-controller="w-action"
-                                                data-w-action-url-value="{{ view.get_enable_url }}"
-                                                type="submit">{{ view.enable_item_label }}
+                                                data-action="w-action#post"
+                                                data-w-action-continue-value="true"
+                                                data-w-action-url-value = '{{ view.get_enable_url }}'
+                                                type="submit"
+                                            >
+                                                {{ view.enable_item_label }}
                                             </button>
                                         </li>
                                     {% elif can_disable %}

+ 2 - 5
wagtail/admin/tests/pages/test_page_locking.py

@@ -154,11 +154,8 @@ class TestLocking(TestCase, WagtailTestUtils):
         self.child_page.save()
         response = self.client.get(reverse("wagtailadmin_home"))
         self.assertContains(response, "Your locked pages")
-        # check that LockUnlockAction is present and passes a valid csrf token
-        self.assertRegex(
-            response.content.decode("utf-8"),
-            r"LockUnlockAction\(\'\w+\'\, \'\/admin\/'\)",
-        )
+        # check that Unlock button is present
+        self.assertContains(response, "Unlock")
 
     def test_unlock_post(self):
         # Lock the page

+ 2 - 2
wagtail/admin/views/generic/mixins.py

@@ -433,7 +433,7 @@ class CreateEditViewOptionalFeaturesMixin:
         if lock_message:
             if user_can_unlock:
                 lock_message = format_html(
-                    '{} <span class="buttons"><button type="button" class="button button-small button-secondary" data-action-lock-unlock data-url="{}">{}</button></span>',
+                    '{} <span class="buttons"><button type="button" class="button button-small button-secondary" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{}">{}</button></span>',
                     lock_message,
                     self.get_unlock_url(),
                     _("Unlock"),
@@ -441,7 +441,7 @@ class CreateEditViewOptionalFeaturesMixin:
 
             if user_can_unschedule:
                 lock_message = format_html(
-                    '{} <span class="buttons"><button type="button" class="button button-small button-secondary" data-action-lock-unlock data-url="{}">{}</button></span>',
+                    '{} <span class="buttons"><button type="button" class="button button-small button-secondary" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{}">{}</button></span>',
                     lock_message,
                     reverse(
                         self.revisions_unschedule_url_name,

+ 2 - 2
wagtail/admin/views/pages/edit.py

@@ -391,7 +391,7 @@ class EditView(TemplateResponseMixin, ContextMixin, HookResponseMixin, View):
             if lock_message:
                 if isinstance(self.lock, BasicLock) and self.page_perms.can_unlock():
                     lock_message = format_html(
-                        '{} <span class="buttons"><button type="button" class="button button-small button-secondary" data-action-lock-unlock data-url="{}">{}</button></span>',
+                        '{} <span class="buttons"><button type="button" class="button button-small button-secondary" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{}">{}</button></span>',
                         lock_message,
                         reverse("wagtailadmin_pages:unlock", args=(self.page.id,)),
                         _("Unlock"),
@@ -402,7 +402,7 @@ class EditView(TemplateResponseMixin, ContextMixin, HookResponseMixin, View):
                     and self.page_perms.can_unschedule()
                 ):
                     lock_message = format_html(
-                        '{} <span class="buttons"><button type="button" class="button button-small button-secondary" data-action-lock-unlock data-url="{}">{}</button></span>',
+                        '{} <span class="buttons"><button type="button" class="button button-small button-secondary" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{}">{}</button></span>',
                         lock_message,
                         reverse(
                             "wagtailadmin_pages:revisions_unschedule",

+ 0 - 1
wagtail/snippets/templates/wagtailsnippets/snippets/edit.html

@@ -61,7 +61,6 @@
                 placement: 'bottom',
             });
 
-            LockUnlockAction('{{ csrf_token|escapejs }}', '{{ action_url }}');
         });
     </script>
 {% endblock %}

+ 5 - 5
wagtail/snippets/tests/test_locking.py

@@ -441,7 +441,7 @@ class TestEditLockedSnippet(BaseLockingTestCase):
 
                 # Should show unlock buttons, one in the message and one in the side panel
                 self.assertTagInHTML(
-                    f'<button type="button" data-url="{unlock_url}" data-action-lock-unlock>Unlock</button>',
+                    f'<button type="button" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{unlock_url}">Unlock</button>',
                     html,
                     count=2,
                     allow_extra_attrs=True,
@@ -490,7 +490,7 @@ class TestEditLockedSnippet(BaseLockingTestCase):
 
         # Should show unlock buttons, one in the message and one in the side panel
         self.assertTagInHTML(
-            f'<button type="button" data-url="{unlock_url}" data-action-lock-unlock>Unlock</button>',
+            f'<button type="button" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{unlock_url}">Unlock</button>',
             html,
             count=2,
             allow_extra_attrs=True,
@@ -544,7 +544,7 @@ class TestEditLockedSnippet(BaseLockingTestCase):
 
         # Should not show unlock buttons
         self.assertTagInHTML(
-            f'<button type="button" data-url="{unlock_url}" data-action-lock-unlock>Unlock</button>',
+            f'<button type="button" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{unlock_url}">Unlock</button>',
             html,
             count=0,
             allow_extra_attrs=True,
@@ -595,7 +595,7 @@ class TestEditLockedSnippet(BaseLockingTestCase):
 
         # Should not show the lock button
         self.assertTagInHTML(
-            f'<button type="button" data-url="{lock_url}" data-action-lock-unlock>Lock</button>',
+            f'<button type="button" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{lock_url}">Lock</button>',
             html,
             count=0,
             allow_extra_attrs=True,
@@ -640,7 +640,7 @@ class TestEditLockedSnippet(BaseLockingTestCase):
 
         # Should show the lock button
         self.assertTagInHTML(
-            f'<button type="button" data-url="{lock_url}" data-action-lock-unlock>Lock</button>',
+            f'<button type="button" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{lock_url}">Lock</button>',
             html,
             count=1,
             allow_extra_attrs=True,

+ 3 - 3
wagtail/snippets/tests/test_snippets.py

@@ -3049,7 +3049,7 @@ class TestScheduledForPublishLock(BaseTestSnippetEditView):
             args=[self.test_snippet.pk, self.latest_revision.pk],
         )
         self.assertTagInHTML(
-            f'<button data-action-lock-unlock data-url="{unschedule_url}">Cancel scheduled publish</button>',
+            f'<button data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{unschedule_url}">Cancel scheduled publish</button>',
             html,
             count=1,
             allow_extra_attrs=True,
@@ -3115,7 +3115,7 @@ class TestScheduledForPublishLock(BaseTestSnippetEditView):
             args=[self.test_snippet.pk, self.latest_revision.pk],
         )
         self.assertTagInHTML(
-            f'<button data-action-lock-unlock data-url="{unschedule_url}">Cancel scheduled publish</button>',
+            f'<button data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{unschedule_url}">Cancel scheduled publish</button>',
             html,
             count=0,
             allow_extra_attrs=True,
@@ -3177,7 +3177,7 @@ class TestScheduledForPublishLock(BaseTestSnippetEditView):
             args=[self.test_snippet.pk, self.latest_revision.pk],
         )
         self.assertTagInHTML(
-            f'<button data-action-lock-unlock data-url="{unschedule_url}">Cancel scheduled publish</button>',
+            f'<button data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{unschedule_url}">Cancel scheduled publish</button>',
             html,
             count=1,
             allow_extra_attrs=True,