WordPress
wp-cli

エンジニアな自分がWordPressで何度も調べたり感心したりした事

More than 1 year has passed since last update.


はじめに

インフラ〜プログラム辺りの担当でWordPressに携わって10年ぐらいになります。

「WordPressを使ったシステム開発」ならかなり長くやっているなーと自負しています。

他にはCakePHP3、Laravelの開発を行っているので、システム開発者としてコーダーさんやWordPressを使う人にオススメしたい内容を書いています。


注意

この記事は2015年06月03日に書きはじめました。新しい書き方や内容があれば加筆修正していますが、古い内容も混じっています。

編集リクエスト絶賛受け付けています。


環境編


wp-config.phpに書いておく物

書いておくと幸せになる。


wp-config.php

define('FS_METHOD', 'direct'); // プラグインとかFTP情報いらず

define('WP_DEBUG', true); // デバッグON
define('SAVEQUERIES', true); // クエリセーブ
define('SCRIPT_DEBUG', true); // スクリプトデバッグ

https://codex.wordpress.org/Debugging_in_WordPress


wp-cli

インストールとかをお手軽にできるWP-CLIを使いましょう。

teckingさんありがたいです

http://www.tecking.org/archives/tag/wp-cli


wp-cliのインストール

初回だけですが。

$ cd ~/tmp/

$ curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
$ chmod +x wp-cli.phar
$ mv wp-cli.phar /usr/local/bin/wp

僕はOS Xでhomebrewを使っているので /usr/local/bin に一般ユーザ権限でおきました。


記事の作成

# 記事の生成

$ wp post generate
# 件数指定
$ wp post generate --count=100
# カスタム投稿タイプ
$ wp post generate --post_type=mycpt


本家のテーマから子テーマを作って有効化

$ wp scaffold child-theme twentysixteen-child --parent_theme=twentysixteen

$ wp theme activate twentysixteen-child


ユーザの追加

$ wp create user name hoge@example.com --role=administrator --user_pass=password


homeとかsiteurl

$ wp option update home http://example.com

$ wp option update siteurl http://example.com

ちなみに/wp/とかディレクトリを付けるのは siteurl の方。


カスタム投稿タイプ

# カスタム投稿タイプを出力

$ wp scaffold post-type test
# 有効化されているテーマに出力
# wp-content/themes/mytheme/post-types/test.php に書き出される
$ wp scaffold post-type test --theme
# 上のコマンドの後に実行するとfunctionsで読み込ませる
$ echo "require_once __DIR__.'/post-types/test.php';" >> wp-content/themes/twentysixteen-child/functions.php


パーマリンク設定

# post_idに

$ wp rewrite structure '/%post_id%'
# デフォルト?
$ wp rewrite structure '/%year%/%monthnum%/%postname%'


便利エイリアス

wphere ってやると現在ディレクトリにWordPressをインストール


.zshrc

alias wphere='wp core download'

alias wpconfig='wp core config --dbname=yousan_wordpress --
dbuser=yousan_php --dbpass=passwordhere --dbhost=localhost'

こっちは現在ディレクトリが ex1.example.com だった場合にそれをホスト名として展開、インストール、プラグインのインストール、有効化。

他にDEBUGのオンなど。

#!/bin/bash

DBHOST=localhost
DBNAME=yousan_wordpress
DBUSER=yousan_php
DBPASS=dbpasshere
WPUSER=yousan
WPPASS=userpasswordhere
WPMAIL=yousan@l2tp.org

PWD=`pwd`
DOMAIN=${PWD##*/}
IFS='.'
set -- ${DOMAIN}
PREFIX=$1_
IFS=','

wp core download --locale=ja

wp core config \
--dbname=${DBNAME} \
--dbuser=${DBUSER} \
--dbpass=${DBPASS} \
--dbhost=${DBHOST} \
--dbprefix=${PREFIX} \
--extra-php <<PHP
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'FS_METHOD', 'direct' );
PHP

wp core install \
--url=${DOMAIN} \
--title=${DOMAIN} \
--admin_user=${WPUSER} \
--admin_password=${WPPASS} \
--admin_email=${WPMAIL}

wp plugin install \
add-page-from-template \
advanced-custom-fields \
backwpup \
custom-post-type-permalinks \
custom-post-type-ui \
debug-bar \
debug-bar-console \
duplicate-post \
fg-debug-bar-rewrite-rules \
post-type-switcher \
show-current-template \
--activate


プラグインの一覧取得

有効化されているものの一覧取得。

$ wp plugin list --status=active

Screen Shot 2016-03-29 at 3.18.45 PM.png


JSON表示

$ wp plugin list --status=active --format=json

Screen Shot 2016-03-29 at 3.19.08 PM.png


サイト移転用

CSV形式で出力しつつバージョン情報などを省く。

CSV形式で出力し、ヘッダ部の1行目を消して、awkでカンマ区切りを分割してスペース区切りにして出力。

目視確認しつつこの後に wp plugin --activate へ繋げると良い。

動作確認はCentOS7。awkなどの環境によっては動かないかも。

$ wp plugin list --status=active --format=csv | tail -n +2 | awk 'BEGIN {FS=",";} {printf "%s ", $1;}'

Screen Shot 2016-03-29 at 3.30.50 PM.png


過去のWordPressへバージョンダウン

検証の案件ってありますよね。

$ wp core update --locale=ja --version=4.1.1 --force


テーマ作成時に自分のJSを読みこませる

/wp-content/themes/my-theme/js/hoge.js というファイルを読み込ませる場合、


functions.php

function my_scripts() {  

wp_enqueue_script( 'my_script', get_stylesheet_directory_uri().'/js/hoge.js', array('jquery'), '1.0.0', true);

}
add_action( 'wp_enqueue_scripts', 'my_scripts' );


get_bloginfoはstylesheet_directoryにしておかないでtemplate_directoryとしてしまうと、子テーマの場合、親テーマを呼んでしまうので注意。

第一引数はこの呼び出しの名前

第二引数がパス

第三引数は依存するパッケージ

暖四引数はバージョン情報、ココを更新するとGETの値が変わってブラウザのキャッシュをクリアしやすくなる。

第五引数はフッターに書くかどうか

PHP5.3以降であれば無名関数でも可。


functions.php

}
add_action( 'wp_enqueue_scripts', function() {
wp_enqueue_script( 'my_script', get_stylesheet_directory_uri().'/js/hoge.js', array('jquery'), '1.0.0', true);
});



固定ページのslugと一致するjsを自動で読み込ませる

上記の応用編。

http://example.com/hoge

という固定ページがある時に

/mytheme/js/hoge.js

を自動的に読み込ませる方法。

「共通じゃなくて固定ページごとにJSを作ったけど毎度読み込ませるのってどうやるんだっけなー」というときにとりあえずページ名でjs/hoge.jsって作っとけば勝手に読み込むので便利。


functions.php

/**

* JSファイルを読み込ませる
*/

function load_scripts() {
if ($pagename = get_query_var('pagename')) {
if (file_exists(get_stylesheet_directory().'/js/'.$pagename.'.js')) {
wp_enqueue_script( $pagename, get_stylesheet_directory_uri().'/js/'.$pagename.'.js');
}
}
}
add_action( 'wp_enqueue_scripts', 'load_scripts' );


固定ページのページ名(スラッグ)の取得方法

get_query_var('pagename')で取得可能。ページごとに切り替えたい場合には


functions.php


if (get_query_var('pagename') == 'fugapage') {
}

のように記述可能。


時刻の取り方

日本とかで運用する場合、date()とか使うと九時間ずれてしまうので注意。


page-something.php

$date = date_i18n('Y-m-d H:i:s');


とするとうまく取れる。date_default_timezone_set()は色々な面で厳禁。


WP_Queryをうまく書きたい

WP_Queryにはたくさんの子供がいるのでそれを駆使するとうまく行く。


  • WP_Tax_Query

  • WP_Meta_Query

  • WP_Date_Query

  • WP_User_Query

  • WP_Comment_Query

個人的にはQuery五兄弟って呼んでる。でもまだまだ増えつつある。

TaxとMetaはよく使うので便利。


page-something.php

$args = array(

'meta_query' => array(
array(
'key' => 'hoge',
'value' => 'fuga',
),
),
);
$query = new WP_Query($args);

まだ日が浅くてマニュアルが違っていたりするので注意。間違いを見つけたら面倒でも是非報告を。

あなたの指摘で100人の人が救われます(適当)。

meta_queryを使うときにやりがちなミスとして


  • arrayを二重の入れ子にすること

  • 中身の連想配列名をkeyとかじゃなくてmeta_keyって書いちゃう。

というのがあるので注意。


jQuery


WordPressでjQueryを使う

だいたいデフォルトで読み込まれてるんだけど、「jQuery動かねー」って時。というか$が使えないことについての原因。

http://learn.jquery.com/using-jquery-core/avoid-conflicts-other-libraries/


Avoiding Conflicts with Other Libraries

http://learn.jquery.com/using-jquery-core/avoid-conflicts-other-libraries/


原因はjQueryとprototype.jsの衝突を避けるため、$を使えなくしてる。

以下のように書くと動く。

(function($){

// do something
})(jQuery);

または$()の代わりにjQuery()でを使う。

以前のWordPress2.xとかの時はjQueryよりprototype.jsが覇権を握っていて、その頃にWordPressにもjQueryとかが搭載されて、そのころからの名残な気がします。


wp_headとかwp_footerを使わずにjQueryだけを読み込む

wp_head()とか呼ぶと管理バーが出てしまってCSSで消すのめんどくせーとかでヘッダーフッターのカスタマイズを無しでjQueryだけ使いたいときに。

<?php wp_enqueue_script( 'jquery' );

wp_print_footer_scripts();
?>

これをしておけば本家(wp core側)のjQueryが呼ばれるので良い感じ。


すべてのチェックボックスをオンにする

WordPressというかjQueryが入っている環境で可能。

jQuery(':checkbox').prop({'checked':'checked'});

管理画面などで利用できる。開発者コンソールで入れれば複雑なカスタムフィールドも全チェック出来る。


フィルタフックを取得


function print_filters_for( $hook = '' ) {
global $wp_filter;
if( empty( $hook ) || !isset( $wp_filter[$hook] ) )
return;

print '<pre>';
print_r( $wp_filter[$hook] );
print '</pre>';
}


WordPress: How do I get all the registered functions for 'the_content' filter


print_filters_for('wp_footer');とかすると良い感じ。


よく使うbloginfo系定数まとめ

名前
関数、定数

テーマディレクトリURL
bloginfo('stylesheet_directory')

テーマディレクトリパス
get_stylesheet_directory()

テーマにあるファイルのURL
get_theme_file_uri($filename)

親テーマにあるファイルのURL
get_parent_theme_file_uri($filename)

テーマにあるファイルのパス
get_theme_file_path($filename)

親テーマにあるファイルのパス
get_parent_theme_file_path($filename

WordPressパス
ABSPATH

@gatespace さんより


get_bloginfo()は最終的に get_stylesheet_directory_uri() あるいは get_template_directory_uri() を読みに行く


との指摘を頂きましたありがとうございます。bloginfo()系を使うときにはその辺りを考慮できればと思います。

WordPress4.7.0より、テーマディレクトリに置いたファイルのURLやパス(/usr/local的なパスです)について、get_theme_file系のテンプレートタグが追加されたようです。

Screen Shot 2017-05-26 at 14.09.40.png

引数にファイル名をテーマディレクトリからの相対パスで渡すとファイルの存在確認を行いつつ、URLを返してくれるようです。


よく使うbloginfo

公式を見れば乗っているんですが良く忘れるのでメモ


https://codex.wordpress.org/Function_Reference/bloginfo


テストコードを下記に。


page-something.php

$keys = array(

'site', // サイト名
'url', // サイトのURL: http://www.example.com/
'wpurl', // wpのインストールurl: http://www.example.com/wp/
'stylesheet_url', // 子テーマのスタイルシート: http://www.example.com/wp/wp-content/themes/twentyfourteen-child/style.css
'stylesheet_directory', // 子テーマのテーマディレクトリ: http://www.example.com/wp/wp-content/themes/twentyfourteen-child/
'template_url', // 親テーマのディレクトリ: http://www.example.com/wp/wp-content/themes/twentyfourteen/
'template_directory', // template_urlと一緒: http://www.example.com/wp/wp-content/themes/twentyfourteen/
'home', // deprecatedなので使わないように
);
foreach ($keys as $key) {
echo "$key: ".get_bloginfo($key)."<br>\n";
}


bloginfo以外でよく使うような定数っぽいやつら

// 子テーマのディレクトリパス 

// /home/yousan/public_html/www.example.com/wp/wp-content/themes/twentyfourteen-child
echo get_stylesheet_directory()."<br>\n";

// 子テーマのURL = bloginfo('stylesheet_directory')
// http://www.example.com/wp/wp-content/themes/twentyfourteen-child
echo get_stylesheet_directory_uri()."<br>\n";

// 親テーマのディレクトリパス 
// /home/yousan/public_html/www.example.com/wp/wp-content/themes/twentyfourteen/
echo get_template_directory()."<br>\n";

// wordpressのインストールディレクトリへのパス
// /home/yousan/public_html/www.example.com/wp/
echo ABSPATH."<br>\n";

// アップロードディレクトリへのパスとかが配列で
// [path] => /Users/yousan/Sites/aramaki.ticket.com/hotel_coupon/wp-content/uploads/2015/06
// 引数で時間指定も可能
echo print_r(wp_upload_dir())."<br>\n";

get_stylesheet_directory()get_stylesheet_directory_uri()が似ているのでbloginfoと併せてこんがらがる。

_uriは使わない、というルール付けをしておくと良い。


GETで値を追加したい

つい自前で&hog=fugaとか追加してて?と&の判断も自前で、とかやっちゃうけど公式の関数あるのでそっちで。

$url = add_query_arg('key', 'value');

$url = add_query_arg('key', 'value', $baseurl); // 基準となるURLが別の場合


モックでWordPressの時刻をずらす

WordPressでのシステム開発時、時刻を意図的にずらしたいとき。


function.php


function get_date_offset() {
return 10; // 10日ずらす
}

// ここを
add_filter('date_i18n', function($j, $req_format, $i, $gmt) {
//var_dump($j, $req_format, $i, $gmt);
return date($req_format, strtotime(get_date_offset().' days', $i));
//exit;
}, 10, 4);
date_i18n('Y-m-d H:i:s');


ただしpost_dateとかにinsertするときはずれないので全ていけるとは思わないこと。


SQL操作


WordPressでシステム開発をする時に必要なクエリ操作について

http://qiita.com/yousan/items/f29e7010a1384c1e1206


$query = "SELECT count(*) FROM $wpdb->posts ORDER BY ID LIMIT 20;";

$count = $wpdb->get_var($query);


転送

テス鯖、開発鯖、本鯖でディレクトリ群をコピーするときのコマンド。

FTPだとファイルが増えたときのオーバーヘッドが大変なので覚えておくと凄く便利。

ずっと前からあるけどいつも忘れる。

example.comというディレクトリを dest.example.com というホストへ転送するテスト。


tar(圧縮)

uploadsは重いことが多いので除外する

shell> tar zcvf example.com.tar.gz  --exclude 'uploads'  example.com


tar(圧縮) + ssh (転送)

圧縮と同時に転送。並列化出来て効率的。良く忘れるので。

shell> tar zcvf -  --exclude 'uploads' example.com | ssh dest.example.com 'cat > ~/tmp/example.com.tar.gz'

それマグさんThanks! [http://takuya-1st.hatenablog.jp/entry/2013/01/10/024636]


tar (圧縮) + ssh (転送) + tar (解凍)

shell> tar zcf - --exclude 'uploads'  example.com | ssh dest.example.com tar zxf - -C /home/yousan/tmp/


補足

zshとか使ってると ~ (チルダ、ホームディレクトリ)を勝手に解釈するので、Mac <--> Ubuntuとかだと /Usersと/homeで混濁するので注意。絶対パス表記を使うべし。

tarの詳細を表示するvオプションは転送元と転送先で表示が入り乱れてひどいことになるのでこれも注意。使わないように。

またsshのオプションで圧縮を有効にしている場合にはあまり良くない結果になりそうなので注意。

計測していないけれど自分はsshをssh -C -o CompressionLevel=9にエイリアスしているのでこれをオフにして転送するのが早いと思う。


add_actionなどに変数を渡す

クロージャへ引数を渡すと良いっぽい。

$var = 'yahoo!';

add_action('wp_footer', function() use ($var) {
echo $var;
});


http://php.net/manual/ja/functions.anonymous.php



データベースを手元に拾ってくる


そのままmysqldump

環境変数にそれぞれを入れて標準出力にmysqldump。

$ DB_NAME=yousan_wordpress

$ DB_USER=yousan
$ DB_PASSWORD=password
$ DB_HOST=mysql.example.com
$ DB_PREFIX=prefix_
$ mysqldump -h '$DB_HOST' -u '$DB_USER' -p'$DB_PASSWORD' '$DB_NAME' --tables \
`mysql -N -h '$DB_HOST' -u '$DB_USER' -p'$DB_PASSWORD' information_schema -e \
"select table_name from tables where table_schema = '"'$DB_NAME'"' and table_name like '"'$DB_PREFIX%'"' ";`


SSHでダンプ

踏み台サーバを踏みつつダンプ

$ SSHHOST=example.com

$ PORT=21
$ DB_NAME=yousan_wordpress
$ DB_USER=yousan
$ DB_PASSWORD=password
$ DB_HOST=mysql.example.com
$ DB_PREFIX=prefix_
$ ssh -p $PORT $SSHHOST ' \
$ mysqldump -h '
$DB_HOST' -u '$DB_USER' -p'$DB_PASSWORD' '$DB_NAME' --tables \
`mysql -N -h '
$DB_HOST' -u '$DB_USER' -p'$DB_PASSWORD' information_schema -e \
"select table_name from tables where table_schema = '
"'$DB_NAME'"' and table_name like '"'$DB_PREFIX%'"' ";`'

クォートが入れ子になってて訳分からなくなりそう。

テスト環境はzshなのでもしかすると違うかも


更にインポート

変数の定義が大変なので上記を適宜変更してパイプで繋げば。


インポート直後にsiteurlとhomeを変更

$ SSHHOST=example.com

$ PORT=21
$ DB_NAME=yousan_wordpress
$ DB_USER=yousan
$ DB_PASSWORD=password
$ DB_HOST=mysql.example.com
$ DB_PREFIX=prefix_
$ SITEURL='http://exmaple.com'
$ HOME='http://exmaple.com'
$ mysql -h '$DB_NAME' -u $DB_USER -p'$DB_PASS '$DB_NAME' -e \
"UPDATE '$DB_PREFIX'options SET option_value='$SITEURL' WHERE option_name='siteurl';"; \
mysql -h $DB_NAME -u $DB_USER -p$DB_PASS $DB_NAME -e \
"UPDATE $DB_PREFIXoptions SET option_value='$HOME' WHERE option_name='home';"


wp-cli

wp-cliにwp-config.phpからmysqlコマンドの接続用オプションが取れるってのを妄想。

$ mysql -h`wp db_name` -u`wp db_user` -p `db_password`

とか

`wp dumpmysql`

とか…。って下はありそう。

wp db export --tables=$(wp db tables --all-tables-with-prefix --format=csv)

というかあった


http://wp-cli.org/commands/db/export/



Advanced Custom Field(ACF) の名前からキーを引く

ACFはthe_field()とかを呼び出すfield_nameと内部的に一意なfield_keyがあって、更新にはfield_keyを使わないとダメ。

ACFのupdate_field()はキー名で更新しないとバグる。

field_nameは例えば user_name とかで、 field_keyは例えば field_54af861d8886bという文字列。

ACFの項目編集画面で確認できて本来はそっちを使うべし。


field_key vs field_name

Each value stored in the database consists of 2 rows of data. These are:

value – the value, saved with a meta_key of “$fieldName”

reference – the field_key, used to find the field object, saved with a meta_key of “$fieldName”

http://www.advancedcustomfields.com/resources/update
field/


/**

* ACFで更新をデータのかける際には注意が必要。the_fieldとかで通常使っているのは'name'だけれど、
* 正式にはkeyを使うべき。でもkeyって結構分かりにくい。
* たとえば 1990_hoge ~ 2015_hoge を更新したくても、全てのキーを調べ上げるのは苦労する。
* なのでそれを調べる関数。
* ただし複数の投稿タイプで同じキー名とかが出現する可能性があるので注意。
* これを防ぐには$post_idにACFのフィールドグループのIDを指定すること。
* 複数の投稿タイプで同じキー名を利用しているなどで複数のフィールドが合致した場合には例外を投げる。
*/

function get_acfkey_by_name($name, $post_id=0) {
global $wpdb;
if (is_numeric($post_id) && $post_id) { // ID指定あり
$query = "SELECT * FROM $wpdb->postmeta ".
"WHERE meta_key like %s ".
" AND meta_value like %s " .
" AND post_id = %d;";
$rows = $wpdb->get_results($wpdb->prepare($query, 'field_%', '%'.$name.'%', $post_id), OBJECT);
} else { // ID指定無し
$query = "SELECT * FROM $wpdb->postmeta ".
"WHERE meta_key like %s ".
" AND meta_value like %s;";
$rows = $wpdb->get_results($wpdb->prepare($query, 'field_%', '%'.$name.'%'), OBJECT);
}
$found = false; $key = false;
foreach ($rows as $row) {
$meta_value = unserialize($row->meta_value);
if ($name == $meta_value['name']) {
if ($found) { // 二個以上同じ物が見つかった場合にはエラー
$msg = 'More than 1 ACF keys found. It may cause an error.'.
'Please specify ACF group ID';
throw new Exception($msg);
} else { // 同じ物がヒットしていないかチェックする
$key = $meta_value['key'];
}
$found = true;
}
}
return $key;
}


特定の権限を持っていないとキックする

    public static function bootOut($capabilities=array('administrator')) {

/** @var WP_User */
global $current_user;
foreach ( $current_user->get_role_caps() as $capability => $isEnabled) {
if ( FALSE !== array_search($capability, $capabilities) ) {
return;
}
}
wp_safe_redirect(home_url()); // boot out!
exit;
}


includesにあるクラスをオートロードする


functions.php

/**

* @param $classname
*/

function autoloader($classname) {
if (0 !== (strpos($classname, 'PREFIX_'))) {
return;
}
// to lower, remove AP_ prefix. ex) AP_Opiton => option
$classname = str_replace('PREFIX_', '', $classname);
$dirpath = dirname( __FILE__ ).'/includes/';
$filepath = $dirpath . 'class-'.$classname.'.php';
if (file_exists($filepath)) {
include $filepath;
} else if ( file_exists(strtolower($filepath)) ) {
include strtolower($filepath);
}
}
spl_autoload_register('autoloader');


よくわかんないけど404になる

Screen Shot 2015-06-08 at 2.28.42 AM.png

なぜかテンプレートが選択されない、というのが意外に結構ある。

良くありがちなのはwp_rewrite周りをカスタマイズしたときとか、wp_rewrite flush_rulesとか呼んだりしたときとか。

今回はPOSTの変数でyearを送ったせいで年別アーカイブが呼ばれてた。

<form>

<input name="year" type="hidden">
</form>

get_quer_varで取得してるようなquery_varsに該当するものを変数名にすると(たぶん)全面的にエラーになる、それかそれぞれのテンプレートヒエラルキーに合わせたテンプレートが呼ばれる。


get_posts, WP_Queryが動かない

引数はうまく行ってそうなのにうまく行かない、途中で切れる、白くなる。

原因は様々だけど最近よく遭遇するのはメモリ不足。

get_postsWP_Query実は(というは必然として)かなり重い。

posts_per_pageを-1とかにすると途中でサクッと落ちてる。しかも静かに落ちてる。

posts_per_page=1として試してうごくならメモリの可能性大。


ログインした後にリダイレクトさせたい

ユーザがログインしたら普通の管理画面じゃなくて固定ページとかに飛ばしたい場合。


functions.php

add_action( 'admin_init', function() {

if (FALSE !== strpos(@$_SERVER['HTTP_REFERER'], '/wp-login.php')) {
wp_redirect(get_bloginfo('url').'/supermenu/');
}
});

以下のサイトを参考にしたんだけれども、WordPressを/wp/とかに置いていると==では比較できないのでちょっとだけ修正。


http://www.nxworld.net/wordpress/wp-redirect-dashiboard.html


標準のwp-login.phpを使わない場合には上記コードの修正が必要。

でも標準のwp-login.phpを使わない場合には

http://www.example.com/wp-login.php?redirect_to=customposttype

のようにするとうまく飛ぶ。


「あれ、変だなと思った時の『表示オプション』」

「リビジョンってないんだっけ」とか「メニューに固定ページ追加できないっけ」という時には右上の「表示オプション」を見てみるべし。


WordpressでもwordpressでもなくてWordPress