Your error doesn’t appears on the screen because the page is reloaded after the action publish_post
.
I have followed a different approach altogether, I’m checking the post title at wp_insert_post_data
if the title is empty, I mark the post_status as draft and save a simple flag in options table. The flag I’ve saved is used to hide the Post Published notice and display a admin notice for specifying title in order to publish the post.
At the same moment I delete the option, so the notice is one time thing. You can modify it to a greater extent as per your requirements, but this is a basic solution and should work fine.
/**
* Checks for empty post title, if empty sets the post status to draft
*
* @param $data
* @param $postarr
*
* @return array
*/
function wse_279994_check_post_title( $data, $postarr ) {
if ( is_array( $data ) && 'publish' == $data['post_status'] && empty( $data['post_title'] ) ) {
$data['post_status'] = 'draft';
update_option( 'wse_279994_post_error', 'empty_title' );
}
return $data;
}
add_filter( 'wp_insert_post_data', 'wse_279994_check_post_title', 10, 2 );
/**
* If the post title was empty, do not show post published message
*/
add_filter( 'post_updated_messages', 'wse_279994_remove_all_messages' );
function wse_279994_remove_all_messages( $messages ) {
if ( get_option( 'wse_279994_post_error' ) ) {
return array();
} else {
return $messages;
}
}
/**
* Show admin notice for empty post title
*/
add_action( 'admin_notices', 'wse_279994_show_error' );
function wse_279994_show_error() {
$screen = get_current_screen();
if ( $screen->id != 'post' ) {
return;
}
if ( ! get_option( 'wse_279994_post_error' ) ) {
return;
}
echo '<div class="error"><p>' . esc_html__( "You need to enter a Post Title in order to publish it.", "wse" ) . '</p></div>';
delete_option( 'wse_279994_post_error' );
}