Workflow 2.0: creating the CPTs and roles

Register a post type

Registering the custom post types is almost the same as it was before, with one notable exception. CPTs have had labels for some time, so that instead of WP calling everything a “post,” it can know to call your CPT a “portfolio” or a “policy” or whatever you need it to say. WP 5.0 introduced an additional label: “item_reverted_to_draft.” In most cases, you could leave this alone as most users would never see the message. But in our case, when a Policy Editor edits a published CPT, they need not to see the default “Post reverted to draft” message because our plugin is preventing that from happening. Instead, they need to see a message more along the lines of, “Successfully submitted for review.”

There are several ways to use the new CPT label. (This WP StackExchange article has a couple of other examples.) The way I found easiest for our needs was to set the label right when I registered the post type, so only 1 line here changes:

$labels = array(
	/* Here's the one line: a new line, setting the "item_reverted_to_draft" label. */
	'item_reverted_to_draft' => 'Successfully submitted for review.'
	'name'               => 'Academic Affairs',
	'singular_name'      => 'Academic Affairs Policy',
	'menu_name'          => 'Academic Affairs',
	'name_admin_bar'     => 'Academic Affairs Policy',
	'add_new'            => 'Add New',
	'add_new_item'       => 'Add New Policy',
	'new_item'           => 'New Policy',
	'edit_item'          => 'Edit Policy',
	'view_item'          => 'View Policy',
	'all_items'          => 'All Policies',
	'search_items'       => 'Search Policies',
	'not_found'          => 'No policies found.',
	'not_found_in_trash' => 'No policies found in trash.',
$capabilities = array(
	'edit_post'          => 'edit_aap',
	'read_post'          => 'read_aap',
	'delete_post'        => 'delete_aap',
	'create_posts'      		=> 'create_aaps',
	'delete_posts'				=> 'delete_aaps',
	'delete_others_posts'		=> 'delete_others_aaps',
	'delete_private_posts'		=> 'delete_private_aaps',
	'delete_published_posts'	=> 'delete_published_aaps',
	'edit_posts'				=> 'edit_aaps',
	'edit_others_posts'			=> 'edit_others_aaps',
	'edit_private_posts'		=> 'edit_private_aaps',
	'edit_published_posts'		=> 'edit_published_aaps',
	'publish_posts'				=> 'publish_aaps',
	'read_private_posts'		=> 'read_private_aaps',
$args = array(
	'show_in_rest' => true,
	'map_meta_cap' => true,
	'menu_icon' => 'dashicons-book',
	'menu_position' => 1.01,
	'public' => true,
	'labels'  => $labels,
	'has_archive' => true,
	'hierarchical' => false,
	'supports' => array('title', 'editor', 'author', 'revisions', 'page-attributes'),
	'capabilities' => $capabilities,
	'rewrite' => array('slug' => 'academic-affairs'),
register_post_type( 'aap', $args );

This one little label provides much-needed confirmation to our Policy Editors that they have pressed the correct button, and now it’s time for the Policy Approver to do his or her part.

Change the Publish Panel text

WP 5.x has a new Publish panel by default. Instead of immediately publishing or updating a post when you use the Publish or Update button, WP provides an intermediate panel with contextual information. Unfortunately, it contains text specific to WP Core roles – when a Policy Editor hits Publish the first time and the pre-publish checks show up, a Core message says “When you’re ready, submit your work for review, and an Editor will be able to approve it for you.” This could be fairly confusing, since the Policy Editor may think they are the Editor, when in fact they have a custom role and are awaiting a Policy Approver’s review.

So, the first (and simplest) way to make a clearer message is to present a different static message using “translation.”

add_action('admin_print_footer_scripts', function() {
	if(wp_script_is('wp-i18n')) {
				'When you’re ready, submit your work for review, and an Editor will be able to approve it for you.': [
                    'When you’re ready, submit your work for review, and the Policy Approver for your office will receive an email notifying them that they need to review your work. It will not be published until they have approved the policy.'
}, 11);

Of course, you should only use this method if all of your post types needs a custom message. You may be working on a site with multiple post types, each treated differently.

However, we don’t have to stop here – we can dynamically tell our Policy Editors who these mysterious Policy Approvers are. That way, no matter whether the site has only Core roles or a bunch of custom roles, the user will know for sure who exactly they need to bug if they urgently need to get their work published.

add_action( 'admin_print_footer_scripts', function(){
	global $pagenow;
	/* Only run this script once internationalization is set up, and the current screen is "add new post" */
	if ( wp_script_is( 'wp-i18n' ) && $pagenow == 'post-new.php') {
		/* Get the post type so we can dynamically check for the right capability */
		$screen = get_current_screen();
		$capability = 'publish_' . $screen->id . 's';
		$wp_roles = wp_roles();
		$role_names = [];
		/* Get the roles which can publish this CPT but are not admins */
		foreach ( $wp_roles->role_objects as $name => $role ) {
			if ( $role && $role->has_cap( "$capability" ) && !$role->has_cap( 'manage_options' ) ) {
				$role_names[] = $name;
		/* Get all users with those roles */
		$users = get_users([
			'role__in' => $role_names,
		$user_names = [];
		foreach ( $users as $user ) {
			$user_names[] = $user->display_name;
		$data = [
			'When you’re ready, submit your work for review, and an Editor will be able to approve it for you.' => [
				'When you are ready, submit the policy for review, and one of these users will be able to approve it for you: ' . implode( ', ', $user_names )
		wp.i18n.setLocaleData(<?php echo json_encode( $data ); ?>);
}, 11 );

I owe this part completely to Sally from StackExchange. (The same WP StackExchange article referenced above is the basis for this code, though I tweaked it to our needs.)

One last “translation”: on the Revision Comparison screen, the CTA button normally says “Restore This Revision.” Since the Revision Comparison screen is also where Policy Approvers will be reviewing Policy Editors’ edits, it would be more appropriate to have that button say “Approve and Publish” – but only if the person viewing the revisions has publishing rights.

add_filter('gettext', 'workflow_2_change_revision_button_text', 10, 3);
function workflow_2_change_revision_button_text($translation, $text, $domain) {
	global $pagenow;
	if($pagenow == 'revision.php') {
		// Get post type of this revision's parent
		global $post;
		$post_type = get_post_type($post->post_parent);
		$capability = 'publish_' . $post_type . 's';
		// If the current user can publish, and we're using the default textdomain, and the text is "Restore This Revision"
		if(current_user_can($capability) && 'default' === $domain && 'Restore This Revision' === $text) {
			$translation = 'Approve and Publish';
	return $translation;

Create and update roles

Nothing has changed from version 1, so please see Workflow 1.0: creating the CPTs and roles for the explanation and the code.

This post is part of a series. Next up: Workflow 2.0: creating the workflow

Leave a Reply

Your email address will not be published. Required fields are marked *