Skip to content

Instantly share code, notes, and snippets.

@carlodaniele
Created January 2, 2023 17:24
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save carlodaniele/05d4e8b6d982a4ef30840015fbdfb35f to your computer and use it in GitHub Desktop.
Save carlodaniele/05d4e8b6d982a4ef30840015fbdfb35f to your computer and use it in GitHub Desktop.
A custom Gutenberg block with a custom sidebar to add and manage custom meta fields in WordPress
import { __ } from '@wordpress/i18n';
import { compose } from '@wordpress/compose';
import { withSelect, withDispatch } from '@wordpress/data';
import { PluginSidebar, PluginSidebarMoreMenuItem } from '@wordpress/edit-post';
import { PanelBody, PanelRow, TextControl, DateTimePicker } from '@wordpress/components';
const CustomSidebar = ( { postType, metaFields, setMetaFields } ) => {
if ( 'post' !== postType ) return null;
return (
<>
<PluginSidebarMoreMenuItem
target="metadata-sidebar"
icon="book"
>
Metadata Sidebar
</PluginSidebarMoreMenuItem>
<PluginSidebar
name="metadata-sidebar"
icon="book"
title="My Sidebar"
>
<PanelBody title="Book details" initialOpen={ true }>
<PanelRow>
<TextControl
value={ metaFields._meta_fields_book_title }
label={ __( "Title" ) }
onChange={ (value) => setMetaFields( { _meta_fields_book_title: value } ) }
/>
</PanelRow>
<PanelRow>
<TextControl
value={ metaFields._meta_fields_book_author }
label={ __("Author", "textdomain") }
onChange={ (value) => setMetaFields( { _meta_fields_book_author: value } ) }
/>
</PanelRow>
<PanelRow>
<TextControl
value={ metaFields._meta_fields_book_publisher }
label={ __("Publisher", "textdomain") }
onChange={ (value) => setMetaFields( { _meta_fields_book_publisher: value } ) }
/>
</PanelRow>
<PanelRow>
<DateTimePicker
currentDate={ metaFields._meta_fields_book_date }
onChange={ ( newDate ) => setMetaFields( { _meta_fields_book_date: newDate } ) }
__nextRemoveHelpButton
__nextRemoveResetButton
/>
</PanelRow>
</PanelBody>
</PluginSidebar>
</>
)
}
const applyWithSelect = withSelect( ( select ) => {
return {
metaFields: select( 'core/editor' ).getEditedPostAttribute( 'meta' ),
postType: select( 'core/editor' ).getCurrentPostType()
};
} );
const applyWithDispatch = withDispatch( ( dispatch ) => {
return {
setMetaFields ( newValue ) {
dispatch('core/editor').editPost( { meta: newValue } )
}
}
} );
export default compose([
applyWithSelect,
applyWithDispatch
])(CustomSidebar);
/**
* Retrieves the translation of text.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
*/
import { __ } from '@wordpress/i18n';
/**
* React hook that is used to mark the block wrapper element.
* It provides all the necessary props like the class name.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
*/
import { useBlockProps, InspectorControls, RichText } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { useEntityProp } from '@wordpress/core-data';
import { TextControl, DatePicker, TimePicker, PanelBody, PanelRow } from '@wordpress/components';
/**
* Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
* Those files can contain any CSS code that gets applied to the editor.
*
* @see https://www.npmjs.com/package/@wordpress/scripts#using-css
*/
import './editor.scss';
/**
* The edit function describes the structure of your block in the context of the
* editor. This represents what the editor will render when the block is used.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
*
* @return {WPElement} Element to render.
*/
export default function Edit() {
const blockProps = useBlockProps();
const postType = useSelect(
( select ) => select( 'core/editor' ).getCurrentPostType(),
[]
);
const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' );
const bookTitle = meta[ '_meta_fields_book_title' ];
const bookAuthor = meta[ '_meta_fields_book_author' ];
const bookPublisher = meta[ '_meta_fields_book_publisher' ];
const bookDate = meta[ '_meta_fields_book_date' ];
const updateBookTitleMetaValue = ( newValue ) => {
setMeta( { ...meta, _meta_fields_book_title: newValue } );
};
const updateBookAuthorMetaValue = ( newValue ) => {
setMeta( { ...meta, _meta_fields_book_author: newValue } );
};
const updateBookPublisherMetaValue = ( newValue ) => {
setMeta( { ...meta, _meta_fields_book_publisher: newValue } );
};
return (
<>
<InspectorControls>
<PanelBody
title={ __( 'Book Details' )}
initialOpen={true}
>
<PanelRow>
<fieldset>
<TextControl
label={__( 'Book title' )}
value={ bookTitle }
onChange={ updateBookTitleMetaValue }
/>
</fieldset>
</PanelRow>
<PanelRow>
<fieldset>
<TextControl
label={__( 'Book author' )}
value={ bookAuthor }
onChange={ updateBookAuthorMetaValue }
/>
</fieldset>
</PanelRow>
<PanelRow>
<fieldset>
<TextControl
label={__( 'Publisher' )}
value={ bookPublisher }
onChange={ updateBookPublisherMetaValue }
/>
</fieldset>
</PanelRow>
<PanelRow>
<fieldset>
<DatePicker
currentDate={ bookDate }
onChange={ ( newValue ) => setMeta( { ...meta, _meta_fields_book_date: newValue } ) }
__nextRemoveHelpButton
__nextRemoveResetButton
/>
</fieldset>
</PanelRow>
</PanelBody>
</InspectorControls>
<div { ...blockProps }>
<RichText
tagName="h3"
onChange={ updateBookTitleMetaValue }
allowedFormats={ [ 'core/bold', 'core/italic' ] }
value={ bookTitle }
placeholder={ __( 'Write your text...' ) }
/>
<TextControl
label={ __( 'Book author' ) }
value={ bookAuthor }
onChange={ updateBookAuthorMetaValue }
/>
<TextControl
label={ __( 'Book publisher' ) }
value={ bookPublisher }
onChange={ updateBookPublisherMetaValue }
/>
{
/*
https://github.com/WordPress/gutenberg/tree/trunk/packages/components/src/date-time
https://developer.wordpress.org/block-editor/reference-guides/components/date-time/
*/
}
<TimePicker
currentTime={ bookDate }
onChange={ ( newValue ) => setMeta( { ...meta, _meta_fields_book_date: newValue } ) }
__nextRemoveHelpButton
__nextRemoveResetButton
/>
</div>
</>
);
}
/**
* Registers a new block provided a unique name and an object defining its behavior.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
*/
import { registerBlockType } from '@wordpress/blocks';
/**
* Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
* All files containing `style` keyword are bundled together. The code used
* gets applied both to the front of your site and to the editor.
*
* @see https://www.npmjs.com/package/@wordpress/scripts#using-css
*/
import './style.scss';
/**
* Internal dependencies
*/
import Edit from './edit';
import metadata from './block.json';
/**
* Registers a plugin for adding items to the Gutenberg Toolbar
*
* @see https://developer.wordpress.org/block-editor/reference-guides/slotfills/plugin-sidebar/
*/
import { registerPlugin } from '@wordpress/plugins';
import CustomSidebar from './components/CustomSidebar';
// import MetaBox from './components/MetaBox';
registerPlugin( 'metadata-block', {
render: CustomSidebar
} );
/**
* Every block starts by registering a new block type definition.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
*/
registerBlockType( metadata.name, {
/**
* @see ./edit.js
*/
edit: Edit,
} );
import { __ } from '@wordpress/i18n';
import { compose } from '@wordpress/compose';
import { withSelect, withDispatch } from '@wordpress/data';
import { PluginDocumentSettingPanel } from '@wordpress/edit-post';
import { PanelRow, TextControl, DateTimePicker } from '@wordpress/components';
const MetaBox = ( { postType, metaFields, setMetaFields } ) => {
if ( 'post' !== postType ) return null;
return(
<PluginDocumentSettingPanel
title={ __( 'Book details' ) }
icon="book"
initialOpen={ false }
>
<PanelRow>
<TextControl
value={ metaFields._meta_fields_book_title }
label={ __( "Title" ) }
onChange={ (value) => setMetaFields( { _meta_fields_book_title: value } ) }
/>
</PanelRow>
<PanelRow>
<TextControl
value={ metaFields._meta_fields_book_author }
label={ __( "Author" ) }
onChange={ (value) => setMetaFields( { _meta_fields_book_author: value } ) }
/>
</PanelRow>
<PanelRow>
<TextControl
value={ metaFields._meta_fields_book_publisher }
label={ __( "Publisher" ) }
onChange={ (value) => setMetaFields( { _meta_fields_book_publisher: value } ) }
/>
</PanelRow>
<PanelRow>
<DateTimePicker
currentDate={ metaFields._meta_fields_book_date }
onChange={ ( newDate ) => setMetaFields( { _meta_fields_book_date: newDate } ) }
__nextRemoveHelpButton
__nextRemoveResetButton
/>
</PanelRow>
</PluginDocumentSettingPanel>
);
}
const applyWithSelect = withSelect( ( select ) => {
return {
metaFields: select( 'core/editor' ).getEditedPostAttribute( 'meta' ),
postType: select( 'core/editor' ).getCurrentPostType()
};
} );
const applyWithDispatch = withDispatch( ( dispatch ) => {
return {
setMetaFields ( newValue ) {
dispatch('core/editor').editPost( { meta: newValue } )
}
}
} );
export default compose([
applyWithSelect,
applyWithDispatch
])(MetaBox);
<?php
/**
* Plugin Name: Meta Fields
* Description: Block description
* Requires at least: 5.9
* Requires PHP: 7.0
* Version: 0.1.0
* Author: The Kinsta team
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: metadata-block
*
* @package meta-fields
*/
/**
* Registers the block using the metadata loaded from the `block.json` file.
* Behind the scenes, it registers also all assets so they can be enqueued
* through the block editor in the corresponding context.
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function meta_fields_metadata_block_block_init() {
register_block_type(
__DIR__ . '/build',
array(
'render_callback' => 'meta_fields_metadata_block_render_callback',
)
);
}
add_action( 'init', 'meta_fields_metadata_block_block_init' );
/**
* Render callback function.
*
* @param array $attributes The block attributes.
* @param string $content The block content.
* @param WP_Block $block Block instance.
*
* @return string The rendered output.
*/
function meta_fields_metadata_block_render_callback( $attributes, $content, $block ) {
$post_meta = get_post_meta( get_the_ID() );
$output = "";
if( ! empty( $post_meta['_meta_fields_book_title'][0] ) ){
$title = '<h3>' . esc_html( $post_meta['_meta_fields_book_title'][0] ) . '</h3>';
} else {
$title = '<h3>' . esc_attr( get_the_title() ) . '</h3>';
}
if( ! empty( $post_meta['_meta_fields_book_author'][0] ) ){
$output .= '<li>' . __( 'Book author: ' ) . esc_html( $post_meta['_meta_fields_book_author'][0] ) . '</li>';
}
if( ! empty( $post_meta['_meta_fields_book_publisher'][0] ) ){
$output .= '<li>' . __( 'Book publisher: ' ) . esc_html( $post_meta['_meta_fields_book_publisher'][0] ) . '</li>';
}
if( ! empty( $post_meta['_meta_fields_book_date'][0] ) ){
$date = date_create( esc_html( $post_meta['_meta_fields_book_date'][0] ) );
$output .= '<li>' . __( 'Book date: ' ) . date_format( $date, "F d, Y" ) . '</li>';
}
if( strlen( $output ) > 0 ){
return '<div ' . get_block_wrapper_attributes() . '>' . $title . '<ul>' . $output . '</ul></div>';
} else {
return '<strong>' . __( 'Sorry. No fields available here!' ) . '</strong>';
}
}
// register metabox
function meta_fields_add_meta_box(){
add_meta_box(
'meta_fields_meta_box',
__( 'Book details' ),
'meta_fields_build_meta_box_callback',
'post',
'side',
'default',
// hide the metabox in Gutenberg
array('__back_compat_meta_box' => true)
);
}
// build metabox
function meta_fields_build_meta_box_callback( $post ){
wp_nonce_field( 'meta_fields_save_meta_box_data', 'meta_fields_meta_box_nonce' );
$author = get_post_meta( $post->ID, '_meta_fields_book_title', true );
$author = get_post_meta( $post->ID, '_meta_fields_book_author', true );
$publisher = get_post_meta( $post->ID, '_meta_fields_book_publisher', true );
$date = get_post_meta( $post->ID, '_meta_fields_book_date', true );
?>
<div class="inside">
<p><strong>Title</strong></p>
<p><input type="text" id="meta_fields_book_title" name="meta_fields_book_title" value="<?php echo esc_attr( $title ); ?>" /></p>
<p><strong>Author</strong></p>
<p><input type="text" id="meta_fields_book_author" name="meta_fields_book_author" value="<?php echo esc_attr( $author ); ?>" /></p>
<p><strong>Publisher</strong></p>
<p><input type="text" id="meta_fields_book_publisher" name="meta_fields_book_publisher" value="<?php echo esc_attr( $publisher ); ?>" /></p>
<p><strong>Date</strong></p>
<p><input type="date" id="meta_fields_book_date" name="meta_fields_book_date" value="<?php echo esc_attr( $date ); ?>" /></p>
</div>
<?php
}
add_action( 'add_meta_boxes', 'meta_fields_add_meta_box' );
// save meta data
function meta_fields_save_meta_box_data( $post_id ) {
if ( ! isset( $_POST['meta_fields_meta_box_nonce'] ) )
return;
if ( ! wp_verify_nonce( $_POST['meta_fields_meta_box_nonce'], 'meta_fields_save_meta_box_data' ) )
return;
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return;
if ( ! current_user_can( 'edit_post', $post_id ) )
return;
if ( ! isset( $_POST['meta_fields_book_title'] ) )
return;
if ( ! isset( $_POST['meta_fields_book_author'] ) )
return;
if ( ! isset( $_POST['meta_fields_book_date'] ) )
return;
if ( ! isset( $_POST['meta_fields_book_publisher'] ) )
return;
$title = sanitize_text_field( $_POST['meta_fields_book_title'] );
$author = sanitize_text_field( $_POST['meta_fields_book_author'] );
$date = sanitize_text_field( $_POST['meta_fields_book_date'] );
$publisher = sanitize_text_field( $_POST['meta_fields_book_publisher'] );
update_post_meta( $post_id, '_meta_fields_book_title', $title );
update_post_meta( $post_id, '_meta_fields_book_author', $author );
update_post_meta( $post_id, '_meta_fields_book_date', $date );
update_post_meta( $post_id, '_meta_fields_book_publisher', $publisher );
}
add_action( 'save_post', 'meta_fields_save_meta_box_data' );
/**
* Register the custom meta fields
*/
function meta_fields_register_meta() {
$metafields = [ '_meta_fields_book_title', '_meta_fields_book_author', '_meta_fields_book_date', '_meta_fields_book_publisher' ];
foreach( $metafields as $metafield ){
// Pass an empty string to register the meta key across all existing post types.
register_post_meta( '', $metafield, array(
'show_in_rest' => true,
'type' => 'string',
'single' => true,
'sanitize_callback' => 'sanitize_text_field',
'auth_callback' => function() {
return current_user_can( 'edit_posts' );
}
));
}
}
add_action( 'init', 'meta_fields_register_meta' );
/**
* The following styles get applied both on the front of your site
* and in the editor.
*
* Replace them with your own styles or remove the file completely.
*/
.wp-block-meta-fields-metadata-block {
background-color: #21759b;
color: #fff;
padding: 2rem;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment