Skip to content
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

JavaScript publish event callbacks #4674

Closed
IeuanCaseyNewton opened this issue Jan 25, 2018 · 18 comments
Closed

JavaScript publish event callbacks #4674

IeuanCaseyNewton opened this issue Jan 25, 2018 · 18 comments
Labels
[Feature] Extensibility The ability to extend blocks or the editing experience

Comments

@IeuanCaseyNewton
Copy link

IeuanCaseyNewton commented Jan 25, 2018

Issue Overview

Are there or could there be added JavaScript events (for example on publish) we as theme / plugin developers could hook onto to run our own callback functions?

Why would this be useful?

There are times when on pre or post publishing there are additional custom steps we want to run.

As an example of where this would be useful is on a project I am currently working on. We have a custom publishing workflow:

  1. Create post.
  2. Publish post.
  3. On update - instead create an 'amendment' (a copy of that original post, with a post status of amendment) and redirect the editor to that amendment.
  4. This amendment is reviewed (it is not publicly viewable).
  5. On publish, the amendment replaces the original post and is deleted.

So what we need is to be able to do in this case is following the publish capture the response from WordPress REST API, and perform a redirect based on the new post ID that is returned.

Expected Behaviour

As an example of how this could work as a plugin / theme developer would be to register for an event, something like this:

window.wp.events.registerListener( 'postPublish', ( xmlHttpRequestObj ) => {
    // my code here...
} );

Workaround

We are able to work around this by capturing all ajax requests with something like the following, but to have event provided by Gutenberg would be much more desirable as this is pretty much a hack:

const thisObject = this;
window.XMLHttpRequest.prototype.send = function() {
             const request = this;
             const intervalId = window.setInterval( () => {
 
                 // If this is not a response or meetings the given criteria.
                 if ( request.readyState !== 4 || ! thisObject.testFunction( request ) ) {
                     return;
                 }
 
                 // Call the call back function.
                 thisObject.callback( request );
 
                 clearInterval( intervalId );
             }, 1 );
             return thisObject.originalSend.apply( this, [].slice.call( arguments ) );
         }; 

Apologies in advance if this already exists and I have missed it!

@sharazghouri
Copy link
Contributor

Yes 100% agree with you @IeuanCaseyNewton

@gziolo gziolo added the [Feature] Extensibility The ability to extend blocks or the editing experience label Jan 25, 2018
@gziolo gziolo added the [Feature] Writing Flow Block selection, navigation, splitting, merging, deletion... label Feb 1, 2018
@gziolo
Copy link
Member

gziolo commented Feb 1, 2018

We could use @wordpress/hooks package which is JS implementation of WordPress hooks. This library is already integrated into new editor. You can find full API here. I'm still not sure what would be the best way to use it. How is it implemented in the current editor?

@IeuanCaseyNewton
Copy link
Author

IeuanCaseyNewton commented Feb 1, 2018

In the classic editor updates are performed as post backs rather than ajax requests. This meant we were able to just send a 301 redirect to the editor on publish when we want to switch the editor between the post and the amendment, something like this:

add_action( 'pre_post_update', array( $this, 'maybe_create_amendment' ), 50, 2 );
...
function maybe_create_amendment( $post_id, $data ) {
    ...
    wp_safe_redirect( get_edit_post_link( $amendment_id, null ) );
}

@gziolo
Copy link
Member

gziolo commented Feb 1, 2018

In the classic editor updates are performed as post backs rather than ajax requests. This meant we were able to just send a 301 redirect to the editor on publish when we want to switch the editor between the post and the amendment, something like this

I'm trying to find out how that could translate to the new editor and the way it works.

I have no idea how WP REST API works, but I would assume that you can use some hook on the PHP side which would return a different result from the request when necessary. In the example you shared, it would return the redirect url when the amendment is created. Then we would need to have another hook on JS side which could intercept the result of the API call and execute custom JS code which would be able to use the returned redirect url to perform redirect on the client. Does it make sense what I'm saying? I want to make sure we can identify where we exactly need to add extensibility points and how much we can defer to the existing architecture.

@IeuanCaseyNewton
Copy link
Author

Hey @gziolo
So in our workaround we are using the WordPress REST API. So what we are doing is instead of performing the redirect on publish, we are responding with the ID of the created amendment:

// If this is the WordPress REST API (Gutenberg).
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
    $response = array(
        'data' => array(
	    'id' => $post_id,
	),
    );
    wp_send_json( $response );

Then we are using the JS ajax intercept code I have in the issue description to then detect this change in ID to then perform a redirect. This intercept code is capturing all ajax requests to find the one that was for publish and checking if in the response if ID has changed.

What would be better is if Gutenberg provided a hook that could call a custom callback function on post publish, passing the server response as a parameter to that call back.

@adamsilverstein
Copy link
Member

So it sounds like all you would need would be a doAction event fired with the newPost data here -
https://github.com/WordPress/gutenberg/blob/master/editor/store/effects.js#L89 - after the post is successfully saved that passed the value returned from the request, does that sound right?

@adamsilverstein
Copy link
Member

@IeuanCaseyNewton & @gziolo

Something like this would leverage hooks by triggering an explicit action:

0b5c712

You would then hook in to that action to handle the updated id.

Ultimately we would want to add similar actions for other events, perhaps a more clever solution could do actions for every redux effect automatically.

@IeuanCaseyNewton
Copy link
Author

Hey @adamsilverstein - This sounds just like what we need - thanks! I will try using your branch later and let you know. Although as you mention others will probably need more events than just this, such as pre-post update (before it sends the update request to the server).

@gziolo
Copy link
Member

gziolo commented Feb 5, 2018

Ultimately we would want to add similar actions for other events, perhaps a more clever solution could do actions for every redux effect automatically.

@aduth or @youknowriad - can you confirm whether proposed solution by @adamsilverstein would be the way to go for all effects or should we rather add hooks case by case?

I'd like also to share one concern about the proposed solution by @adamsilverstein. In this particular case, I would expect to see a completely different behavior when action result is returned from the server, so we might not want to dispatch all default actions. We might want to rethink how we handle all the side-effects in general if we want to support hooks in here.

@gziolo
Copy link
Member

gziolo commented Feb 5, 2018

@adamsilverstein, I should start with many thanks for your valuable input here 💯 This is a very interesting use case, I'm sure we will find a way to make this flexible enough to support more sophisticated user flows.

@adamsilverstein
Copy link
Member

One advantage I can see to adding desired hooks case-by-case is that this allows us to document them inline (with JSDoc). This was hugely successful for the php hooks in WordPress core and is now used to generate the reference here: https://developer.wordpress.org/reference/hooks/.

@gziolo
Copy link
Member

gziolo commented Feb 6, 2018

One advantage I can see to adding desired hooks case-by-case is that this allows us to document them inline (with JSDoc). This was hugely successful for the php hooks in WordPress core and is now used to generate the reference here: https://developer.wordpress.org/reference/hooks/.

@adamsilverstein yes, it makes sense what you shared. Do you plan to open a PR with some proposals how to tackle hooks inside effects?

@adamsilverstein
Copy link
Member

Do you plan to open a PR with some proposals how to tackle hooks inside effects?

Yes, I'll work on a PR.

@adamsilverstein
Copy link
Member

@IeuanCaseyNewton I started working on a PR to add an action, then realized I think you can detect the post status change to published using the existing wp.data api. Can you try this and see if it solves your use case:

const { subscribe } = wp.data;

const initialPostStatus = wp.data.select( 'core/editor' ).getEditedPostAttribute( 'status' );

if ( 'publish' !== initialPostStatus ) {
	// Watch for the publish event.
	const unssubscribe = subscribe( () => {
		const currentPostStatus = wp.data.select( 'core/editor' ).getEditedPostAttribute( 'status' );
		if ( 'publish' === currentPostStatus ) {
			// ...do something here - the post has been published
		}
	} );
}

how do you want to use the pre Publish event you mentioned? Are you trying to make publish-ability conditional?

@mcsf mcsf removed the [Feature] Writing Flow Block selection, navigation, splitting, merging, deletion... label Jul 16, 2018
@IeuanCaseyNewton
Copy link
Author

Hey @adamsilverstein

Sorry for the delay in getting back to you - I did not see your message. Yes - this works well for us! I was not aware of wp.data.subscribe. Thanks for your help!

I can now remove my ajax intercept code.

@gziolo
Copy link
Member

gziolo commented Jul 17, 2018

@adamsilverstein, awesome tip 👍

Can we close this one? Do we track a similar use case where we would like to prevent publish until some certain action or state change happens? I think this is the last missing bit to make all this publish flow easy to customize. An example would be, keep Publish button disabled until the user ticks checkbox field.

@IeuanCaseyNewton
Copy link
Author

From my point of view my initial issue has been resolved so can be closed.

@gziolo
Copy link
Member

gziolo commented Jul 17, 2018

@IeuanCaseyNewton 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Extensibility The ability to extend blocks or the editing experience
Projects
None yet
Development

No branches or pull requests

5 participants