add_posts_page()

管理画面のメニューのどこかに項目を追加して新たにページを表示させるのは、テーマとかプラグインでは既にやっていることで、どれも基本的には同じこと。ただ、項目を追加する場所によって、使う関数が違ってくるだけのこと。
今回は投稿メニューの中に項目を追加するので、使うのは add_posts_page()
テーマメニューなら、add_theme_page()。プラグインメニューならば add_plugins_page() と。当然のこと、他にもそれぞれのメニュー用に用意されてる。
その書式はというと、アクションフック admin_menu によって指定する関数を登録する。

<?php
    add_action( 'admin_menu', 'mypost_add_menu' );

    function mypost_add_menu(){
        //                      ( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position );
        // $icon_url と $position は option
        add_posts_page( 'Post-Calendar', 'Post-Calendar', 'administrator', 'strix_post_calendar', 'mypost_calendar_output' );
    }
?>
PHP
CopyExpand

3つ目の $capability に関しては codex の説明を見たほうが良いと思われる。
4つ目の $menu_slug はこのページを表示する時の slug として使用されるので重要。
そして 5つ目の $function は実際に表示するこのページの内容のコードとなる関数を指定する。

さて、今回は何のページを追加するかというと。
長いこと使ってきた WordPress Editorial Calendar というプラグインの代わりとなるページ。投稿をその投稿した日時によりカレンダーに表示してくれて、なおかつ、その場で投稿する日付や時間を変更でき、日付の変更はドラッグでも出来るというすぐれもの。
ただ、日付や時間の変更ということでは、あまり使ったことはなく、予約投稿をするのにどこまで投稿のある日付がうまっているかなどの確認に重宝してた。そう、逆にいえば、ただそれだけのことで使うのであれば、プラグインでなくてもいいし、できれば祝祭日も表示されていたほうがありがたかったのである。
と、いうことで、ただの投稿カレンダーなら簡単に自作できるし、プラグインを一つ減らす事ができるというメリットもついてくるってわけ。
で、できたものはこんなかんじ。

投稿カレンダーは、すでに自作しているプラグインのものからの流用。やっていることはほぼ同じ。
ただ、こっちは今月を含め、前後の三ヶ月分の表示にしてる。実際に使っていて見るのはそのぐらいだったというか、前月もいらなかったのかもしれない。
休日データもプラグインのものと全く同じものを併用してる(自サイトの場合は、プラグインという形ではなく、functions.php に内蔵しているので同じデータを呼び出して使ってる)。

私的にカレンダーを描写する理屈としては、とある月のカレンダーを作成するのに必要なデータは、その月の日数とその月の1日が何曜日であるかということ。grid で日付の表を作れば、その初日の曜日分、grid-column-start を指定してやれば、その分初日が offset される。その月が何年何月何日というのは、ただ表示用として必要なだけ。
まずは、その2つのデータから三ヶ月分の日付を key として配列をつくる。
初日の曜日からその月の日曜日と土曜日が何日かも取得出来る。
SQL で投稿の情報を取得。
で、休日データとその日曜、土曜データ、投稿の情報を三ヶ月分の日付配列へと反映させて、最後にその配列をループさせて HTML を組み立てる、といったあんばい。
とにかく条件式を極力少なくするということを前提にした結果がこうなったということです。

と、いうことでまずはその初日の曜日からその月の指定した曜日の日付を得る関数と休日データを返す関数。

<?php
    // その月の1日の曜日から指定した曜日のその月においての日にちを配列にして返す関数
    // $wdaynum 1:日曜、2:月、3:火、4:水、5:木、6:金、7:土 求める日にちの曜日を指定
    // $fdwd : その月の1日の曜日 0:日1:月2:火3:水4:木5:金6:土
    // $dnum : その月の日数
    function ret_daynum ( $wdaynum, $fdwd, $dnum  ) {

        $tmpnum = 0;
        $daynumary = array();
        $limitnum = $dnum - 6;

        if ( $wdaynum ) {

            $firstdaynum = ( ( int ) $wdaynum - $fdwd + 7 ) % 7;

            // 1日が月曜の場合(1 === $tmpwday['wday'])に日曜の1が指定してある場合、日曜の初日の日にちが0になってしまうことを避ける。
            $i = 0 === $firstdaynum ? 1 : 0;
            while ( $tmpnum < $limitnum ) {
                $tmpnum = $firstdaynum + 7 * $i;
                $daynumary[] = $tmpnum;
                ++$i;
            };
        }
        return $daynumary;
    }

    function ret_holidays() {
        //↓2013~2025までの休日データ、ちなみに最後のykxは251124(2025/11/24のこと)を表す
        $holidays_data = 'maa,man,mbk,mct,mdz,mec,med,mef,mgo,mip,miw,mjn,mkd,mkw,mlw,naa,nam,nbk,ncu,ndz,nec,nee,nef,ngu,nio,niw,njm,nkc,nkx,nlw,oaa,oal,obk,ocu,odz,oed,oee,oef,ogt,oiu,oiv,oiw,ojl,okc,okw,olw,paa,pak,pbk,pcu,pdz,pec,ped,pee,pgr,phk,pis,piv,pjj,pkc,pkw,plw,qab,qai,qbk,qct,qdz,qec,qed,qee,qgq,qhk,qir,qiw,qji,qkc,qkw,qlw,raa,rah,rbl,rcu,rd!,rec,red,ree,rgp,rhk,riq,rix,rjh,rkc,rkw,rlx,saa,san,sbk,scu,sdz,sd!,sea,seb,sec,sed,sef,sgo,shl,sip,siw,sjn,sjv,skd,skw,taa,tam,tbk,tbx,tct,tdz,ted,tee,tef,tgw,tgx,thj,tiu,tiv,tkc,tkw,uaa,uak,ubk,ubw,uct,udz,uec,ued,uee,ugv,ugw,uhi,uit,uiw,ukc,ukw,vaa,vaj,vbk,vbw,vcu,vdz,vec,ved,vee,vgr,vhk,vis,viw,vjj,vkc,vkw,wab,wai,wbk,wbw,wcu,wdz,wec,wed,wee,wgq,whk,wir,wiw,wji,wkc,wkw,xaa,xah,xbl,xbw,xct,xdz,xec,xed,xef,xgo,xhl,xip,xiw,xjn,xkd,xkw,yaa,yam,ybk,ybx,yct,ydz,yec,yee,yef,ygu,yhk,yio,yiw,yjm,ykc,ykx';
        // ローマ字で短縮された日付を数字に戻すための連想配列
        $alp_num = array( 'a'=>'01','b'=>'02','c'=>'03','d'=>'04','e'=>'05','f'=>'06','g'=>'07','h'=>'08','i'=>'09','j'=>'10','k'=>'11','l'=>'12','m'=>'13','n'=>'14','o'=>'15','p'=>'16','q'=>'17','r'=>'18','s'=>'19','t'=>'20','u'=>'21','v'=>'22','w'=>'23','x'=>'24','y'=>'25','z'=>'29','!'=>'30' );
    
        return array( $holidays_data, $alp_num );
    } 
?>
PHP
CopyExpand

そして、add_posts_page() で指定したメインとなる関数。
現在日時を取得するのに WordPress においては 9時間のずれが生じるので、以前は自前の関数を使っていたのだけれど、その部分を DateTimeImmutable クラスを使う手法に直してる。シンプルで解りよいし、なにかと安心して使えると思う。

<?php
    function mypost_calendar_output() {
        global $wpdb;

	$now = new DateTimeImmutable( '', new DateTimeZone( 'Asia/Tokyo' ) );

       // 現在の年月の情報取得
	$curyear = $now->format( 'Y' );// ex. 2021
	$curmonth = $now->format( 'm' );// 1-12
	$curday = ( int ) $now->format( 'd' );// 1-31
	$curwday = ( int ) $now->format( 'w' );// 0 (日曜) から 6 (土曜)
	$curYm = $now->format( 'Ym' );// 202108
       // その月の日数(最後の日の数字)
	$curlastdate = ( int ) $now->format ( 't' );
       // 現在の月の1日の曜日を取得
	$tmpwd = ( ( $curwday + 7 ) - ( ( $curday % 7) - 1 ) ) % 7;

        // 前月の情報を取得
	$prvYm = $now->modify( 'first day of last month' )->format( 'Ym' );
        // SQL 検索用の文字列
	$prvYmd =  $now->modify( 'first day of last month' )->format( 'Y-m' ) . '-01';
	$prvfirstwd = $now->modify( 'first day of last month' )->format( 'w' );
       // 前月の日数(最後の日の数字)
	$prvlastdate = ( int ) $now->modify( 'first day of last month' )->format( 't' );

       // 次月の情報を取得
	$nxtYm = $now->modify( 'first day of next month' )->format( 'Ym' );
	$nxtfirstwd = $now->modify( 'first day of next month' )->format( 'w' );
       // 次月の日数
	$nxtlastdate = ( int ) $now->modify( 'first day of next month' )->format( 't' );
       // SQL 検索用の文字列
	$nxtYmld = $now->modify( 'first day of next month' )->format( 'Y-m' ) . '-' . ( string ) $nxtlastdate;

        $daysary = array();
        // ループさせるのに必要な三ヶ月分の情報を配列にする ex. 202108 => [ 31, 2 ]
        $targetmonth = array( $prvYm => [ $prvlastdate, $prvfirstwd ], $curYm => [ $curlastdate, $tmpwd ], $nxtYm => [ $nxtlastdate, $nxtfirstwd ] );
    
        $holidays_data = ret_holidays();// $holidays と $alp_num が配列で返される
        $holidays = explode( ',', $holidays_data[0] );
    
        // $alp_num 配列の key と value を反転させる
        $alp_num_flip = array_flip( $holidays_data[1] );
    
        foreach ( $targetmonth as $key => $val ) {
            $alp_year = $alp_num_flip[ substr( $key, 2, 2 ) ];
            $num_month = substr( $key, 4, 2 );
            $alp_month = $alp_num_flip[ $num_month ];
            $sameflg = 0;
            $num_month = ( int ) $num_month;
    
            // 三ヶ月分の日にちを key とした配列を作る
            for ( $i = 1; $i <= $val[0]; ++$i ) {
                $daysary[ $num_month ][ $i ] = array( 'post' => '', 'class' => '' );
            }
            $daysary[ $num_month ][ 1 ]['offset'] = $val[1] + 1;
    
            // その月の日曜日の日にちを配列で取得し、日付配列に反映させる
            $sunnum = ret_daynum( 1, $val[1], $val[0] );
            foreach ( $sunnum as $sun ) {
                $daysary[ $num_month ][ $sun ]['class'] = ' class="nitiyou"';
            }
    
            // その月の土曜日の日にちを配列で取得し、日付配列に反映させる
            $satnum = ret_daynum( 7, $val[1], $val[0] );
            foreach ( $satnum as $sat ) {
                $daysary[ $num_month ][ $sat ]['class'] = ' class="doyou"';
            }
            
            // 祭日を日付配列に反映させる
            foreach ( $holidays as $value ) {
                if ( $alp_year === $value[0] ) {
                    if ( $alp_month === $value[1] ) {
                        $tmpday = ( int ) $holidays_data[1][ $value[2] ];// $holidays_data[1] は $alp_numの配列
                        $daysary[ $num_month ][ $tmpday ][ 'class' ] = ' class="nitiyou"';
                        $sameflg = 1;	
                    }
                } else {
                    if ( 1 === $sameflg ) {
                        break;
                    }
                }
            }
    
            // 今日の日付
            if ( ( int ) $curYm === $key ) {
                $daysary[ $num_month ][ $curday ]['class'] .= ' id="today"';
            }
        }

        // データベースから投稿の情報を取得する
        // meta_key='classic-editor-remember' の meta_value の値はその投稿がclassic editor か block editor を使用しているか
        $sql=<<<HERE
SELECT a.ID,a.post_date,a.post_title,a.post_status,b.meta_value FROM $wpdb->posts a,$wpdb->postmeta b
WHERE a.post_type = 'post'
AND a.ID = b.post_id
AND a.post_status IN ( 'publish', 'future' )
AND a.post_date >= %s
AND a.post_date <= %s 
AND b.meta_key = 'classic-editor-remember'
ORDER BY a.post_date ASC
HERE;
            
        $myposts = $wpdb->get_results( $wpdb->prepare( $sql, $prvYmd, $nxtYmld ) ); // $prvYmd →例:2021-08-01

        // 投稿の情報を投稿編集ページへのリンクにして日付配列に入力する
        foreach ( $myposts as $val ) {
            $tmpmon = ( int ) substr( $val->post_date, 5, 2 );
            $tmpdate = ( int ) substr( $val->post_date, 8, 2 );
            $posthour = substr( $val->post_date, 11, 5 );
            $pstatus = 'future' === $val->post_status ? 'futurep' : 'publishp';
    
            $daysary[ $tmpmon ][ $tmpdate ]['post'] .= '<li><a href="https://strix.main.jp/wp-admin/post.php?post=' . $val->ID . '&action=edit&' . $val->meta_value . '"><span class="' . $pstatus . '">' . $posthour . ' ' . $val->post_title . '</span></a></li>';
        }
        
        $alp_num_flip = null;
        $holidays_data = null;

        // いよいよ HTML 生成
        echo '<div id="mypostcalendar"><h2>Post-Calendar</h2>';
    
        $showwd = array( 'Su' => ' class="nitiyou"', 'Mo' => '', 'Tu' => '', 'We' => '', 'Th' => '', 'Fr' =>'', 'Sa' => ' class="doyou"' );
        // grid としてレイアウトさせるための inline style 設定
        $gridstyle = ' style="display:grid;grid-template-columns:repeat( 7, 1fr );text-align:center;"';
        $mark = array( '♣ ', '♠ ', '♦ ' );// 不要不急のかざりもの
        $j = 0;
    
        foreach ( $daysary as $key => $val ) {
            $currentmonth = ( int ) $curmonth === $key ? ' id="currentym">' . $mark[ $j ] . $curyear . ' ' : '>' . $mark[ $j ];
            echo '<div  class="my-post-calendar"><h3' . $currentmonth . $key . '月</h3>';
            echo '<div class="cal-wd"' . $gridstyle . '>';
            
            foreach ( $showwd as $wd => $cl ) {
                echo '<b' . $cl . ' title="' . $wd . '">' . $wd . '</b>';
            }
            echo '</div><div class="cal-body"' . $gridstyle . '>';

            // 1日の曜日指定
            $rowstart = ' style="grid-column-start:' . ( string ) $val[1]['offset'] . ';"';
    
            foreach ( $val as $day => $ele ) {
                $poststr = '' !== $ele['post'] ? '<ul class="targetposts">' . $ele['post'] . '</ul>' : '';
    
                echo '<div' . $rowstart . '><p' . $ele['class'] . '>' . $day . '</p>' . $poststr . '</div>';
    
                $rowstart = '';
            }
            echo '</div>';
            ++$j;
        }
        echo '</div>';
    }   
?>
PHP
CopyExpand

get_current_screen()

で、あとはこのページのスタイル設定など。
管理画面にスタイルを吐き出すのには、アクションフックの admin_print_styles を使えばいい。
なんだけれど、必要なそのページだけに排出させたいときはどうすれば良いのか、ってこと。

ちょっと調べてみると、この get_current_screen() というのが使えそう。
これは現在の画面のオブジェクト、というかいろんな情報を返してくれるとのことで。この中の要素の base において、add_posts_page() で指定したページの slug として使用されている $menu_slug の値が入っているので、それにて判別すれば良さそう、ということにした。

<?php
    add_action( 'admin_print_styles', 'mypost_calendar_style' );

    function mypost_calendar_style() {

        $pageinfo = get_current_screen();
        if ( false !== strpos( $pageinfo -> base, 'strix_post_calendar' ) ) {
    
?>
        <!--admin My Post Calendar style-->
        <style type="text/css">
            #mypostcalendar{font-size:1.5em;color:#003333;width:95%;margin:0 auto;}
            #mypostcalendar :is( h2, h3 ){text-shadow:3px 3px 3px white, 4px 4px 4px #2B2B34;}
            .cal-wd b{display:inline-block;padding:0 1px;}
            .cal-body div{position:relative;min-height:80px;padding:0 1px;border:solid 1px #D9D9D9;border-radius:10px;text-align:center;}
            .cal-body p{position:relative;font-size:1.0em;width:50px;margin:0 auto;}
            .cal-body .targetposts{position:relative;width:100%;font-size:0.8em;text-align:left;list-style-type:none;}
            .cal-body a{color:#0000ff;text-decoration:none;border-bottom:dashed 1px red;}
            .cal-body a:hover{color:#fff00f;border-bottom:dashed 1px blue;}
            .cal-ym{font-weight:bold;text-align:left;border-bottom:ridge 2px #778899;padding-bottom:8px;margin:0 0 8px 0;}
            #currentym{color:blue;}
            #today{font-weight:bold;color:#ffffff;background-color:#C5D8F5;border:solid 1px #0064FA;border-radius:10px;text-shadow:2px 2px 2px blue;}
            .doyou{color:#008b8b;background-color:#E3F9F5;border-radius:10px;}
            .nitiyou{color:#DA0275;background-color:#F9D4E8;border-radius:10px;text-shadow:2px 2px 2px white;}
            .targetposts{padding:5px 3px;}
            .futurep{color:blue;}
            .publishp{color:#9595A2;}
            :is( .futurep, .publishp ):hover{color:#00FF01;border-bottom:dashed 1px blue;}
        </style>
<?php
        }
    }
?>
PHP
CopyExpand

ちなみに、functions.php において、このような管理画面だけで必要なコードの類は別ファイルにまとめ、 is_admin() を使って include_once するようにしてる。ただし、アクションフックの publish_post などに関わる関数は、管理画面にアクセスしているときだけに動くものではないので要注意。管理画面だけで必要なコードの量は、自分の場合、全体の 34% ほどあった。ページの表示の高速化は小さなことの積み重ねだと。
以上。

Leave a Reply!

JavaScript is necessary to send a comment.
You can edit and delete your comment if you input a edit key.
Edit key is necessary for attesting you when you edit and delete it.
The tag of HTML cannot be used in comment.
When you comment for the first time, it is displayed after the approval of the administrator.
Because I cannot speak English so much, it takes time to answer.
Required fields are marked *.

※Please enter more than 5 characters only alphabets.
※Edit or delete are possible for 2000 days after approval.

*

♠Simplistic Comment User Editable v4.0

♠When visitors leave comments on the site this site collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.
♠This site does not use cookie when visitors leave comments and commenter edit comment.
♠This site uses Akismet to reduce spam. Learn how your comment data is processed.

Comments feed

Trackback URL : https://strix.main.jp/wp-trackback.php?p=167455

Sanbanse Funabashi
2011.01.01 sunrise

Top

スクロールさせるか画像をクリックすると元に戻ります。