kopia lustrzana https://github.com/wagtail/wagtail
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 #9815pull/9899/head
rodzic
9e9a84c953
commit
b929694203
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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({});
|
|
@ -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/');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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' },
|
||||
|
|
|
@ -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;
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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' %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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' %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
placement: 'bottom',
|
||||
});
|
||||
|
||||
LockUnlockAction('{{ csrf_token|escapejs }}', '{{ action_url }}');
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Ładowanie…
Reference in New Issue