Page No.2
( function( blocks, blockEditor, element ) {
const el = element.createElement,
RichText = blockEditor.RichText,
useBlockProps = blockEditor.useBlockProps;
blocks.registerBlockType( 'stx-parag/wrappedparag', { // ブロックの名称
title: 'wrapped p tag',
icon: 'editor-rtl',
category: 'design',
// description: 'wrapped p tag',
attributes: {
content: {
type: 'array',
source: 'children',
selector: 'p',
},
},
supports: {
//save関数で返される要素に対する設定
className: false, //(default:true)ブロック要素を作成した際に付く .wp-block-[ブロック名]で自動生成されるクラス名の設定。
},
edit: function( props ) {
var blockProps = useBlockProps(),
content = props.attributes.content;
function onChangeContent( newContent ) {
props.setAttributes( { content: newContent } );
}
return el(
RichText,
Object.assign( blockProps, {
tagName: 'p',
className: props.className,
onChange: onChangeContent,
value: content,
})
);
},
save: function( props ) {
var blockProps = useBlockProps.save();
return el(
RichText.Content,
Object.assign( blockProps, {
tagName: 'p',
className: 'contpara',
value: props.attributes.content,
} )
);
},
} );
}(
window.wp.blocks,
window.wp.blockEditor,
window.wp.element
) );
// 他サイトにアップロードされている画像をurlを指定して表示するblock
// 複数の画像を指定でき、こちらはserver side render を使用
( function( blocks, element, components, blockEditor, serverSideRender ) {
const el = element.createElement,
// { serverSideRender: ServerSideRender } = wp,// これも分割代入、ServerSideRender = wp.serverSideRender と同意
ServerSideRender = serverSideRender,
Fragment = element.Fragment,
InspectorControls = blockEditor.InspectorControls,
TextControl = components.TextControl,
useBlockProps = blockEditor.useBlockProps;
blocks.registerBlockType( 'stx-out-imgs/wrappedoutimages', {
title: 'STX wrapped out-side images SSR',
icon: 'images-alt',
category: 'layout',
description: '他のサイトにおいてアップロードされている画像をラップするための Server-Side-Render Block',
// attributesの値はblockが保存されるデータのコメントに一緒に含まれて保存される
attributes: {// render_callback 関数に渡すパラメータの設定
// InspectorControls で使う各値において default 値を設定する必要あり、以前は出なかった Warning が出るようになった
baseurl: { type: 'string', default: '' },// 画像ファイルurl の共通する部分
imgs: { type: 'string', default: '' },
alt: { type: 'string', default: '' },
},
edit: function ( {attributes, setAttributes, className, focus, id } ) {
var blockProps = useBlockProps();
return(
el(
Fragment,
null,
el(
InspectorControls,
null,
el( 'div', { id: 'stx_imgs_sdbr'},
el(
TextControl,
{
label: 'images base url',
help: '画像の共通する url 部分',
value: attributes.baseurl,
onChange: function( newValue ){ setAttributes( { baseurl: newValue } ) }
}
),
el(
TextControl,
{
label: 'images urls',
help: '画像ファイルのurl ".jpg"は省略、個別にheightを指定する場合はurlの後ろに":"を付けて指定、複数の場合は","でつなげる',
value: attributes.imgs,
onChange: function( newValue ){ setAttributes( { imgs: newValue } ); }
}
),
el(
TextControl,
{
label: 'alt',
value: attributes.alt,
onChange: function( newValue ){ setAttributes( { alt: newValue } ); }
}
),
),
),
el(
'div',
blockProps,
el( ServerSideRender, {
block: 'stx-out-imgs/wrappedoutimages',
attributes: attributes
})
)
)
);
},
} );
})(
window.wp.blocks,
window.wp.element,
window.wp.components,
window.wp.blockEditor,
window.wp.serverSideRender
);
( function( blocks, element, components, blockEditor ) {
const el = element.createElement,
Fragment = element.Fragment,
RichText = blockEditor.RichText;
InspectorControls = blockEditor.InspectorControls,
TextControl = components.TextControl,
useBlockProps = blockEditor.useBlockProps;
// こちらも同じく他サイトにアップロードされている画像をurlを指定して表示するためのblock
// 複数の画像を指定でき、こちらは フロントエンドにおいて React で実行
blocks.registerBlockType( 'stx-out-imgs2/wrappedoutimages', {
title: 'STX wrapped out-side images 2',
icon: 'images-alt',
category: 'layout',
description: '他のサイトにおいてアップロードされている画像をラップするためのblock',
// attributesの値はblockが保存されるデータのコメントに一緒に含まれて保存される
// contentの値はブロックで表示される本文の部分を指定しておかないと、その内容がタブってコメント部分にも保存されることになるので要注意
attributes: {
baseurl: { type: 'string', default: 'https://strix.main.jp/wp-content/uploads/2020/09/' },
imgs: { type: 'string', default: '' },
alt: { type: 'string', default: '' },
width: { type: 'string', default: '500' },
content: { type: 'string', source: 'html', selector: 'p' },
},
supports: {
//save関数で返される要素に対する設定
className: false, //(default:true)ブロック要素を作成した際に付く .wp-block-[ブロック名]で自動生成されるクラス名の設定。
},
edit: function( props ) {
var blockProps = useBlockProps();
let baseurl = props.attributes.baseurl,
imgs = props.attributes.imgs,
alt = props.attributes.alt,
width = props.attributes.width,
img = [],
img_len,
img_elem = [],
alts = [],
alts_len,
img_str = '';
function updateimgs () {
// 指定されている画像ファイル名は複数の場合は , 区切りなので
// どちらにしても個別にして配列に代入しておく
if ( -1 === imgs.indexOf( ',' ) ) {
img[0] = imgs;
} else {
img = imgs.split( ',' );
}
img_len = img.length;
// altも複数指定がある場合は , 区切り。
// 指定が一つの場合は全ての画像共通にする
// 複数の場合足りない分は空文字で画像個数分の配列にする
if ( -1 === alt.indexOf( ',' ) ) {
for ( let i = 0; i < img_len; i++ ) {
alts[ i ] = alt;
}
} else {
alts = alt.split( ',' );
alts_len = alts.length;
if ( alts_len < img_len ) {
for ( let i = 0; i < img_len; i++ ) {
if ( i >= alts_len ) {
alts[ i ] = '';
}
}
}
}
// 画像はファイル名とその画像のheightを指定できる
// heightのデフォルトは375でファイル名とセットにして配列に入れておく
for ( let i = 0; i < img.length; i++ ) {
if ( -1 === img[ i ].indexOf( ':' ) ) {
img_elem[0] = img[ i ];
img_elem[1] = '375';
} else {
img_elem = img[ i ].split( ':' );
}
// img_str += '<span class="pimgb"><img src="' + baseurl + img_elem[0] + '-' + width + 'x' + img_elem[1] + '.jpg" width="' + width + '" height="' + img_elem[1] + '" alt="' + alts[ i ] + '" /></span>';
img_str += '<span class="pimgb"><img src="' + baseurl + img_elem[0] + '.jpg" width="' + width + '" height="' + img_elem[1] + '" alt="' + alts[ i ] + '" /></span>';
}
props.setAttributes( { content: img_str } );
}
return(
el(
Fragment,
null,
el(
InspectorControls,
null,
el( 'div', { id: 'stx_imgs_sdbr'},
el(
TextControl,
{
label: 'images base url',
help: '画像の共通する url 部分',
value: baseurl,
onChange: function( newValue ){ props.setAttributes( { baseurl: newValue } );updateimgs(); }
}
),
el(
TextControl,
{
label: 'images urls',
help: '画像ファイルのurl ".jpg"は省略、個別にheightを指定する場合はurlの後ろに":"を付けて指定、複数の場合は","でつなげる',
value: imgs,
onChange: function( newValue ){ props.setAttributes( { imgs: newValue } );updateimgs(); }
}
),
el(
TextControl,
{
label: 'alt',
value: alt,
onChange: function( newValue ){ props.setAttributes( { alt: newValue } );updateimgs(); }
}
),
el(
TextControl,
{
label: 'image width',
value: width,
onChange: function( newValue ){ props.setAttributes( { width: newValue } );updateimgs(); }
}
),
),
),
el(
RichText,
Object.assign( blockProps, {
tagName: 'p',
value: props.attributes.content,
})
)
)
);
},
save: function( props ) {
var blockProps = useBlockProps.save();
return (
el(
RichText.Content,
Object.assign( blockProps, {
tagName: 'p',
value: props.attributes.content,
} )
)
);
},
} );
})(
window.wp.blocks,
window.wp.element,
window.wp.components,
window.wp.blockEditor,
);
この部分に関する事ということで、上の server side render における部分から呼び出される php 側の render_callback 関数が下のよう。
<?php
function stx_render_callback( $attributes ){
$params = $attributes;
$ret_cont = '<p>';
$imgs = [];
if ( $params['imgs'] ) {
if ( false === strpos( $params['imgs'], ',' ) ) {
$imgs[] = $params['imgs'];
} else {
$imgs = explode( ',', $params['imgs'] );
}
if ( false === strpos( $params['alt'], ',' ) ) {
$alts = [ $params['alt'] ];
$alts = array_pad( $alts, count( $imgs ), $params['alt'] );
} else {
$alts = explode( ',', $params['alt'] );
$alts = array_pad( $alts, count( $imgs ), '' );
}
foreach ( $imgs as $key => $val ) {
if ( false !== strpos( $val, ':' ) ) {
$img = explode( ':', $val );
} else {
$img = [ $val, '375' ];
}
$ret_cont .= '<span class="pimgb"><img loading="lazy" src="' . $params['baseurl'] . $img[0] . '.jpg" alt="' . $alts[ $key ] . '" width="500" height="' . $img[1] . '" /></span>';
}
}
$ret_cont .= '</p>';
return $ret_cont;
}
?>
で、その server side render に関してのことなのだけれど、まず、これが必要となったのは、自プラグインの Holiday Class Post Calendar においてのこと。これにカレンダーを表示させるブロックを内蔵させようと思ったのが始まり。
しかし、これがなかなかうまくいかない。保存する時に、『更新に失敗しました。 返答が正しい JSON レスポンスではありません』のエラーが出てしまう。この場合の原因は、カレンダー関数によって生成される html 文のなかに htmlコメントの <!– –> が含まれていた事。まぁ、これは落ち着いて考えてみればごく当然のこと。Gutenberg はブロックの保存において htmlコメントで attributes のデータを持っているので、余分なコメントはタグの整合性を壊してしまうのだと思う。とはわかっていながら、何度やってもエラーが出てしまい迷宮に迷い込んでしまっていたのですが、実は、気が付かないような細かい部分にひょっこりコメントがあったのが原因でした。ちょっとした思い込みで無駄な時間と手間をかけてしまった良い例です。どうにもわからなくなった時は、原点に立ち返る、が基本です。
ということで、お次はそのプラグイン Holiday Class Post Calendar の custom block。
plugin Holiday Class Post Calendar custom block
前述の通り、その plugin Holiday Class Post Calendar に付属させたのは server side render block です。これは表示される内容が固定されたものではないので、server side render を使うべき block だと。
まずはその php 側の処理。これにおいては、エディタ画面において適用させるスタイルシートの読み込みもあります。この部分は、プラグインの設定クラスの中に記述してあることなので変数には $this なんてのがあります。より詳しくはそちらのページを → プラグイン Holiday Class Post Calendar
で、これも前述した InspectorControls において使う attributes の各値において、初期値を設定していなかったがために出ることになった Warning を消すために、修正してあります。React とそして php の方も。php の方の block を resister する関数において、以前は各 attributes の値に対してここで default 値を設定していたのだけれど、いろいろ試してみたところ、 ここでの default 値の設定というのは必須ではないよう。(訂正:これは間違っていた思う。javascript 側の registerBlockType の attributes で設定したデフォルト値はブロックには保存されていないようで、php 側には渡されないと思われる) ただし、attributes のメンバーとして、送信されたメンバーだけを対象に処理をするのならという話である。それが default 値のままであったりした場合、そのメンバーはその時の attributes のメンバーとしては無かったりもするので、無いメンバーをあるものとして処理すれば当然のことエラーとなってしまう。そういう事を前提にすると、送信されない可能性のあるメンバーにおいては、ここでの default 値の設定はしておいたほうが良いと思う。各メンバーの登録は必須。メンバーの登録が欠けているものがあると、block はエラーとなってしまう。で、プラグインオプションの設定値の適用は、callback 関数の方でやる方法へと変更した次第。
<?php
// blockを登録するためのjsファイルとそのblock用のスタイルシートの登録、そしてphpにおいてのblockのregister(登録)。プラグインの class の中の関数なので public なるものがついています。
public function gb_register_block() {
wp_register_script(
'hldycls-pcldr-01',
plugins_url( 'hldycls_gb.js', __FILE__ ),
array( 'wp-blocks', 'wp-block-editor', 'wp-element', 'wp-components', 'wp-server-side-render' ),
filemtime( plugin_dir_path( __FILE__ ) . 'hldycls_gb.js' )
);
if ( ! $this->ret_option['colorstyle'] ) {
wp_register_style(
'hldycls-editor-style',
plugins_url( 'hldycls_pcldr_gb.css', __FILE__ ),
array( 'wp-edit-blocks' ),
filemtime( plugin_dir_path( __FILE__ ) . 'hldycls_pcldr_gb.css' )
);
}
register_block_type( 'hldycls-pcldr/post-calendar',
array( 'editor_script' => 'hldycls-pcldr-01',
'editor_style' => 'hldycls-editor-style',
'render_callback' => array( $this, 'hldyclspcldr_render_callback' ),
'attributes' => array(
'prtwidth' => array( 'type' => 'string', 'default' => '100%' ),
'lang' => array( 'type' => 'string' ),
'wf' => array( 'type' => 'string' ),
'capt' => array( 'type' => 'string' ),
'footer' => array( 'type' => 'string' ),
'closewd' => array( 'type' => 'string' ),
'closel' => array( 'type' => 'string' ),
'anniver' => array( 'type' => 'string' ),
'monthly' => array( 'type' => 'string' ),
'postype' => array( 'type' => 'string' ),
'acvheader' => array( 'type' => 'string' ),
'acvoptorlist' => array( 'type' => 'string' ),
'daypostlink' => array( 'type' => 'string' ),
'myholidays' => array( 'type' => 'string' ),
'adddeldays' => array( 'type' => 'string' ),
'en_cache' => array( 'type' => 'string' ),
)
)
);
}
// server side render によるblock なので、server 側からカレンダーのデータを取得するcallback 関数
public function hldyclspcldr_render_callback( $attributes ){
$str = '';
$params = $this->ret_option;
// block editor から送信されたデータをオプション設定値に上書き
foreach ( $attributes as $key => $val ) {
if ( '' !== $val ) {
$params[ $key ] = $val;
}
}
// 投稿画面においては、キャッシュデータを使用せず、その都度、生成させるためのフラグを設定し、
// 他のオプション値と一緒にして、カレンダーの関数へと渡す
if ( is_admin() ) {
$params['editor'] = '1';
}
$prtwidth = '';
if ( isset( $params['prtwidth'] ) ) {
$prtwidth = ' style="width:' . esc_attr( $params['prtwidth'] ) . ';"';
}
$calendar = $this->holiday_class_post_calendar( $params );
// 排出されるhtmlの冒頭にはコメントがあるのでそれを取り除く処理
$calendar = mb_substr( $calendar, mb_strpos( $calendar, '-->' ) + 3 );
$ret_cont = '<div id="hldyclspcldr"' . $prtwidth . '>' . $calendar . $str . '</div>';
return $ret_cont;
}
?>
そしてこのブロックの javascript 。
これも指定できるパラメータは、InspectorControls を使ってサイドバーにて設定できるようになってます。それ以外はほとんど sever side render の雛形通り。
( function( blocks, element, components, blockEditor, serverSideRender ) {
const el = element.createElement,
ServerSideRender = serverSideRender,
Fragment = element.Fragment,
InspectorControls = blockEditor.InspectorControls,
TextControl = components.TextControl,
RadioControl = components.RadioControl,
useBlockProps = blockEditor.useBlockProps;
blocks.registerBlockType( 'hldycls-pcldr/post-calendar', {
title: 'Holiday Class Post Calendar Block',
icon: 'smiley',
category: 'widgets',
description: '※ブロックで表示されているスタイルの設定は、プラグインのデフォルトスタイルによるものです。設定を変更してもリアルタイムで表示が変更されない場合は、ブラウザ等のキャッシュによるものと思われます。',
// 初期値の設定していないものは、使用しているのが RadioControl なので、各コントロールにおいて設定
attributes: {
prtwidth: { type: 'string', default: '' },
lang: { type: 'string' },
wf: { type: 'string' },
capt: { type: 'string', default: '' },
footer: { type: 'string', default: '' },
closewd: { type: 'string', default: '' },
closel: { type: 'string', default: '' },
anniver: { type: 'string', default: '' },
monthly: { type: 'string', default: '' },
postype: { type: 'string', default: '' },
acvheader: { type: 'string', default: '' },
acvoptorlist: { type: 'string' },
daypostlink: { type: 'string' },
myholidays: { type: 'string', default: '' },
adddeldays: { type: 'string', default: '' },
en_cache: { type: 'string', default: '' },
},
edit: function ( { attributes, setAttributes, className, focus, id } ) {
var blockProps = useBlockProps();
return(
el(
Fragment,
null,
el(
InspectorControls,
null,
el( 'div', { id: 'hldyclspcldr_sdbr'},
el(
TextControl,
{
label: 'parent div width',
help: '親ボックスの幅、デフォルトは"100%"、%, px, vw, etc. で指定',
value: attributes.prtwidth,
onChange: function( newValue ){ setAttributes( { prtwidth: newValue } ) }
}
),
el(
RadioControl,
{
label: '日本語か英語表示かの指定',
selected: attributes.lang,
options: [ { label: '日本語', value: 'j' },{ label: '英語', value: 'e' } ],
onChange: function( newValue ){ setAttributes( { lang: newValue } ) }
}
),
el(
RadioControl,
{
label: '週の始めを日曜か月曜かの指定',
selected: attributes.wf,
options: [ { label: '日曜', value: 's' },{ label: '月曜', value: 'm' } ],
onChange: function( newValue ){ setAttributes( { wf: newValue } ) }
}
),
el(
TextControl,
{
label: '表題 caption',
help: '表題のcaptionに表示する文字列',
value: attributes.capt,
onChange: function( newValue ){ setAttributes( { capt: newValue } ); }
}
),
el(
TextControl,
{
label: 'footer 文字列',
help: '下部に表示する文字列',
value: attributes.footer,
onChange: function( newValue ){ setAttributes( { footer: newValue } ); }
}
),
el(
TextControl,
{
label: '休日曜日指定',
help: '独自休日を曜日で指定する場合。毎週水曜日だとか。1が日曜、7が土曜で1~7までの数字で指定。複数ももちろん可でその場合は","カンマ区切りで指定',
value: attributes.closewd,
onChange: function( newValue ){ setAttributes( { closewd: newValue } ); }
}
),
el(
TextControl,
{
label: '休日日付指定',
help: '独自休日を日にちで指定。年月日8ケタ、月日4ケタないし3ケタ、日にち2ケタ以下で指定。日にちだけなら毎月に設定され、月日なら毎年。これも","カンマ区切り',
value: attributes.closel,
onChange: function( newValue ){ setAttributes( { closel: newValue } ); }
}
),
el(
TextControl,
{
label: '休日指定(アニバーサリー)',
help: '上記2つとは別に特別な日を設定するときはこのanniverを使用し、これは毎年の事と考えて4ケタもしくは3ケタのみ使用可、複数可、上に同じ',
value: attributes.anniver,
onChange: function( newValue ){ setAttributes( { anniver: newValue } ); }
}
),
el(
RadioControl,
{
label: '月別アーカイブのリストの表示',
selected: attributes.monthly,
options: [ { label: '表示', value: '1' },{ label: '表示しない', value: '0' } ],
onChange: function( newValue ){ setAttributes( { monthly: newValue } );console.log( newValue ); }
}
),
el(
TextControl,
{
label: '月別アーカイブリストのヘッダー文字指定',
value: attributes.acvheader,
onChange: function( newValue ){ setAttributes( { acvheader: newValue } ); }
}
),
el(
RadioControl,
{
label: '月別アーカイブリスト表示がプルダウンかリストか',
selected: attributes.acvoptorlist,
options: [ { label: 'pull-down', value: '1' },{ label: 'list', value: '0' } ],
onChange: function( newValue ){ setAttributes( { acvoptorlist: newValue } );console.log( newValue ); }
}
),
el(
RadioControl,
{
label: '投稿がある日にオンマウスで表示させるのはツールチップかその投稿へのリンクか',
selected: attributes.daypostlink,
options: [ { label: '投稿へのリンク', value: '1' },{ label: 'ツールチップ', value: '0' } ],
onChange: function( newValue ){ setAttributes( { daypostlink: newValue } );console.log( newValue ); }
}
),
el(
TextControl,
{
label: 'post type',
help: 'カレンダーに表示させる投稿の投稿タイプ、デフォルトは"post"。',
value: attributes.postype,
onChange: function( newValue ){ setAttributes( { postype: newValue } ); }
}
),
el(
TextControl,
{
label: 'my holiday list',
help: 'デフォルトで設定されている日本の祝祭日ではなく、独自の祝祭日だけを表示する場合にその日付を","カンマで区切ったリストにして入力。デフォルトは空文字',
value: attributes.myholidays,
onChange: function( newValue ){ setAttributes( { myholidays: newValue } ); }
}
),
el(
TextControl,
{
label: 'デフォルトで設定されている祝祭日リストに加える、または削除する日付。',
help: '年月日8ケタ、月日4ケタないし3ケタ、日にち2ケタ以下で指定。無視させる日付にだけ"-"を付けて","(半角カンマ)で区切って指定。例:3,929-,0927,20140911-。',
value: attributes.adddeldays,
onChange: function( newValue ){ setAttributes( { adddeldays: newValue } ); }
}
),
el(
TextControl,
{
label: 'キャッシュ設定',
help: '"0"でdisable。ここで指定した数字がキャッシュファイル名の末尾に付加される。表示設定が違う場合など、異なる数値を指定することで複数のキャッシュファイルを設定できる',
value: attributes.en_cache,
onChange: function( newValue ){ setAttributes( { en_cache: newValue } ); }
}
),
),
),
el(
'div',
blockProps,
el( ServerSideRender, {
block: 'hldycls-pcldr/post-calendar',
attributes: attributes
} )
)
)
);
},
} );
})(
window.wp.blocks,
window.wp.element,
window.wp.components,
window.wp.blockEditor,
window.wp.serverSideRender
);
と、いうことで server side render の block においては、その React の部分はほとんど雛形通りで同じ。
で、実は一番はじめに作った block というのが次のもの。これは、prism.js でシンタックスハイライトしたコードを掲載するためのブロック。
Post : 2020/09/03 17:39