-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support custom validation by allowing bypass of the Publish/Update button saving process #49811
Conversation
I've searched high and low for code that would allow me to keep the Save / Publish button active initially so that upon interacting with the button it would trigger some additional validations before bailing on the save and then subsequently enabling save locking. Ultimately, the only way around this is to listen to the click event on the button itself and then bail there. I believe this could be handled much more directly within the component through a new filter which would allow a better approach towards running any custom validations that need to happen before allowing the save to continue.
👋 Thanks for your first Pull Request and for helping build the future of Gutenberg and WordPress, @sc0ttkclark! In case you missed it, we'd love to have you join us in our Slack community, where we hold regularly weekly meetings open to anyone to coordinate with each other. If you want to learn more about WordPress development in general, check out the Core Handbook full of helpful information. |
You can use When you interact with the Publish / Update button, it will have already started saving at the point in which you can work with the subscribe method. |
Example code using the new filterfunction checkValidation() {
if ( isHandlingValidation ) {
return false;
}
isHandlingValidation = true;
const isSavingPost = wp.data.select( 'core/editor' ).isSavingPost();
const isPublishingPost = wp.data.select( 'core/editor' ).isPublishingPost();
const isAutosavingPost = wp.data.select( 'core/editor' ).isAutosavingPost();
const hasLock = wp.data.select( 'core/editor' ).isPostSavingLocked();
const notices = wp.data.select( 'core/notices' ).getNotices();
let hasNotice = false;
notices.every( (notice) => {
if (notice.id === 'customValidationLock') {
hasNotice = true;
return false;
}
} );
if ( !validateCustomField() ) {
if ( !hasLock ) {
wp.data.dispatch( 'core/editor' ).lockPostSaving( 'customValidationLock' );
}
wp.data.dispatch( 'core/notices' ).createErrorNotice( 'Custom Validation Field is required!', {
id : 'customValidationLock',
isDismissible: false,
} );
isHandlingValidation = false;
return false;
}
wp.data.dispatch( 'core/editor' ).unlockPostSaving( 'customValidationLock' );
wp.data.dispatch( 'core/notices' ).removeNotice( 'customValidationLock' );
isHandlingValidation = false;
return true;
}
wp.hooks.addFilter( 'editor.PostPublishButton.shouldSubmit', (shouldSubmit) => {
return shouldSubmit ? checkValidation() : false;
} ); Example workaround without the filter that uses wp.data.subscribe() and does not stop savelet isHandlingValidation = false;
wp.data.subscribe( () => {
if ( isHandlingValidation ) {
return;
}
isHandlingValidation = true;
const isSavingPost = wp.data.select( 'core/editor' ).isSavingPost();
const isPublishingPost = wp.data.select( 'core/editor' ).isPublishingPost();
const isAutosavingPost = wp.data.select( 'core/editor' ).isAutosavingPost();
const hasLock = wp.data.select( 'core/editor' ).isPostSavingLocked();
if ( !validateCustomField() ) {
if ( !isAutosavingPost && (isSavingPost || isPublishingPost) ) {
if ( !hasLock ) {
wp.data.dispatch( 'core/editor' ).lockPostSaving( 'customValidationLock' );
}
wp.data.dispatch( 'core/notices' ).createErrorNotice( 'Custom Validation Field is required!', {
id : 'customValidationLock',
} );
}
}
else {
wp.data.dispatch( 'core/editor' ).unlockPostSaving( 'customValidationLock' );
wp.data.dispatch( 'core/notices' ).removeNotice( 'customValidationLock' );
}
isHandlingValidation = false;
} ); Example workaround without the filter that uses a hacky click eventlet initListener = false;
let inited = false;
function checkValidation() {
if ( isHandlingValidation ) {
return false;
}
isHandlingValidation = true;
const isSavingPost = wp.data.select( 'core/editor' ).isSavingPost();
const isPublishingPost = wp.data.select( 'core/editor' ).isPublishingPost();
const isAutosavingPost = wp.data.select( 'core/editor' ).isAutosavingPost();
const hasLock = wp.data.select( 'core/editor' ).isPostSavingLocked();
const notices = wp.data.select( 'core/notices' ).getNotices();
let hasNotice = false;
notices.every( (notice) => {
if (notice.id === 'customValidationLock') {
hasNotice = true;
return false;
}
} );
if ( !validateCustomField() ) {
if ( !hasLock ) {
wp.data.dispatch( 'core/editor' ).lockPostSaving( 'customValidationLock' );
}
wp.data.dispatch( 'core/notices' ).createErrorNotice( 'Custom Validation Field is required!', {
id : 'customValidationLock',
isDismissible: false,
} );
isHandlingValidation = false;
return false;
}
wp.data.dispatch( 'core/editor' ).unlockPostSaving( 'customValidationLock' );
wp.data.dispatch( 'core/notices' ).removeNotice( 'customValidationLock' );
isHandlingValidation = false;
return true;
}
wp.data.subscribe( () => {
const publishButton = document.querySelector( '.editor-post-publish-button__button' );
if (!publishButton) {
return;
}
if (!initListener) {
initListener = true;
publishButton.addEventListener( 'click', function ( e ) {
inited = true;
if (checkValidation()) {
return true;
}
e.preventDefault();
} );
}
if (inited) {
checkValidation();
}
} ); |
For reference, plugins like ACF have to override the click event to prevent the block editor from saving when it has their own validations that fail. That's the same implementation that a plugin like Pods will also have to implement to address this. |
@Mamaduka Let me know if you notice any areas I can improve here. This is my first contribution for the Gutenberg project via PR and I'm sure I've done something wrong somewhere :) I'm also unsure how to approach a test for this. |
Hi, @sc0ttkclark I believe the "Publication Checklist" plugin achieves similar behavior by enabling the pre-publication checklist and using the "lock saving" actions. There was a similar proposal (#10115), which was superseded by save-locking actions. |
@Mamaduka For reference to the original issue I encountered that led me here -- I attempted to use the post locking feature but I was unable to find any workable way to stop the post from saving when they click to Update the post. The subscribe call lets me hook in and lock the post but the save has already started/completed at that point. |
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
@@ -157,9 +158,15 @@ export class PostPublishButton extends Component { | |||
} | |||
|
|||
const onClickButton = () => { | |||
// Allow for overriding saving for custom validations. | |||
if (!applyFilters('editor.PostPublishButton.shouldSubmit',true,this.props)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The static analysis CI has me a little confused as to what change needs to be made here but I'm happy to make that change to get it passing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I'm not sure here -- I've adjusted it a few times but taking out spaces doesn't seem to get it passing and the https://developer.wordpress.org/coding-standards/wordpress-coding-standards/javascript/ code standards themselves aren't passing so I'm 🤷
What?
This PR adds a new filter in the click handler for the Publish / Save button that can stop the save from proceeding.
Why?
I've searched high and low for code that would allow me to keep the Save / Publish button active initially so that upon interacting with the button it would trigger some additional validations before bailing on the save and then subsequently enabling save locking.
Ultimately, the only way around this is to listen to the click event on the button itself and then bail there.
I believe this could be handled much more directly within the component through a new filter which would allow a better approach towards running any custom validations that need to happen before allowing the save to continue.
How?
It uses a filter and also allows for utilizing the properties for the button which give easy access to booleans and other context towards whether the save should be bypassed.
Testing Instructions
Testing Instructions for Keyboard
N/A
Screenshots or screencast
N/A