通知メッセージを表示条件で切り替え表示する方法

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

はじめに

以下の記事では、「投稿ごと」「カテゴリーごと」に通知メッセージを切り替える方法を紹介しました。
これらはテンプレートやカスタムフィールドを活用する方法でしたが、今回はプラグインの形式で通知切り替え機能を実装します。

過去に公開されているCocoon専用プラグイン「CocoonNoticeAreaScheduler」では、Cocoon設定よりもプラグイン側の通知が優先される問題が報告されています。

そこで、Cocoonの通常設定を維持しつつ、条件を満たす場合のみプラグイン側が優先して通知を表示する仕組みを構築します。

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

完成イメージ

通知一覧
  • 通知の「開始日」「終了日」が設定されていて、かつ現在日がその範囲内の場合、一覧の行に色が付きます。
  • 「複製」機能により既存の通知投稿を簡単にコピーできますが、保存するまで新しい通知は登録されません。
通知を追加
設定項目
Cocoon設定説明備考
通知表示–Cocoon設定に準拠します
通知メッセージ通知エリアに表示する内容を入力HTMLタグは利用できません
通知URLメッセージにリンクを設定する場合に入力
リンクの開き方–Cocoon設定に準拠します
通知タイプ–Cocoon設定に準拠します
色–Cocoon設定に準拠します
表示条件通知メッセージを表示する条件を選択してください。
  • 全ページ
  • 投稿
  • 固定ページ
  • カテゴリーページ
IDページ単位で絞り込む場合にIDを入力カンマ区切りで複数指定可能(全ページでは無効)
表示期間通知を表示する期間を指定(表2参照)
表示期間
開始日終了日表示
未入力未入力常に表示
入力未入力開始日≦現在日で表示
未入力入力現在日≦終了日で表示
入力入力開始日≦現在日≦終了日で表示
  • ページ種別ごと(投稿・固定・カテゴリ)に、表示条件を満たす投稿のうち最も新しい投稿日の通知が表示されます。
  • IDを指定していない場合は、対象ページ全体が通知対象となります。
  • IDを指定している場合は、その IDのページのみ通知対象となります。
  • 条件を満たす通知が存在しない場合は、Cocoon設定が使用されます。

実装手順

プラグインの形式で実装します。

以下の手順で実装します。

  • my-notice-manager
    • notice-manager.php
    • includes
      • admin-list.php
      • cpt.php
      • display.php
      • metabox.php
  • ラベル
    PHPを追加

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

    <?php
    /*
    Plugin Name: 通知エリア管理
    Description: 通知エリアの通知メッセージを条件に基づいて表示切り替えします。
    Version: 1.0.0
    Author: CHU-YA
    */
    
    if (!defined('ABSPATH')) {
      exit;
    }
    
    // 定数定義
    define('MY_NOTICE_MANAGER_PLUGIN_DIR', plugin_dir_path(__FILE__));
    
    
    // ファイル読み込み
    require_once MY_NOTICE_MANAGER_PLUGIN_DIR . 'includes/cpt.php';
    require_once MY_NOTICE_MANAGER_PLUGIN_DIR . 'includes/metabox.php';
    require_once MY_NOTICE_MANAGER_PLUGIN_DIR . 'includes/display.php';
    require_once MY_NOTICE_MANAGER_PLUGIN_DIR . 'includes/admin-list.php';
    
    
    // プラグイン有効化時の処理
    register_activation_hook(__FILE__, function () {
      register_notice_manager_post_type();
      flush_rewrite_rules();
    });
  • ラベル
    PHPを追加(通知一覧)

    以下のコードをadmin-list.phpに入力します。

    <?php
    if (!defined('ABSPATH')) {
      exit;
    }
    
    class My_Notice_Manager_Admin_List {
      public function __construct() {
    
        // 管理画面一覧にアクションリンクを追加
        add_filter('post_row_actions', function ($actions, $post) {
          return $this->add_duplicate_link($actions, $post);
        }, 10, 2);
    
        // 複製時のタイトル設定
        add_filter('default_title', function ($title, $post) {
          return $this->set_default_title_on_duplicate($title, $post);
        }, 10, 2);
    
        // 管理画面一覧にカラムを追加
        add_filter('manage_my_notice_manager_posts_columns', function ($columns) {
          return $this->add_custom_columns($columns);
        });
    
        // 管理画面一覧のカラムにデータを表示
        add_action('manage_my_notice_manager_posts_custom_column', function ($column_name, $post_id) {
          $this->render_custom_columns($column_name, $post_id);
        }, 10, 2);
    
        // 開始日・終了日のソート対応
        add_filter('manage_edit-my_notice_manager_sortable_columns', function ($columns) {
          return $this->make_columns_sortable($columns);
        });
    
        // 管理画面一覧のカラムを並び替え可能に設定
        add_action('pre_get_posts', function ($query) {
          $this->handle_custom_column_sorting($query);
        });
    
        // 管理画面一覧のスタイル追加
        add_action('admin_enqueue_scripts', function ($hook_suffix) {
          $this->add_custom_row_style($hook_suffix);
        });
      }
    
    
      /**
       * 行アクションに「複製」リンクを追加
       *
       * @param array $actions アクション配列
       * @param WP_Post $post 投稿オブジェクト
       * @return array 変更後のアクション配列
       */
      public function add_duplicate_link($actions, $post) {
        // 通知投稿タイプ以外を除外
        if (!$post || $post->post_type !== 'my_notice_manager') {
          return $actions;
        }
    
        // 編集権限がない場合を除外
        if (!current_user_can('edit_post', $post->ID)) {
          return $actions;
        }
    
        $duplicate_url = wp_nonce_url(
          admin_url('post-new.php?post_type=my_notice_manager&copy_id=' . $post->ID),
          'notice_manager_action',
          'notice_manager_nonce'
        );
    
        $actions['duplicate'] = sprintf(
          '<a href="%s" aria-label="この通知を複製する">複製</a>',
          esc_url($duplicate_url)
        );
    
        return $actions;
      }
    
    
      /**
       * 複製時の初期タイトルを設定
       *
       * @param string $title タイトル文字列
       * @param WP_Post|null $post 投稿オブジェクト
       * @return string 変更後のタイトル文字列
       */
      public function set_default_title_on_duplicate($title, $post) {
        if (
          !isset($_GET['post_type'], $_GET['copy_id']) ||
          $_GET['post_type'] !== 'my_notice_manager'
        ) {
          return $title;
        }
    
        $copy_id = absint($_GET['copy_id']);
    
        if ($copy_id <= 0) {
          return $title;
        }
    
        // nonceチェック
        $nonce = $_GET['notice_manager_nonce'] ?? '';
    
        if (!wp_verify_nonce($nonce, 'notice_manager_action')) {
          return $title;
        }
    
        // 複製元の投稿を取得
        $source_post = get_post($copy_id);
    
        if (!$source_post || $source_post->post_type !== 'my_notice_manager') {
          return $title;
        }
    
        // 権限チェック
        if (!current_user_can('edit_post', $copy_id)) {
          return $title;
        }
    
        return $source_post->post_title . '(コピー)';
      }
    
    
      /**
       * 通知一覧のカラムを変更
       *
       * @param array $columns 元のカラム配列
       * @return array 再構築されたカラム配列
       */
      public function add_custom_columns($columns) {
        // 不要なカラムを削除
        unset($columns['thumbnail'], $columns['post-id']);
    
        $new_columns = [];
    
        foreach ($columns as $key => $value) {
          $new_columns[$key] = $value;
    
          // タイトルの後に通知情報を追加
          if ($key === 'title') {
            $new_columns['message']      = '通知メッセージ';
            $new_columns['display_type'] = '表示条件';
            $new_columns['display_ids']  = '対象ID';
            $new_columns['start_date']   = '開始日';
            $new_columns['end_date']     = '終了日';
          }
        }
    
        return $new_columns;
      }
    
    
      /**
       * 通知一覧のカラム内容を出力
       *
       * @param string $column_name カラム名
       * @param int $post_id 投稿ID
       * @return void
       */
      public function render_custom_columns($column_name, $post_id) {
        switch ($column_name) {
    
          // 通知メッセージの出力
          case 'message':
            $content = get_post_field('post_content', $post_id);
            $excerpt = wp_trim_words(wp_strip_all_tags($content), 20, '...');
            echo esc_html($excerpt);
            break;
    
          // 表示条件の出力
          case 'display_type':
            $type = get_post_meta($post_id, 'my_notice_manager_display_type', true) ?: 'global';
            $options = My_Notice_Manager_Metabox::get_display_types();
            echo esc_html($options[$type] ?? $type);
            break;
    
          // 対象IDの出力
          case 'display_ids':
            $ids = get_post_meta($post_id, 'my_notice_manager_display_ids', true);
            echo esc_html($ids);
            break;
    
          // 開始日の出力
          case 'start_date':
            echo esc_html(get_post_meta($post_id, 'my_notice_manager_start_date', true));
            break;
    
          // 終了日の出力
          case 'end_date':
            echo esc_html(get_post_meta($post_id, 'my_notice_manager_end_date', true));
            break;
        }
      }
    
    
      /**
       * 開始日・終了日カラムをソート可能に設定
       *
       * @param array $columns カラム配列
       * @return array ソート設定を追加したカラム配列
       */
      public function make_columns_sortable($columns) {
        $columns['start_date'] = 'my_notice_manager_start_date';
        $columns['end_date']   = 'my_notice_manager_end_date';
    
        return $columns;
      }
    
    
      /**
       * カスタムフィールドによる一覧ソート処理
       *
       * @param WP_Query $query クエリオブジェクト
       * @return void
       */
      public function handle_custom_column_sorting($query) {
        if (!is_admin()) {
          return;
        }
    
        // メインクエリ以外を除外
        if (!$query->is_main_query()) {
          return;
        }
    
        // 通知投稿タイプ以外を除外
        if ($query->get('post_type') !== 'my_notice_manager') {
          return;
        }
    
        $orderby = $query->get('orderby');
    
        // 対象カラム以外を除外
        if (!$orderby || !in_array($orderby, ['my_notice_manager_start_date', 'my_notice_manager_end_date'], true)) {
          return;
        }
    
        // メタ値による日付ソート
        $query->set('meta_key', $orderby);
        $query->set('orderby', 'meta_value');
      }
    
    
      /**
       * 管理画面一覧の行スタイルを追加
       *
       * @param string $hook_suffix 管理画面のフック名
       * @return void
       */
      public function add_custom_row_style($hook_suffix) {
        if ($hook_suffix !== 'edit.php') {
          return;
        }
    
        $screen = get_current_screen();
    
        if (
          !$screen ||
          $screen->post_type !== 'my_notice_manager'
        ) {
          return;
        }
    
        wp_register_style('notice-manager-admin', false);
        wp_enqueue_style('notice-manager-admin');
    
        ob_start();
        ?>
        .wp-list-table .column-message {
          width: 30%;
        }
    
        .wp-list-table .column-display_type {
          width: 120px;
        }
    
        .wp-list-table .column-display_ids {
          width: 150px;
        }
    
        .wp-list-table .column-start_date,
        .wp-list-table .column-end_date {
          width: 110px;
        }
        <?php
    
        wp_add_inline_style('notice-manager-admin', ob_get_clean());
      }
    }
    
    
    new My_Notice_Manager_Admin_List();
  • ラベル
    PHPを追加

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

    <?php
    if (!defined('ABSPATH')) {
      exit;
    }
    
    // 投稿タイプを追加
    add_action('init', function () {
      register_post_type('my_notice_manager', [
        'label'         => '通知',
        'menu_icon'     => 'dashicons-megaphone',
        'menu_position' => 25,
        'public'        => false,
        'show_in_menu'  => true,
        'show_in_rest'  => false,
        'show_ui'       => true,
        'supports'      => ['title'],
      ]);
    });
  • ラベル
    PHP(通知メッセージ表示)

    以下のコードをdisplay.phpに入力します。

    <?php
    if (!defined('ABSPATH')) {
      exit;
    }
    
    class My_Notice_Manager_Display {
    
      public function __construct() {
        add_filter('get_notice_area_message', function ($msg) {
          return $this->filter_notice_manager_message($msg);
        });
    
        add_filter('get_notice_area_url', function ($url) {
          return $this->filter_notice_manager_url($url);
        });
      }
    
    
      /**
       * 通知メッセージをフィルタリングして返却
       *
       * @param string $msg デフォルトのメッセージ
       * @return string フィルタリングされたメッセージ
       */
      public function filter_notice_manager_message($msg) {
        if (is_admin()) {
          return $msg;
        }
    
        $notice = $this->get_active_notice_manager();
    
        if (!$notice) {
          return $msg;
        }
    
        return $notice->post_content;
      }
    
    
      /**
       * 通知URLをフィルタリングして返却
       *
       * @param string $url デフォルトのURL
       * @return string フィルタリングされたURL
       */
      public function filter_notice_manager_url($url) {
        if (is_admin()) {
          return $url;
        }
    
        $notice = $this->get_active_notice_manager();
    
        if (!$notice) {
          return $url;
        }
    
        return get_post_meta($notice->ID, 'my_notice_manager_url', true);
      }
    
    
      /**
       * 現在有効な通知投稿を取得
       *
       * @return WP_Post|null 有効な通知投稿。なければnull
       */
      public function get_active_notice_manager() {
        $today = current_time('Y-m-d');
    
        /*
         * 開始日・終了日の条件
         *
         * 開始日:
         * - 未設定
         * - 今日以前
         *
         * 終了日:
         * - 未設定
         * - 今日以降
         */
        $meta_query = [
          'relation' => 'AND',
    
          [
            'relation' => 'OR',
    
            [
              'key'     => 'my_notice_manager_start_date',
              'value'   => '',
              'compare' => '=',
            ],
    
            [
              'key'     => 'my_notice_manager_start_date',
              'value'   => $today,
              'compare' => '<=',
              'type'    => 'DATE',
            ],
          ],
    
          [
            'relation' => 'OR',
    
            [
              'key'     => 'my_notice_manager_end_date',
              'value'   => '',
              'compare' => '=',
            ],
    
            [
              'key'     => 'my_notice_manager_end_date',
              'value'   => $today,
              'compare' => '>=',
              'type'    => 'DATE',
            ],
          ],
        ];
    
        $args = [
          'post_type'      => 'my_notice_manager',
          'post_status'    => 'publish',
          'posts_per_page' => -1,
          'orderby'        => 'date',
          'order'          => 'DESC',
          'meta_query'     => $meta_query,
        ];
    
        // 日付条件に合う通知を取得
        $notices = get_posts($args);
    
        if (!$notices) {
          return null;
        }
    
        /*
         * 通知は投稿日降順で取得済み
         *
         * 最新の通知から表示条件を判定し、
         * 最初に条件へ一致した通知を採用
         */
        foreach ($notices as $notice) {
          if ($this->check_notice_manager_display_condition($notice)) {
            return $notice;
          }
        }
    
        // 表示条件に一致する通知がない場合はnull
        return null;
      }
    
    
      /**
       * 通知の表示条件を判定
       *
       * @param WP_Post $notice 通知投稿
       * @return bool 表示対象の場合true
       */
      private function check_notice_manager_display_condition($notice) {
        global $post;
    
        // 表示条件を取得。未設定時は全ページ
        $display_type = get_post_meta($notice->ID, 'my_notice_manager_display_type', true) ?: 'global';
    
        // 表示対象IDを取得
        $display_ids_str = get_post_meta($notice->ID, 'my_notice_manager_display_ids', true);
    
        // カンマ区切りのIDを整数配列へ変換
        $ids = array_filter(
          array_map('intval', explode(',', $display_ids_str))
        );
    
        // 現在表示している投稿・固定ページのIDを取得
        $current_id = isset($post->ID) ? (int) $post->ID : 0;
    
        switch ($display_type) {
    
          // 全ページ
          case 'global':
            return true;
    
          // 投稿
          case 'post':
            return is_singular('post') && (empty($ids) || in_array($current_id, $ids, true));
    
          // 固定ページ
          case 'page':
            return is_page() && (empty($ids) || in_array($current_id, $ids, true));
    
          // カテゴリーページ
          case 'category':
            if (!is_category()) {
              return false;
            }
    
            $queried_obj = get_queried_object();
    
            if (!$queried_obj || !isset($queried_obj->term_id)) {
              return false;
            }
    
            return empty($ids) || in_array((int) $queried_obj->term_id, $ids, true);
    
          // 未定義の表示条件
          default:
            return false;
        }
      }
    }
    
    
    new My_Notice_Manager_Display();
  • ラベル
    PHPを追加(通知を追加)

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

    <?php
    if (!defined('ABSPATH')) {
      exit;
    }
    
    class My_Notice_Manager_Metabox {
    
      public function __construct() {
        add_action('add_meta_boxes', function () {
          $this->add_notice_manager_metabox();
        });
    
        add_action('save_post_my_notice_manager', function ($post_id) {
          $this->save_notice_manager($post_id);
        });
      }
    
    
      /**
       * 通知の表示条件を取得
       *
       * @return array 表示条件の配列
       */
      public static function get_display_types() {
        return [
          'global'   => '全ページ',
          'post'     => '投稿',
          'page'     => '固定ページ',
          'category' => 'カテゴリーページ',
        ];
      }
    
    
      /**
       * カスタムメタフィールド定義
       */
      private $meta_fields = [
        'my_notice_manager_url'          => ['default' => '',       'sanitize' => 'sanitize_url'],
        'my_notice_manager_start_date'   => ['default' => '',       'sanitize' => 'sanitize_text_field'],
        'my_notice_manager_end_date'     => ['default' => '',       'sanitize' => 'sanitize_text_field'],
        'my_notice_manager_display_type' => ['default' => 'global', 'sanitize' => 'sanitize_text_field'],
        'my_notice_manager_display_ids'  => ['default' => '',       'sanitize' => 'sanitize_text_field'],
      ];
    
    
      /**
       * メタボックスを追加
       *
       * @return void
       */
      public function add_notice_manager_metabox() {
        add_meta_box(
          'notice_manager',
          '通知情報',
          function ($post) {
            $this->render_notice_manager($post);
          },
          'my_notice_manager',
          'normal',
          'default'
        );
      }
    
    
      /**
       * メタボックスのHTMLを出力
       *
       * @param WP_Post $post 現在編集中の投稿オブジェクト
       * @return void
       */
      public function render_notice_manager($post) {
        $source_post_id = $post->ID;
    
        // 複製元の投稿IDを取得
        if (!empty($_GET['copy_id'])) {
          $source_post_id = absint($_GET['copy_id']);
        }
    
        // メタ情報を取得
        $meta_values = [];
    
        foreach ($this->meta_fields as $key => $config) {
          $value = get_post_meta($source_post_id, $key, true);
    
          $meta_values[$key] = ($value !== '')
            ? $value
            : $config['default'];
        }
    
        // 本文を取得
        $body = '';
    
        if ($source_post_id > 0) {
          $source_post = get_post($source_post_id);
    
          if ($source_post) {
            $body = $source_post->post_content;
          }
        }
    
        wp_nonce_field('notice_manager_action', 'notice_manager_nonce');
        ?>
        <p>
          <label for="my_notice_manager_message">通知メッセージ</label><br>
          <input type="text" id="my_notice_manager_message" name="content" value="<?= esc_attr($body); ?>" style="width: 100%;">
        </p>
        <p>
          <label for="my_notice_manager_url">通知URL</label><br>
          <input type="url" id="my_notice_manager_url" name="my_notice_manager_url" value="<?= esc_attr($meta_values['my_notice_manager_url']); ?>" style="width: 100%;">
        </p>
        <p>
          <label for="my_notice_manager_display_type">表示条件</label><br>
          <select id="my_notice_manager_display_type" name="my_notice_manager_display_type">
            <?php foreach (self::get_display_types() as $key => $label) : ?>
              <option value="<?= esc_attr($key); ?>" <?php selected($meta_values['my_notice_manager_display_type'], $key); ?>><?= esc_html($label); ?></option>
            <?php endforeach; ?>
          </select>
        </p>
        <p>
          <label for="my_notice_manager_display_ids">ID(カンマ区切り)</label><br>
          <input type="text" id="my_notice_manager_display_ids" name="my_notice_manager_display_ids" value="<?= esc_attr($meta_values['my_notice_manager_display_ids']); ?>" style="width: 100%;" placeholder="例: 123,456,789">
        </p>
        <p>
          <label for="my_notice_manager_start_date">開始日</label><br>
          <input type="date" id="my_notice_manager_start_date" name="my_notice_manager_start_date" value="<?= esc_attr($meta_values['my_notice_manager_start_date']); ?>">
        </p>
        <p>
          <label for="my_notice_manager_end_date">終了日</label><br>
          <input type="date" id="my_notice_manager_end_date" name="my_notice_manager_end_date" value="<?= esc_attr($meta_values['my_notice_manager_end_date']); ?>">
        </p>
        <?php
      }
    
    
      /**
       * 投稿を保存
       *
       * @param int $post_id 投稿ID
       * @return void
       */
      public function save_notice_manager($post_id) {
        $nonce = $_POST['notice_manager_nonce'] ?? '';
    
        // nonceチェック
        if (!wp_verify_nonce($nonce, 'notice_manager_action')) {
          return;
        }
    
        // 自動保存チェック
        if (
          (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) ||
          wp_is_post_autosave($post_id)
        ) {
          return;
        }
    
        // リビジョンを除外
        if (wp_is_post_revision($post_id)) {
          return;
        }
    
        // 権限チェック
        if (!current_user_can('edit_post', $post_id)) {
          return;
        }
    
        // メタ情報を保存
        foreach ($this->meta_fields as $key => $config) {
          if (!isset($_POST[$key])) {
            continue;
          }
    
          $value = call_user_func(
            $config['sanitize'],
            wp_unslash($_POST[$key])
          );
    
          update_post_meta($post_id, $key, $value);
        }
    
        // 通知メッセージを本文として保存
        if (isset($_POST['content'])) {
          static $updating_content = false;
    
          if ($updating_content) {
            return;
          }
    
          $updating_content = true;
    
          wp_update_post([
            'ID'           => $post_id,
            'post_content' => wp_kses_post(wp_unslash($_POST['content'])),
          ]);
    
          $updating_content = false;
        }
      }
    }
    
    
    new My_Notice_Manager_Metabox();

さいごに

今回紹介した方法を使えば、通知エリアを投稿・固定ページ・カテゴリーページごとに柔軟に切り替えて表示できます。
表示期間やID 条件を組み合わせることで、時間や内容を細かく制御できる高機能な通知システムを構築できます。

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