How To Stop a Post Publishing in WordPress with PHP

How to Stop a Post Publishing in WordPress with PHP

Last time, we talked about how the Require Featured Image plugin stops a WordPress “post” from being published without a feature image attached in JavaScript. That approach is limited (as a few commenters pointed out) because users may disable JavaScript, and WordPress sometimes publishes posts at times other than when a user is clicking around the WordPress administration interface in a web browser.

So this week, we focus on the parts of the plugin written in PHP which serve to prevent WordPress content from being published without a featured image. And again, video was the first way I compiled this content, so feel free to watch and skip reading this week:

(As with last time, you may need to full-screen the video with 720p or better resolution to be able to follow well. Again, my apologies for that.) If reading is how you learn better, please read on. We’ll cover three basic things: how you can stop a post from getting published in WordPress, how you can check if a featured image is attached and big enough in PHP, and how you can use PHP to conditionally enqueue JavaScript files.

How To Stop a Post From Publishing in WordPress

I don’t remember how I initially stopped a post from publishing in WordPress for this plugin, but I do know that it was more complicated and less robust than the way the plugin does it now. That’s because I didn’t realize there was a very convenient WordPress hook for exactly this sort of situation, called transitation_post_status. Here’s how it’s used in the plugin:

add_action( 'transition_post_status', 'rfi_guard', 10, 3 );
function rfi_guard( $new_status, $old_status, $post ) {
    if ( $new_status === 'publish' && rfi_should_stop_post_publishing( $post ) ) {
        wp_die( rfi_get_warning_message() );
    }
}

The first thing to realize is that transition_post_status is an action and that we’re actually capturing three arguments from it (which is less common for actions): the post’s transitioning-to status, it’s transitioning-from status, and the entire $post object for the piece of content in question. To get all three, we’re passing the fourth argument to add_action. If you’ve forgotten, the arguments for add_action are:

  1. Name of action you’re hooking onto.
  2. Name of (or the literal of) the function you want to run when that action fires.
  3. The priority (run-order) of your hooked function.
  4. The number of arguments your function (#2) will expect.

Because I wanted the first and third arguments, the second is also getting captured. In the rfi_guard function itself, we see the way that we’re stopping WordPress from publishing a post we don’t want it to: we’re just stopping it from doing everything with the violently-named wp_die. Is it a little drastic to “kill” the process just to stop a post from publishing? Maybe, but this is the best way I know of to make sure it doesn’t publish.

When the Plugin Stops a Post From Publishing

The above rfi_guard function only stops publishing by reaching out for wp_die() if certain things are true. The first is that the post’s transitioning-to status is the literal string 'publish'. This means that only when a post is going to 'publish' — whatever its previous state — we’ll stop it. This is because some people may internally have posts as drafts, and once the author has finished writing them they go out to an editor for, say, copy-edits and featured-image-attachment. To not create havoc where it is not needed, the plugin simply does nothing in that transition.

The more interesting part of the conditional is that second half, rfi_should_stop_post_publishing. Let’s look at that function:

function rfi_should_stop_post_publishing( $post ) {
    $is_watched_post_type = rfi_is_supported_post_type( $post );
    $is_after_enforcement_time = rfi_is_in_enforcement_window( $post );
    $large_enough_image_attached = rfi_post_has_large_enough_image_attached( $post );

    if ( $is_after_enforcement_time && $is_watched_post_type ) {
        return !$large_enough_image_attached;
    }
    return false;
}

This function itself calls three subsequent functions to instantiate three temporary variables (whose sole purpose is to make the conditional easier to understand). Basically, the code says “does this post publish date fall after the enforcement change AND is this post of a post type we watch?” The enforcement window is how the plugin deals with the fact that some sites may be older than the featured image feature in WordPress, and they may want to edit old posts without adding featured images to them. (More details can be given if needed…) The post type question is because though the plugin defaults to dealing only with the “Post” post type, it can also be used on, say, “Testimonials.”

Inside of that conditional, you’ll notice the exclamation point. (Programmers like to say “bang” when reading that symbol.) That is a negative or inversion, so if $large_enough_featured_image is true, the function returns false and vice-versa. This is because the function is about if the post should be stopped from publishing. A big enough featured image should mean that it is not stopped from publishing. A not-big-enough image means that should be stopped.

How to Tell if a Post Object Has a Large-Enough Featured Image

Finally, the function that matters most is the one that determines if a large-enough featured image is attached. It looks like this:

function rfi_post_has_large_enough_image_attached( $post ) {
    $image_id = get_post_thumbnail_id( $post->ID );
    if ( $image_id === null ) {
        return false;
    }
    $image_meta = wp_get_attachment_image_src( $image_id, 'full' );
    $width = $image_meta[1];
    $height = $image_meta[2];
    $minimum_size = get_option( 'rfi_minimum_size' );

    if ( $width >= $minimum_size['width'] && $height >=  $minimum_size['height'] ){
        return true;
    }
    return false;
}

Like all the other functions called in rfi_should_stop_post_publishing, this function returns simple true or false values to answer the question its name suggests. How does it do that? First it tries to get the featured image with get_post_thumbnail_id. (In WordPress, the functions that relate to “Featured Images” generally have thumbnail in their name. You kind of just get used to it after a while, but it’s an artifact of how the idea first came into WordPress.)

So if a numeric ID is found for the image, then it will be a number not null (empty). If it is empty, we return false, because the post in question does not have a featured image at all. If it does have a featured image, we keep going. We use the wp_get_attachment_image_src function to get access to the image size. This then lets us do the last check, which is to compare that width and height with the saved plugin settings for how big the width and height need to be. If both dimensions are as-big or bigger, then we return true. If not false.

How to Conditionally Enqueue JavaScript in WordPress

The last thing that’s interesting (to me) about the PHP for the plugin is related to the JavaScript file we discussed last time. To keep the jQuery stuff as simple as possible, I eliminated the checks around this “enforcement window” and “post type” that we’ve had to do in the PHP checks. This is enabled by the fact that we only enqueue the JavaScript when it should do the enforcing.

How? With an enqueueing block that looks like this:

add_action( 'admin_enqueue_scripts', 'rfi_enqueue_edit_screen_js' );
function rfi_enqueue_edit_screen_js( $hook ) {
    global $post;
    if ( $hook !== 'post.php' && $hook !== 'post-new.php' ) {
        return;
    }

    if ( rfi_is_supported_post_type( $post ) && rfi_is_in_enforcement_window( $post ) ) {
        wp_register_script( 'rfi-admin-js', plugins_url( '/require-featured-image-on-edit.js', __FILE__ ), array( 'jquery' ) );
        wp_enqueue_script( 'rfi-admin-js' );

        $minimum_size = get_option( 'rfi_minimum_size' );
        wp_localize_script(
            'rfi-admin-js',
            'passedFromServer',
            // array of values for JS
        );
    }
}

The first part is a simple check that makes sure we don’t enqueue the script on pages where it’s irrelevant. The second block uses the same familiar functions from the conditional above — though without the convenient legibility-enhancing use of temporary variables. But basically the conditional is the same: if the PHP we wrote before would get to that final image-size check, then we enqueue the script. If it wouldn’t, then we don’t. This makes sense: the role of JavaScript is to provide a better experience to users than the PHP checks do, but to fundamentally accomplish the same job. And so that is how it works.

Now You Know…

Coupled with the article from two weeks ago, you now can understand all the interesting features of the Require Featured Image plugin. Some more prosaic (boring) details are omitted — the settings page, the details of the translation stuff, some of the quieter helper functions and features — but the outline should be clear to you.

You’ll probably never have cause to write a plugin that does exactly the same thing this one does, but I do hope that the exploration of how this one works was enlightening and useful to you. Happy hacking!

Image credit: SpaceX via Unsplash


1 Response

Comments

  • Anthony Ji says:

    why don’t just try this hook add_action(‘pre_post_update’, ‘yourfuction’).I am also trying to stop a post from publising.And I found this one .