tag:crieit.net,2005:https://crieit.net/tags/%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3/feed 「プラグイン」の記事 - Crieit Crieitでタグ「プラグイン」に投稿された最近の記事 2022-03-06T00:02:57+09:00 https://crieit.net/tags/%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3/feed tag:crieit.net,2005:PublicArticle/18136 2022-03-06T00:02:57+09:00 2022-03-06T00:02:57+09:00 https://crieit.net/posts/count-term-using-include-private-article-20220306 (WordPress) 非公開記事も含めた記事数カウントを管理画面に表示するプラグイン <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>WordPress でブログを運用していると、カテゴリーやタグ、カスタムタクソノミーを整理したくなることがあります。</p> <p>特にタグのように非階層型の入力形式のタクソノミーの場合は自由入力なので、スペースの有無や誤字等で似たようなタームを誤って生成しがちです。</p> <ul> <li><code>Windows 11</code> と <code>Windows11</code> (スペースの有無)</li> <li><code>Google Chrome</code> と <code>Gooogle Chrome</code> (誤字。 <code>o</code> が1つ多い)</li> </ul> <p>しかも、異なるタグとして集計されるのでタグクラウドの中から絞り込むと思わぬところで必要な記事が漏れていて「あの記事絶対タグが付いているはずなのに……」と見付からずに右往左往したり。</p> <p>基本的には管理画面の「投稿」→「タグ」に進んでタグの一覧からどちらか一方にタグを付け直して、もう片方のタグで「カウント」つまり記事数が0になったタグを削除すれば良いとは思います。</p> <p>しかし、ここで一つ課題が。</p> <p>技術記事等で自分用メモとして残すための非公開記事がいくつかあったりするのですが、先程のタグ編集画面の「カウント」は非公開記事は除外された数のようです。</p> <p>そのため、例えタグ編集画面で「カウント」が0になっていても、実際は非公開記事で使用されていた……ということが考えられます。これでは迂闊にタグを削除することができません。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <p>そこで、タグ編集画面で非公開記事も含めたカウントを表示するプラグインを作りました。</p> <pre><code class="php">class FrostColumns { protected $taxName; protected $postTypeName; protected $countIncludePrivateID; protected $countIncludePrivateLabel; /** * mb_strlen() での空文字列判定のラッパー関数 * * @param string $str : 入力文字列 * * @return boolean : 出力文字列 */ public function boolStrLen( $str ) { return mb_strlen( $str, 'UTF-8' ) > 0; } /** * コンストラクタ * */ public function __construct() { if( mb_strpos( parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ), 'edit-tags.php', 0, 'UTF-8' ) !== false ) { // edit-tags.php のみで動作 $tax = esc_html( filter_input( INPUT_GET, 'taxonomy', FILTER_SANITIZE_SPECIAL_CHARS ) ); // タクソノミーの名前を取得 $this->taxName = $this->boolStrLen( $tax ) ? $tax : ''; $posttype = esc_html( filter_input( INPUT_GET, 'post_type', FILTER_SANITIZE_SPECIAL_CHARS ) ); // 投稿タイプの名前を取得 $this->postTypeName = $this->boolStrLen( $posttype ) ? $posttype : ''; // ラベル $this->countIncludePrivateID = 'count_include_private'; $this->countIncludePrivateLabel = 'カウント(非公開・予約含)'; } } /** * SQLを直接発行して公開済み・非公開・予約投稿の記事の投稿IDのみの一覧を取得、その数をリンクと共に出力する * * @param int $termID : タームID * */ public function countArticlesBySQL( $termID ) { // $wpdb 読み込み (global で宣言した変数のスコープの兼ね合いでここで読み込み宣言する) require_once( ABSPATH . '/wp-load.php'); global $wpdb; // 各タームのオブジェクトを取得 $termObj = get_term( $termID, $this->taxName ); // 投稿のカテゴリー、タグはキー名がタクソノミー編集ページと投稿一覧ページで異なるので変換する $taxNameParam = $this->taxName; switch ( $this->taxName ) { case 'category': $taxNameParam = 'category_name'; break; case 'post_tag': $taxNameParam = 'tag'; break; default: $taxNameParam = $this->taxName; break; } // カスタム投稿タイプならばリンクのGETパラメータに投稿タイプの名前を付与 $postTypeParam = $this->boolStrLen( $this->postTypeName ) ? '&post_type=' . $this->postTypeName : ''; // SQLのクエリで指定する投稿タイプの名前の文字列をセット $postTypeDBParam = $this->boolStrLen( $this->postTypeName ) ? $this->postTypeName : 'post'; // WP_Query では結果の投稿オブジェクトが大き過ぎてメモリを食い潰すので直接SQLを発行してIDのみ結果として取得して省力化する $the_query = "SELECT " . $wpdb->prefix . "posts.ID FROM " . $wpdb->prefix . "posts LEFT JOIN " . $wpdb->prefix . "term_relationships ON (" . $wpdb->prefix . "posts.ID = " . $wpdb->prefix . "term_relationships.object_id) WHERE 1=1 AND ( " . $wpdb->prefix . "term_relationships.term_taxonomy_id IN ( %d ) ) AND " . $wpdb->prefix . "posts.post_type = %s AND ((" . $wpdb->prefix . "posts.post_status = 'publish' OR " . $wpdb->prefix . "posts.post_status = 'future' OR " . $wpdb->prefix . "posts.post_status = 'private')) GROUP BY " . $wpdb->prefix . "posts.ID ORDER BY " . $wpdb->prefix . "posts.post_date DESC"; $results = $wpdb->get_results( $wpdb->prepare( $the_query, $termID, $postTypeDBParam ) ); $cnt = count($results); echo <<<CNT <a href="edit.php?{$taxNameParam}={$termObj->slug}{$postTypeParam}">{$cnt}</a> CNT; } /** * 欄としての列の出力 * * @param array $columns : 列 * * @return array $columns : 列 * */ function addCountIncludePrivateColumns( $columns ) { echo <<<STL <style> .taxonomy-{$this->taxName} .manage-column.num \{width: 90px;\} .taxonomy-{$this->taxName} .manage-column.column-id \{width: 60px;\} </style> STL; $columns[$this->countIncludePrivateID] = $this->countIncludePrivateLabel; return $columns; } /** * 追加した列に実際の値を表示させる * * @param string $content : 出力するコンテンツ * @param string $column_name : 列名 * @param int $term_id : タームID * */ function customCountIncludePrivateColumns( $content, $column_name, $term_id ) { if ( $column_name == $this->countIncludePrivateID ) { $this->countArticlesBySQL( $term_id ); } } /** * 初期処理。アクションフック・フィルターフックを発動させる * */ public function initialize() { // 列の追加 add_filter( 'manage_edit-' . $this->taxName . '_columns', [ $this, 'addCountIncludePrivateColumns' ] ); // 追加した列に実際の値を表示させる add_action( 'manage_' . $this->taxName . '_custom_column', [ $this, 'customCountIncludePrivateColumns' ], 10, 3 ); } } // 処理 $wp_ab_frostcolumns = new FrostColumns(); if( is_admin() ) { // 管理者画面を表示している場合のみ実行 $wp_ab_frostcolumns->initialize(); } </code></pre> <ul> <li>コンストラクタ: <ul> <li>URLをパースして <code>edit-tags.php</code> のみで動作するように判定</li> <li>GETパラメータからタクソノミーの名前と(必要ならば)投稿タイプの名前を取得</li> </ul></li> <li><code>countArticlesBySQL</code>メソッド: <ul> <li>本プラグインの肝</li> <li>後で使用するため <code>wp-load.php</code> から <code>$wpdb</code> で WordPress のDBオブジェクトを読み込み</li> <li><code>edit-tags.php</code> ではカテゴリーは <code>category</code>、タグは <code>post_tag</code> だが投稿一覧ページではそれぞれ <code>category_name</code>, <code>tag</code> とキーとなる文字列が変わるため遷移時の GETパラメータ に付与するときのために変換をかけておく</li> <li>カスタム投稿タイプの場合、 SQLクエリ や GETパラメータ への付与が必要になるため処理</li> <li>タクソノミーと投稿タイプ、投稿ステータス( <code>publish</code>, <code>private</code>, <code>future</code> のいずれか)で絞り込むSQLクエリを発行して投稿IDのみのオブジェクトを取得する <ul> <li>WP_Query で投稿データ全体のオブジェクトを取得すると重過ぎて PHP のメモリバッファを食い潰してしまうため、最小限になるように調整</li> </ul></li> <li>記事数を <code>count</code> で取得して、 <code>a</code>タグ として出力</li> </ul></li> <li><code>addCountIncludePrivateColumns</code>メソッド: <ul> <li>テーブル出力にオリジナルの列を出力する。フィルターフック</li> </ul></li> <li><code>customCountIncludePrivateColumns</code>メソッド: <ul> <li>追加した列に実際の値を表示させるアクションフック</li> <li>テーブル出力のキーが予めセットされていた文字列 (今回のオリジナル列) と同じ場合、 <code>countArticlesBySQL</code>メソッド を呼ぶ</li> </ul></li> </ul> <p>やっていることとしてはこのような感じ。</p> <h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2> <p>それでは、デモで検証してみます。</p> <p><a href="https://crieit.now.sh/upload_images/f1a6256dabde4394bf1b525d84800e4c62237b343fbf6.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f1a6256dabde4394bf1b525d84800e4c62237b343fbf6.jpg?mw=700" alt="デモ環境の9つの記事" /></a></p> <p>このように9つの記事を適当に作ってみました。今回はプラグインの効果をタグで試すことにします。</p> <p><a href="https://crieit.now.sh/upload_images/13ea92d49935623f0ecaa3028b7f88e662237b3ce7c1d.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/13ea92d49935623f0ecaa3028b7f88e662237b3ce7c1d.jpg?mw=700" alt="「ヘイルストーム」で記事を検索すると公開記事2件、非公開記事2件と2件ずつ、合計4件がヒット" /></a></p> <p>例えば、「ヘイルストーム」で記事を検索すると公開記事2件、非公開記事2件と2件ずつ、合計4件がヒットします。</p> <p><a href="https://crieit.now.sh/upload_images/969bb40d5160e13ccca7510c8dcbd18262237b4494120.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/969bb40d5160e13ccca7510c8dcbd18262237b4494120.jpg?mw=700" alt="タグの編集画面。先の「ヘイルストーム」の記事数は2と表示されている" /></a></p> <p>ところが、通常のタグの編集画面では先の「ヘイルストーム」の記事数は2と表示されています。実際は非公開記事も含めて4件あるはずで、今回は「4」と表示されてほしいわけです。</p> <p><a href="https://crieit.now.sh/upload_images/7450e2f60c9a03d568fea581118bc4a762237b4c77772.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/7450e2f60c9a03d568fea581118bc4a762237b4c77772.jpg?mw=700" alt="プラグインを有効化した際のタグの編集画面。先の「ヘイルストーム」の記事数は追加された列で4と表示されている" /></a></p> <p>それじゃスペルカード発動。先の「ヘイルストーム」の記事数は追加された列で4と表示されるようになりました。意図通りです。</p> <p><a href="https://crieit.now.sh/upload_images/34fefff76205debe5afe7a16178ecb7362237b5698652.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/34fefff76205debe5afe7a16178ecb7362237b5698652.jpg?mw=700" alt="別のサンプル。伸ばし棒のない「コールドディヴィニティ」は非公開記事1件のみ" /></a></p> <p>別のサンプル。誤字した例として伸ばし棒のない「コールドディヴィニティ」は上述のタグ編集画面だと通常0件、追加列では1件と表示されています。ページ遷移すると、非公開記事1件のみが一覧に表示されています。</p> <p>こういうケースでは伸ばし棒のある「コールドディヴィニティー」に直して、プラグインで追加された列も含めて「0」表示となったことを確認した後に伸ばし棒のない「コールドディヴィニティ」タグを削除する、というフローで活用できる、という寸法です。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="一覧ページのテーブルに列を追加・カスタマイズ"><a href="#%E4%B8%80%E8%A6%A7%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AE%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AB%E5%88%97%E3%82%92%E8%BF%BD%E5%8A%A0%E3%83%BB%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA">一覧ページのテーブルに列を追加・カスタマイズ</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://code-ex.dev/wordpress/322/">WordPress の管理画面でターム一覧をカスタマイズする方法 | Source Code EX</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.nxworld.net/wp-add-taxonomy-columns-term-id.html">WordPress:管理画面のカテゴリーやタクソノミー一覧ページにID(タームID)項目を追加する方法 - NxWorld</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wemo.tech/2342">WordPress管理画面のカテゴリー・タグの一覧テーブルにIDを表示する方法 | WEMO</a></li> </ul> <h3 id="(未使用だが参考にはなった) 表示処理"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%E3%81%A0%E3%81%8C%E5%8F%82%E8%80%83%E3%81%AB%E3%81%AF%E3%81%AA%E3%81%A3%E3%81%9F%29+%E8%A1%A8%E7%A4%BA%E5%87%A6%E7%90%86">(未使用だが参考にはなった) 表示処理</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-admin/edit-tags.php#L613">WordPress\/edit-tags.php at master ・ WordPress\/WordPress ・ GitHub</a></li> </ul> <p><code>$wp_list_table->display();</code> でテーブルを出力している。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-admin/includes/class-wp-list-table.php#L1268">WordPress\/class-wp-list-table.php at master ・ WordPress\/WordPress ・ GitHub</a></li> </ul> <p>基底クラスのこの部分が該当処理。ただし汎用的な内容。</p> <h4 id="(未使用だが参考にはなった) WP_Terms_List_Table"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%E3%81%A0%E3%81%8C%E5%8F%82%E8%80%83%E3%81%AB%E3%81%AF%E3%81%AA%E3%81%A3%E3%81%9F%29+WP_Terms_List_Table">(未使用だが参考にはなった) WP_Terms_List_Table</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/classes/wp_terms_list_table/">WP_Terms_List_Table | Class | WordPress Developer Resources</a></li> </ul> <p>使用しているクラス</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-admin/includes/class-wp-terms-list-table.php#L569">WordPress\/class-wp-terms-list-table.php at master ・ WordPress\/WordPress ・ GitHub</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/classes/wp_terms_list_table/column_posts/">WP_Terms_List_Table::column_posts() | Method | WordPress Developer Resources</a></li> </ul> <p><code>column_posts()</code>メソッドがそれっぽい。が、引数の <code>$tag</code> に既に情報が入っているようなのでこの中ではない。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/classes/wp_list_table/">WP_List_Table | Class | WordPress Developer Resources</a></li> </ul> <p><code>display_rows()</code>メソッドでもない。</p> <h4 id="ソート可能な列の出力"><a href="#%E3%82%BD%E3%83%BC%E3%83%88%E5%8F%AF%E8%83%BD%E3%81%AA%E5%88%97%E3%81%AE%E5%87%BA%E5%8A%9B">ソート可能な列の出力</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/hooks/manage_this-screen-id_sortable_columns/">manage_{$this->screen->id}_sortable_columns | Hook | WordPress Developer Resources</a></li> </ul> <h3 id="$wpdb"><a href="#%24wpdb">$wpdb</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://agohack.com/wpdb-select-with-prepare/">[WordPress] $wpdb を使って SELECT 結果取得</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://hyperschool.net/textbook/wordpress/wordpress_wp_query.html">WP_Queryを利用した投稿の取得 · GitBook</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/benibana2001/items/259fb83ad37ca950cd78">$wpdb->prepare のプレースホルダの書き方 - Qiita</a></li> </ul> <h4 id="テーブルのプレフィックス"><a href="#%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AE%E3%83%97%E3%83%AC%E3%83%95%E3%82%A3%E3%83%83%E3%82%AF%E3%82%B9">テーブルのプレフィックス</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://drawer-jp.com/programing/wordpress-table-prefix/">WordPress テーブル接頭辞 取得方法 | 引き出しブログ</a></li> </ul> <h3 id="(未使用) WP_Query"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+WP_Query">(未使用) WP_Query</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://notnil-creative.com/blog/archives/1288">WP_Queryの使い方をPHPコードにまとめた便利なコード・スニペット :: Takuro Hishikawa</a></li> </ul> <h4 id="(未使用) 件数取得"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+%E4%BB%B6%E6%95%B0%E5%8F%96%E5%BE%97">(未使用) 件数取得</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="http://kihon-no-ki.com/wordpress-wp-query-found-posts-property">[WordPress] WP_Queryで投稿取得後に全件数を求める | きほんのき</a></li> </ul> <p><code>found_posts</code>プロパティ</p> <h4 id="(未使用) wp_count_posts()"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+wp_count_posts%28%29">(未使用) wp_count_posts()</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://simplesimples.com/web/application/wordpress/wp_count_posts/">WordPressで記事の数を取得する → wp_count_posts() か found_posts - シンプルシンプルデザイン WordPress</a></li> </ul> <p>複雑な条件の件数となると <code>WP_Query</code> 一択。</p> <h3 id="タクソノミー→ターム"><a href="#%E3%82%BF%E3%82%AF%E3%82%BD%E3%83%8E%E3%83%9F%E3%83%BC%E2%86%92%E3%82%BF%E3%83%BC%E3%83%A0">タクソノミー→ターム</a></h3> <h4 id="(未使用だが参考にはなった) WP_Term_Query"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%E3%81%A0%E3%81%8C%E5%8F%82%E8%80%83%E3%81%AB%E3%81%AF%E3%81%AA%E3%81%A3%E3%81%9F%29+WP_Term_Query">(未使用だが参考にはなった) WP_Term_Query</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://wemo.tech/800">WordPressでカテゴリー・タグ・タクソノミーのタームを全取得して一覧表示する方法【WP_Term_Query \/ get_terms()】 | WEMO</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/classes/wp_term_query/">WP_Term_Query | Class | WordPress Developer Resources</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-includes/class-wp-term-query.php">WordPress\/class-wp-term-query.php at master · WordPress\/WordPress · GitHub</a></li> </ul> <p><code>count</code>プロパティ はDBの値を読むだけ。逆に言えばDBに値が保存されていることが判明。</p> <h4 id="(未使用だが参考にはなった) タクソノミー操作系の目星"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%E3%81%A0%E3%81%8C%E5%8F%82%E8%80%83%E3%81%AB%E3%81%AF%E3%81%AA%E3%81%A3%E3%81%9F%29+%E3%82%BF%E3%82%AF%E3%82%BD%E3%83%8E%E3%83%9F%E3%83%BC%E6%93%8D%E4%BD%9C%E7%B3%BB%E3%81%AE%E7%9B%AE%E6%98%9F">(未使用だが参考にはなった) タクソノミー操作系の目星</a></h4> <ul> <li><code>get_tax_sql()</code> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-includes/taxonomy.php#L902">WordPress\/taxonomy.php at master · WordPress\/WordPress · GitHub</a></li> </ul></li> <li>実際にテーブルを作っているクラス <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-admin/includes/class-wp-terms-list-table.php#L569">WordPress\/class-wp-terms-list-table.php at master · WordPress\/WordPress · GitHub</a> <ul> <li>基底クラス: <a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-admin/includes/class-wp-list-table.php">WordPress\/class-wp-list-table.php at master · WordPress\/WordPress · GitHub</a></li> </ul></li> </ul></li> </ul> <h4 id="(未使用だが参考にはなった) タクソノミーやタームに関係ありそうなクラス"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%E3%81%A0%E3%81%8C%E5%8F%82%E8%80%83%E3%81%AB%E3%81%AF%E3%81%AA%E3%81%A3%E3%81%9F%29+%E3%82%BF%E3%82%AF%E3%82%BD%E3%83%8E%E3%83%9F%E3%83%BC%E3%82%84%E3%82%BF%E3%83%BC%E3%83%A0%E3%81%AB%E9%96%A2%E4%BF%82%E3%81%82%E3%82%8A%E3%81%9D%E3%81%86%E3%81%AA%E3%82%AF%E3%83%A9%E3%82%B9">(未使用だが参考にはなった) タクソノミーやタームに関係ありそうなクラス</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-includes/class-wp-tax-query.php">WordPress\/class-wp-tax-query.php at master · WordPress\/WordPress · GitHub</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-includes/class-wp-taxonomy.php">WordPress\/class-wp-taxonomy.php at master · WordPress\/WordPress · GitHub</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/classes/wp_term/">WP_Term | Class | WordPress Developer Resources</a> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-includes/class-wp-term.php">WordPress\/class-wp-term.php at master · WordPress\/WordPress · GitHub</a> <ul> <li>Termのオブジェクト(ハコ)という感じ</li> </ul></li> </ul></li> </ul> <h4 id="get_term()"><a href="#get_term%28%29">get_term()</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/get_term">関数リファレンス\/get term - WordPress Codex 日本語版</a></li> </ul> <h3 id="PHP"><a href="#PHP">PHP</a></h3> <h4 id="ヒアドキュメント"><a href="#%E3%83%92%E3%82%A2%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88">ヒアドキュメント</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/language.types.string.php">PHP: 文字列 - Manual</a></li> </ul> <h4 id="スコープ"><a href="#%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97">スコープ</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/language.variables.scope.php">PHP: 変数のスコープ - Manual</a></li> </ul> <h4 id="GETパラメータ"><a href="#GET%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF">GETパラメータ</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.filter-input.php">PHP: filter_input - Manual</a></li> </ul> <p><code>filter_input(INPUT_GET, 'taxonomy', FILTER_SANITIZE_SPECIAL_CHARS)</code> で取得できる。</p> arm-band tag:crieit.net,2005:PublicArticle/16229 2020-11-22T13:53:28+09:00 2020-11-22T13:53:28+09:00 https://crieit.net/posts/wordpress-plugin-hidden-category-only-adminpage WordPress の管理画面内のみ投稿の特定のカテゴリを非表示にするプラグイン <p>管理画面の投稿一覧・投稿編集画面でカテゴリの一覧の表示を少なくするプラグインを作りました。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>カテゴリを大量に使用している WordPress サイトがあるのですが、投稿編集画面や投稿一覧画面でカテゴリを選択使用とした際にどこにあるのかスクロールバーを右往左往させることが多いのが悩みでした。</p> <p>サイトの用途として</p> <ul> <li>フロント側: <ul> <li>カテゴリで記事を辿ることがままある (検索的な用途) <ul> <li>カテゴリの一覧としては残しておきたい <ul> <li>「カテゴリを削除・整理する」のは今回の用途では不適</li> </ul></li> </ul></li> </ul></li> <li>管理画面: <ul> <li>カテゴリの新規追加はそこそこある <ul> <li>過去記事もカテゴリを付け直すことがある (上述の検索用途のため)</li> </ul></li> <li>ただし、一度追加した既存のカテゴリはホットな時期が終われば以降はほぼ再利用しない</li> </ul></li> </ul> <p>という状態です。端的に言えば「<strong>追加するだけ追加するけど、一定の期間を終えたらそのカテゴリを使用することはほぼない</strong>」「<strong>検索用途として使用するため、カテゴリの削除は基本しない</strong>」という運用です。</p> <p>そうなると、冒頭のように「(編集の際に)<strong>大量のカテゴリのチェックボックスからスクロールしまくって目的のカテゴリを探す</strong>」羽目になるわけです。</p> <p>これをどうにかしたい。</p> <h2 id="機能"><a href="#%E6%A9%9F%E8%83%BD">機能</a></h2> <p>この悩みを解決するために、以下のような機能を考えました。</p> <ul> <li>設定画面: <ul> <li>カテゴリの一覧から非表示にするカテゴリを選択する <ul> <li>カテゴリを選択した際に子孫カテゴリがいる場合は、これも一括で非表示にする</li> </ul></li> </ul></li> <li>投稿一覧画面: <ul> <li>設定画面で選択したカテゴリを非表示</li> </ul></li> <li>新規投稿画面・投稿編集画面: <ul> <li>設定画面で選択したカテゴリを非表示</li> </ul></li> <li>フロント画面: <ul> <li>記事のカテゴリ一覧の出力やカテゴリアーカイブのウィジェット等、カテゴリの表示は上述の設定に依らず通常通り全てのカテゴリを表示する</li> </ul></li> </ul> <h2 id="プラグイン"><a href="#%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3">プラグイン</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/arm-band/wp_plugin_furusoma">arm-band/wp_plugin_furusoma</a></li> </ul> <p>できたものがこちらになります。</p> <h2 id="動作デモ(キャプチャ画像)"><a href="#%E5%8B%95%E4%BD%9C%E3%83%87%E3%83%A2%28%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3%E7%94%BB%E5%83%8F%29">動作デモ(キャプチャ画像)</a></h2> <h3 id="1. カテゴリのサンプル"><a href="#1.+%E3%82%AB%E3%83%86%E3%82%B4%E3%83%AA%E3%81%AE%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB">1. カテゴリのサンプル</a></h3> <p><a href="https://crieit.now.sh/upload_images/277db8038d4d82604c07e76b2902efa45fb9edb2a3302.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/277db8038d4d82604c07e76b2902efa45fb9edb2a3302.jpg?mw=700" alt="カテゴリのサンプル" /></a></p> <p>まずは準備。適当な WordPress サイトを Docker 等で建てて上述のようなサンプルのカテゴリを作成します。</p> <ul> <li>test <ul> <li>テスト子供1 <ul> <li>テスト孫1 <ul> <li>テストひ孫1</li> </ul></li> </ul></li> <li>テスト子供2</li> </ul></li> <li>サンプル <ul> <li>サンプル子供</li> </ul></li> <li>未分類 (デフォルト)</li> </ul> <p>これで試験します。</p> <h3 id="2. 設定画面"><a href="#2.+%E8%A8%AD%E5%AE%9A%E7%94%BB%E9%9D%A2">2. 設定画面</a></h3> <p>次に設定画面。</p> <p><a href="https://crieit.now.sh/upload_images/612eeafdb43cfd3b538d3901b2d81d395fb9edc447633.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/612eeafdb43cfd3b538d3901b2d81d395fb9edc447633.jpg?mw=700" alt="プラグイン設定画面" /></a></p> <p>非表示にする投稿のカテゴリを選択するチェックボックスと、 JavaScript 側の制御のタイムアウト期限の2つの枠を作りました。</p> <p>前者は親子関係にきちんと沿った表示をするために <code>Walker_Category_Checklist</code>クラス を extends して <code>start_el</code>メソッド をカスタマイズしています。</p> <p>後者は JavaScript でポーリングする回数の最大値(後述)です。</p> <p>これについては、投稿新規作成画面と投稿編集画面でのカテゴリ一覧の非表示で壁に当たったのがきっかけで作りました。</p> <p>これらの画面は AJAX 経由でカテゴリ一覧を作成しているようなので、サーバサイド( PHP )での制御は難しいと判断しました(やるとしたら、 WP REST API の出力を絞る?)。</p> <p>また、仮にサーバサイドでカテゴリ一覧を絞ってしまうと、過去記事のカテゴリを整理する際に</p> <ul> <li>プラグインで非表示にする前にチェックを付けていた</li> <li>記事編集時点ではプラグインで非表示設定にしている</li> </ul> <p>の2つの条件を満たすカテゴリが存在した場合、チェックされずに記事の保存が行われてしまう、つまり「カテゴリに既に付けてあったチェックが外れてしまう」可能性が考えられるため、この点でもサーバサイドのみでの制御は難しいと考えました。</p> <p>そこで苦肉の策ではありますが、 JavaScript で「一定時間おきにカテゴリの一覧が描画されたか(クラス <code>editor-post-taxonomies__hierarchical-terms-list</code> の要素があるか)をポーリング」し、「存在したことを確認したら該当カテゴリの項目を非表示にする」ことにしました。</p> <p>このポーリングの回数が上述のタイムアウト期限の項目になります。</p> <h4 id="2.1. バリデーション"><a href="#2.1.+%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">2.1. バリデーション</a></h4> <p><code>register_setting</code> のコールバックにバリデーションも登録しました。</p> <p><a href="https://crieit.now.sh/upload_images/aceb87ed040f036103cbb9a9aabcc1c25fb9edd573930.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/aceb87ed040f036103cbb9a9aabcc1c25fb9edd573930.jpg?mw=700" alt="タイムアウト期限に意図しない入力値を設定する" /></a></p> <p>開発者ツールを使ってタイムアウト期限の <code>input</code> タグを <code>type="number"</code> から <code>type="text"</code> に変えて文字列を入力します。</p> <p><a href="https://crieit.now.sh/upload_images/5dfe957f73ff468862564dd0f379d0765fb9ede12f3d2.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5dfe957f73ff468862564dd0f379d0765fb9ede12f3d2.jpg?mw=700" alt="バリデーションエラー (タイムアウト期限に意図しない入力値を設定して送信)" /></a></p> <p>結果、エラー表示されました。</p> <p><a href="https://crieit.now.sh/upload_images/efc13b859dd5a15172a0cacd69e7a3295fb9edef4298e.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/efc13b859dd5a15172a0cacd69e7a3295fb9edef4298e.jpg?mw=700" alt="正しい入力の場合 (タイムアウト期限)" /></a></p> <p>正しい入力の場合は保存の通知が表示されます。</p> <p><a href="https://crieit.now.sh/upload_images/68d9292464dedd46a22c54121adbb23d5fb9ee0345395.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/68d9292464dedd46a22c54121adbb23d5fb9ee0345395.jpg?mw=700" alt="非表示にする項目の設定に意図しない入力値を設定する" /></a></p> <p>今度はチェックボックスで送信される値を操作します。</p> <p><a href="https://crieit.now.sh/upload_images/03762eb06340573e6d5f9f044de74ce45fb9ee1520314.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/03762eb06340573e6d5f9f044de74ce45fb9ee1520314.jpg?mw=700" alt="バリデーションエラー (非表示にする項目の設定に意図しない入力値を設定して送信)" /></a></p> <p>結果、エラー表示されました。</p> <p><a href="https://crieit.now.sh/upload_images/f1bf1bad02a190bd150b750941fd17ec5fb9ee20e7a97.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f1bf1bad02a190bd150b750941fd17ec5fb9ee20e7a97.jpg?mw=700" alt="正しい入力の場合 (非表示にする項目の設定)" /></a></p> <p>こちらも正しい入力の場合は保存の通知が表示されます。</p> <h4 id="2.2. 子孫要素だけチェックを外すことを阻害"><a href="#2.2.+%E5%AD%90%E5%AD%AB%E8%A6%81%E7%B4%A0%E3%81%A0%E3%81%91%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%82%92%E5%A4%96%E3%81%99%E3%81%93%E3%81%A8%E3%82%92%E9%98%BB%E5%AE%B3">2.2. 子孫要素だけチェックを外すことを阻害</a></h4> <p>「カテゴリを選択した際に子孫カテゴリがいる場合は、これも一括で非表示にする」という仕様ですが、これもやや頭を悩ませるものでした。</p> <p>というのは、 <code>Walker_Category_Checklist</code>クラス で探索するカテゴリは「自身から見て親と子」の情報は持っていますが、親のさらに上の祖先や、逆に孫以下の情報は直接は持っていなさそうでした。</p> <p>そのため、「あるカテゴリを非表示にした」場合、子供はともかく孫以降まで非表示にする判定をして行かなければなりません。</p> <p>逆に、あるカテゴリに非表示のチェックが付いていなかったとしても、親より上の祖先のカテゴリが非表示チェックが付いていないか判定しなければなりません。</p> <p>どちらも再帰的に探索して潰していく形になるので、そこそこ面倒かと。</p> <p>今回は実装スピードを重視したかったので、上述のような再帰的な判定ではなく、「設定画面であるカテゴリにチェックを付けたら、子孫カテゴリも全て連動してチェックを付ける」機能を JavaScript 側で実装しました。</p> <p><a href="https://crieit.now.sh/upload_images/5411b55f0e902ff7914475f060d22c135fb9ee2c68511.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5411b55f0e902ff7914475f060d22c135fb9ee2c68511.jpg?mw=700" alt="正しい入力の場合 (非表示にする項目の設定)" /></a></p> <p>そこで、「親以上のカテゴリでチェックが付いていたら子孫カテゴリはチェックを外せない」という機能も追加しました。</p> <h3 id="3. 投稿編集画面"><a href="#3.+%E6%8A%95%E7%A8%BF%E7%B7%A8%E9%9B%86%E7%94%BB%E9%9D%A2">3. 投稿編集画面</a></h3> <p>続いて、投稿編集画面(新規投稿、既存投稿の編集)。</p> <p><a href="https://crieit.now.sh/upload_images/11ee5a29d06bc1790fb2798d183c0e5e5fb9ee376b26c.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/11ee5a29d06bc1790fb2798d183c0e5e5fb9ee376b26c.jpg?mw=700" alt="テストのため、「テスト子供2」の他に、非表示にする予定の「テスト孫1」「サンプル子供」もカテゴリとしてチェックを付ける" /></a></p> <p>テストのため、一度プラグインを無効化して、表示し続ける予定の「テスト子供2」の他に、非表示にする予定の「テスト孫1」「サンプル子供」もカテゴリとしてチェックを付けておきます。</p> <p><a href="https://crieit.now.sh/upload_images/2c7f6bd5794b0eb05bc7bc5b4a92971a5fb9ee409d534.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2c7f6bd5794b0eb05bc7bc5b4a92971a5fb9ee409d534.jpg?mw=700" alt="投稿一覧画面で「テスト子供2」「テスト孫1」「サンプル子供」のカテゴリに所属していることを確認" /></a></p> <p>投稿一覧画面で「テスト子供2」「テスト孫1」「サンプル子供」のカテゴリに所属していることを確認しておきます。</p> <p>続いてプラグインを有効化し、以下のように非表示設定をします。</p> <ul> <li>test <ul> <li>[x] テスト子供1 <ul> <li>[x] テスト孫1 <ul> <li>[x] テストひ孫1</li> </ul></li> </ul></li> <li>テスト子供2</li> </ul></li> <li>サンプル <ul> <li>[x] サンプル子供</li> </ul></li> <li>未分類 (デフォルト)</li> </ul> <p>先ほどの投稿では、上述の通り「テスト子供2」は残りますが、「テスト孫1」「サンプル子供」の2つは非表示になります。</p> <p><a href="https://crieit.now.sh/upload_images/e802e9bbd09effcfefd9df77bba1944f5fb9ee4a702f4.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e802e9bbd09effcfefd9df77bba1944f5fb9ee4a702f4.jpg?mw=700" alt="投稿編集画面で「テスト孫1」「サンプル子供」が非表示になっていることを確認" /></a></p> <p>投稿編集画面で「テスト孫1」「サンプル子供」が非表示になっていることを確認。「テスト子供2」だけ表示上残っています。</p> <p><a href="https://crieit.now.sh/upload_images/292b879606f6d3ce08f52acdf583516f5fb9ee59a073f.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/292b879606f6d3ce08f52acdf583516f5fb9ee59a073f.jpg?mw=700" alt="投稿編集画面で「未分類」もチェック" /></a></p> <p>この状態で「未分類」カテゴリにも所属するようにチェックを付けて「更新」します。</p> <p><a href="https://crieit.now.sh/upload_images/779d2c7d6df2bcc707517feab3f5339e5fb9ee6770578.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/779d2c7d6df2bcc707517feab3f5339e5fb9ee6770578.jpg?mw=700" alt="投稿一覧画面で「テスト子供2」「未分類」「テスト孫1」「サンプル子供」に所属していることを確認" /></a></p> <p>投稿一覧画面で元々チェックが付いていて表示されていた「テスト子供2」、新規で追加した「未分類」はもちろん、非表示になっている「テスト孫1」「サンプル子供」も依然として所属していることが確認できました(原案時点、 <code>display: none;</code> で全くの非表示にした場合は「テスト孫1」「サンプル子供」のチェックが外れてしまったので、若干の工夫が必要でした)。</p> <p>なお、投稿編集画面のカテゴリは以下の操作を行う度にメニューの中が再描画されるため、最初の編集画面表示時以外のタイミングでも非表示にするように JavaScript の調整が必要でした。</p> <ul> <li>⚙ボタンを押してメニューの表示・非表示を切り替える</li> <li>タブで「ブロック」を表示させた後、「文書」タブに戻る</li> </ul> <h3 id="4. 投稿一覧画面"><a href="#4.+%E6%8A%95%E7%A8%BF%E4%B8%80%E8%A6%A7%E7%94%BB%E9%9D%A2">4. 投稿一覧画面</a></h3> <p>続いて投稿一覧画面。</p> <p><a href="https://crieit.now.sh/upload_images/a8b979beec64efb53a9d4cd1f84bf7765fb9ee740d08d.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a8b979beec64efb53a9d4cd1f84bf7765fb9ee740d08d.jpg?mw=700" alt="投稿一覧画面で「テスト子供2」「テスト孫1」「サンプル子供」に所属していることを確認" /></a></p> <p>いったん先ほどのテスト投稿のカテゴリを「テスト子供2」「テスト孫1」「サンプル子供」の3つに戻しておきます。</p> <p><a href="https://crieit.now.sh/upload_images/1cc4a0a1f036ae12597b7757fe2b64195fb9ee7d49566.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/1cc4a0a1f036ae12597b7757fe2b64195fb9ee7d49566.jpg?mw=700" alt="クイック編集のカテゴリ一覧の表示" /></a></p> <p>この状態で「クイック編集」をクリックし、カテゴリの一覧を表示させます。先ほどと同様、「テスト孫1」「サンプル子供」の2つは非表示になっています。</p> <p>この状態で「更新」。</p> <p><a href="https://crieit.now.sh/upload_images/aec844a69a76f658a071a67cde393c755fb9ee85667a0.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/aec844a69a76f658a071a67cde393c755fb9ee85667a0.jpg?mw=700" alt="投稿一覧画面で「テスト子供2」「テスト孫1」「サンプル子供」に所属していることを確認" /></a></p> <p>投稿一覧画面に戻り、依然「テスト子供2」「テスト孫1」「サンプル子供」の3つに所属し続けています。</p> <h2 id="躓いたポイント"><a href="#%E8%BA%93%E3%81%84%E3%81%9F%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88">躓いたポイント</a></h2> <ul> <li>カテゴリの一覧を親子関係を保持したまま表示させるために <code>Walker_Category_Checklist</code>クラス をカスタマイズした <ul> <li>設定画面だけでなく、投稿一覧画面で項目を非表示にする際も同様の手法を採用</li> </ul></li> <li>投稿編集画面で JavaScript 側で非表示の制御をかけた点(結局 jQuery で頑張ることに) <ul> <li>上述したように、「⚙ボタンを押す」「ブロック→文書に戻る」などのタイミングで再描画が走ってしまうため、再度非表示にする処理が走るように調整</li> </ul></li> <li>投稿編集画面、投稿一覧画面共に非表示の項目を <code>display: none;</code> で潰してしまうと、保存時にチェックが外れてしまうので 1x1px で <code>position: absolute;</code> して……など、「見た目上は非表示に見えるが要素としては存在している」状態にした</li> <li><code>wp_terms_checklist_args</code> は投稿編集画面、投稿一覧画面以外でもフックが発火するので、「管理画面内」全体でアクションフックを掛けると動作に不具合が出るため、アクションフック発火前に別個でページ判定を実施し、意図した部分だけアクションフックが掛かるように制限した点 <ul> <li>独自 css や JavaScript を読み込むアクションフックについてはそこまでシビアではないので、メソッド内で <code>$hook_suffix</code> で判定して回避可能</li> </ul></li> <li>上述したように、非表示の処理の際にカテゴリの階層が深くなってしまうと探索が難しいので、設定画面で親カテゴリにチェックを付けたら子孫カテゴリ全てに連動してチェックを付けるように処理を追加</li> </ul> <p>他、<a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2020/11/19/wordpress_plugin_clean_head/">WordPress の head タグの中を整理するプラグイン (設定画面付き) の自作</a>に類する点。</p> <hr /> <p>連続でプラグインを作ったことで、ある程度知見を貯めることはできたのではないかと個人的には思います。</p> <h2 id="備考"><a href="#%E5%82%99%E8%80%83">備考</a></h2> <p>今回は</p> <ul> <li>カテゴリの兄弟や子孫(一本の木の中の枝、森の中の木)を非表示(切る)にする</li> <li>非表示にする(見えない)</li> <li>あくまで非表示にするだけで、削除などの実際のデータに影響を及ぼすわけではない(気配はあるが、証拠が掴めない)</li> </ul> <p>というイメージから、「木の切り落とす音はするが、音のした方向を探しても倒れた木が見付からない」怪異である<strong>古杣</strong>(ふるそま)を採用しました。</p> <p>なお、類似の怪異は「天狗倒し」「天狗ナメシ」「空木(からぎ)倒し」「空木(そらぎ)倒し」「天崩し」「木伐り坊」等のバリエーションがあります。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="JavaScript"><a href="#JavaScript">JavaScript</a></h3> <h4 id="jQueryで描画完了後に動作"><a href="#jQuery%E3%81%A7%E6%8F%8F%E7%94%BB%E5%AE%8C%E4%BA%86%E5%BE%8C%E3%81%AB%E5%8B%95%E4%BD%9C">jQueryで描画完了後に動作</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://ja.stackoverflow.com/questions/27279/jquery-%E5%8B%95%E7%9A%84%E3%81%AB%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%82%93%E3%81%A0dom%E8%A6%81%E7%B4%A0%E3%81%AE%E8%A1%A8%E7%A4%BA%E5%AE%8C%E4%BA%86%E6%99%82%E3%81%ABjquery%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%92%E9%81%A9%E7%94%A8%E3%81%97%E3%81%9F%E3%81%84">javascript - jQuery 動的に読み込んだDOM要素の表示完了時にjQueryプラグインを適用したい - スタック・オーバーフロー</a></li> </ul> <h4 id="jQueryでDOMの変化をキャッチして動作"><a href="#jQuery%E3%81%A7DOM%E3%81%AE%E5%A4%89%E5%8C%96%E3%82%92%E3%82%AD%E3%83%A3%E3%83%83%E3%83%81%E3%81%97%E3%81%A6%E5%8B%95%E4%BD%9C">jQueryでDOMの変化をキャッチして動作</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="http://engineer-memo.goodhead.work/pages/27">JQuery|DOMの変更を検知する|WEBエンジニアのメモ??</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/Hashibata/items/1113d9cc6a1ad04a9644">要素の中身が変更されたことを知る - Qiita</a></li> </ul> <h4 id="JavaScriptで要素の存在判定"><a href="#JavaScript%E3%81%A7%E8%A6%81%E7%B4%A0%E3%81%AE%E5%AD%98%E5%9C%A8%E5%88%A4%E5%AE%9A">JavaScriptで要素の存在判定</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/Rasukarusan/items/754de5d8943eea8cc4ce">【Javascript】setInterval()で要素が現れるまで待つ - Qiita</a></li> </ul> <h4 id="親要素のチェックボックスをチェックしたら子孫要素のチェックボックスもチェックする"><a href="#%E8%A6%AA%E8%A6%81%E7%B4%A0%E3%81%AE%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E3%82%92%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%81%97%E3%81%9F%E3%82%89%E5%AD%90%E5%AD%AB%E8%A6%81%E7%B4%A0%E3%81%AE%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E3%82%82%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%81%99%E3%82%8B">親要素のチェックボックスをチェックしたら子孫要素のチェックボックスもチェックする</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://teratail.com/questions/241202">jQuery - 親にチェックするだけで子や孫のチェックボックスもまとめてチェックしたい|teratail</a></li> </ul> <h3 id="PHP"><a href="#PHP">PHP</a></h3> <ul> <li><code>in_array</code> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.in-array.php">PHP: in_array - Manual</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/ritukiii/items/3a6add378ae089ab5d70">in_arrayを使うときは黙って第三引数を付けること - Qiita</a></li> </ul></li> <li><code>json_encode</code> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.json-encode.php">PHP: json_encode - Manual</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/json.constants.php">PHP: 定義済み定数 - Manual</a></li> </ul></li> <li><code>htmlspecialchars</code> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.htmlspecialchars.php">PHP: htmlspecialchars - Manual</a></li> </ul></li> <li><code>parse_url</code> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.parse-url.php">PHP: parse_url - Manual</a></li> </ul></li> <li><code>mb_strpos</code> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.mb-strpos.php">PHP: mb_strpos - Manual</a></li> </ul></li> <li>文字列展開(恒等関数) <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/tadsan/items/e4796449c736cfb5c9bd">PHPで文字列リテラルに式展開 - Qiita</a></li> </ul></li> </ul> <h3 id="WordPress"><a href="#WordPress">WordPress</a></h3> <h4 id="投稿編集画面のカスタマイズ"><a href="#%E6%8A%95%E7%A8%BF%E7%B7%A8%E9%9B%86%E7%94%BB%E9%9D%A2%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA">投稿編集画面のカスタマイズ</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://2inc.org/blog/2013/04/05/3121/">WordPressのカスタムタクソノミーのチェックボックスをラジオボタンにする方法 - モンキーレンチ</a></li> </ul> <h4 id="コード参考: Adjust Admin Categories"><a href="#%E3%82%B3%E3%83%BC%E3%83%89%E5%8F%82%E8%80%83%3A+Adjust+Admin+Categories">コード参考: Adjust Admin Categories</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/kanakogi/adjust-admin-categories/blob/master/adjust-admin-categories.php">adjust-admin-categories/adjust-admin-categories.php at master ・ kanakogi/adjust-admin-categories</a></li> </ul> <h4 id="関数リファレンス"><a href="#%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9">関数リファレンス</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/wp_enqueue_style">関数リファレンス/wp enqueue style - WordPress Codex 日本語版</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/plugins_url">関数リファレンス/plugins url - WordPress Codex 日本語版</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://elearn.jp/wpman/function/checked.html">checked - WordPress私的マニュアル</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://elearn.jp/wpman/function/get_option.html">get_option - WordPress私的マニュアル</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://elearn.jp/wpman/function/maybe_unserialize.html">maybe_unserialize - WordPress私的マニュアル</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://elearn.jp/wpman/function/get_term.html">get_term - WordPress私的マニュアル</a></li> </ul> <h4 id="フィルターフック"><a href="#%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%BC%E3%83%95%E3%83%83%E3%82%AF">フィルターフック</a></h4> <h5 id="フィルターフック一覧"><a href="#%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%BC%E3%83%95%E3%83%83%E3%82%AF%E4%B8%80%E8%A6%A7">フィルターフック一覧</a></h5> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://yukiyuriweb.com/summary-of-action-and-filter-hook-calls-in-wordpress-core/">【WordPress 入門】WordPress で使えるアクション・フィルターフックの呼び出し元をまとめてみた | 滋賀/京都/大阪でホームページ制作ならYUKiYURi WEB</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3_API/%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%BC%E3%83%95%E3%83%83%E3%82%AF%E4%B8%80%E8%A6%A7">プラグイン API/フィルターフック一覧 - WordPress Codex 日本語版</a></li> </ul> <h5><code>wp_terms_checklist_args</code></h5> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/wp_terms_checklist">関数リファレンス/wp terms checklist - WordPress Codex 日本語版</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://liginc.co.jp/programmer/archives/4137">解決!WordPress投稿画面のカテゴリーをカスタマイズする方法 | 東京のWeb制作会社LIG</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://fantastech.net/wp-category-checklist">カテゴリー選択チェックボックスを表示するwp_category_checklistの使い方 ? Fantastech(ファンタステック)</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-admin/includes/class-walker-category-checklist.php">WordPress/class-walker-category-checklist.php at master ・ WordPress/WordPress</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/get_post_types">関数リファレンス/get post types - WordPress Codex 日本語版</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/get_object_taxonomies">関数リファレンス/get object taxonomies - WordPress Codex 日本語版</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/is_taxonomy_hierarchical">関数リファレンス/is taxonomy hierarchical - WordPress Codex 日本語版</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/get_terms">関数リファレンス/get terms - WordPress Codex 日本語版</a></li> </ul> <h5><code>Walker_Category_Checklist</code></h5> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/classes/walker_category_checklist/">Walker_Category_Checklist | Class | WordPress Developer Resources</a></li> </ul> <h5><code>admin_print_styles</code></h5> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://memocarilog.info/wordpress/7570">[WordPress] 管理画面に独自の CSS・JavaScript を適用させる方法まとめ | memocarilog</a></li> </ul> <h5><code>hook_suffix</code></h5> <ul> <li><a target="_blank" rel="nofollow noopener" href="http://wpcj.net/1610">管理バーに $hook_suffix の値を表示する | WordPressカスタマイズ事典</a></li> </ul> <h5 id="設定画面でチェックボックスを使用"><a href="#%E8%A8%AD%E5%AE%9A%E7%94%BB%E9%9D%A2%E3%81%A7%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E3%82%92%E4%BD%BF%E7%94%A8">設定画面でチェックボックスを使用</a></h5> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://h2ham.net/wordpress-creating-options-pages-checkbox">WordPress オリジナルの設定ページでチェックボックスを利用する方法 - HAM MEDIA MEMO</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16228 2020-11-22T13:46:27+09:00 2020-11-22T13:46:43+09:00 https://crieit.net/posts/wordpress-plugin-clean-head WordPress の head タグの中を整理するプラグイン (設定画面付き) の自作 <p>表題の通り、 WordPress の head タグの中を整理するプラグインを自作したので躓いた部分についてメモしておきます。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <p>まずはコードから。勢いで作ったので色々粗削りなのはご容赦を。</p> <pre><code class="php"><?php class ScreamingHead { /** * const */ const SCREAMINGHEAD = 'screaminghead'; const SCREAMINGHEAD_SETTINGS = 'Screaming Head 設定'; const SCREAMINGHEAD_SETTINGS_EN = 'screaminghead-settings'; const SCREAMINGHEAD_SETTINGS_VALIDATION = 'screaminghead_validation'; const SCREAMINGHEAD_SETTINGS_PARAMETERS = 'screaminghead_settings_parameters'; const ENCODING = 'UTF-8'; protected $keyArray; /** * コンストラクタ */ public function __construct() { $this->keyArray = [ 'wp_version' => [ 'label' => 'WordPress バージョン', 'description' => 'name="generator" で出力される WordPress のバージョン情報を非表示にします。', 'value' => 1, ], 'shortlink' => [ 'label' => 'ショートリンクURL', 'description' => 'ショートリンクURL を非表示にします。', 'value' => 1, ], 'wlwmanifest' => [ 'label' => 'wlwmanifest', 'description' => 'wlwmanifest を非表示にします。', 'value' => 1, ], 'xmlrpc' => [ 'label' => 'xmlrpc.php', 'description' => 'xmlrpc.php を非表示にします。', 'value' => 1, ], 'dns_prefetch' => [ 'label' => 'DNS プリフェッチ', 'description' => 'DNS プリフェッチ を非表示にします。', 'value' => 1, ], 'feed' => [ 'label' => 'フィード', 'description' => 'フィード を非表示にします。(サイト全体フィード、サイト全体コメントフィードを除く)', 'value' => 0, ], 'emoji' => [ 'label' => '絵文字', 'description' => '絵文字 用の css や JavaScript を読み込まないようにします。', 'value' => 0, ], 'rest_api' => [ 'label' => 'REST API', 'description' => 'REST API を出力しないようにします(oEmbed, Jetpack, ブロックエディタ 除く)。', 'value' => 0, ], 'prev_next_article' => [ 'label' => '前後の記事', 'description' => '前後の記事 への rel 属性を出力しないようにします。', 'value' => 1, ], ]; } /** * データ読み出し * * @return array $ANONYMOUS get_option で取得したデータを maybe_unserialize で配列に変換して戻り値にしている */ public function dataRead() { return maybe_unserialize( get_option( self::SCREAMINGHEAD_SETTINGS_PARAMETERS ) ); } /** * 管理者画面にメニューと設定画面を追加 */ public function admin_create_page() { // メニューを追加 add_action( 'admin_menu', [ $this, 'screaminghead_create_menu' ] ); // 独自関数をコールバック関数とする add_action( 'admin_init', [ $this, 'register_screaminghead_settings' ] ); } /** * メニュー追加 */ public function screaminghead_create_menu() { // add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position ); // $page_title : 設定ページの `title` 部分 // $menu_title : メニュー名 // $capability : 権限 ( 'manage_options' や 'administrator' など) // $menu_slug : メニューのslug // $function : 設定ページの出力を行う関数 // $icon_url : メニューに表示するアイコン // $position : メニューの位置 ( 1 や 99 など ) add_menu_page( self::SCREAMINGHEAD_SETTINGS, self::SCREAMINGHEAD_SETTINGS, 'administrator', self::SCREAMINGHEAD, [ $this, self::SCREAMINGHEAD . '_settings_page' ], 'dashicons-album' ); } /** * コールバック */ public function register_screaminghead_settings() { // register_setting( $option_group, $option_name, $sanitize_callback ) // $option_group : 設定のグループ名 // $option_name : 設定項目名(DBに保存する名前) // $sanitize_callback : 入力値調整をする際に呼ばれる関数 register_setting( self::SCREAMINGHEAD_SETTINGS_EN, self::SCREAMINGHEAD_SETTINGS_PARAMETERS, [ $this, self::SCREAMINGHEAD_SETTINGS_VALIDATION ] ); } /** * バリデーション。コールバックから呼ばれる * * @param array $newInput 設定画面で入力されたパラメータ * * @return string $newInput / $ANONYMOUS バリデーションに成功した場合は $newInput そのものを返す。失敗した場合はDBに保存してあった元のデータを get_option で呼び戻す。 */ public function screaminghead_validation( $newInput ) { // nonce check check_admin_referer( self::SCREAMINGHEAD . '_options', 'name_of_nonce_field' ); // validation $errCnt = 0; foreach($newInput as $key => $value) { if(preg_match('/^[\d]{1}$/i', $value)) { $newInput[$key] = (int) $value; if ($newInput[$key] !== 0 && $newInput[$key] !== 1) { $errCnt++; } } else { $errCnt++; } } if($errCnt > 0) { // add_settings_error( $setting, $code, $message, $type ) // $setting : 設定のslug // $code : エラーコードのslug (HTMLで'setting-error-{$code}'のような形でidが設定されます) // $message : エラーメッセージの内容 // $type : メッセージのタイプ。'updated' (成功) か 'error' (エラー) のどちらか add_settings_error( self::SCREAMINGHEAD, self::SCREAMINGHEAD . '-validation_error', __( '設定しようとしたパラメータに不正なデータが含まれています。', self::SCREAMINGHEAD ), 'error' ); return get_option( self::SCREAMINGHEAD_SETTINGS_PARAMETERS ); } else { return $newInput; } } /** * 設定画面ページの生成 */ public function screaminghead_settings_page() { if ( get_settings_errors( self::SCREAMINGHEAD ) ) { // エラーがあった場合はエラーを表示 settings_errors( self::SCREAMINGHEAD ); } else if( true == $_GET['settings-updated'] ) { // 設定変更時にメッセージ表示 ?> <div id="settings_updated" class="updated notice is-dismissible"><p><strong>設定を保存しました。</strong></p></div> <?php } ?> <div class="wrap"> <h1><?= esc_html( self::SCREAMINGHEAD_SETTINGS ); ?></h1> <h2>非表示にする項目の設定</h2> <p>以下の一覧から、非表示にしたい項目を選択してください。</p> <form method="post" action="options.php"> <?php settings_fields( self::SCREAMINGHEAD_SETTINGS_EN ); ?> <?php do_settings_sections( self::SCREAMINGHEAD_SETTINGS_EN ); ?> <table class="form-table" id="<?= esc_attr( self::SCREAMINGHEAD_SETTINGS_EN ); ?>"> <?php $data = self::dataRead(); foreach ( $this->keyArray as $key => $value ) { if (get_option( self::SCREAMINGHEAD_SETTINGS_PARAMETERS ) !== false) { // プラグインのパラメータの読み込みができた場合 // $data にキー $key の値が存在するならばチェックボックスにチェックを入れる $checked = array_key_exists($key, $data) && $data[$key] === 1 ? ' checked="checked"' : ''; } else { // プラグインのパラメータの読み込みができなかった場合 // $this->keyArray で保持しているデフォルト値を参照する $checked = $value['value'] === 1 ? ' checked="checked"' : ''; } ?> <tr> <th> <label for="<?= esc_attr( $key ); ?>"><?= esc_html( $value['label'] ); ?></label> </th> <td> <input type="checkbox" id="<?= esc_attr( $key ); ?>" name="<?= esc_attr( self::SCREAMINGHEAD_SETTINGS_PARAMETERS ); ?>[<?= esc_attr( $key ); ?>]" <?= $checked ?> value="1"><br> <p><?= esc_html( $value['description'] ); ?></p> </td> </tr> <?php } ?> </table> <?php wp_nonce_field( self::SCREAMINGHEAD . '_options', 'name_of_nonce_field' ); ?> <?php submit_button( '設定を保存', 'primary large', 'submit', true, [ 'tabindex' => '1' ] ); ?> </form> </div> <?php } /** * メイン処理 (フロント側) */ function time_crunch() { $data = self::dataRead(); $flagArray = []; foreach( $this->keyArray as $key => $value ) { if( get_option( SCREAMINGHEAD_SETTINGS_PARAMETERS ) !== false ) { // プラグインのパラメータの読み込みができた場合 // $data にキー $key の値が存在するならば $flagArray[$key] = array_key_exists($key, $data) ? $data[$key] : 0; } else { // プラグインのパラメータの読み込みができなかった場合 // $this->keyArray で保持しているデフォルト値を参照する $flagArray[$key] = $value['value']; } } // WordPressバージョン情報の削除 if( $flagArray['wp_version'] ) { remove_action( 'wp_head', 'wp_generator' ); } // ショートリンクURLの削除 if($flagArray['shortlink'] ) { remove_action( 'wp_head', 'wp_shortlink_wp_head' ); } // wlwmanifestの削除 if ($flagArray['wlwmanifest']) { remove_action( 'wp_head', 'wlwmanifest_link' ); } // application/rsd+xmlの削除 if( $flagArray['xmlrpc'] ) { remove_action( 'wp_head', 'rsd_link' ); } // dns-prefetchの削除 if( $flagArray['dns_prefetch'] ) { function remove_dns_prefetch( $hints, $relation_type ) { if( 'dns-prefetch' === $relation_type ) { return array_diff( wp_dependencies_unique_hosts(), $hints ); } return $hints; } add_filter( 'wp_resource_hints', 'remove_dns_prefetch', 10, 2 ); } // フィードの削除 if( $flagArray['feed'] ) { remove_action( 'wp_head', 'feed_links_extra', 3 ); } // 絵文字用css, JSの削除 if( $flagArray['emoji'] ) { remove_action( 'wp_head', 'print_emoji_detection_script', 7 ); remove_action('wp_print_styles', 'print_emoji_styles', 10); remove_action( 'admin_print_scripts', 'print_emoji_detection_script' ); remove_action( 'wp_print_styles', 'print_emoji_styles' ); remove_action( 'admin_print_styles', 'print_emoji_styles' ); remove_filter( 'the_content_feed', 'wp_staticize_emoji' ); remove_filter( 'comment_text_rss', 'wp_staticize_emoji' ); remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' ); add_filter( 'emoji_svg_url', '__return_false' ); } // REST API if( $flagArray['rest_api'] ) { function deny_restapi_except_plugins( $result, $wp_rest_server, $request ) { $namespaces = $request->get_route(); // oembed if( strpos( $namespaces, 'oembed/' ) === 1 ) { return $result; } // Jetpack if( strpos( $namespaces, 'jetpack/' ) === 1 ) { return $result; } // BlockEditor if( current_user_can( 'edit_posts' ) ) { return $result; } return new WP_Error( 'rest_disabled', __( 'The REST API on this site has been disabled.' ), array( 'status' => rest_authorization_required_code() ) ); } add_filter( 'rest_pre_dispatch', 'deny_restapi_except_plugins', 10, 3 ); } // 前後記事 if( $flagArray['prev_next_article'] ) { remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' ); } } } // 処理 $wp_ab_screaminghead = new ScreamingHead(); if( is_admin() ) { // 管理者画面を表示している場合のみ実行 $wp_ab_screaminghead->admin_create_page(); } else { // 管理者画面以外 $wp_ab_screaminghead->time_crunch(); } </code></pre> <ul> <li>クラス: 関数名のバッティングを防ぐためにカプセル化しました</li> <li><code>protected $keyArray;</code>: 設定画面で表示するための以下の項目を持たせました <ul> <li>ラベル</li> <li>説明文</li> <li>初期値 <ul> <li>特にプラグイン有効化直後はDBにデータがないので初期値を持たせる方向にしました</li> </ul></li> </ul></li> <li>コンストラクタ: コンストラクタは上述 <code>$keyArray</code> を決めるだけにしました <ul> <li>今回は「フロント画面のみで走ってほしい」処理と「管理画面のみで走ってほしい」処理の双方があるため、インスタンス化までは共通ですが <code>is_admin()</code> の結果でキックする処理を分けました</li> </ul></li> <li><code>admin_create_page()</code>: 管理者画面のみ走る処理 <ul> <li>サイドメニューに設定画面へのリンクを追加</li> <li>設定項目やバリデーション処理の初期化など</li> </ul></li> <li><code>screaminghead_validation()</code>: <ul> <li>nonce チェックもしています</li> <li>が、内部のエラーチェックは正直適当です(どこでエラーが発生したかを記録していない)</li> <li>入力に誤りがあった場合は元の値をセットしなおしています</li> </ul></li> <li><code>screaminghead_settings_page()</code>: <ul> <li><code>get_settings_errors( self::SCREAMINGHEAD )</code> でエラー表示</li> <li><code>wp_nonce_field</code> で nonce セット</li> </ul></li> <li><code>time_crunch()</code>: <ul> <li>フロント側の処理</li> <li>基本的には設定されたフラグに応じてアクションフックを付け外ししています</li> </ul></li> </ul> <h2 id="動作デモ"><a href="#%E5%8B%95%E4%BD%9C%E3%83%87%E3%83%A2">動作デモ</a></h2> <p><a href="https://crieit.now.sh/upload_images/6478422567b350ae178fa21d4794110c5fb9ecb87bf08.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/6478422567b350ae178fa21d4794110c5fb9ecb87bf08.jpg?mw=700" alt="設定画面" /></a></p> <p>管理者画面の設定画面。</p> <p><a href="https://crieit.now.sh/upload_images/2b92d1c6ec351ada940fe8ffdd0502d45fb9ecc537463.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2b92d1c6ec351ada940fe8ffdd0502d45fb9ecc537463.jpg?mw=700" alt="設定変更完了(更新通知の表示)" /></a></p> <p>更新通知の表示。</p> <p><a href="https://crieit.now.sh/upload_images/f9ce1bca2158c8f137bb472043919f975fb9eccf17fa6.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f9ce1bca2158c8f137bb472043919f975fb9eccf17fa6.jpg?mw=700" alt="開発者ツールで細工" /></a></p> <p>開発者ツールで送信パラメータを細工します。</p> <p><a href="https://crieit.now.sh/upload_images/bab07a84923b3346800ce86b1c3854fd5fb9ecd94e222.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/bab07a84923b3346800ce86b1c3854fd5fb9ecd94e222.jpg?mw=700" alt="設定変更時エラー(データに不正なパラメータがあった場合の通知の表示)" /></a></p> <p>バリデーションに引っかかってエラーの通知が表示されます。</p> <h2 id="躓いたポイント"><a href="#%E8%BA%93%E3%81%84%E3%81%9F%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88">躓いたポイント</a></h2> <ul> <li>最初は関数ベタで書いていたのですが、他の自作プラグインと同じ名前の関数を用意していたのでバッティングしてしまい、途中からクラスに置き換えました</li> <li>テンプレートタグで関数名を引数として渡す処理(<code>add_menu_page()</code>, <code>register_setting()</code> など)は関数名の文字列から自身のクラスと関数名の文字列の2つの要素からなる配列に置き換える必要がある <ul> <li>これでヌケがあって設定画面がブランク表示(コンテンツ部分に何も表示されない)になってしまいしばらく嵌まっていました。PHP でエラーにもならないのでヒントが少ない……</li> <li>例(第三引数が該当): <code>register_setting( SCREAMINGHEAD_SETTINGS_EN, SCREAMINGHEAD_SETTINGS_PARAMETERS, SCREAMINGHEAD_SETTINGS_VALIDATION );</code> → <code>register_setting( self::SCREAMINGHEAD_SETTINGS_EN, self::SCREAMINGHEAD_SETTINGS_PARAMETERS, [ $this, self::SCREAMINGHEAD_SETTINGS_VALIDATION ] );</code></li> </ul></li> <li>nonce のチェックの仕込み位置: <code>check_admin_referer()</code> をコールバックから呼ばれるバリデーションの中に入れましたが、ここで良いのかどうかは微妙……</li> </ul> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3><code>head</code> タグの中身をきれいにする</h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://wpqw.jp/wordpress/themes/head-clean/">プラグインを使わずに、wp_head()の出力する不要なコードを削除して整理する | WPQW</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://choppydays.com/wordpress-remove-unnecessary-header-section-without-any-plugin/">Wordpressの不必要なヘッダー情報をプラグインなしで削除し、セキュリティを高めて速度改善をする方法をまとめました。 - チョッピーデイズ</a></li> </ul> <h3 id="プラグイン"><a href="#%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3">プラグイン</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="http://wpcj.net/354">自作のプラグインに独自の設定画面を追加する | WordPressカスタマイズ事典</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://geek-memo.com/settingsapi2/">WordPressテーマやプラグインの設定ページを作成#2</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://oxynotes.com/?p=9384">WordPressプラグイン用の設定を追加する方法 | OXY NOTES</a></li> </ul> <h3 id="nonce"><a href="#nonce">nonce</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://webutubutu.com/webdesign/4076">wp_nonce_fieldに関するメモ - WEBUTUBUTU</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.imamura.biz/blog/6553">【WordPressの自作プラグインの設定画面に入力フォームを設置した場合に気をつける基本的なこと】そしてWordCampのあるセッションでプラグイン開発について改めて考えさせられました | 今村だけがよくわかるブログ</a></li> </ul> <h3 id="バリデーション"><a href="#%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">バリデーション</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/settings_errors">関数リファレンス/settings errors - WordPress Codex 日本語版</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/get_settings_errors">関数リファレンス/get settings errors - WordPress Codex 日本語版</a></li> </ul> <h3 id="PHP-Doc"><a href="#PHP-Doc">PHP-Doc</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/zaburo/items/ebd0b0d55f8abf41f001">PHP Documentorを使う - Qiita</a></li> </ul> arm-band