How to do conditional publishing?

I am trying to write a plugin that will stop a user from publishing (or updating) a post if a given condition is not met, for example, if the title of the post is already being used in a different post (I am using a custom post type, if that makes a difference).
I wrote a function that gets called through the ‘transition_post_status’ hook to attempt to catch the post before it gets published or updated in the database, so that I can stop that from happening.
When I test this out with a title I know I’ve already used I see the error message when the wp_die() function happens, so I know this function is getting called. However, the post is being created anyways. Is there something I’m missing? Is this even the best way of checking for conditions before letting a post be saved in the database?

This is what I have in the constructor:

add_action( 'transition_post_status', array( $this, 'post_publish_control' ), 10, 3 );

Later in the class:

public function post_publish_control( $new_status, $old_status, $post ) {

   // Make sure this function runs on correct post type
   if( $post->post_type === 'my_custom_post_type' ) {

      // On first-time publish or post update
      if( $new_status === 'publish' ) {

         // Try to catch an already existing title before post is saved
         if( does_post_title_exist( $post->post_title ) ) {
            wp_die( 'Title is already being used, try a different one.' );
         }
         // Continue if conditions are met
         else {
            return;
         }
      }
   }
}

I am a bit more used to object-oriented programming, so I’m trying to write this plugin as its own class.

1 Answer
1

That’s because the hook “transition_post_status” is called:

  1. After the post has been published or updated in the database
  2. After the status has been updated in the database.

Based on your problem statement, I believe the hook you want is “pre_post_update”, as stated in relevant WordPress source code:

/**
 * Fires immediately before an existing post is updated in the database.
 *
 * @since 2.5.0
 *
 * @param int   $post_ID Post ID.
 * @param array $data    Array of unslashed post data.
 */
do_action( 'pre_post_update', $post_ID, $data );

This code is immediately followed by a call to $wpdb->update( $wpdb->posts, $data, $where ), which actually saves the post to the database.

Usability issue. I would advocate for not calling wp_die() if e.g. just the title is identical to one in the database. That’s a bad user experience. It would make more sense to use a add_filter to rename the title if this happened:

/**
 * Filters slashed post data just before it is inserted into the database.
 *
 * @since 2.7.0
 *
 * @param array $data    An array of slashed post data.
 * @param array $postarr An array of sanitized, but otherwise unmodified post data.
 */
$data = apply_filters( 'wp_insert_post_data', $data, $postarr );

Leave a Comment