Quantcast
Channel: Tutorials – RachieVee: Rachel's Blog
Viewing all articles
Browse latest Browse all 5

How to intercept post publishing based on post meta

$
0
0

It’s been a while since I’ve written a step by step tutorial. I decided to write one based on a real world task I had to work on.This is a tutorial on how to intercept post publishing in WordPress. In summary, I needed to stop people from publishing posts based on a post meta value. If someone tried to publish a post when they’re not supposed to, from both “quick edit” or regular edit, an error needed to display.

A good use case for this is a news blog where content has to be approved by a head honcho before it gets published. After all, no one wants to publish unvetted content – especially news!

If you have post meta that determines what “stage” the post is in an “approval process” – you can check against that post meta before WordPress decides whether it’s allowed to be published. For those unfamiliar with post meta, I mean any values saved through WordPress’ default custom fields, or if you’re using a field plugin like ACF or Carbon Fields. Post meta can be also be saved via PHP with the add_post_meta() function.

Before we begin, this tutorial assumes:

  • You have familiarity with editing/creating WordPress themes.
  • You have a basic understanding of how WordPress hooks work. Need some pointers on getting acquainted with them? Check out an older post of mine: 10 Ways to Learn WordPress Hooks.
  • You already have a custom field/post meta to work with. In this tutorial, we’re going to assume there is a field called _approval_status (meta key), and if that field is set to “approved” (meta value), the post should publish successfully. Meta key/value are what the field label/value are called in the WordPress database table wp_postmeta.
  • You have added this field to “quick edit”. Not mandatory, but the tutorial will include how to intercept post publishing on quick edit as well as regular edit.


How to prevent #WordPress from publishing posts based on post meta via @rachelrvasquez #webdev #PHP
Click To Tweet


Before we begin, I want to give a quick disclaimer. I’m learning just like you are. I am in no way claiming that this is the only or the best way to accomplish this task. I am sharing my solution. So far, that solution is working on a live site – fingers crossed!

If you have any suggestions or constructive feedback, I’m all ears and happy to knowledge share. Now, back to the tutorial!

Finding the right hook.

My first obstacle was finding the right entry point where I can check when a post is about to be published. In addition, I needed access to any values that had changed in the post meta.

I initially thought save_post would be the best hook, but it fires after changes have already been saved to the database. If the post meta was incorrect, I wanted to prevent it from saving or publishing. Therefore, I needed to find a hook that fired earlier. This is where I discovered pre_post_update.

pre_post_update: Fires immediately before an existing post is updated in the database.

This hook gives me access to two parameters: the post ID and the post data before they’re saved. This was exactly what I needed.

Start the function.

In my WordPress theme, I hooked a custom function to the pre_post_update action in a WordPress class. You’re free to add it wherever it makes sense in your theme. For many people, functions.php is the right file. For others, who have built their customizations into a plugin, rather than in the theme (which is best practice), it will go in that plugin.

First, add the action, and the name of the custom function. I named my function “intercept_publishing”.

add_action( 'pre_post_update', 'intercept_publishing', 10, 2 );

Next, I wanted this function to only run when the correct post type was detected. I had several different post types in my theme. If I didn’t limit my functionality to the only post type I needed to check, the site’s performance could suffer.

Since I only needed to intercept publishing for the “post” post type, I added conditionals to check. These conditionals check if we’re not on the admin side of WordPress and if a post type, that isn’t a post, is about to be saved. If either of those conditions are true, none of the functionality after the returns will run.

function intercept_publishing( $post_ID, $data ) {
	$post = get_post( $post_ID );

	if ( ! is_admin() ) {
			return;
		}

	if ( 'post' !== get_post_type( $post_ID ) ) {
			return;
	}

	//Custom functionality here
}


The save_post hook fires after #WordPress data is saved. Need an earlier hook? Try pre_post_update instead. via @rachelrvasquez #webdev #PHP
Click To Tweet


Check for post meta.

The next step was checking for our post meta. I already had my custom field added to WordPress’ quick edit screen. Since posts could be changed to “approved” from either quick edit or inside the post, I had to make sure I covered both cases.

Retrieving the post meta values before they’re saved with this hook is different in quick edit than it is in regular edit. In quick edit, the value is saved in a $_REQUEST parameter whereas regular edit uses $_POST instead.

If you’re not checking for a field value on quick edit, then feel free to omit the pieces of code below that check the $_REQUEST.

Another tip: When writing the name of your post meta field, be mindful of whether or not the name should have an underscore prefixing it. When using $_REQUEST, the underscore wasn’t necessary. When using $_POST, I needed to make sure the underscore was in the beginning of the post meta name. Or “meta key” which is what it’s called in the WordPress database, in wp_postmeta.

Next, I added another condition to prevent errors if my post meta value does not exist in the $_REQUEST or $_POST parameters. If the field has been changed and there’s a new value that’s supposed to save, it should exist.

I added the parameter conditional after the admin and the post type conditionals. Using isset(), if the _approval_status field didn’t have a value to save, the function returned. Once again, nothing will run after this return.

If my post meta value does exist in the parameters, and has gotten past the conditional, then the next step was to save the post meta value to a variable. I saved it to $approval_status. To be super safe, I also check if my $approval_status variable is set after the parameter checks.

function intercept_publishing( $post_ID, $data ) {
	$post = get_post( $post_ID );

	if ( ! is_admin() ) {
		return;
	}

	if ( 'post' !== get_post_type( $post_ID ) ) {
		return;
	}

	//$_REQUEST is for quick edit assuming that the post meta field was added to the quick edit view by you elsewhere
	if ( isset( $_POST['_adherence_status'] ) ) {
		$approval_status = $_POST['_approval_status'];
	} elseif ( isset( $_REQUEST['adherence_status'] ) ) {
		$approval_status = $_REQUEST['approval_status'];
	}

	if ( ! isset( $approval_status ) ) {
		return;
	}

}

Check the post status.

The pre_post_update function fires every time a post is about to update. Meaning this hook fires even if the post is being saved as a draft or pending review or any other status that isn’t necessarily published. Since the goal was to intercept post publishing, I needed to check the post’s post status.

Thanks to the hook, the second parameter $data retrieves the post status for regular edit. Unfortunately, that only works for regular edit, and not quick edit. For quick edit, I used the $post_ID available from the first parameter and the get_post_status() function.

I then set the post status value to another variable called $post_status.

function intercept_publishing( $post_ID, $data ) {
	$post = get_post( $post_ID );

	if ( ! is_admin() ) {
		return;
	}

	if ( 'post' !== get_post_type( $post_ID ) ) {
		return;
	}

	//$_REQUEST is for quick edit assuming that the post meta field was added to the quick edit view by you elsewhere
	if ( isset( $_POST['_adherence_status'] ) ) {
		$approval_status = $_POST['_approval_status'];
	} elseif ( isset( $_REQUEST['adherence_status'] ) ) {
		$approval_status = $_REQUEST['approval_status'];
	}

	if ( ! isset( $approval_status ) ) {
		return;
	}

	$post_status   = isset( $data['post_status'] ) ? $data['post_status'] : get_post_status( $post_ID );
}

Create two new variables.

We’re almost there! Now that all the conditionals are in place to prevent errors, we can finally get to the fun stuff. So far, we have the post meta value saved to $approval_status and the post status saved to $post_status.

I also created two new variables. The first was $edit_type, where I wanted to detect whether we were in quick edit or regular edit. Luckily because $data isn’t supported in quick edit, I used that to check, and either save “regular” or “quick” to the variable.

Tip: If you’re only using regular editing, and not quick edit, you don’t need to check which one you’re in. You can skip having an $edit_type variable.

My goal was to display two different error messages because publishing from quick edit is different from regular edit. In quick edit, there’s a dropdown to change the post status to “Publish”. Then you have to click the “Update” button. It’s a two step process.

In comparison, when inside the post to edit, you just have to click the “Publish” button. It’s a one step process. Being able to serve a different message with different instructions was just another way to help out my users who were not familiar with WordPress.

The second variable I created was $error_message with an empty string. This variable would eventually contain the error message.

add_action( 'pre_post_update', 'intercept_publishing', 10, 2 );

function intercept_publishing( $post_ID, $data ) {
		$post = get_post( $post_ID );

	if ( ! is_admin() ) {
		return;
	}

	if ( 'post' !== get_post_type( $post_ID ) ) {
		return;
	}

	//$_REQUEST is for quick edit assuming that the post meta field was added to the quick edit view by you elsewhere
	if ( isset( $_POST['_adherence_status'] ) ) {
		$approval_status = $_POST['_approval_status'];
	} elseif ( isset( $_REQUEST['adherence_status'] ) ) {
		$approval_status = $_REQUEST['approval_status'];
	}

	if ( ! isset( $approval_status ) ) {
		return;
	}

	$post_status   = isset( $data['post_status'] ) ? $data['post_status'] : get_post_status( $post_ID );
        $edit_type     = isset( $data['post_status'] ) ? 'regular' : 'quick';
	$error_message = '';

	//almost there!
}

Finally adding the intercept post publishing part…

Finally, we’re ready to use all of our variables and make magic happen. I added a conditional to check if the post meta value is equal to “approved” and if the post status is equal to “publish” with my $approval_status and $post_status variables.

Then, I checked the $edit_type variable to see if I’m in quick edit or regular. I’ve saved two different messages as strings to the variable $error_message.

To take advantage of WordPress’ native functions, I made use of wp_die(). This function kills any process, in our case, the saving/publishing process, and displays an error on the front-end of the site instead. The parameters let you pass an error title, the error message and even whether you want to provide a “back” link so the user can return to where they came from before the error.

I passed the $error_message variable to this function and set the “back” link to true.

With that, the function is complete. The final code is below.

add_action( 'pre_post_update', 'intercept_publishing', 10, 2 );

function intercept_publishing( $post_ID, $data ) {
		$post = get_post( $post_ID );

	if ( ! is_admin() ) {
		return;
	}

	if ( 'post' !== get_post_type( $post_ID ) ) {
		return;
	}

	//$_REQUEST is for quick edit assuming that the post meta field was added to the quick edit view by you elsewhere
	if ( isset( $_POST['_adherence_status'] ) ) {
		$approval_status = $_POST['_approval_status'];
	} elseif ( isset( $_REQUEST['adherence_status'] ) ) {
		$approval_status = $_REQUEST['approval_status'];
	}

	if ( ! isset( $approval_status ) ) {
		return;
	}

	$post_status   = isset( $data['post_status'] ) ? $data['post_status'] : get_post_status( $post_ID );
        $edit_type     = isset( $data['post_status'] ) ? 'regular' : 'quick';
	$error_message = '';

	if ( ( $post_status === 'publish' ) && ( $approval_status !== 'approved' ) ) {
		//different messages for regular edit vs quick edit
		if ( $edit_type === 'regular' ) {
			$error_message = 'Please check the "Approval Status" field before publishing. Posts should only be published if approved by the team editor.';
		} else {
			$error_message = 'Please check the "Approval Status" and "Status" fields before clicking "Update". Posts should only be published if approved by the team editor. ';
		}

		wp_die( '<b>Publishing Error: </b> ' . $error_message, 'Adherence Publishing Error', [ 'back_link' => true ] );
	}
}

Last Notes.

While this code is working on a live site, I didn’t test it on a brand new fresh install of WordPress. Please let me know if something doesn’t work for you, and I’ll try to do what I can to debug.

Take a gander at Debugging in WordPress and take advantage of var_dump(). Test the function in pieces if something breaks to isolate where the problem is. I also have a very old post about debugging called Troubleshooting WordPress Like a Sniper. While my writing style has evolved and WordPress has updated since 2015, the general rules to how to debug haven’t changed very much.

Hope that helps, and as always, happy WordPressing!


Viewing all articles
Browse latest Browse all 5

Trending Articles