独自の「制限」ブロックを追加する方法

Cocoonカスタマイズ集
この記事は約10分で読めます。
記事内に広告が含まれています。

Cocoon 2.9.1.3でサポート

はじめに

Gutenbergエディター

Xwriteの「制限」ブロックは期間を設定できるようになっています。
Cocoonにも期間表示できる独自のcampaignショートコードがあります。

「カスタム HTML」ブロックを用いて[campaign]で囲むことで、期間中だけブロックを表示することが可能です(図1参照)。
この機能をGutenbergブロック化し、設定パネルから開始日時・終了日時を直接設定できるようにします。

さらに、表示をログインユーザーのみに制限するlogin_user_onlyショートコードも存在します。
今回作成するブロックでは、期間制限とログインユーザー制限の切り替えが可能です。

この記事では、この方法について説明します。

完成イメージ

実装手順

以下のコードをfunctions.phpに追加します。

// ブロックを登録
add_action('init', function () {
  ob_start();
  ?>
  (function (wp) {
    const { registerBlockType } = wp.blocks;
    const { createElement: el, Fragment } = wp.element;
    const { InnerBlocks, InspectorControls } = wp.blockEditor || wp.editor;
    const { PanelBody, TextControl, SelectControl } = wp.components;

    registerBlockType('custom/restrict-block', {
      title: '制限ブロック',
      icon: 'lock',
      category: 'layout',
      supports: {
        html: false
      },
      attributes: {
        mode: { type: 'string', default: 'period' },
        from: { type: 'string', default: '' },
        to:   { type: 'string', default: '' },
        msg:  {
          type: 'string',
          default: 'こちらのコンテンツはログインユーザーのみに表示されます。'
        }
      },

      /**
       * エディター描画コンポーネント
       *
       * @param {Object} props ブロックのProps
       * @return {import('react').ReactElement} Reactエレメント
       */
      edit: function (props) {
        const { attributes, setAttributes } = props;

        return el(Fragment, null,
          el(InspectorControls, null,
            el(PanelBody, {
              title: '制限設定',
              initialOpen: true
            },
              el(SelectControl, {
                label: '制限タイプ',
                value: attributes.mode,
                options: [
                  { label: '期間制限', value: 'period' },
                  { label: 'ログイン制限', value: 'login' }
                ],
                onChange: val => setAttributes({ mode: val })
              }),

              attributes.mode === 'period' && el(Fragment, null,
                el(TextControl, {
                  label: '開始日時 (from)',
                  value: attributes.from,
                  onChange: val => setAttributes({ from: val }),
                  placeholder: 'YYYY-MM-DD HH:MM:SS'
                }),

                el(TextControl, {
                  label: '終了日時 (to)',
                  value: attributes.to,
                  onChange: val => setAttributes({ to: val }),
                  placeholder: 'YYYY-MM-DD HH:MM:SS'
                })
              ),

              attributes.mode === 'login' && el(TextControl, {
                label: 'メッセージ',
                value: attributes.msg,
                onChange: val => setAttributes({ msg: val })
              })
            )
          ),

          el('div', {
            style: {
              border: '1px dashed #999',
              padding: '12px'
            },
            'data-mode': attributes.mode,
            'data-from': attributes.from,
            'data-to': attributes.to,
            'data-msg': attributes.msg
          },
            attributes.mode === 'period'
              ? el(
                  'p',
                  null,
                  '表示期間 開始:',
                  attributes.from || '未設定',
                  ' 終了:',
                  attributes.to || '未設定'
                )
              : el(
                  'p',
                  null,
                  'ログイン制限 メッセージ:',
                  attributes.msg
                ),

            el(InnerBlocks)
          )
        );
      },

      /**
       * 保存用コンポーネント
       *
       * @param {Object} props ブロックのProps
       * @return {import('react').ReactElement} Reactエレメント
       */
      save: function (props) {
        const { attributes } = props;

        return el(
          'div',
          {
            'data-mode': attributes.mode,
            'data-from': attributes.from,
            'data-to': attributes.to,
            'data-msg': attributes.msg
          },
          el(InnerBlocks.Content)
        );
      }
    });
  })(window.wp);
  <?php

  $handle = 'restrict-block';

  wp_register_script($handle, false, ['wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n'], false, true);
  wp_add_inline_script($handle, ob_get_clean());

  // ブロックを登録
  register_block_type('custom/restrict-block', [
    'editor_script' => $handle,
  ]);
});


// 本文を変更
add_filter('the_content', function ($content) {
  if (!preg_match_all(
    '/<div\b[^>]*?\bdata-mode="([^"]*)"[^>]*>(.*?)<\/div>/is',
    $content,
    $matches,
    PREG_SET_ORDER
  )) {
    return $content;
  }

  foreach ($matches as $match) {
    $mode  = $match[1];
    $html  = $match[0];
    $inner = $match[2];

    preg_match('/data-from="([^"]*)"/', $html, $from_match);
    preg_match('/data-to="([^"]*)"/', $html, $to_match);
    preg_match('/data-msg="([^"]*)"/', $html, $msg_match);

    $from = $from_match[1] ?? '';
    $to   = $to_match[1] ?? '';
    $msg  = $msg_match[1] ?? '';

    if ($mode === 'period') {
      $replacement =
        '[campaign from="' . esc_attr($from) . '" to="' . esc_attr($to) . '"]'
        . $inner
        . '[/campaign]';
    } else {
      $replacement =
        '[login_user_only msg="' . esc_attr($msg) . '"]'
        . $inner
        . '[/login_user_only]';
    }

    $content = str_replace($html, $replacement, $content);
  }

  return $content;
}, 5);

さいごに

今回紹介した方法を使えば、CocoonのcampaignショートコードをGutenbergブロック化し、投稿編集画面から開始日時・終了日時を直接設定できるようになります。
これにより、期間限定で表示したいコンテンツを直感的に管理でき、テーマ側の既存ショートコード処理ともスムーズに連携可能です。

同様の仕組みをlogin_user_onlyショートコードにも適用することで、ログインユーザー限定コンテンツの管理も簡単に行えます。
この方法を活用して、より柔軟で使いやすい投稿編集環境を実現してください。

タイトルとURLをコピーしました