Wordpress - Add pre-publish conditions to the block editor

const { registerPlugin } = wp.plugins;
const { PluginPrePublishPanel } = wp.editPost;
const { select, dispatch } = wp.data;
const { count } = wp.wordcount;
const { serialize } = wp.blocks;
const { PanelBody } = wp.components;

const PrePublishCheckList = () => {
    let lockPost = false;

    // Get the WordCount
    const blocks = select( 'core/block-editor' ).getBlocks();
    const wordCount = count( serialize( blocks ), 'words' );
    let wordCountMessage = `${wordCount}`;
    if ( wordCount < 500 ) {
        lockPost = true;
        wordCountMessage += ` - Minimum of 500 required.`;
    }

    // Get the cats
    const cats = select( 'core/editor' ).getEditedPostAttribute( 'categories' );
    let catsMessage = 'Set';
    if ( ! cats.length ) {
        lockPost = true;
        catsMessage = 'Missing';
    } else {
        // Check that the cat is not uncategorized - this assumes that the ID of Uncategorized is 1, which it would be for most installs.
        if ( cats.length === 1 && cats[0] === 1 ) {
            lockPost = true;
            catsMessage = 'Cannot use Uncategorized';
        }
    }

    // Get the tags
    const tags = select( 'core/editor' ).getEditedPostAttribute( 'tags' );
    let tagsMessage = 'Set';
    if ( tags.length < 3 || tags.length > 5 ) {
        lockPost = true;
        tagsMessage = 'Required 3 - 5 tags';
    }

    // Get the featured image
    const featuredImageID = select( 'core/editor' ).getEditedPostAttribute( 'featured_media' );
    let featuredImage = 'Set';

    if ( featuredImageID === 0 ) {
        lockPost = true;
        featuredImage = 'Not Set';
    }

    // Do we need to lock the post?
    if ( lockPost === true ) {
        dispatch( 'core/editor' ).lockPostSaving();
    } else {
        dispatch( 'core/editor' ).unlockPostSaving();
    }
    return (
        <PluginPrePublishPanel title={ 'Publish Checklist' }>
            <p><b>Word Count:</b> { wordCountMessage }</p>
            <p><b>Categories:</b> { catsMessage }</p>
            <p><b>Tags:</b> { tagsMessage }</p>
            <p><b>Featured Image:</b> { featuredImage }</p>
        </PluginPrePublishPanel>
    )
};

registerPlugin( 'pre-publish-checklist', { render: PrePublishCheckList } );

Display:

enter image description here

The solution above addresses the requirements listed in the question. One thing that can be expanded on is the category checking, I am making some assumptions about the category ID.

I have kept all of the checks in the same component for the sake of brevity and readability here. I would recommend moving each portion into a separate component and potentially making them Higher Order Components ( i.e withWordCount ).

I have inline comments that explain what is being done but am happy to explain further if there are any questions.

EDIT: Here's how I'm enqueuing the script

function enqueue_block_editor_assets() {
    wp_enqueue_script(
        'my-custom-script', // Handle.
        plugin_dir_url( __FILE__ ) . '/build/index.js',
        array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor', 'wp-edit-post', 'word-count' ) // Dependencies, defined above.
    );
}
add_action( 'enqueue_block_editor_assets', 'enqueue_block_editor_assets' );

Adding some more details about the build process. I am using @wordpress/scripts and running the following scripts:

"scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  },

Edit 2:

You can get the attachment data via:

wp.data.select('core').getMedia( ID )

Update 29.02.2020

You have to replace select( 'core/editor' ).getBlocks() with select( 'core/block-editor' ).getBlocks() in order for this to work

This worked for me:

path\to\theme\pre-publish-checklist\src\index.js

const { registerPlugin } = wp.plugins;
const { PluginPrePublishPanel } = wp.editPost;
const { select, dispatch } = wp.data;
const { count } = wp.wordcount;
const { serialize } = wp.blocks;
const { PanelBody } = wp.components;

const PrePublishCheckList = () => {
    let lockPost = false;

    // Get the WordCount
    const blocks = select( 'core/block-editor' ).getBlocks();
    const wordCount = count( serialize( blocks ), 'words' );
    let wordCountMessage = `${wordCount}`;
    if ( wordCount < 500 ) {
        lockPost = true;
        wordCountMessage += ` - Minimum of 500 required.`;
    }

    // Get the cats
    const cats = select( 'core/editor' ).getEditedPostAttribute( 'categories' );
    let catsMessage = 'Set';
    if ( ! cats.length ) {
        lockPost = true;
        catsMessage = 'Missing';
    } else {
        // Check that the cat is not uncategorized - this assumes that the ID of Uncategorized is 1, which it would be for most installs.
        if ( cats.length === 1 && cats[0] === 1 ) {
            lockPost = true;
            catsMessage = 'Cannot use Uncategorized';
        }
    }

    // Get the tags
    const tags = select( 'core/editor' ).getEditedPostAttribute( 'tags' );
    let tagsMessage = 'Set';
    if ( tags.length < 3 || tags.length > 5 ) {
        lockPost = true;
        tagsMessage = 'Required 3 - 5 tags';
    }

    // Get the featured image
    const featuredImageID = select( 'core/editor' ).getEditedPostAttribute( 'featured_media' );
    let featuredImage = 'Set';

    if ( featuredImageID === 0 ) {
        lockPost = true;
        featuredImage = 'Not Set';
    }

    // Do we need to lock the post?
    if ( lockPost === true ) {
        dispatch( 'core/editor' ).lockPostSaving();
    } else {
        dispatch( 'core/editor' ).unlockPostSaving();
    }
    return (
        <PluginPrePublishPanel title={ 'Publish Checklist' }>
            <p><b>Word Count:</b> { wordCountMessage }</p>
            <p><b>Categories:</b> { catsMessage }</p>
            <p><b>Tags:</b> { tagsMessage }</p>
            <p><b>Featured Image:</b> { featuredImage }</p>
        </PluginPrePublishPanel>
    )
};

registerPlugin( 'pre-publish-checklist', { render: PrePublishCheckList } );

Full steps to create the panel with @wordpress/scripts

  1. Create a folder pre-publish-checklist in your theme
  2. Create inside the folder package.json file with
 "scripts": {
   "build": "wp-scripts build",
   "check-engines": "wp-scripts check-engines",
   "check-licenses": "wp-scripts check-licenses",
   "format:js": "wp-scripts format-js",
   "lint:css": "wp-scripts lint-style",
   "lint:js": "wp-scripts lint-js",
   "lint:md:docs": "wp-scripts lint-md-docs",
   "lint:md:js": "wp-scripts lint-md-js",
   "lint:pkg-json": "wp-scripts lint-pkg-json",
   "packages-update": "wp-scripts packages-update",
   "start": "wp-scripts start",
   "test:e2e": "wp-scripts test-e2e",
   "test:unit": "wp-scripts test-unit-js"
 },
 "dependencies": {
   "@wordpress/scripts": "^7.1.2"
 }
}
  1. create a file in the folder with the path 'src/index.js' and place the code in the file
  2. yarn
  3. yarn build
  4. Add this code to functions.php to enqueue the file
function enqueue_block_editor_assets() {
    wp_enqueue_script(
        'pre-publish-checklist', // Handle.
        get_template_directory_uri(). '/pre-publish-checklist/build/index.js',
        array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor', 'wp-edit-post', 'word-count' ) // Dependencies, defined above.
    );
}
add_action( 'enqueue_block_editor_assets', 'enqueue_block_editor_assets' );

Tags:

Block Editor