tag:crieit.net,2005:https://crieit.net/tags/WordPress/feed 「WordPress」の記事 - Crieit Crieitでタグ「WordPress」に投稿された最近の記事 2022-03-06T00:02:57+09:00 https://crieit.net/tags/WordPress/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/18125 2022-02-25T22:46:15+09:00 2022-02-25T22:46:15+09:00 https://crieit.net/posts/change-timzezone-in-docker-for-wordpress-post-failed-20220224 Docker 上の Almalinux のタイムゾーン設定について <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>AlmaLinux ベースの自作LAMP環境構築用 Docker で WordPress を動かしたところ、投稿が「予約投稿の失敗」となってしまい正常に投稿できない現象に遭遇したため対処。</p> <h2 id="現象"><a href="#%E7%8F%BE%E8%B1%A1">現象</a></h2> <p><a href="https://crieit.now.sh/upload_images/49f0d2b150a7b5d422313af680baf9ae62124ffe1e1d2.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/49f0d2b150a7b5d422313af680baf9ae62124ffe1e1d2.jpg?mw=700" alt="今までの設定で発生していた現象" /></a></p> <p>今までの Docker Compose 環境で発生していた現象が上述の画像。普通に投稿したにも関わらず、「予約投稿の失敗」となってしまいます。</p> <p>困ったことに WordPress の管理画面で見ると日時はずれていない (WordPress インストール時のサンプル投稿「Hello World!」の投稿日時とほぼ同じ) ので、ぱっと見原因が掴みづらいところ。</p> <p>ちなみに、 <code>wp_posts</code> のテーブルを直接見たところ、 <code>post_date</code>, <code>post_date_gmt</code>, <code>post_modified</code>, <code>post_modified_gmt</code> が全て同じ日時になっていました。</p> <p>……あれ、 <code>_gmt</code> 系はグリニッジ標準時刻なので日本の時刻とは9時間ずれるはずでは……。ということで、どうもこの辺りが宜しくなさそうです。</p> <p>なお、この時の Dockerfile 等の各種設定は次のようになっていました。</p> <h3 id="Dockerfile (共通)"><a href="#Dockerfile+%28%E5%85%B1%E9%80%9A%29">Dockerfile (共通)</a></h3> <pre><code class="dockerfile">RUN \cp -pf /usr/share/zoneinfo/Japan /etc/localtime </code></pre> <h3 id="php.ini"><a href="#php.ini">php.ini</a></h3> <pre><code class="ini">[Date] ; Defines the default timezone used by the date functions ; http://php.net/date.timezone ;date.timezone = date.timezone = 'Asia/Tokyo' </code></pre> <h3 id="my.cnf 等"><a href="#my.cnf+%E7%AD%89">my.cnf 等</a></h3> <p>MySQL 側では特に指定なし。</p> <h2 id="変更"><a href="#%E5%A4%89%E6%9B%B4">変更</a></h2> <p>これらの設定を次のようにしました。</p> <h3 id="Dockerfile (共通)"><a href="#Dockerfile+%28%E5%85%B1%E9%80%9A%29">Dockerfile (共通)</a></h3> <pre><code class="dockerfile">RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux \ && dnf -y update && dnf -y install \ # 略 # timezone glibc-locale-source \ # locale, timezone && localedef -f UTF-8 -i ja_JP ja_JP.UTF-8 \ && ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime # local & timezone ENV LANG="ja_JP UTF-8" \ LANGUAGE="ja_JP:ja" \ LC_ALL="ja_JP.UTF-8" \ TZ="Asia/Tokyo" </code></pre> <p>やり方を大幅に変更。強制コピーで localtime ファイルを上書きするのではなく、段取りを踏んで設定するようにしました。</p> <h3 id="php.ini"><a href="#php.ini">php.ini</a></h3> <pre><code class="ini">[Date] ; Defines the default timezone used by the date functions ; http://php.net/date.timezone ;date.timezone = date.timezone = 'Asia/Tokyo' </code></pre> <p>変わらず。</p> <h3 id="my.cnf 等"><a href="#my.cnf+%E7%AD%89">my.cnf 等</a></h3> <p>こちらも変わらず。</p> <h3 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h3> <p><a href="https://crieit.now.sh/upload_images/d64288e5b382b9fe2564274e8237a20c6212500befe90.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d64288e5b382b9fe2564274e8237a20c6212500befe90.jpg?mw=700" alt="新しい設定方法で投稿した場合の挙動" /></a></p> <p>新しい方法で設定した Docker 環境上に先程と同様 WordPress をインストールして記事を投稿すると、無事投稿が公開されました。</p> <p>このとき、 <code>wp_posts</code> のテーブルを直接見ると、 <code>post_date</code>, <code>post_modified</code> は同じ日時、 <code>post_date_gmt</code>, <code>post_modified_gmt</code> は -9時間の日時になっていました。</p> <p>やはりここがおかしかったようです。</p> <h2 id="続・検証"><a href="#%E7%B6%9A%E3%83%BB%E6%A4%9C%E8%A8%BC">続・検証</a></h2> <p>さて、それではこの時刻の狂いはどこに起因しているのでしょうか。</p> <p><a href="https://crieit.now.sh/upload_images/67aea8dcc499e2cd3be8f9a4698e5de1621250140823c.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/67aea8dcc499e2cd3be8f9a4698e5de1621250140823c.jpg?mw=700" alt="Webサーバのみ新しい設定方法で投稿した場合の挙動" /></a></p> <p>Webサーバの Dockerコンテナ のみ今回の新しい方法で localtime を設定した場合。OKですね。</p> <p><a href="https://crieit.now.sh/upload_images/35de93c1057c97f109f3c8e152c7ec526212501e5c450.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/35de93c1057c97f109f3c8e152c7ec526212501e5c450.jpg?mw=700" alt="DBサーバのみ新しい設定方法で投稿した場合の挙動" /></a></p> <p>DBサーバの Dockerコンテナ のみ今回の新しい方法で localtime を設定した場合。NGです。</p> <p>ということで、原因は Webサーバ 側にありそうです。</p> <h3 id="Webサーバでの date"><a href="#Web%E3%82%B5%E3%83%BC%E3%83%90%E3%81%A7%E3%81%AE+date">Webサーバでの date</a></h3> <p>ただ、 Webサーバ のコンテナで <code>date</code> コマンドを打っても日時は正しい日時が返ってくるのですよね。</p> <h4 id="設定方法が未変更の状態の Webサーバ コンテナ"><a href="#%E8%A8%AD%E5%AE%9A%E6%96%B9%E6%B3%95%E3%81%8C%E6%9C%AA%E5%A4%89%E6%9B%B4%E3%81%AE%E7%8A%B6%E6%85%8B%E3%81%AE+Web%E3%82%B5%E3%83%BC%E3%83%90+%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A">設定方法が未変更の状態の Webサーバ コンテナ</a></h4> <pre><code class="bash"># php -r 'echo date_default_timezone_get() . PHP_EOL; echo date(&quot;y-m-d H:i:s&quot;) . PHP_EOL;' Asia/Tokyo 22-02-13 19:06:21 # date Thu Feb 13 19:06:28 JST 2022 </code></pre> <h4 id="設定方法を変更した状態の Webサーバ コンテナ"><a href="#%E8%A8%AD%E5%AE%9A%E6%96%B9%E6%B3%95%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%9F%E7%8A%B6%E6%85%8B%E3%81%AE+Web%E3%82%B5%E3%83%BC%E3%83%90+%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A">設定方法を変更した状態の Webサーバ コンテナ</a></h4> <pre><code class="bash"># php -r 'echo date_default_timezone_get() . PHP_EOL; echo date(&quot;y-m-d H:i:s&quot;) . PHP_EOL;' Asia/Tokyo 22-02-13 19:16:50 # date 2022年 2月 13日 日曜日 19:16:56 JST </code></pre> <p>ご覧の通り時刻は正常……ってあれ、確かに日時は正常ですが、出力フォーマットが異なっています。</p> <p>どうやら今までの設定変更ではやはり不完全な部分があるようです。どこが、というところまでは追い切れていないですが、ここまでの検証で確かに今までの設定方法では宜しくないであろう、ということが断片的に表われているので、ここは大人しく新しい設定方法に変更していった方が良さそうです。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/rururu_kenken/items/972314402d588e073d40">Dockerコンテナのタイムゾーン変更方法 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/jeffi7/items/9d4c85cf049644f711b8">Docker で \/etc\/localtime をホストに volume マウントしてハマった話 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://saito-shion-technology.hatenablog.jp/entry/2020/02/27/115040">Docker : コンテナのタイムゾーンを変更する - saito-shion technology diary</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/polarbear08/items/e5c00869c7566db5f7b8">CentOS7・CentOS8のDockerコンテナの日本語化および日本時間設定 - Qiita</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17862 2021-12-17T10:48:49+09:00 2021-12-17T19:31:20+09:00 https://crieit.net/posts/WordPress-Web 使ってないWordPressからWebサイトをグチャグチャにされかけた件 <h2 id="概要"><a href="#%E6%A6%82%E8%A6%81">概要</a></h2> <p>昔試しにデプロイしてそのまま忘れていたWordPressに不正アクセスされ、自分の運営するWebサイトをグチャグチャにされかけた。</p> <h2 id="発端"><a href="#%E7%99%BA%E7%AB%AF">発端</a></h2> <p>ある日突然、自サイトのトップページが真っ白になり何も表示されなくなった。<br /> 自サイトはPukiwikiメインで動いており、この時は「またPHPか」と思っていた。</p> <h2 id="最初にした対処(勘違い)"><a href="#%E6%9C%80%E5%88%9D%E3%81%AB%E3%81%97%E3%81%9F%E5%AF%BE%E5%87%A6%28%E5%8B%98%E9%81%95%E3%81%84%29">最初にした対処(勘違い)</a></h2> <p>レンタルサーバーの現在のPHP環境を見たら、PHP5ではなくPHP7を推奨していた。ここで自分は、今のサーバーはPHP5の扱いが終了したのだと思った。<br /> ダメ元でPukiwikiの最新版を取りに行ったら、PHP7対応版が公開されており、その一式をダウンロードして、とりあえずまるごと差し替える事で無事Pukiwikiが表示された。<br /> 細かい設定や独自に入れたPluginは全部上書き初期化されてしまったが、もうサイトの維持に疲れていたので、制作物とかはGithubに移動し、可能な限り縮小していくことを決めた。なのでテキストとかが取り出せればいいやと思っていた。<br /> また、この機会にShift-JISとEUC-JPとUTF8が混在していたのを全部UTF8に統一し、SSL/TLS対応もした。</p> <h2 id="疑問"><a href="#%E7%96%91%E5%95%8F">疑問</a></h2> <p>デフォルトのPukiwikiのページ下部には使用しているPHPのバージョンが出るのだが、「PHP 5.6.24」と表示されていた。「PHP7になったから動かなくなったのでは?」と疑問に思ったが、まあ今動いているからいいかと思った。<br /> また、今回の更新作業のためにWebサイトのデータ一式をダウンロードしたが、いくつかのphpファイルがウイルス判定されていた。なんでphpファイルがウイルス判定?と思い、エディタで開こうとしてもWindowsが開く事を拒否する。誤検知だと思ってそのままにしていた。</p> <h2 id="確実におかしい事に気付く"><a href="#%E7%A2%BA%E5%AE%9F%E3%81%AB%E3%81%8A%E3%81%8B%E3%81%97%E3%81%84%E4%BA%8B%E3%81%AB%E6%B0%97%E4%BB%98%E3%81%8F">確実におかしい事に気付く</a></h2> <p>20年間付き合ってきた自サイトなので、中身はかなりゴチャゴチャしていて、整理はかなり難しい状況だったが、それでも明らかに使ってないファイルは消していこうとしていた。<br /> その中で、そんな名前のファイル自分は作らないぞ!?といったファイルや、100k超えのphpファイルが目についた。<br /> レンタルサーバーの提供する機能でブラウザ上でサーバー内のファイルを直接扱えるサービスがあるので、それでphpファイルを開いたら、デコードされた読めない内容が展開されたので、やっと何かがおかしいと気づいた。</p> <h2 id="調査"><a href="#%E8%AA%BF%E6%9F%BB">調査</a></h2> <p>様子がおかしいphpファイルはいくつかあったが、「wp-」で始まるファイルが多かったので調べたところ、WordPressのファイルだった。WordPressなんか入れた覚えないぞ・・と思ったが、サイト更新を楽にしようといろいろ試していた時は確かにあり、もしかしたらWordPressも入れてみた事があるのかもしれない・・・<br /> そして、WordPressはかなり攻撃を受けやすく、常に最新版に更新する必要があるとの事。WordPressに不正にログインされると何が起こるのかも、調べた結果と自分の状況が一致していた。<br /> 自分が入れたWordPressはどこからもリンクされていないが、おそらくURL決め打ち+脆弱性を突かれてアクセスされ、各所にファイルを作られたり、書き換えられたりしていた。</p> <h2 id="被害"><a href="#%E8%A2%AB%E5%AE%B3">被害</a></h2> <ul> <li>index.phpを書き換えられていた<br /> これでTOPページが真っ白になっていた。数十回に1回は詐欺サイトに飛ばされるタイプもあるとの事で、何回かリロードしてたら飛ばされたのかもしれない。</li> <li>.htaccessを書き換えられていた<br /> ガバガバにされてた。</li> <li>知らないphpファイルを各所に作られていた<br /> デコードされていてすぐに読めないものが大半。中身が空のものもあった。</li> </ul> <p>サーバー運営からは何も怒られていないので、大量にメールを誰かに送るとかはしていないと思う。</p> <h2 id="対処"><a href="#%E5%AF%BE%E5%87%A6">対処</a></h2> <ul> <li>勘違い対処で最新のPukiwikiと差し替えた事で、Pukiwiki関係のphpは修正された状態。</li> <li>WordPress関係のファイルを全部削除した。そもそもWordPressは入れただけで全く使ってなかったので、まるごと消すだけでよかったのは楽。</li> <li>.htaccessを修正。古いWebサイト一式のバックアップがあったので、それを使用した。</li> <li>phpファイルを検索し、怪しいファイルを全部削除した。</li> <li>その他、使ってないCGIも全部削除。</li> </ul> <h2 id="最後に"><a href="#%E6%9C%80%E5%BE%8C%E3%81%AB">最後に</a></h2> <p>今回の件や、php更新されてスクリプトが動かなくなる等の突然の悲しい作業が降って湧くのがきつく、最近は個人でのWebサイト運営に限界を感じる事が多くなってきています。<br /> Webサイト運営は手段であってこれ自体が趣味とかではないので、自分で決めてない作業に時間をとられたくないです。まあこの機会に制作物をgithubにまとめられたのはよかったと思います。<br /> いまはPukiwiki入れてますが、これすらもやめて、最終的に静的なHTMLを置くだけにしたくなっています。</p> uskz tag:crieit.net,2005:PublicArticle/17822 2021-12-05T12:17:15+09:00 2021-12-05T12:17:15+09:00 https://crieit.net/posts/wordpress-plugin-modify-slug-format-20211205 (WordPress) 記事投稿時にスラッグを機械的に投稿IDと投稿日付で整形するプラグイン <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>記事投稿時にスラッグを機械的に投稿IDと投稿日付で整形するプラグインが欲しくなったので作りました。</p> <p>条件としては以下の通り。</p> <ul> <li>基本的に1日1記事しか作成しない運用の WordPress サイト</li> <li>通常の投稿のみ適用する</li> <li>フォーマットは <code><post_type>-<post_id>-yyyy-mm-dd</code> とする <ul> <li>month, date は0詰めあり</li> <li>通常の投稿のみなので <code><post_type></code> は不要ではありますが、念のためつけておきます</li> </ul></li> </ul> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <pre><code class="php"><?php if( is_admin() ) { function my_modifiy_slug( $slug, $post_ID, $post_status, $post_type ) { if ( $post_type === 'post' ) { $slug = utf8_uri_encode( $post_type ) . '-' . $post_ID . '-' . date('Y-m-d'); } return $slug; } add_filter( 'wp_unique_post_slug', 'my_modifiy_slug', 10, 4 ); } </code></pre> <p>これで意図した挙動になることを確認。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://takayakondo.com/wordpress-auto-post-slug/">WordPressの投稿の日本語スラッグを自動的に英字スラッグに書き換える方法 - Offise Kondo</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://eng-entrance.com/php-date">PHPでの日付取得方法! 詳細まとめました</a></li> </ul> <h3 id="(余談) 「スラッグ」の語源は?"><a href="#%28%E4%BD%99%E8%AB%87%29+%E3%80%8C%E3%82%B9%E3%83%A9%E3%83%83%E3%82%B0%E3%80%8D%E3%81%AE%E8%AA%9E%E6%BA%90%E3%81%AF%EF%BC%9F">(余談) 「スラッグ」の語源は?</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://ohbarye.hatenablog.jp/entry/2017/07/04/234737">ソフトウェアの世界での slug / スラグ / スラッグの意味 - valid,invalid</a> <ul> <li>原文: <a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/4230846/what-is-the-etymology-of-slug">url - What is the etymology of 'slug'? - Stack Overflow</a></li> <li>slug line: <a target="_blank" rel="nofollow noopener" href="https://en.wikipedia.org/wiki/Screenplay#Format_and_style">Screenplay - Wikipedia</a></li> </ul></li> <li><a target="_blank" rel="nofollow noopener" href="https://whatswp.t-haku.com/whats-wp/permalink-and-slug/">パーマリンクとスラッグ | What's wp ?</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%A9%E3%82%B0">スラグ - Wikipedia</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17794 2021-11-29T16:12:33+09:00 2021-11-29T16:12:33+09:00 https://crieit.net/posts/home-url-get-template-directory-uri-get-stylesheet-directory-uri-WordPress home_url、get_template_directory_uri、get_stylesheet_directory_uriの違い【WordPress勉強備忘録】 <h2 id="home_url"><a href="#home_url">home_url</a></h2> <p>サイトのURL<br /> 例)https://example.com/</p> <h2 id="get_template_directory_uri"><a href="#get_template_directory_uri">get_template_directory_uri</a></h2> <p>親テーマディレクトリまでのURL<br /> 例)https://example.com/wp-content/themes/xxx/</p> <h2 id="get_stylesheet_directory_uri"><a href="#get_stylesheet_directory_uri">get_stylesheet_directory_uri</a></h2> <p>子テーマディレクトリまでのURL<br /> 例)https://example.com/wp-content/themes/xxx-child/</p> みみみみみ tag:crieit.net,2005:PublicArticle/17769 2021-11-18T00:03:13+09:00 2021-11-18T00:03:13+09:00 https://crieit.net/posts/output-var-dump-as-log-in-wordpress-20211118 WordPress で処理の結果をログに出力したい <p>WordPress のアクションフックで処理している内容を覗き見たいと考えました。</p> <p>ただし、記事を公開した際の <code>publish_post</code> で処理される内容を見たい……というようなケースでは、画面出力するとブロックエディタの画面に出力されることになり、表示が大幅に崩れたりして悪影響が出ることが想定されます。</p> <p>そこで、ログファイルにこっそり吐き出す方法を採ることにしました。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <pre><code class="php"><?php ob_start(); var_dump($post); var_dump($term); $result = ob_get_clean(); $log_message = sprintf("%s:%s\n", date_i18n('Y-m-d H:i:s'), htmlspecialchars($result, ENT_QUOTES, 'UTF-8')); error_log($log_message, 3, __DIR__ . '/debug_log.txt'); </code></pre> <p>サンプルですが、上述のようなイメージ。 <code>var_dump()</code> を文字列として出力するためのバッファリングを使用しつつ、その文字列に日付等を付与してログの出力のような体裁を整えることにしました。</p> <p>そしてその内容をPHP標準の <code>error_log()</code>関数 でファイルに出力します。</p> <p>なお、このコードを使用するに当たり WordPress の <code>wp-config.php</code> で次のようにデバッグモードとデバッグのログ出力を有効にしておきます。</p> <pre><code class="php">//define('WP_DEBUG', false); define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); </code></pre> <p>これでファイルに変数の中身が出力されるので確認できます。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://futureys.tokyo/how-to-log-and-its-mechanism-in-wordpress/">WordPress のログ出力方法と仕組み | ultra code</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.sofplant.com/blog/tech/wordpress-log-output/">WordPressの本番運用中にログ出力をする方法 | ソフプラント</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://tm.root-n.com/programming:php:etc:error_log">PHP :: error_log() 関数を利用したロギング [Tipsというかメモ]</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17764 2021-11-17T00:01:52+09:00 2021-11-17T00:01:52+09:00 https://crieit.net/posts/wordpress-survey-hook-suffix-20211117 (WordPress) 自作プラグインの設定ページの $hook_suffix を調べる <p>WordPress で自作プラグインの設定画面にアクションフックを引っかけたかったのですが、引っかからなかったので <code>$hook_suffix</code> について調べました。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>自作プラグインの設定画面で処理を走らせるため、アクションフックを引っかけようとしました。</p> <p>イメージとしては以下の流れを想定しています。</p> <ol> <li>自作プラグインの設定画面(サブメニュー)でボタンをクリック</li> <li>POSTパラメータを投げた先のページで処理を実行 (アクションフック) <ul> <li>ただしDBにオプションの値としてデータを保存したいわけではない</li> <li>そのため、 <code>options.php</code> 経由はしないと想定</li> </ul></li> <li>2.より、1.と同じ自作プラグインの設定画面(サブメニュー)に2.POSTパラメータがセットされたリクエストがあった場合は処理をする、としたい</li> </ol> <p>そこで</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/hooks/admin_head-hook_suffix/">admin_head-{$hook_suffix} | Hook | WordPress Developer Resources</a></li> </ul> <p>このアクションフックを使おうとしました。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://wp-doctor.jp/blog/2020/04/01/%E3%83%AF%E3%83%BC%E3%83%89%E3%83%97%E3%83%AC%E3%82%B9%E3%81%AE%E7%AE%A1%E7%90%86%E7%94%BB%E9%9D%A2%E3%81%AB%E9%81%A9%E5%BF%9C%E3%81%99%E3%82%8B%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB%E3%82%B7%E3%83%BC/">ワードプレスの管理画面に適応するスタイルシートを読み込む方法 | ワードプレスドクター</a></li> </ul> <p><code>$hook_suffix</code> は該当ページの名前、とのこと。</p> <p>今回は <code>wp-admin/admin.php?myplugin_settings_submenu_page</code> というようなURLだったので安直に以下のように書いてみました。</p> <pre><code class="php">function hoge() { echo "HOGEEEEEEEE"; } add_action( 'admin_head-admin.php?myplugin_settings_submenu_page', 'hoge' ); </code></pre> <p>が、実行されず。そもそもGETパラメータ付きの名前は許容されているのか、というところも含めて調べることにしました。</p> <h2 id="調査"><a href="#%E8%AA%BF%E6%9F%BB">調査</a></h2> <p>調査した結果、やはり <code>$hook_suffix</code> は異なる値でした。</p> <p>今回のように自作プラグインで設定画面を設けている場合は、次のようなルールになります。</p> <h3 id="独自追加プラグイントップメニュー"><a href="#%E7%8B%AC%E8%87%AA%E8%BF%BD%E5%8A%A0%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%83%88%E3%83%83%E3%83%97%E3%83%A1%E3%83%8B%E3%83%A5%E3%83%BC">独自追加プラグイントップメニュー</a></h3> <pre><code>toplevel_page_<メニュースラッグ> </code></pre> <h3 id="独自追加プラグインサブメニュー"><a href="#%E7%8B%AC%E8%87%AA%E8%BF%BD%E5%8A%A0%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%B5%E3%83%96%E3%83%A1%E3%83%8B%E3%83%A5%E3%83%BC">独自追加プラグインサブメニュー</a></h3> <pre><code><トップレベルのメニュー名を sanitize_title() した値>_page_<メニュースラッグ名> </code></pre> <p>今回は後者のパターンですね。そして地味に厄介なのは「トップレベルのメニュー名を <code>sanitize_title()</code> した値」という部分。メニュー名はサイドバーのメニューの表示文字列なので、日本語でメニューを作ったプラグインの場合はサニタイズの結果パーセントエンコードされたものになります。</p> <p>かといってここを小文字の半角英数字とハイフンのみにすると名前がよく分からないことになってしまうので微妙なところ。</p> <p>例えば、メニュー名を「マイプラグイン 設定」として、サブメニューのスラッグを <code>myplugin_settings_submenu_page</code> としていた場合、 <code>$hook_suffix</code> は <code>%E3%83%9E%E3%82%A4%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3-%E8%A8%AD%E5%AE%9A_myplugin_settings_submenu_page</code> となります。</p> <p>そして、冒頭のアクションフックは次のようになります。</p> <pre><code class="php">function hoge() { echo "HOGEEEEEEEE"; } add_action( 'admin_head-%E3%83%9E%E3%82%A4%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3-%E8%A8%AD%E5%AE%9A_myplugin_settings_submenu_page', 'hoge' ); </code></pre> <p>気持ち的には小文字の半角英数字とハイフンのみにしたいところですが、止む無し。</p> <h2 id="検証用コード"><a href="#%E6%A4%9C%E8%A8%BC%E7%94%A8%E3%82%B3%E3%83%BC%E3%83%89">検証用コード</a></h2> <p><code>$hook_suffix</code> を通知バーに表示させるコード。分かりやすいのでこの表示のさせ方は良いですね。</p> <pre><code class="php">function current_pagehook() { global $hook_suffix; if( !current_user_can( 'manage_options') ) return; echo '<div class="updated"><p>hook_suffix : ' . $hook_suffix . '</p></div>'; } add_action( 'admin_notices', 'current_pagehook' ); </code></pre> <p>※<a target="_blank" rel="nofollow noopener" href="https://digipress.info/wordpress/tips/how-to-load-script-and-css-in-specific-admin-page/">特定のWordPress管理画面でのみ独自のCSSやJavaScriptを読み込む方法 | WordPress テーマ DigiPress</a>より</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/hooks/admin_head-hook_suffix/">admin_head-{$hook_suffix} | Hook | WordPress Developer Resources</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wp-doctor.jp/blog/2020/04/01/%E3%83%AF%E3%83%BC%E3%83%89%E3%83%97%E3%83%AC%E3%82%B9%E3%81%AE%E7%AE%A1%E7%90%86%E7%94%BB%E9%9D%A2%E3%81%AB%E9%81%A9%E5%BF%9C%E3%81%99%E3%82%8B%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB%E3%82%B7%E3%83%BC/">ワードプレスの管理画面に適応するスタイルシートを読み込む方法 | ワードプレスドクター</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://plugin.gritt.jp/customize-topic/hook_suffix/">$hook_suffix | WordPressのプラグインを作ろう</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://free-leaf.org/tomorrow/wordpres-enqueue-srcipt-guide/">WordPressの管理画面に独自のスクリプトやスタイルを読み込ませる方法まとめ | 明日のための記録</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://digipress.info/wordpress/tips/how-to-load-script-and-css-in-specific-admin-page/">特定のWordPress管理画面でのみ独自のCSSやJavaScriptを読み込む方法 | WordPress テーマ DigiPress</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://elearn.jp/wpman/function/add_submenu_page.html">add_submenu_page - WordPress私的マニュアル</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://elearn.jp/wpman/function/add_menu_page.html">add_menu_page - WordPress私的マニュアル</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17762 2021-11-15T00:07:31+09:00 2021-11-15T00:07:31+09:00 https://crieit.net/posts/forbid-link-direct-image-in-wordpress-20211115 WordPress で画像の直リンクを禁止する <p>表題の通り、 WordPress で画像の直リンクを禁止する方法についてメモ。</p> <p>ページ内とかであれば WordPress の処理である程度どうにかできますが、直接参照されると WordPress としての処理は走らないので効かないのですよね……。</p> <h2 id="対処"><a href="#%E5%AF%BE%E5%87%A6">対処</a></h2> <pre><code class="htaccess"><IfModule mod_rewrite.c> RewriteEngine on RewriteCond %{HTTP_REFERER} !^http(s)?://example\.com [NC] RewriteRule \.(jpg|png|gif|wepb)$ - [F] </IfModule> </code></pre> <p>対処としては <code>.htaccess</code> を記述することで抑制できます。</p> <p><code>RewriteEngine on</code> でリダイレクトを有効にして、 <code>RewriteCond %{HTTP_REFERER} !^http(s)?://example\.com [NC]</code> (実際は <code>example.com</code> ではなく実際のサイトのドメイン) で自サイトは許可、それ以外は禁止とします。</p> <p>最後に <code>RewriteRule \.(jpg|png|gif|wepb)$ - [F]</code> で画像に該当するファイルの拡張子の場合は参照できなくしてしまう、と。</p> <p>これである程度は抑制できるのではないでしょうか。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://webllica.com/disable-direct-access-to-image-by-htaccess/">WordPress でアップロードした画像への直リンクを禁止する方法</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://mori.moripower.jp/eqwip/wordpress/1296.html">Wordpressの画像等の直リンクを禁止する ? GettingOUT</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://fukuro-press.com/direct-link-ban-with-htaccess/">画像などの直リンクを禁止してブログのパクリを防ぐ方法 | Fukuro Press</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://emmalanglab.com/prevent-hotlinking/">画像への直リンク防止策 - kotonoha</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17742 2021-11-06T00:02:35+09:00 2021-11-06T00:02:35+09:00 https://crieit.net/posts/wordpress-restapi-404notfound-permalink-basic-20211106 WordPress の REST API に wp-json/ でアクセスしたら 404 not Found. エラー <p>非常に簡単な内容ですが、表題の通り WordPress の REST API に <code>wp-json/wp/v2/</code> でアクセスしたら 404 Not found. になり戸惑ったのでメモ。</p> <h2 id="現象"><a href="#%E7%8F%BE%E8%B1%A1">現象</a></h2> <p><code>https://example.com/wp-json/wp/v2/</code> で REST API にアクセスしたら、 404 Not found. エラーになってしまいました。</p> <p>プラグインやテーマの <code>functions.php</code> で REST API をオフにするようなものがないか探しましたが該当するものなし。</p> <p>しかも、ソースコードを表示すると <code>https://example.com/index.php?rest_route=/</code> という記述があり、その通り <code>https://example.com/index.php?rest_route=/wp/v2/</code> にアクセスすると REST API の結果が表示されました。</p> <p><code>index.php?rest_route=/</code> とは……。</p> <h2 id="原因"><a href="#%E5%8E%9F%E5%9B%A0">原因</a></h2> <p>パーマリンク設定が「基本」の場合、 <code>wp-json/wp/v2/</code> は 404 Not Found. になるとのこと。</p> <p>そして、 <code>index.php?rest_route=/</code> もれっきとした REST API にアクセスするURIでした。パーマリンク設定が「基本」の場合はこちらでしかアクセスできないようです。</p> <p>普段「基本」以外にしてしまうので、なかなかお目にかかる機会がありませんでした。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/DaisukeNishi/items/18332bf947c3b6bb88a1">Wordpress | WP REST API | /wp-json が見えない(無効化されている) - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://takeshit.info/404_not_found_occurs_when_using_wp_rest_api_with_wordpress/">wordpressでWP REST API使用時に404 Not Foundが出たときの対応 | takeshit.info</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17740 2021-11-04T23:46:16+09:00 2021-11-04T23:46:16+09:00 https://crieit.net/posts/wordpress-switch-if-match-certain-terms-in-singlepage-20211104 WordPress のシングルページで特定のタクソノミーの場合のみ表示を変えたい <p>表題の通り、WordPress のシングルページで特定のタクソノミーの場合のみ表示を変えたいケースのスニペットをメモ。</p> <p>さっくり終わらせたかったのでとりあえずできたものを貼っておきます。</p> <h2 id="スニペット"><a href="#%E3%82%B9%E3%83%8B%E3%83%9A%E3%83%83%E3%83%88">スニペット</a></h2> <pre><code class="php"><?php if( in_array( 'AN_TAXONOMY_SLUG', array_column( get_the_terms( $post->ID, 'AN_TERMS_KEY' ), 'slug' ) ) ) { ?> <!-- Some codes --> <? } ?> </code></pre> <p>今回はこれでfix。</p> <h2 id="サンプルケース"><a href="#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B1%E3%83%BC%E3%82%B9">サンプルケース</a></h2> <p>例えば「cms」というカスタムタクソノミーに「wordpress」,「basercms」,「concretecms」といったタクソノミー、およびそのスラッグが存在した場合に「wordpress」のみ if内 の内容を表示させたい場合は次のようなイメージ。</p> <pre><code class="php"><?php if( in_array( 'wordpress', array_column( get_the_terms( $post->ID, 'cms' ), 'slug' ) ) ) { ?> <!-- Some codes --> <? } ?> </code></pre> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="カスタムタクソノミーの表示"><a href="#%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%82%BF%E3%82%AF%E3%82%BD%E3%83%8E%E3%83%9F%E3%83%BC%E3%81%AE%E8%A1%A8%E7%A4%BA">カスタムタクソノミーの表示</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.web-myoko.net/blog/wordpress/custom-post-type-term-name-single-xx-php-wordpress/">カスタム投稿タイプのターム名を[single-XX.php]に表示する方法【WordPress】 | 妙高Web屋</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_the_terms">関数リファレンス/get the terms - WordPress Codex 日本語版</a></li> </ul> <h3 id="PHP, 連想配列の中の特定のキーの値で判定"><a href="#PHP%2C+%E9%80%A3%E6%83%B3%E9%85%8D%E5%88%97%E3%81%AE%E4%B8%AD%E3%81%AE%E7%89%B9%E5%AE%9A%E3%81%AE%E3%82%AD%E3%83%BC%E3%81%AE%E5%80%A4%E3%81%A7%E5%88%A4%E5%AE%9A">PHP, 連想配列の中の特定のキーの値で判定</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://1-notes.com/php-in-array/">PHP | in_array()で配列・多次元配列のデータから値が存在するか判別する方法 | ONE NOTES</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17719 2021-10-23T14:21:14+09:00 2021-10-23T14:21:14+09:00 https://crieit.net/posts/wp-cli-yikes-error2-20211023 WP CLI で Error: YIKES! It looks like you're running this as root. エラー <p>WP CLI で Error: YIKES! It looks like you're running this as root. ... というエラーが発生したのでメモ。</p> <pre><code class="bash"># wp help Error: YIKES! It looks like you're running this as root. You probably meant to run this as the user that your WordPress installation exists under. If you REALLY mean to run this as root, we won't stop you, but just bear in mind that any code on this site will then have full control of your server, making it quite DANGEROUS. If you'd like to continue as root, please run this again, adding this flag: --allow-root If you'd like to run it as the user that this site is under, you can run the following to become the respective user: sudo -u USER -i -- wp <command> </code></pre> <p>原因としては root で実行しようとしているけど大丈夫?他のユーザで <code>sudo</code> で実行してね?ということのようです。</p> <p>今回は Docker 環境の中で特に気にするものもないので安直に <code>--allow-root</code> で落ち着きました。</p> <h3 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://b.0218.jp/20150729230610.html">[WordPress] WP-CLI でエラーが出た時の対処方法</a></li> <li><a target="_blank" rel="nofollow noopener" href="http://memories.zal.jp/WP/wordpress/archives/478">WP-CLI自身をアップデートするコマンド wp cli update | サイト構築日記(WordPress編)</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17620 2021-08-28T00:05:38+09:00 2021-08-28T00:05:38+09:00 https://crieit.net/posts/static-page-embed-in-wordpress-20210827 静的ページで WordPress の埋め込みに対応できるかテスト <p>ふと気になったので静的ページで WordPress の埋め込みに対応できるかテストしてみることにしました。</p> <h2 id="調査"><a href="#%E8%AA%BF%E6%9F%BB">調査</a></h2> <p>調べてみたところ、 WordPress の埋め込みは <a target="_blank" rel="nofollow noopener" href="https://oembed.com/">oEmbed</a> の仕様をベースに独自カスタマイズが入った仕組みとなっている模様。</p> <p>必要なのは以下の3つ</p> <ul> <li>oEmbed の仕様に則ったデータ ( XML または JSON 、あるいは両方)</li> <li>ページ自体に上述のデータへの <code>link</code> タグ</li> <li>埋め込み時に表示する HTML</li> </ul> <h2 id="サンプルコード"><a href="#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89">サンプルコード</a></h2> <p>今回はひとまず以下のような構成に。</p> <pre><code>dist/ ├ css/ // 略 ├ embed/ // 埋め込み用のリソース │ ├ index.html // 埋め込み時に表示するHTML │ └ index.json // 埋め込みに必要なデータ │ ├ js/ // 略 └ index.html // 本来の閲覧者が実際に見るページ </code></pre> <h3 id="embed/index.json"><a href="#embed%2Findex.json">embed/index.json</a></h3> <p>まずはデータ。今回は JSON 形式を用意しました。</p> <pre><code class="json">{ "version": "1.0", "provider_name": "Embed Test Page", "provider_url": "https://embedtest.example.jp/", "author_name": "アルム=バンド", "title": "TopPage", "type": "rich", "width": 600, "height": 338, "html": "<blockquote><a href=\"https://embedtest.example.jp/index.html\">TopPage</a></blockquote>\n<script type=\"text/javascript\">\n<!--//--><![CDATA[//><!--\n\t\t/*! This file is auto-generated */\n\t\t!function(c,d){\"use strict\";var e=!1,n=!1;if(d.querySelector)if(c.addEventListener)e=!0;if(c.wp=c.wp||{},!c.wp.receiveEmbedMessage)if(c.wp.receiveEmbedMessage=function(e){var t=e.data;if(t)if(t.secret||t.message||t.value)if(!/[^a-zA-Z0-9]/.test(t.secret)){for(var r,a,i,s=d.querySelectorAll('iframe[data-secret=\"'+t.secret+'\"]'),n=d.querySelectorAll('blockquote[data-secret=\"'+t.secret+'\"]'),o=0;o<n.length;o++)n[o].style.display=\"none\";for(o=0;o<s.length;o++)if(r=s[o],e.source===r.contentWindow){if(r.removeAttribute(\"style\"),\"height\"===t.message){if(1e3<(i=parseInt(t.value,10)))i=1e3;else if(~~i<200)i=200;r.height=i}if(\"link\"===t.message)if(a=d.createElement(\"a\"),i=d.createElement(\"a\"),a.href=r.getAttribute(\"src\"),i.href=t.value,i.host===a.host)if(d.activeElement===r)c.top.location.href=t.value<span>}</span><span>}</span>},e)c.addEventListener(\"message\",c.wp.receiveEmbedMessage,!1),d.addEventListener(\"DOMContentLoaded\",t,!1),c.addEventListener(\"load\",t,!1);function t(){if(!n){n=!0;for(var e,t,r=-1!==navigator.appVersion.indexOf(\"MSIE 10\"),a=!!navigator.userAgent.match(/Trident.*rv:11\\./),i=d.querySelectorAll(\"iframe.wp-embedded-content\"),s=0;s<i.length;s++){if(!(e=i[s]).getAttribute(\"data-secret\"))t=Math.random().toString(36).substr(2,10),e.src+=\"#?secret=\"+t,e.setAttribute(\"data-secret\",t);if(r||a)(t=e.cloneNode(!0)).removeAttribute(\"security\"),e.parentNode.replaceChild(t,e)<span>}</span><span>}</span><span>}</span><span>}</span>(window,document);\n//--><!]]>\n</script><iframe sandbox=\"allow-scripts\" security=\"restricted\" src=\"https://embedtest.example.jp/embed/index.html\" width=\"600\" height=\"338\" title=\"&#8220;TopPage&#8221; &#8212; Embed Test Page\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"></iframe>" } </code></pre> <p>各種パラメータはもとより、 <code>html</code> の部分がボリューミーです。ベースは WordPress が出力している JSON の中身をベースにして、サイト名やページ名、URL等のパラメータ部分だけ必要に応じて書き換えたものとなります。</p> <p>WordPress の場合、 <code><blockquote>~</blockquote>~~<iframe>~</iframe></code> という形式になっていなければならないようです。また、 <code>blockquote</code>, <code>a</code>, <code>iframe</code> 以外は使用できない等の制約もあるようです。</p> <h3 id="index.html"><a href="#index.html">index.html</a></h3> <p>実際にビジターが閲覧するページでは上述のデータへのリンクを貼ります。</p> <pre><code class="html"><link rel="alternate" type="application/json+oembed" href="https://embedtest.example.jp/embed/index.json" /> </code></pre> <p>今回は JSON だけなので1行ですが、 XML の場合は XML 用の、両方用意した場合は2行になります。</p> <h3 id="embed/index.html"><a href="#embed%2Findex.html">embed/index.html</a></h3> <pre><code class="html"><!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title></title> <link rel="canonical" href=""> <style type="text/css"> /*! This file is auto-generated */ body,html{padding:0;margin:0}body{font-family:sans-serif}.screen-reader-text{border:0;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;word-wrap:normal!important}.dashicons{display:inline-block;width:20px;height:20px;background-color:transparent;background-repeat:no-repeat;background-size:20px;background-position:center;transition:background .1s ease-in;position:relative;top:5px}.dashicons-no{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2020%2020%27%3E%3Cpath%20d%3D%27M15.55%2013.7l-2.19%202.06-3.42-3.65-3.64%203.43-2.06-2.18%203.64-3.43-3.42-3.64%202.18-2.06%203.43%203.64%203.64-3.42%202.05%202.18-3.64%203.43z%27%20fill%3D%27%23fff%27%2F%3E%3C%2Fsvg%3E")}.dashicons-admin-comments{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2020%2020%27%3E%3Cpath%20d%3D%27M5%202h9q.82%200%201.41.59T16%204v7q0%20.82-.59%201.41T14%2013h-2l-5%205v-5H5q-.82%200-1.41-.59T3%2011V4q0-.82.59-1.41T5%202z%27%20fill%3D%27%2382878c%27%2F%3E%3C%2Fsvg%3E")}.wp-embed-comments a:hover .dashicons-admin-comments{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2020%2020%27%3E%3Cpath%20d%3D%27M5%202h9q.82%200%201.41.59T16%204v7q0%20.82-.59%201.41T14%2013h-2l-5%205v-5H5q-.82%200-1.41-.59T3%2011V4q0-.82.59-1.41T5%202z%27%20fill%3D%27%230073aa%27%2F%3E%3C%2Fsvg%3E")}.dashicons-share{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2020%2020%27%3E%3Cpath%20d%3D%27M14.5%2012q1.24%200%202.12.88T17.5%2015t-.88%202.12-2.12.88-2.12-.88T11.5%2015q0-.34.09-.69l-4.38-2.3Q6.32%2013%205%2013q-1.24%200-2.12-.88T2%2010t.88-2.12T5%207q1.3%200%202.21.99l4.38-2.3q-.09-.35-.09-.69%200-1.24.88-2.12T14.5%202t2.12.88T17.5%205t-.88%202.12T14.5%208q-1.3%200-2.21-.99l-4.38%202.3Q8%209.66%208%2010t-.09.69l4.38%202.3q.89-.99%202.21-.99z%27%20fill%3D%27%2382878c%27%2F%3E%3C%2Fsvg%3E");display:none}.js .dashicons-share{display:inline-block}.wp-embed-share-dialog-open:hover .dashicons-share{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2020%2020%27%3E%3Cpath%20d%3D%27M14.5%2012q1.24%200%202.12.88T17.5%2015t-.88%202.12-2.12.88-2.12-.88T11.5%2015q0-.34.09-.69l-4.38-2.3Q6.32%2013%205%2013q-1.24%200-2.12-.88T2%2010t.88-2.12T5%207q1.3%200%202.21.99l4.38-2.3q-.09-.35-.09-.69%200-1.24.88-2.12T14.5%202t2.12.88T17.5%205t-.88%202.12T14.5%208q-1.3%200-2.21-.99l-4.38%202.3Q8%209.66%208%2010t-.09.69l4.38%202.3q.89-.99%202.21-.99z%27%20fill%3D%27%230073aa%27%2F%3E%3C%2Fsvg%3E")}.wp-embed{padding:25px;font-size:14px;font-weight:400;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;line-height:1.5;color:#8c8f94;background:#fff;border:1px solid #dcdcde;box-shadow:0 1px 1px rgba(0,0,0,.05);overflow:auto;zoom:1}.wp-embed a{color:#8c8f94;text-decoration:none}.wp-embed a:hover{text-decoration:underline}.wp-embed-featured-image{margin-bottom:20px}.wp-embed-featured-image img{width:100%;height:auto;border:none}.wp-embed-featured-image.square{float:left;max-width:160px;margin-right:20px}.wp-embed p{margin:0}p.wp-embed-heading{margin:0 0 15px;font-weight:600;font-size:22px;line-height:1.3}.wp-embed-heading a{color:#2c3338}.wp-embed .wp-embed-more{color:#c3c4c7}.wp-embed-footer{display:table;width:100%;margin-top:30px}.wp-embed-site-icon{position:absolute;top:50%;left:0;transform:translateY(-50%);height:25px;width:25px;border:0}.wp-embed-site-title{font-weight:600;line-height:1.78571428}.wp-embed-site-title a{position:relative;display:inline-block;padding-left:35px}.wp-embed-site-title{display:table-cell}.wp-embed-excerpt p{white-space: nowrap;text-overflow: ellipsis;overflow: hidden;} </style> </head> <body> <div class="wp-embed"> <p class="wp-embed-heading"> <a href="https://embedtest.example.jp/index.html" target="_top">TopPage</a> </p> <div class="wp-embed-excerpt"> <p>静的サイトで WordPress の埋め込みに対応できるかどうかのテストです。仕様はある程度 oEmbed に則っている模様です。</p> </div> <div class="wp-embed-footer"> <div class="wp-embed-site-title"> <a href="https://embedtest.example.jp/" target="_top"> <span>Embed Test Page</span> </a> </div> </div> </div> <script type="text/javascript"> /*! This file is auto-generated */ !function(c,u){"use strict";var r,t,e,n=u.querySelector&&c.addEventListener,b=!1;function f(e,t){c.parent.postMessage({message:e,value:t,secret:r},"*")}function i(){if(!b){b=!0;var e,r=u.querySelector(".wp-embed-share-dialog"),t=u.querySelector(".wp-embed-share-dialog-open"),n=u.querySelector(".wp-embed-share-dialog-close"),i=u.querySelectorAll(".wp-embed-share-input"),a=u.querySelectorAll(".wp-embed-share-tab-button button"),o=u.querySelector(".wp-embed-featured-image img");if(i)for(e=0;e<i.length;e++)i[e].addEventListener("click",function(e){e.target.select()});if(t&&t.addEventListener("click",function(){r.className=r.className.replace("hidden",""),u.querySelector('.wp-embed-share-tab-button [aria-selected="true"]').focus()}),n&&n.addEventListener("click",function(){l()}),a)for(e=0;e<a.length;e++)a[e].addEventListener("click",s),a[e].addEventListener("keydown",d);u.addEventListener("keydown",function(e){var t;27===e.keyCode&&-1===r.className.indexOf("hidden")?l():9===e.keyCode&&(t=e,e=u.querySelector('.wp-embed-share-tab-button [aria-selected="true"]'),n!==t.target||t.shiftKey?e===t.target&&t.shiftKey&&(n.focus(),t.preventDefault()):(e.focus(),t.preventDefault()))},!1),c.self!==c.top&&(f("height",Math.ceil(u.body.getBoundingClientRect().height)),o&&o.addEventListener("load",function(){f("height",Math.ceil(u.body.getBoundingClientRect().height))}),u.addEventListener("click",function(e){var t=((t=e.target).hasAttribute("href")?t:t.parentElement).getAttribute("href");event.altKey||event.ctrlKey||event.metaKey||event.shiftKey||t&&(f("link",t),e.preventDefault())}))}function l(){r.className+=" hidden",u.querySelector(".wp-embed-share-dialog-open").focus()}function s(e){var t=u.querySelector('.wp-embed-share-tab-button [aria-selected="true"]');t.setAttribute("aria-selected","false"),u.querySelector("#"+t.getAttribute("aria-controls")).setAttribute("aria-hidden","true"),e.target.setAttribute("aria-selected","true"),u.querySelector("#"+e.target.getAttribute("aria-controls")).setAttribute("aria-hidden","false")}function d(e){var t,r=e.target,n=r.parentElement.previousElementSibling,i=r.parentElement.nextElementSibling;if(37===e.keyCode)t=n;else{if(39!==e.keyCode)return!1;t=i}(t="rtl"===u.documentElement.getAttribute("dir")?t===n?i:n:t)&&(t=t.firstElementChild,r.setAttribute("tabindex","-1"),r.setAttribute("aria-selected",!1),u.querySelector("#"+r.getAttribute("aria-controls")).setAttribute("aria-hidden","true"),t.setAttribute("tabindex","0"),t.setAttribute("aria-selected","true"),t.focus(),u.querySelector("#"+t.getAttribute("aria-controls")).setAttribute("aria-hidden","false"))<span>}</span><span>}</span>n&&(!function e(){c.self===c.top||r||(r=c.location.hash.replace(/.*secret=([\d\w]{10}).*/,"$1"),clearTimeout(t),t=setTimeout(function(){e()},100))}(),u.documentElement.className=u.documentElement.className.replace(/\bno-js\b/,"")+" js",u.addEventListener("DOMContentLoaded",i,!1),c.addEventListener("load",i,!1),c.addEventListener("resize",function(){c.self!==c.top&&(clearTimeout(e),e=setTimeout(function(){f("height",Math.ceil(u.body.getBoundingClientRect().height))},100))},!1))}(window,document); </script> </body> </html> </code></pre> <p>埋め込み時に表示される HTML 。こちらも基本的に WordPress のコードを持ってきて、共有オプションのところで dashicon を使っていたり WordPress 特有のリソースを使用している部分は削除してシンプルにしました。</p> <h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2> <p>これらをサーバにアップロードして、 WordPress で動作確認してみます。</p> <p><a href="https://crieit.now.sh/upload_images/eb3f2c07d10f13f3be8f4d8a5d1245286128fae0438de.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/eb3f2c07d10f13f3be8f4d8a5d1245286128fae0438de.jpg?mw=700" alt="記事作成画面で埋め込みブロックを選択" /></a></p> <p>記事作成画面で埋め込みブロックを選択します。</p> <p><a href="https://crieit.now.sh/upload_images/c2566ef2aaa0f49aa0444c7835e9f54c6128fef4ddd56.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c2566ef2aaa0f49aa0444c7835e9f54c6128fef4ddd56.jpg?mw=700" alt="URLを貼り付けると、埋め込みに成功した" /></a></p> <p>URLを貼り付けると、埋め込みに成功しました。大丈夫そう……と思ったのですが。</p> <p><a href="https://crieit.now.sh/upload_images/6bbbb4f8713f67f6246bcf1f14e76afd6128fefd6c2e0.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/6bbbb4f8713f67f6246bcf1f14e76afd6128fefd6c2e0.jpg?mw=700" alt="別の WordPress 環境だと失敗した" /></a></p> <p>別の WordPress 環境だと失敗してしまいました。こちらは使用しているテーマや PHP のバージョン等条件が諸々異なるので何とも言えませんが、環境によってできたりできなかったりすることがあるようです。</p> <p>……うーん、微妙。</p> <hr /> <p>検証した結果としては「最低限動くかどうか」というラインになってしまいました。</p> <p>また、埋め込みに対応するためにはパラメータを記述したデータ (XML or/and JSON) や専用の HTML が必要ということが検証して分かりました。</p> <p>これを静的サイトの各ページごとに生成するのは大変なので、仮に対応するならば何かしらの仕組みが必要となりそうです。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://ja.wordpress.org/support/article/embeds/">オブジェクトの埋め込み | WordPress.org 日本語</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://ayudante.jp/column/2020-04-02/11-00/">WordPressの「埋め込み」に非WordPressサイトで対応するための方法</a><br /> -<a target="_blank" rel="nofollow noopener" href="https://nixeneko.hatenablog.com/entry/2018/03/16/000000">静的なHTMLページをoEmbedで他サイトに埋め込み可能にする - にせねこメモ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://oembed.com/">oEmbed</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17069 2021-05-06T23:40:30+09:00 2021-05-06T23:40:30+09:00 https://crieit.net/posts/wordpress-admin-edit-list-per-page-20210506 WordPress 管理画面の一覧ページ当たりに表示される件数を増やす <p>WordPress の管理画面の中で、記事一覧ページとメディア一覧ページで1ページ当たりの表示される件数を増やす方法についてメモ。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <p>早速ですがコードは以下。テーマの <code>functions.php</code> に記述するかプラグインとしてインストールするかどちらでも。</p> <pre><code class="php"><?php // 投稿・固定ページ・カスタム投稿の一覧ページで有効 function variable_posts_per_page ($posts_per_page) { return 100; } add_filter('edit_posts_per_page', 'variable_posts_per_page', 10, 1); // メディア一覧ページで有効 function variable_attachment_per_page ($media_per_page) { return 100; } add_filter('upload_per_page', 'variable_attachment_per_page', 10, 1); </code></pre> <p>フィルターフックを2つほど追加することで実現できました。なお、上述の例では共に1ページ当たり100件とました。</p> <h2 id="備考"><a href="#%E5%82%99%E8%80%83">備考</a></h2> <p>上述のフィルターフックについていくつか気付いた点を書き留めておきます。</p> <ul> <li><strong>要微調整</strong> <ul> <li>スペックやによって、データの量などに応じては表示件数が50件でも一覧表示でエラーを起こすことがある</li> <li>300件では一括削除の際のGETリクエストのURIが長すぎて怒られることがある</li> </ul></li> <li><strong><code>edit_{$post_type}_per_page</code></strong> <ul> <li>ドキュメント(<a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/hooks/edit_post_type_per_page/">edit_{$post_type}_per_page | Hook | WordPress Developer Resources</a>)では <code>edit_post_per_page</code> の仲間として <code>edit_attachment_per_page</code>, <code>edit_post_per_page</code>, <code>edit_page_per_page</code> が例として挙げられています</li> <li>しかし、 <code>edit_posts_per_page</code> だけで投稿一覧ページだけでなく、固定ページ一覧ページやカスタム投稿一覧ページまで効力を発揮しました</li> <li>「固定ページだけ」のときは <code>edit_pages_per_page</code> 、といったように「のみ」という場合は使い分け、全体で構わない場合は <code>edit_posts_per_page</code> だけで良い、という感じの使い分けになりそうです</li> </ul></li> <li><strong>s</strong> <ul> <li>上述で <code>edit_attachment_per_page</code>, <code>edit_post_per_page</code>, <code>edit_page_per_page</code> を挙げましたし、ドキュメントやコアファイル(<a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/af4b043a693fc7178ef8a1fcd0402f14fba0a040/wp-admin/includes/post.php#L1170">WordPress/post.php at af4b043a693fc7178ef8a1fcd0402f14fba0a040 · WordPress/WordPress · GitHub</a>)でもそう書かれています</li> <li>しかし、実際は edit_attachment<strong>s</strong>_per_page, edit_post<strong>s</strong>_per_page でした。おそらく edit_page_per_page も edit_page<strong>s</strong>_per_page ではないかと思われます( <code>edit_pages_per_page</code> については未検証)</li> </ul></li> <li>メディア一覧ページ <ul> <li>上述で <code>edit_attachments_per_page</code> があったためこれが該当するのかと思いきや、効果なし</li> <li>メディア一覧ページのフィルターフックは <code>upload_per_page</code> でした</li> </ul></li> </ul> <p>特に <strong>s</strong> は気を付けないと見逃すので注意 (いずれも WordPress 5.7.1 で検証)。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://core.trac.wordpress.org/ticket/14135">#14135 (edit_posts_per_page) – WordPress Trac</a></li> </ul> <p>2010年の <code>edit_posts_per_page</code> と <code>edit_post_per_page</code> についての Trac がありましたが、議論されているコードは <code>wp-admin/includes/template.php</code> 内のもので今回の <code>wp-admin/includes/post.php</code>, <code>wp_edit_posts_query</code> とは別の場所のコードなので直接の関係はなさそうです。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://on-ze.com/archives/3484">【WordPress】管理画面の「投稿一覧」と「固定ページ一覧」の最大表示数を変更する方法。 - 株式会社オンズ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/hooks/edit_posts_per_page/">edit_posts_per_page | Hook | WordPress Developer Resources</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/hooks/edit_post_type_per_page/">edit_{$post_type}_per_page | Hook | WordPress Developer Resources</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/hooks/upload_per_page/">upload_per_page | Hook | WordPress Developer Resources</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/af4b043a693fc7178ef8a1fcd0402f14fba0a040/wp-admin/includes/post.php#L1170">WordPress/post.php at af4b043a693fc7178ef8a1fcd0402f14fba0a040 · WordPress/WordPress · GitHub</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16859 2021-04-18T17:11:54+09:00 2021-04-18T17:11:54+09:00 https://crieit.net/posts/wordpress-upload-failed-certain-excel-2-20210418 WordPress で一部の .xlsファイル だけがアップロードできない (解決) <p><a href="https://crieit.net/posts/wordpress-upload-failed-certain-excel-20210417">前回の続き</a>です。プラグインを作成しましたが、挙動が変わらなかったためさらに調査を続行しました。</p> <h2 id="結果"><a href="#%E7%B5%90%E6%9E%9C">結果</a></h2> <p>結果から記すと、プラグインを以下のように改修することで解決できました。</p> <pre><code class="php"><?php /* Plugin Name: MIMEuruwashii Description: PHP の MIME タイプ判定で特定の .xlsファイル が弾かれる現象への対策 Version: 0.0.3 Author: アルム=バンド */ /** * MIMEuruwashii : PHP の MIME タイプ判定で特定の .xlsファイル が弾かれる現象への対策 */ class MIMEuruwashii { /** * __construct : コンストラクタ * */ public function __construct() { add_filter( 'wp_check_filetype_and_ext', [ $this, 'Bibishii', ], 99, 3 ); } /** * Vivid : 追加する MIMEタイプ の定義 * * @return {Object} : 誤判定している MIME タイプを追記する * */ public static function Vivid(){ return [ [ 'xla|xls|xlt|xlw' => 'application/vnd.ms-office' ], [ 'xla|xls|xlt|xlw' => 'application/vnd.ms-excel' ], ]; } /** * Bibishii : フィルター追加 * * @param {Object} $check : MIMEタイプ の一覧のオブジェクト * @param {File} $file : チェック対象ファイル * @param {Object} $filename : チェック対象ファイルの名前 * * @return {Object} : 誤判定している MIME タイプを追記する * */ public function Bibishii ( $check, $file, $filename ) { if ( empty( $check['ext'] ) && empty( $check['type'] ) ) { foreach ( self::Vivid() as $mime ) { remove_filter( 'wp_check_filetype_and_ext', [ $this, 'Bibishii' ], 99 ); $mime_filter = function($mimes) use ($mime) { return array_merge($mimes, $mime); }; add_filter( 'upload_mimes', $mime_filter, 99 ); $check = wp_check_filetype_and_ext( $file, $filename, $mime ); remove_filter( 'upload_mimes', $mime_filter, 99 ); add_filter( 'wp_check_filetype_and_ext', [ $this, 'Bibishii' ], 99, 3 ); if ( ! empty( $check['ext'] ) || ! empty( $check['type'] ) ) { return $check; } } } return $check; } } // instantiate $ab_wp_plugin_mimeuruwashii = new MIMEuruwashii(); </code></pre> <p>元の原型ないですね。引っかけるアクションフックも <code>upload_mimes</code> から <code>wp_check_filetype_and_ext</code> に変わっています。</p> <h2 id="調査"><a href="#%E8%AA%BF%E6%9F%BB">調査</a></h2> <p>調査の過程を以下に記します。</p> <p>挙動が変わらなかった理由を <code>wp-includes/functions.php</code> を追いかけて調べて行きます。</p> <ul> <li>メソッド<code>wp_get_mime_types()</code> では拡張子と MIMEタイプ をそれぞれキー・値として持つ連想配列を返却しています</li> <li><code>wp_get_mime_types()</code> が呼ばれている場所を辿っていきます <ul> <li><code>get_allowed_mime_types()</code> はいくつかの処理( <code>.swf</code> や <code>.exe</code> の除外、ユーザ権限がない場合は <code>.html</code>, <code>.htm</code>, <code>.js</code> を除外)をした後に <code>upload_mimes</code> でチェックする拡張子と MIMEタイプ を登録</li> <li><code>do_enclose()</code> は動画関連のチェックのように見えます。しかし、ここで気になるコードを発見 <ul> <li>拡張子の方は <code>if ( preg_match( '!^(' . $exts . ')$!i', $extension ) )</code> で正規表現による判定を行っている (例えば <code>!^(xla|xls|xlt|xlw)$!i</code> のような形式)</li> <li>一方、 MIMEタイプ は <code>$type = $mime;</code> で代入しているだけ <ul> <li>しかも <code>break;</code> している</li> </ul></li> </ul></li> </ul></li> </ul> <p>上述のコードを見て、「ひょっとして複数の MIMEタイプ を想定していないのでは?」と思い至り、検索。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/toiee_kame/items/68ba792239dd548f010c">WordPressでファイルアップロードができない(m4b、m4a、vtt、key) - Qiita</a> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://wordpress.stackexchange.com/questions/323750/how-to-assign-multiple-file-mime-types-to-extension/323757#323757">uploads - How to assign multiple file-mime-types to extension? - WordPress Development Stack Exchange</a></li> </ul></li> </ul> <p>やはり<strong>1つの拡張子に対して複数の MIMEタイプ を持つ場合に対応していない</strong> (今回のケースで言うと「 <code>.xls</code>ファイル は <code>application/vnd.ms-excel</code> または <code>application/vnd.ms-office</code> を受け付ける」としたい) ようです。</p> <p>前回のように MIMEタイプ を付け足す方法は、 <code>.ai</code> や <code>.psd</code> のような、デフォルトの連想配列に拡張子がキーとして存在しない MIMEタイプ ならば良いと思います。しかし、今回の <code>.xls</code> のように既存の拡張子の場合は最後に付け足したキーまで評価されていなさそうです。</p> <p>そこで、上述記事のように前回の <code>upload_mimes</code> 時に判定する MIMEタイプ を付け足すのではなく、そもそものチェック処理の <code>wp_check_filetype_and_ext</code> を変更する、という方針に転換しました。</p> <p>その結果が冒頭のプラグインのコードとなります。</p> <h2 id="雑感"><a href="#%E9%9B%91%E6%84%9F">雑感</a></h2> <p>複数の MIMEタイプ が想定されていない作りということを初めて知って驚きました。</p> <p>上述の Stackoverflow でも取り上げられていますが、 <code>.svg</code> や <code>.zip</code> 等他にも類似のケースが考えられるので意外でした。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/toiee_kame/items/68ba792239dd548f010c">WordPressでファイルアップロードができない(m4b、m4a、vtt、key) - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wordpress.stackexchange.com/questions/323750/how-to-assign-multiple-file-mime-types-to-extension/323757#323757">uploads - How to assign multiple file-mime-types to extension? - WordPress Development Stack Exchange</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16854 2021-04-17T13:25:24+09:00 2021-04-17T13:25:24+09:00 https://crieit.net/posts/wordpress-upload-failed-certain-excel-20210417 WordPress で一部の .xlsファイル だけがアップロードできない (失敗) <p>WordPressサイト で一部の <code>.xls</code>ファイル だけがアップロードできない現象に遭遇したため、対処しました。</p> <h2 id="現象"><a href="#%E7%8F%BE%E8%B1%A1">現象</a></h2> <p>とある WordPressサイト で <code>.xls</code>ファイル をアップロードしようとすると以下のエラーが表示されました。</p> <blockquote> <p>“XXXXXXXXXXX.xls” のアップロードに失敗しました。</p> <p>このファイルタイプはセキュリティ上の理由から、許可されていません。</p> </blockquote> <p><a href="https://crieit.now.sh/upload_images/5764f085787c5d43f32c05e74f90c6d860782a40619c6.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5764f085787c5d43f32c05e74f90c6d860782a40619c6.jpg?mw=700" alt="「“XXXXXXXXXXX.xls” のアップロードに失敗しました。このファイルタイプはセキュリティ上の理由から、許可されていません。」のエラー" /></a></p> <p>しかも困ったことに、「Aの <code>.xls</code>ファイル はOKだが、Bの <code>.xls</code>ファイル はNG」というように、ファイルによって現象が分かれました。</p> <p>両方とも正常に開ける <code>.xls</code>ファイル ですし、見た目上は同じなのですが……。</p> <h2 id="対処1"><a href="#%E5%AF%BE%E5%87%A61">対処1</a></h2> <p>最初の対処として、 <code>wp-config.php</code> にファイルアップロードの際のフィルタリングをオフにする方法を試しました。</p> <pre><code class="php"><?php // 略 $table_prefix = 'wp_'; // 略 define('WP_DEBUG', false); define('ALLOW_UNFILTERED_UPLOADS', true); // この1行を追記 /* 編集が必要なのはここまでです ! WordPress でブログをお楽しみください。 */ // 略 </code></pre> <p>これでファイルアップロードを試したところ、2つの <code>.xls</code>ファイル 共にアップロードできるようになりました。</p> <p>ただし、この方法ですとファイルアップロードの際のフィルタ処理を全てオフにしてしまうため、セキュリティ的に好ましくありません。</p> <p>そこで、もう少し調査を続けることにしました。</p> <h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2> <p>上述の対処1より、ファイルアップロードの際のフィルタリング処理で引っかかっていることは分かりました。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://deep-space.blue/web/555">【WordPress】アップロード可能なファイル拡張子を増やす | deep-space.blue</a></li> </ul> <p>このフィルタリングは、拡張子と MIMEタイプ の判定をしているということが分かりました。</p> <p>ファイルとしては、 <code>wp-includes/functions.php</code> です。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-includes/functions.php">WordPress/functions.php at master · WordPress/WordPress · GitHub</a></li> </ul> <p>中身を見て、関数 <code>wp_get_mime_types()</code> で拡張子と MIMEタイプ の一覧を持っていることが分かります。</p> <p>この処理にアクションフックを引っかけることができれば良さそうです。</p> <h2 id="MIMEタイプ の確認"><a href="#MIME%E3%82%BF%E3%82%A4%E3%83%97+%E3%81%AE%E7%A2%BA%E8%AA%8D">MIMEタイプ の確認</a></h2> <p>次に、問題のファイルの MIMEタイプ を確認します。</p> <p>試しに、以下のような構成を作ります。</p> <pre><code> / ├ files/ │ ├ sample1.xls // ファイルアップロード OK の .xlsファイル │ └ sample2.xls // ファイルアップロード NG の .xlsファイル │ └ index.php </code></pre> <p>また、 <code>index.php</code> を</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://gray-code.com/php/get-kind-of-file/">ファイルの種類(MIMEタイプ)を確認する | GRAYCODE PHPプログラミング</a></li> </ul> <p>この記事を参考にして、以下のようにします。</p> <pre><code class="php"><?php $paths = [ './files/sample1.xls', './files/sample2.xls', ]; // finfoクラスを使う $finfo = new finfo(); foreach ($paths as $key => $value) { echo $value . ": "; echo $finfo->file( $value, FILEINFO_MIME_TYPE ); echo "<br><br>\n\n"; } </code></pre> <p>これを問題が発生している WordPressサイト と同じサーバにアップロードします。</p> <p><a href="https://crieit.now.sh/upload_images/9a54370caa79221d5ac33cdc67687e0360782a4e9f5f6.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9a54370caa79221d5ac33cdc67687e0360782a4e9f5f6.jpg?mw=700" alt="ファイルアップロード NG のファイルの MIMEタイプ が「application/vnd.ms-office」になっている" /></a></p> <p>すると、ファイルアップロード OK の <code>.xls</code>ファイルは MIMEタイプが <code>application/vnd.ms-excel</code> だったのに対し、 NG だった <code>.xls</code>ファイル は <code>application/vnd.ms-office</code> でした。</p> <p><a href="https://crieit.now.sh/upload_images/afa3f25f2a05e601c81b01cff0ccdfbe60782a55ce747.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/afa3f25f2a05e601c81b01cff0ccdfbe60782a55ce747.jpg?mw=700" alt="他の環境でのテスト" /></a></p> <p>ちなみに、他の環境に同じファイル群をアップロードしたところ両方とも <code>application/vnd.ms-excel</code> で、この環境では今回の現象は発生しませんでした。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types">よくある MIME タイプ - HTTP | MDN</a></li> </ul> <p>MIMEタイプ は <code>.xls</code> の場合 <code>application/vnd.ms-excel</code> のはずですが……。</p> <blockquote> <p>先に結論から言ってしまえば、利用しているサーバーのPHPのバージョンが低く、PHPの中で呼び出される「Fileinfo」というファイル操作のモジュールのバージョンが低かった為、アップロードしたエクセルファイルの正しいMIMEタイプが認識がされていないという状態になっていました。</p> <p><a target="_blank" rel="nofollow noopener" href="https://cunelwork.co.jp/blog/web/wp%E3%81%AE%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%81%AB%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E3%81%8C%E5%87%BA%E6%9D%A5%E3%81%AA%E3%81%84/">WordPressのメディアにアップロードが出来ない時 | スタッフブログ | 株式会社クーネルワーク</a></p> </blockquote> <p>PHP のバージョンによって <code>Fileinfo</code>モジュール の判定結果が異なるとのこと。 WordPress の MIMEタイプ 判定もこのモジュールを得利用しているとのことで、ここで影響を受けてしまうようです。</p> <p>以上より、今回の環境では本来 <code>application/vnd.ms-excel</code> となるべき MIMEタイプ が、何故か <code>application/vnd.ms-office</code> と判定され、結果、 WordPress のファイルアップロードの際のフィルタリング処理の中の MIMEタイプ 判定で引っかかっていた、というのが今回の現象の原因でした。</p> <h2 id="対処2"><a href="#%E5%AF%BE%E5%87%A62">対処2</a></h2> <p>以上の検証を踏まえて対処をします。具体的には、アクションフックでファイルアップロードの際のフィルタリング処理の際に <code>application/vnd.ms-office</code> も許可するようなプラグインを作成します。</p> <pre><code class="php"><?php /* Plugin Name: MIMEUruwashii Description: PHP の MIME タイプ判定で特定の .xlsファイル が弾かれる現象への対策 Version: 0.0.1 Author: アルム=バンド */ /** * MIMEUruwashii : PHP の MIME タイプ判定で特定の .xlsファイル が弾かれる現象への対策 * */ class MIMEUruwashii { /** * __construct : コンストラクタ * */ public function __construct() { add_filter( 'upload_mimes', [ $this, 'Bibishii', ] ); } /** * Bibishii : フィルター追加 * * @param {Object} : MIMEタイプ の一覧のオブジェクト * * @return {Object} : 誤判定している MIME タイプを追記する * */ public function Bibishii ( $mimes ) { $mimes['xls'] = 'application/vnd.ms-office'; return $mimes; } } // instantiate $ab_wp_plugin_mimeuruwashii = new MIMEUruwashii(); </code></pre> <p>このプラグインを <code>.zip</code> でアップロード、インストールして有効化します。</p> <p>もちろん、 <code>wp-config.php</code> の <code>ALLOW_UNFILTERED_UPLOADS</code> の行は削除して試験しました。</p> <p>……が、挙動は変わらず、解決できませんでした。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="ALLOW_UNFILTERED_UPLOADS"><a href="#ALLOW_UNFILTERED_UPLOADS">ALLOW_UNFILTERED_UPLOADS</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://kinsta.com/jp/knowledgebase/sorry-this-file-type-is-not-permitted-for-security-reasons/">WordPressの「セキュリティ上の理由によりこのファイル形式は許可されません」エラーの処理方法</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://cunelwork.co.jp/blog/web/wp%E3%81%AE%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%81%AB%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E3%81%8C%E5%87%BA%E6%9D%A5%E3%81%AA%E3%81%84/">WordPressのメディアにアップロードが出来ない時 | スタッフブログ | 株式会社クーネルワーク</a></li> </ul> <h3 id="WordPress 内のファイルアップロードのフィルタ処理について"><a href="#WordPress+%E5%86%85%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E3%81%AE%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E5%87%A6%E7%90%86%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">WordPress 内のファイルアップロードのフィルタ処理について</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://deep-space.blue/web/555">【WordPress】アップロード可能なファイル拡張子を増やす | deep-space.blue</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/WordPress/WordPress/blob/master/wp-includes/functions.php">WordPress/functions.php at master · WordPress/WordPress · GitHub</a></li> </ul> <h3 id="Fileinfoモジュール による MIMEタイプ 判定"><a href="#Fileinfo%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB+%E3%81%AB%E3%82%88%E3%82%8B+MIME%E3%82%BF%E3%82%A4%E3%83%97+%E5%88%A4%E5%AE%9A">Fileinfoモジュール による MIMEタイプ 判定</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://gray-code.com/php/get-kind-of-file/">ファイルの種類(MIMEタイプ)を確認する | GRAYCODE PHPプログラミング</a></li> </ul> <h3 id="MIMEタイプ の一覧"><a href="#MIME%E3%82%BF%E3%82%A4%E3%83%97+%E3%81%AE%E4%B8%80%E8%A6%A7">MIMEタイプ の一覧</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types">よくある MIME タイプ - HTTP | MDN</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16853 2021-04-17T13:14:11+09:00 2021-04-17T13:14:11+09:00 https://crieit.net/posts/wordpress-rest-api-judge-archive-or-siglepage-20210416 WordPress の REST API で投稿の記事一覧か個別記事かを判定する <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>WordPress の REST API で出力されるデータをカスタマイズする際に、記事の一覧なのか個別の記事なのかを判定したくなったので。</p> <p>例えば、「記事一覧のデータでは本文はいらず、個別記事のデータの場合のみ本文をデータに出力する」みたいなことをしたくなったときに、 <code>is_single()</code> のようなテンプレートタグがないかな、と。</p> <p>……検索してみましたが見当たらなかったので、自前で判定することにしました。</p> <h2 id="実装"><a href="#%E5%AE%9F%E8%A3%85">実装</a></h2> <pre><code class="php"><?php /* Plugin Name: Filter REST Description: REST API の出力結果をフィルタリングするプラグイン (パーマリンク設定は「基本」または「数字ベース」にしてください) Version: 0.0.2 Author: アルム=バンド */ /** * Filter REST : REST API の出力結果をフィルタリングするプラグイン */ class FilterREST { /** * __construct : プラグイン有効時にメソッドを呼ぶ * */ public function __construct() { // 有効化の際に発動 add_action( 'rest_prepare_post', [ $this, 'remove_data', ], 10, 3 ); } /** * remove_data : _links や不要な値を非出力にする * * @param {Object} $response : レスポンスとして返却するデータ * @param {Object} $post : * @param {Array} $request : リクレストデータ * * @return {Object} $response : レスポンスとして返却するデータ * */ public function remove_data ( $response, $post, $request ) { // 略 $is_single = false; if ( preg_match( '/posts\/[\d]+/i', $request->get_route() ) ) { // route が .../posts/{記事ID} の形式ならば単独記事のデータ取得と見なす $is_single = true; } if (!$is_single) { // 記事一覧のデータ取得ならばコンテンツ本文を削除 unset( $response->data['content'] ); } return $response; } } // instantiate if( !is_admin() ) { // 管理者画面以外 $filterrest = new FilterREST(); } </code></pre> <p>以前のコードに判定と処理を足してみました。</p> <p><code>is_single()</code> っぽいものが見付からなかったので、力業で GETリクエストの URL(ルーティング) が <code>/wp/v2/posts/{記事ID}</code> の形式ならば個別記事、そうではない <code>/wp/v2/posts</code>(最後にスラッシュ+記事IDの数字が付かない) の形式ならば記事一覧、というイメージです。ルーティングの情報は <code>$request</code>オブジェクト から <code>get_route()</code>メソッド で引っ張ってきています。</p> <p>一応これでできましたが……もう少しスマートにできる方法はないのでしょうか?</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/classes/wp_rest_request/">WP_REST_Request | Class | WordPress Developer Resources</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/classes/wp_rest_request/get_route/">WP_REST_Request::get_route() | Method | WordPress Developer Resources</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16844 2021-04-14T00:00:27+09:00 2021-04-14T00:00:27+09:00 https://crieit.net/posts/wordpress-rest-api-filtering-at-actionhook-20210414 WordPress の REST API の出力内容をフィルタリング (プラグイン編) <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>WordPress の REST API で出力されるデータを GETリクエストのパラメータでフィルタリングすると、クライアント側で指定する URL がごちゃごちゃするので WordPress 側で制御できないかと考えました。</p> <p>「REST API にアクセスされた際に、レスポンスデータを出力する直前」のタイミングという、そこそこカスタマイズのニーズがありそうなポイントではアクションフックが用意されていそうなので、「アクションフックで余計な出力が出ないように制限する」ということはできそうな気がします。</p> <h2 id="調査"><a href="#%E8%AA%BF%E6%9F%BB">調査</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://dev.ore-shika.com/post/wp-rest-delete/">WordPressのREST APIから「guid」など不要な項目を削除する | オレDEV.com</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://dev.ore-shika.com/post/wp_rest_links/">WordPressのREST APIから「_links」を削除する | オレDEV.com</a></li> </ul> <p>調べてみたらあったのでこの方法で考えます。ついでにアクションフックは <code>rest_prepare_post</code> が良さそうです。</p> <h2 id="実装"><a href="#%E5%AE%9F%E8%A3%85">実装</a></h2> <pre><code class="php"><?php /* Plugin Name: Filter REST Description: REST API の出力結果をフィルタリングするプラグイン (パーマリンク設定は「基本」または「数字ベース」にしてください) Version: 0.0.1 Author: アルム=バンド */ /** * Filter REST : REST API の出力結果をフィルタリングするプラグイン */ class FilterREST { /** * __construct : プラグイン有効時にメソッドを呼ぶ * */ public function __construct() { // 有効化の際に発動 add_action( 'rest_prepare_post', [ $this, 'remove_data', ], 10, 3 ); } /** * remove_data : _links や不要な値を非出力にする * * @param {Object} $response : レスポンスとして返却するデータ * @param {Object} $post : * @param {Array} $request : リクレストデータ * * @return {Object} $response : レスポンスとして返却するデータ * */ public function remove_data ( $response, $post, $request ) { // embed用の要素の削除 unset( $response->data['type'] ); unset( $response->data['link'] ); unset( $response->data['excerpt'] ); unset( $response->data['author'] ); unset( $response->data['featured_media']); // アイキャッチだが即座に画僧のパスを取得できるものではないので削除 unset( $response->data['date_gmt'] ); unset( $response->data['modified_gmt'] ); unset( $response->data['guid'] ); unset( $response->data['excerpt'] ); unset( $response->data['comment_status'] ); unset( $response->data['ping_status'] ); unset( $response->data['sticky'] ); unset( $response->data['template'] ); unset( $response->data['format'] ); // _links の全要素を取得 $links = $response->get_links(); // 要素を foreach で全て削除 foreach ( $links as $key => $value ) { $response->remove_link( $key ); } return $response; } } // instantiate if( !is_admin() ) { // 管理者画面以外 $filterrest = new FilterREST(); } </code></pre> <p>ブロックエディタは REST API を使用していますし、他のプラグインでも使用しているケースがあるので、ログインしていない場合のみ絞る方向で調整してみようかと。</p> <p>それから、一応クラスにしてなるべくグローバルスコープを汚染しないようにしておきます。ついでにプラグインとして実装してみました。</p> <h3 id="応用"><a href="#%E5%BF%9C%E7%94%A8">応用</a></h3> <p>ついでに「本文内にある <code>href</code> 属性内の URL がサイト内部へのリンクだった場合は置換をする」ということもやってみます。</p> <pre><code class="php">// 略 class FilterREST { /** * __construct : プラグイン有効時にメソッドを呼ぶ * */ public function __construct() { // 有効化の際に発動 add_action( 'rest_prepare_post', [ $this, 'remove_data', ], 10, 3 ); add_action( 'rest_prepare_post', [ $this, 'url_replace', ], 11, 3 ); // 追加 } // 略 /** * url_replace : content のリンクを書き換える * * @param {Object} $response : レスポンスとして返却するデータ * @param {Object} $post : * @param {Array} $request : リクレストデータ * * @return {Object} $response : レスポンスとして返却するデータ * */ public function url_replace ( $response, $post, $request ) { $search_pattern = '/href=\\"([^"]*?)\\"/i'; $response->data['content']['rendered'] = preg_replace_callback( $search_pattern, [ $this, 'replace_process', ], $response->data['content']['rendered'] ); return $response; } /** * replace_process : 外部サイトへのリンクはそのままで、それ以外は置換 * * @param {string} $matches : url_replace で $search_pattern にマッチした文字列 * * @return {string} : 置換された文字列 * */ public function replace_process ( $matches ) { $siteurl = site_url(); $static_url = 'https://example.com/releasenote/'; // 何かしらの静的サイトの更新履歴の個別記事ページを想定 if( strpos( $matches[1], $siteurl ) === false ) { // サイト外へのリンクならばリンク文字列をそのまま返す return $matches[0]; } else { // サイト内リンクならば置換する if( strpos( $matches[1], '?p=' ) !== false ) { // 「基本」 $id = explode( '?p=', $matches[1] )[1]; return 'href="' . $static_url . $id . '"'; } else if( strpos( $matches[1], 'archives/' ) !== false ) { // 「数字ベース」 $id = explode( 'archives/', $matches[1] )[1]; return 'href="' . $static_url . $id . '"'; } else { // ルールに該当しない場合はブランク return 'href="#"'; } } } } // 略 </code></pre> <p>こんなことをすると、同じ WordPress の投稿記事のどれかの記事へのリンクは <code>https://example.com/releasenote/{記事ID}</code> という形で置換されます。投稿を静的サイト側の JS でごにょごにょしたい……という場合には使えるかもしれません。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="WordPress の REST API"><a href="#WordPress+%E3%81%AE+REST+API">WordPress の REST API</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/RinT_T/items/808941ba52ffa4b51833">WP REST API を使って独自エンドポイントを設定する。 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/TanakanoAnchan/items/f4fc11f66e9cf2d7490e">WordPressのREST API周りのfilter hook / action hook のまとめと、一部のREST APIを開放する方法 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://webcraftlog.net/about-wordpress-rest-api/">WordPress REST APIについてまとめてみた | WebCraftLog</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/d2cdot_ohnami/items/efe35dc9c689b8f10128">WordPress 4.7以降でREST APIのレスポンスを加工する(ACFの値も追加可能) - Qiita</a></li> </ul> <p>ブロックエディタは REST API を使用していますし、他のプラグインでも使用しているケースがあるので、ログインしていない場合のみ絞る方向で調整してみようかと。</p> <p>なお、アクションフックとしては <code>rest_api_init</code>, <code>rest_endpoints</code>, <code>rest_prepare_post</code> 等がある模様。</p> <h4 id="不要な出力を削除"><a href="#%E4%B8%8D%E8%A6%81%E3%81%AA%E5%87%BA%E5%8A%9B%E3%82%92%E5%89%8A%E9%99%A4">不要な出力を削除</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://dev.ore-shika.com/post/wp-rest-delete/">WordPressのREST APIから「guid」など不要な項目を削除する | オレDEV.com</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://dev.ore-shika.com/post/wp_rest_links/">WordPressのREST APIから「_links」を削除する | オレDEV.com</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/d2cdot_ohnami/items/efe35dc9c689b8f10128">WordPress 4.7以降でREST APIのレスポンスを加工する(ACFの値も追加可能) - Qiita</a></li> </ul> <h3 id="今回は使わなかったが今後使いそうなもの"><a href="#%E4%BB%8A%E5%9B%9E%E3%81%AF%E4%BD%BF%E3%82%8F%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%8C%E4%BB%8A%E5%BE%8C%E4%BD%BF%E3%81%84%E3%81%9D%E3%81%86%E3%81%AA%E3%82%82%E3%81%AE">今回は使わなかったが今後使いそうなもの</a></h3> <h4 id="カテゴリの詳細情報も取得"><a href="#%E3%82%AB%E3%83%86%E3%82%B4%E3%83%AA%E3%81%AE%E8%A9%B3%E7%B4%B0%E6%83%85%E5%A0%B1%E3%82%82%E5%8F%96%E5%BE%97">カテゴリの詳細情報も取得</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://dev.ore-shika.com/post/wp-rest-cat-info/">WordPressのREST APIから記事取得時に、まとめてカテゴリ詳細も取得する | オレDEV.com</a></li> </ul> <h4 id="アイキャッチ画像の情報を取得"><a href="#%E3%82%A2%E3%82%A4%E3%82%AD%E3%83%A3%E3%83%83%E3%83%81%E7%94%BB%E5%83%8F%E3%81%AE%E6%83%85%E5%A0%B1%E3%82%92%E5%8F%96%E5%BE%97">アイキャッチ画像の情報を取得</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://dev.ore-shika.com/post/wp-rest-thumbnail/">WordPressのREST APIでアイキャッチ画像を取得する2つの方法 | オレDEV.com</a></li> </ul> <h3 id="PHP"><a href="#PHP">PHP</a></h3> <h4 id="正規表現"><a href="#%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE">正規表現</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/kanaxx/items/daca1c57e48e0a8d674a">PHPで正規表現を使ってHTMLを強引にほじるコツ - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/ao_love/items/6b5299b06214348c03a1">正規表現でhtml内のリンク名称とリンク先を取得 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://d2b6.dg8.top/article/412400033.html">[PHP] preg_replace 後方参照でちょっと悩んだ - 脱力系備忘録BloG</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.preg-replace-callback.php">PHP: preg_replace_callback - Manual</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.zacoding.com/post/regex-comma-separated/">カンマ区切りの文字列を正規表現で表す | クソざこCoding</a></li> </ul> <p>正規表現や後方参照について。念のため。</p> <h4 id="一部に文字列を含むか"><a href="#%E4%B8%80%E9%83%A8%E3%81%AB%E6%96%87%E5%AD%97%E5%88%97%E3%82%92%E5%90%AB%E3%82%80%E3%81%8B">一部に文字列を含むか</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/kazu56/items/2c72d187438de07c2503">【PHP】特定の文字列を含むかのチェック - Qiita</a></li> </ul> <h3 id="WordPress テンプレートタグ"><a href="#WordPress+%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%82%BF%E3%82%B0">WordPress テンプレートタグ</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%82%BF%E3%82%B0/site_url">テンプレートタグ/site url - WordPress Codex 日本語版</a></li> </ul> <p>たまにだと <code>site_url()</code> か <code>home_url()</code> か迷うので。</p> arm-band tag:crieit.net,2005:PublicArticle/16843 2021-04-13T23:43:18+09:00 2021-04-13T23:44:19+09:00 https://crieit.net/posts/wordpress-rest-api-filtering-at-request-20210413 WordPress の REST API で出力内容をフィルタリング (リクエスト編) <p>WordPress の REST API で出力されるデータをフィルタリングできることを知ったのでメモ。</p> <p>例えば、 <code>https://example.jp/</code> という WordPressサイト があったとします。</p> <p>以下の URL の REST API に GETリクエスト を飛ばすと、投稿の一覧が取得できます。</p> <p><code>https://example.jp/wp-json/wp/v2/posts</code></p> <p>個別記事はIDで指定します。</p> <p><code>https://example.jp/wp-json/wp/v2/posts/42</code></p> <p>ここまでは知っていましたが、色々な情報が出力されるため、場合によっては不要なデータの方が多い、というケースも考えられます。</p> <p>例えば、 AJAX で投稿のID、タイトル、投稿日時、URLだけ欲しい場合は以下のように <code>_fields</code> パラメータで指定すると、そのデータのみ抽出できるとのこと。</p> <p><code>https://example.jp/wp-json/wp/v2/posts?_fields=id,title,date,link</code></p> <p>同様に、個別記事でもフィルタリングが可能。IDが <code>42</code> の記事のタイトル、更新日時、本文が欲しい場合は以下。</p> <p><code>https://example.jp/wp-json/wp/v2/posts/42?_fields=title,modified,content</code></p> <p>こうすると余分なデータを省けるのでコードも書きやすいですし、レスポンスが軽くなることが期待できそうです。</p> <p>ちなみに、個別にキーを指定するのではなく、「埋め込み前提である程度絞る」のであれば <code>context=embed</code> も利用できるかと。これは一覧表示の時に役に立ちそうです。</p> <p><code>https://example.jp/wp-json/wp/v2/posts?context=embed</code></p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/shima0218/items/91e707902aaa0d4372fd">WordPressのREST APIの使い方 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://ocws.jp/blog/post1790/">WP REST APIでの記事の取得と表示[PHP版 / JS版] | ocws BLOG</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://dev.ore-shika.com/post/wp-rest-embed/">WordPressのREST APIで記事一覧取得に「embed」を指定すると9割以上サイズを減らせた | オレDEV.com</a></li> </ul> <h2 id="余談"><a href="#%E4%BD%99%E8%AB%87">余談</a></h2> <p>現在 REST API 周りのドキュメントを読もうと思うと、公式の英語のドキュメントになりますかね?</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/rest-api/">REST API Handbook | WordPress Developer Resources</a></li> </ul> <p>4.7までのプラグイン時代の癖で「WP REST API」で検索すると以下がヒットしますが、内容が変わっている部分もあると推測されるのでやはり上述のドキュメントですかね……。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://ja.wp-api.org/">WP REST API v2 Documentation</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16758 2021-03-20T15:23:30+09:00 2021-03-20T15:23:30+09:00 https://crieit.net/posts/wordpress-search-results-custom-order-by-post-date-desc-20210320 WordPress の検索結果を関連度から日時降順にする <p>WordPress のデフォルトの検索結果は 3.7 以降関連度順になっているのですが、これだと少々不便……ということがあったので、サクッと変更しました。</p> <h2 id="サンプルコード (プラグインのケース)"><a href="#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89+%28%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%AE%E3%82%B1%E3%83%BC%E3%82%B9%29">サンプルコード (プラグインのケース)</a></h2> <pre><code class="php"><?php /* Plugin Name: Sort Date Desc My Plugin Description: 検索結果を日付順でソートする Author: My Name Version: 0.1 */ function posts_sort_order_by_date_desc () { return 'post_date desc'; } add_filter('posts_search_orderby', 'posts_sort_order_by_date_desc'); </code></pre> <ul> <li>関数を <code>posts_search_orderby</code> のアクションフックに引っかける</li> <li>関数の戻り値にSQL文 の <code>ORDER BY</code> の後ろに付く文字列を指定する</li> </ul> <p>という感じで良いようです。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/gatespace/items/04794c577d907fbb8e33">[WordPress] 検索結果の並び順 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.wordpress.org/reference/hooks/posts_search_orderby/">posts_search_orderby | Hook | WordPress Developer Resources</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/Version_3.7">Version 3.7 - WordPress Codex 日本語版</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16755 2021-03-17T20:58:16+09:00 2021-03-17T20:58:16+09:00 https://crieit.net/posts/wordpress-plugin-readable-subscriber-20210317 WordPress の非公開記事を購読者でも閲覧できるようにする <p>WordPress の非公開記事を「購読者 (subscriber)」でも閲覧できるようにしたくなったので、やり方をメモしておきます。</p> <p>ちなみに、 WordPress はデフォルトの状態では「管理者 (administrator)」と「編集者 (editor)」のみ非公開記事の閲覧ができます。</p> <p>ただし、ケースによっては「記事の編集権限は与えたくないので、購読者で記事の閲覧権限だけ付与したい」という場合もあるかと思います。</p> <p>そこで対処。</p> <h2 id="シンプルなコード"><a href="#%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%AA%E3%82%B3%E3%83%BC%E3%83%89">シンプルなコード</a></h2> <p>単純に付与することだけを考えるならば、フォーラムにある<a target="_blank" rel="nofollow noopener" href="https://ja.wordpress.org/support/topic/%E3%80%8C%E9%9D%9E%E5%85%AC%E9%96%8B%E3%80%8D%E3%81%AE%E6%A8%A9%E9%99%90%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/">「非公開」の権限について | WordPress.org 日本語</a>のスレッドのコードで良いかもしれません。</p> <pre><code class="php">$subscriber = get_role( 'subscriber' ); $subscriber->add_cap( 'read_private_posts' ); </code></pre> <p>これをテーマの <code>functions.php</code> に追加すれば、要件は満たせるようです。ただし、<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/add_cap">add_cap() の Codex</a>には</p> <blockquote> <p>注意: この設定はデータベース(テーブル wp_options のフィールド wp_user_roles)へ保存されます。そのため、テーマやプラグインを有効化した時に実行したほうがいいかもしれません。</p> <p><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/add_cap">関数リファレンス/add cap - WordPress Codex 日本語版</a></p> </blockquote> <p>という注意書きがありました。設定がDBに保存されるのであれば、元に戻せるようにしておきたいです。また、DBに設定が保存されるのであれば、何度もこのコードが実行されるのではなく、初回の一度だけ実行される方が良さそうな気がします。</p> <p>そこで、「コードをプラグイン化し、プラグインの有効化の際に上記コードが実行、プラグイン無効化の際は逆に設定を戻すコードが実行」されるような設計にしたいと思います。</p> <h2 id="コード (プラグイン)"><a href="#%E3%82%B3%E3%83%BC%E3%83%89+%28%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%29">コード (プラグイン)</a></h2> <p>上述の設計を落とし込むと以下のようになりました。なお、一応生関数をブローバルスコープに置くのではなく、クラスとして使用してみました。</p> <pre><code class="php"><?php /* Plugin Name: Readable Subscriber Version: 0.0.1 */ /** * ReadableSubscriber : プラグイン有効時、非公開記事 ( private posts ) を購読者 ( subscriber ) でも閲覧可能にする。無効時、その権限を削除する */ class ReadableSubscriber { protected $role = 'subscriber'; protected $privilege = 'read_private_posts'; /** * __construct : コンストラクタ。プラグイン有効時・無効時をトリガーにして対応するメソッドを呼ぶ * */ public function __construct() { // 有効化の際に発動 register_activation_hook( __FILE__, [ $this, 'addReadPrivilege', ] ); // 無効化の際に発動 register_deactivation_hook( __FILE__, [ $this, 'removeReadPrivilege', ] ); } /** * addReadPrivilege : 購読者にも非公開記事を閲覧する権限を付与 * */ public function addReadPrivilege () { $subscriber = get_role( $this->role ); $subscriber->add_cap( $this->privilege ); } /** * removeReadPrivilege : 購読者から非公開記事を閲覧する権限を剥奪 * */ public function removeReadPrivilege () { $subscriber = get_role( $this->role ); $subscriber->remove_cap( $this->privilege ); } } // instantiate $ReadableSubscriber = new ReadableSubscriber(); </code></pre> <p>プラグイン名や関数名は適当に。</p> <p>これで簡単にチェックしたところ、望んだ通りの挙動になりました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://ja.wordpress.org/support/topic/%E3%80%8C%E9%9D%9E%E5%85%AC%E9%96%8B%E3%80%8D%E3%81%AE%E6%A8%A9%E9%99%90%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/">「非公開」の権限について | WordPress.org 日本語</a> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://wpdocs.osdn.jp/%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%AE%E7%A8%AE%E9%A1%9E%E3%81%A8%E6%A8%A9%E9%99%90">ユーザーの種類と権限 - WordPress Codex 日本語版</a></li> </ul></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/add_cap">関数リファレンス/add cap - WordPress Codex 日本語版</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#.E6.9C.89.E5.8A.B9.E5.8C.96.2F.E7.84.A1.E5.8A.B9.E5.8C.96.2F.E3.82.A2.E3.83.B3.E3.82.A4.E3.83.B3.E3.82.B9.E3.83.88.E3.83.BC.E3.83.AB">プラグイン API - WordPress Codex 日本語版</a> <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/register_activation_hook">関数リファレンス/register activation hook - 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/register_deactivation_hook">関数リファレンス/register deactivation hook - WordPress Codex 日本語版</a></li> </ul></li> </ul> arm-band