Custom Nextpage プラグインをカスタムブロック対応してみた

記事をページ分けする Page Break を拡張する Custom Nextpage をブロックエディタへ対応してみました。ショートコードを利用するプラグインにブロックを追加する方法や、js 内にあるテキストの翻訳も作成しています。

Custom Nextpage: https://ja.wordpress.org/plugins/custom-nextpage/

GitHub: https://github.com/Webnist/custom-nextpage/tree/dev-1.2.0

Classic エディタと クラシックブロックの挙動

NEXT PAGE でページ分割
次のページへのリンクタイトルを自由に変更可能!

クラシックエディタ・クラシックブロック内で Custom Nextpage ボタンクリックすると、ボックスがポップアップして、タイトルを入力すると、エディタ内には “NEXT PAGE” の区切り画像が入る仕様です。

次ページへのリンクテキストを自由に変更できる便利なプラグインです。
下のキャプチャはクラシックブロックをブロック化した状態。

内部はショートコードです。

ショートコードブロックでも良いけど、ここはやっぱりクラシックエディタと同じ見た目にしたいところ。
現在このプラグインを使っている方はショートコード名知らないし。

ショートコードをブロック化

@wordpress/create-block

カスタムブロックの雛形はコマンド一発で作成できます。

npx @wordpress/create-block custom-nextpage

Create a Block Tutorial: https://developer.wordpress.org/block-editor/getting-started/create-block/

ここではプラグインとして作成します。最終的に既存のプラグイン内に入れるので、テキストドメインを(翻訳ファイルを識別するために必要な個別識別子)同じにしておきます。

プラグインとして作成したブロックをCustom Nextpage プラグイン内へ移動

ブロックの雛形ができたので、Custom Nextpage プラグイン内へ移動します。
管理しやすいように /block ディレクトリを作成し、格納します。

├── block.json
├── build
├── custom-nextpage.php
├── node_modules
├── package-lock.json
├── package.json
├── readme.txt
└── src

custom-nextpage.php プラグインヘッダの記述をを削除して、Custom Nexpage プラグインへ読み込ませます。ブロックエディタで追加したカスタムブロックが選択できることを確認。
これで準備完了。

ブロックの設定

npx @wordpress/create-block を使えば block.json が作成されます。

npx で作成すると block.json 内に設定がまとめられるようになったようです。
WordPress 5.8 から推奨なんですね。

メタデータ: https://ja.wordpress.org/team/handbook/block-editor/reference-guides/block-api/block-metadata/

まずパフォーマンスの観点では、テーマが遅延ロードアセットをサポートする場合、block.json で登録されたブロックは、標準でアセットのエンキューが最適化されます。style や script プロパティにリストされたフロントエンドの CSS や JavaScript アセットは、ブロックがページ上に存在するときにのみエンキューされ、結果的にページサイズが小さくなります。

さらに、ブロックタイプ REST API エンドポイントでは、サーバー上で登録されたブロックしか一覧できないため、サーバーサイドでブロックを登録することが推奨されます。block.jsonファイルを使用すると、この登録が簡単になります。

{
	"$schema": "https://json.schemastore.org/block.json",
	"apiVersion": 2,
	"name": "create-block/custom-nextpage",
	"version": "0.1.0",
	"title": "Custom Nextpage",
	"category": "common",
	"icon": "editor-insertmore",
	"description": "Block for Custom Next Page shortcode.",
	"supports": {
		"html": false
	},
  	"attributes": {
	  "linkText": {
		"type": "string",
		"default": ""
	  }
	},
	"textdomain": "custom-nextpage",
	"editorScript": "file:build/index.js",
}

クラシックエディタ用に出力している js 等を分岐

Custom Nextpage プラグインは、クラシックエディタ上で js が読み込まれます。ブロックエディタでも読み込まれますが、エラーとなってしまいますので、ブロックエディタでは読み込ませないよう分岐を追加します。ただし、クラシックブロックには読み込ませたいので以下の分岐を追加します。

該当箇所: https://github.com/marushu/custom-nextpage-1/blob/dev-1.2.0/admin/class-admin-editor.php#L82

! has_blocks() && ! has_block( 'core/freeform' )

「ブロックエディタでは出力させないが、クラシックブロックが使われているときはロードする」といった分岐です。

Core Blocks Reference | Block Editor Handbook | WordPress Developer Resources https://developer.wordpress.org/block-editor/reference-guides/core-blocks/

クラシックエディタは、core/freeform というブロック名なんですね。

翻訳ファイル作成

edit.js, save.js を適宜編集。

editor.js

/**
 * Retrieves the translation of text.
 *
 * @see https://developer.wordpress.org/block-editor/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/packages/packages-block-editor/#useBlockProps
 */
import { useBlockProps } from '@wordpress/block-editor';

import {
	InspectorControls,
} from '@wordpress/block-editor';

import {
	PanelBody,
	TextControl,
} from '@wordpress/components';

export default function Edit( props ) {
	const { attributes: { linkText }, className, setAttributes } = props;
	const setText = (
		<TextControl
			label={ __( 'Title:', 'custom-nextpage' ) }
			help={ __( 'Text to put before the title navigation', 'custom-nextpage' ) }
			className="user-link-text"
			tagName="span"
			value={ linkText }
			placeholder={ __( 'Enter the link text…', 'custom-nextpage' ) }
			onChange={ ( newValue ) => {
				props.setAttributes( {
					linkText: newValue ? newValue : '',
				} );
			} }
		/>
	);

	return (
		<>
			<Fragment>
				<InspectorControls>
					<PanelBody title={ __( 'Option', 'custom-nextpage' ) }>
						{ setText }
					</PanelBody>
				</InspectorControls>
				<div className='event-editor' { ...useBlockProps() }>
					<img src={image_data.customNextPageImage} alt={ 'Next Page' } />
				</div>
			</Fragment>
		</>
	);
}

save.js

/**
 * Retrieves the translation of text.
 *
 * @see https://developer.wordpress.org/block-editor/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/packages/packages-block-editor/#useBlockProps
 */
import { useBlockProps } from '@wordpress/block-editor';

const { RawHTML } = wp.element;

/**
 * The save function defines the way in which the different attributes should
 * be combined into the final markup, which is then serialized by the block
 * editor into `post_content`.
 *
 * This plugin works dynamic block.
 * Then save function is null.
 *
 * @see https://developer.wordpress.org/block-editor/developers/block-api/block-edit-save/#save
 *
 * @return {WPElement} Element to render.
 */
export default function Save( props ) {
	//return null;

    const nextPageShortcode = '[nextpage title="' + props.attributes.linkText + '"]';

    return (

        <RawHTML>{ nextPageShortcode }</RawHTML>

    );

}

ショートコードはブロック化しやすい

RawHTML で OK

<RawHTML>{ nextPageShortcode }</RawHTML>

ショートコードに対応しているプラグインはブロック化しやすいと思います。
RawHTML で単純にショートコードを出力させれば良い。

今回はエディタとフロントで出力が違うのでダイナミックブロックにはしていませんが、具体的にはコールバック関数を作成してそこで do_shortcode すればエディタ上でも展開されます。

翻訳

追加したブロックのテキストドメインをプラグインと同じにしておくことで、js 内も翻訳可能です。

国際化 – Japanese Team — WordPress.org https://ja.wordpress.org/team/handbook/block-editor/how-to-guides/internationalization/#%E7%BF%BB%E8%A8%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90

  • プラグイン内の翻訳対象をピックアップ (wp i18n make-pot ./ languages/myguten.pot)
  • 日本語用の .po ファイル作成 (cp myguten.pot myguten-ja.po)
  • 翻訳用の json ファイルを作成 (wp i18n make-json myguten-ja.po –no-purge)

翻訳のロード

国際化 – Japanese Team — WordPress.org https://ja.wordpress.org/team/handbook/block-editor/how-to-guides/internationalization/#%E7%BF%BB%E8%A8%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%83%AD%E3%83%BC%E3%83%89

wp_set_script_translations することでブロック内も翻訳可能です。

wp_set_script_translations() | Function | WordPress Developer Resources https://developer.wordpress.org/reference/functions/wp_set_script_translations/

wp_set_script_translations( string $handle, string $domain = 'default', string $path = null )

block.json で作成された $handle は、create-block-(テキストドメイン)-editor-script となります。
今回は以下。

wp_set_script_translations(
	'create-block-custom-nextpage-editor-script',  // $handle
	'custom-nextpage',                                            // $domain
	plugin_dir_path( __FILE__ ) . '../languages/'   // $path
);

まとめ

既存のプラグインでショートコードに対応しているものは比較的楽にブロック化できます。

  • @wordpress/create-block で雛形を作成
  • 既存プラグイン内へ移動
  • ブロック設定 (edit.js, save.js) ここでテキストドメインを合わせておく
  • 改めて .pot ファイル作成 (wp i18n make-pot)
  • -ja,po を作成
  • json ファイル作成 (wp i18n make-json)

wp_set_script_translations で $handle を間違って翻訳が当たらず、若干時間がかかりましたが無事ブロック追加できました。

editorScript の $handle の命名規則

block.json で作成される index.js の $handle は、generate_block_asset_handle() で作成されているようです。

generate_block_asset_handle() | Function | WordPress Developer Resources https://developer.wordpress.org/reference/functions/generate_block_asset_handle/

/wp-includes/blocks.php 内 39 行目から。

/**
 * Generates the name for an asset based on the name of the block
 * and the field name provided.
 *
 * @since 5.5.0
 *
 * @param string $block_name Name of the block.
 * @param string $field_name Name of the metadata field.
 * @return string Generated asset name for the block's field.
 */
function generate_block_asset_handle( $block_name, $field_name ) {
	if ( 0 === strpos( $block_name, 'core/' ) ) {
		$asset_handle = str_replace( 'core/', 'wp-block-', $block_name );
		if ( 0 === strpos( $field_name, 'editor' ) ) {
			$asset_handle .= '-editor';
		}
		return $asset_handle;
	}

	$field_mappings = array(
		'editorScript' => 'editor-script',
		'script'       => 'script',
		'editorStyle'  => 'editor-style',
		'style'        => 'style',
	);
	return str_replace( '/', '-', $block_name ) .
		'-' . $field_mappings[ $field_name ];
}

block.json 内の “name” の “/” を “-” に変換。
今回の場合は、block.json の “name” は、”create-block/custom-nextpate” なので、create-block-custom-nextpage。

block.json 内 “editorScript” を editor-script へ変換。

連結して、create-block-custom-nextpage-editor-script。
これで wp_set_script_translations の $handle 指定も迷わない 😄