How to edit post meta outside of a Gutenberg Block?

The Block API of Gutenberg supports post meta as a attribute source. But what if you want to use post meta in say a plugin sidebar?

With the help of the @wordpress/data package and so called “higher-order components” it’s as easy as for blocks. 🙌

First we import all the required dependencies. This assumes that you’re using webpack and define your externals like this. If you’re looking for an ES5 example take a look at the block editor handbook.

/**
 * WordPress dependencies
 */
import {
	withSelect,
	withDispatch,
} from '@wordpress/data';
import {
	PluginSidebar,
	PluginSidebarMoreMenuItem,
} from '@wordpress/edit-post';
import {
	PanelColor,
} from '@wordpress/editor';
import {
	Component,
	Fragment,
	compose,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { registerPlugin } from '@wordpress/plugins';

To render the UI we extend Component and implement the render() method. For this example I used a PanelColor component which allows a user to select a color. We’ll save the hex value in the post meta my_color_one:

/**
 * Custom component with a simple color panel.
 */
class MyPlugin extends Component {

	render() {
		// Nested object destructuring.
		const {
			meta: {
				my_color_one: colorOne,
			} = {},
			updateMeta,
		} = this.props;

		return (
			<Fragment>
				<PluginSidebarMoreMenuItem
					name="my-plugin-sidebar"
					type="sidebar"
					target="my-plugin-sidebar"
				>
					{ __( 'Color it!', 'my-plugin' ) }
				</PluginSidebarMoreMenuItem>
				<PluginSidebar
					name="my-plugin-sidebar"
					title={ __( 'Color it!', 'my-plugin' ) }
				>
					<PanelColor
						colorValue={ colorOne}
						initialOpen={ false }
						title={ __( 'Color 1', 'my-plugin' ) }
						onChange={ ( value ) => {
							// value is undefined if color is cleared.
							updateMeta( { my_color_one: value || '' } );
						} }
					/>
				</PluginSidebar>
			</Fragment>
		);
	}
}

(Please ignore the incorrect syntax highlighting.)

Now we need a higher-order component which is used to fetch the data, in this case our post meta.

// Fetch the post meta.
const applyWithSelect = withSelect( ( select ) => {
	const { getEditedPostAttribute } = select( 'core/editor' );

	return {
		meta: getEditedPostAttribute( 'meta' ),
	};
} );

The second higher-order component is used to update the data.

// Provide method to update post meta.
const applyWithDispatch = withDispatch( ( dispatch, { meta } ) => {
	const { editPost } = dispatch( 'core/editor' );

	return {
		updateMeta( newMeta ) {
			editPost( { meta: { ...meta, ...newMeta } } ); // Important: Old and new meta need to be merged in a non-mutating way!
		},
	};
} );

Since we now have two higher-order components we have to combine them with compose:

// Combine the higher-order components.
const render = compose( [
	applyWithSelect,
	applyWithDispatch
] )( MyPlugin );

Having a fully renderable component we can finally register our custom plugin:

registerPlugin( 'my-plugin', {
	icon: 'art',
	render,
} );

And that’s it! 🚢

PS: Make sure to register the post meta properly with 'show_in_rest' => true.


Photo by Kimberly Farmer.

Remove WordPress plugins under version control from update checks

Annoyed by the update information for plugins which are under version control like Git or SVN? Use this snippet to finally get rid of them.

/**
 * Removes plugins under version control from update checks.
 *
 * @param array  $request  An array of HTTP request arguments.
 * @param string $url     The request URL.
 * @return $array An array of HTTP request arguments.
 */
add_filter( 'http_request_args', function( $request, $url ) {
	if ( false === strpos( $url, 'api.wordpress.org/plugins/update-check' ) ) {
		return $request;
	}

	$vcs_dirs = [ '.git', '.svn', '.hg', '.bzr' ];

	$plugins = json_decode( $request['body']['plugins'] );
	foreach ( array_keys( (array) $plugins->plugins ) as $plugin ) {
		$plugin_dir = WP_PLUGIN_DIR . '/' . dirname( $plugin ) . '/';

		// Search directory for evidence of version control.
		foreach ( $vcs_dirs as $vcs_dir ) {
			if ( is_dir( $plugin_dir . $vcs_dir ) ) {
				// Remove plugin from the update check.
				unset( $plugins->plugins->$plugin );
				break;
			}
		}
	}

	$request['body']['plugins'] = wp_json_encode( $plugins );

	return $request;
}, 10, 2 );

You can extend $vcs_dirs with additional directories to add support for your VCS.

Photo by TUAN ANH TRAN.