Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Determine Post Publish / Update via Classic Editor, Gutenberg and REST API
<?php
/**
* Example class implementation to perform actions, such as sending a Post
* to a third party API or service, when the Post is published or updated through:
* - Classic Editor
* - Gutenberg
* - REST API
*
* @package Post_To_Social
* @author Tim Carr
* @version 1.0.0
*/
class Post_To_Social {
/**
* Constructor
*
* @since 1.0.0
*/
public function __construct() {
// Actions
add_action( 'wp_loaded', array( $this, 'register_publish_hooks' ), 1 );
}
/**
* Registers publish hooks against all public Post Types,
*
* @since 1.0.0
*/
public function register_publish_hooks() {
add_action( 'transition_post_status', array( $this, 'transition_post_status' ), 10, 3 );
}
/**
* Fired when a Post's status transitions.
*
* Called by WordPress when wp_insert_post() is called.
*
* As wp_insert_post() is called by WordPress and the REST API whenever creating or updating a Post,
* we can safely rely on this hook.
*
* @since 1.0.0
*
* @param string $new_status New Status
* @param string $old_status Old Status
* @param WP_Post $post Post
*/
public function transition_post_status( $new_status, $old_status, $post ) {
// Bail if the Post Type isn't public
// This prevents the rest of this routine running on e.g. ACF Free, when saving Fields (which results in Field loss)
$post_types = array( 'post', 'page' );
if ( ! in_array( $post->post_type, $post_types ) ) {
return;
}
// Bail if we're working on a draft or trashed item
if ( $new_status == 'auto-draft' || $new_status == 'draft' || $new_status == 'inherit' || $new_status == 'trash' ) {
return;
}
/**
* = REST API =
* If this is a REST API Request, we can't use the wp_insert_post action, because any metadata
* included in the REST API request is *not* included in the call to wp_insert_post().
*
* Instead, we must use a late REST API action that gives the REST API time to save metadata.
*
* Thankfully, the REST API supplies an action to do this: rest_after_insert_posttype, where posttype
* is the Post Type in question.
*
* Note that any meta being supplied in the REST API Request MUST be registered with WordPress using
* register_meta(). If you're using a third party plugin to register custom fields, you'll need to
* confirm it uses register_meta() as part of its process.
*
* = Gutenberg =
* If Gutenberg is being used on the given Post Type, two requests are sent:
* - a REST API request, comprising of Post Data and Metadata registered *in* Gutenberg,
* - a standard request, comprising of Post Metadata registered *outside* of Gutenberg (i.e. add_meta_box() data)
*
* If we're publishing a Post, the second request will be seen by transition_post_status() as an update, which
* isn't strictly true.
*
* Therefore, we set a meta flag on the first Gutenberg REST API request to defer acting on the Post until
* the second, standard request - at which point, all Post metadata will be available to the Plugin.
*
* = Classic Editor =
* Metadata is included in the call to wp_insert_post(), meaning that it's saved to the Post before we use it.
*/
// Flag to determine if the current Post is a Gutenberg Post
$is_gutenberg_post = $this->is_gutenberg_post( $post );
// If a previous request flagged that an 'update' request should be treated as a publish request (i.e.
// we're using Gutenberg and request to post.php was made after the REST API), do this now.
$needs_publishing = get_post_meta( $post->ID, '_needs_publishing', true );
if ( $needs_publishing ) {
// Run Publish Status Action now
delete_post_meta( $post->ID, '_needs_publishing' );
add_action( 'wp_insert_post', array( $this, 'wp_insert_post_publish' ), 999 );
// Don't need to do anything else, so exit
return;
}
// If a previous request flagged that an update request be deferred (i.e.
// we're using Gutenberg and request to post.php was made after the REST API), do this now.
$needs_updating = get_post_meta( $post->ID, '_needs_updating', true );
if ( $needs_updating ) {
// Run Publish Status Action now
delete_post_meta( $post->ID, '_needs_updating' );
add_action( 'wp_insert_post', array( $this, 'wp_insert_post_update' ), 999 );
// Don't need to do anything else, so exit
return;
}
// Publish
if ( $new_status == 'publish' && $new_status != $old_status ) {
/**
* Classic Editor
*/
if ( ! defined( 'REST_REQUEST' ) || ( defined( 'REST_REQUEST' ) && ! REST_REQUEST ) ) {
add_action( 'wp_insert_post', array( $this, 'wp_insert_post_publish' ), 999 );
// Don't need to do anything else, so exit
return;
}
/**
* Gutenberg Editor
* - Non-Gutenberg metaboxes are POSTed via a second, separate request to post.php, which appears
* as an 'update'. Define a meta key that we'll check on the separate request later.
*/
if ( $is_gutenberg_post ) {
update_post_meta( $post->ID, '_needs_publishing', 1 );
// Don't need to do anything else, so exit
return;
}
/**
* REST API
*/
add_action( 'rest_after_insert_' . $post->post_type, array( $this, 'rest_api_post_publish' ), 10, 2 );
// Don't need to do anything else, so exit
return;
}
// Update
if ( $new_status == 'publish' && $old_status == 'publish' ) {
/**
* Classic Editor
*/
if ( ! defined( 'REST_REQUEST' ) || ( defined( 'REST_REQUEST' ) && ! REST_REQUEST ) ) {
add_action( 'wp_insert_post', array( $this, 'wp_insert_post_update' ), 999 );
// Don't need to do anything else, so exit
return;
}
/**
* Gutenberg Editor
* - Non-Gutenberg metaboxes are POSTed via a second, separate request to post.php, which appears
* as an 'update'. Define a meta key that we'll check on the separate request later.
*/
if ( $is_gutenberg_post ) {
update_post_meta( $post->ID, '_needs_updating', 1 );
// Don't need to do anything else, so exit
return;
}
/**
* REST API
*/
add_action( 'rest_after_insert_' . $post->post_type, array( $this, 'rest_api_post_update' ), 10, 2 );
// Don't need to do anything else, so exit
return;
}
}
/**
* Helper function to determine if the Post is using the Gutenberg Editor.
*
* @since 1.0.0
*
* @param WP_Post $post Post
* @return bool Post uses Gutenberg Editor
*/
private function is_gutenberg_post( $post ) {
// This will fail if a Post is created or updated with no content and only a title.
if ( strpos( $post->post_content, '<!-- wp:' ) === false ) {
return false;
}
return true;
}
/**
* Called when a Post has been Published via the REST API
*
* @since 1.0.0
*
* @param WP_Post $post Post
* @param WP_REST_Request $request Request Object
*/
public function rest_api_post_publish( $post, $request ) {
$this->wp_insert_post_publish( $post->ID );
}
/**
* Called when a Post has been Published via the REST API
*
* @since 1.0.0
*
* @param WP_Post $post Post
* @param WP_REST_Request $request Request Object
*/
public function rest_api_post_update( $post, $request ) {
$this->wp_insert_post_update( $post->ID );
}
/**
* Called when a Post has been Published
*
* @since 1.0.0
*
* @param int $post_id Post ID
*/
public function wp_insert_post_publish( $post_id ) {
// Call main function
$this->send( $post_id, 'publish' );
}
/**
* Called when a Post has been Updated
*
* @since 1.0.0
*
* @param int $post_id Post ID
*/
public function wp_insert_post_update( $post_id ) {
// Call main function
$this->send( $post_id, 'update' );
}
/**
* Main function. Called when any Page, Post or CPT is published or updated
*
* @since 1.0.0
*
* @param int $post_id Post ID
* @param string $action Action (publish|update)
* @return mixed WP_Error | API Results array
*/
public function send( $post_id, $action ) {
// Get Post
global $post;
$post = get_post( $post_id );
if ( ! $post ) {
return new WP_Error( 'no_post', sprintf( __( 'No WordPress Post could be found for Post ID %s' ), $post_id ) );
}
// @TODO Save any metadata that your Plugin expects now - such as post-specific settings your Plugin may offer via add_meta_box() calls
update_post_meta( $post_id, 'your-key', sanitize_text_field( $_POST['your-key'] ) );
// @TODO Add your code here to send your Post to whichever API / third party service
}
}
@JordanMG

This comment has been minimized.

Copy link

JordanMG commented Feb 3, 2019

Thank you so much for sharing this, and excellent commenting as well. rest_after_insert_post was just the hook I needed to adapt my plugin for Gutenberg. (I'm a big fan of WP to Buffer Pro, btw!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.