はじめに
インフラ〜プログラム辺りの担当でWordPressの開発をしています。
その中で便利だと思った内容を記録しています。コーディングができないので分野が偏っていてすみません。
注意
この記事は2015年06月03日に書きはじめました。新しい書き方や内容があれば加筆修正していますが、古い内容も混じっています。
編集リクエスト絶賛受け付けています。
環境編
wp-config.phpに書いておく物
書いておくと幸せになる。
define('FS_METHOD', 'direct'); // プラグインのインストール時などに書き込み可能であれFTP情報を使わずに書き込みを行う
define('WP_DEBUG', true); // デバッグON
define('SAVEQUERIES', true); // クエリセーブ
define('SCRIPT_DEBUG', true); // スクリプトデバッグ
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をインストール
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
JSON表示
$ wp plugin list --status=active --format=json
プラグインの「有効化」
WordPressがどのプラグインを有効化しているかについては、(wp_)options
テーブルのactive_plugins
に情報が格納されている。
# 有効化されていない
$ wp option get active_plugins | grep debug
# ここで有効化
$ wp plugin activate debug-bar
Plugin 'debug-bar' activated.
Success: Activated 1 of 1 plugins.
# 結果の配列に入っている
$ wp option get active_plugins | grep debug
5 => 'debug-bar/debug-bar.php',
サイト移転用
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;}'
過去のWordPressへバージョンダウン
検証の案件ってありますよね。
$ wp core update --locale=ja --version=4.1.1 --force
テーマ作成時に自分のJSを読みこませる
/wp-content/themes/my-theme/js/hoge.js というファイルを読み込ませる場合、
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以降であれば無名関数でも可。
}
add_action( 'wp_enqueue_scripts', function() {
wp_enqueue_script( 'my_script', get_stylesheet_directory_uri().'/js/hoge.js', array('jquery'), '1.0.0', true);
});
WP-CLIとWooCommerce
基本
wp wc
でWooCommerce関連のWP−CLIを使うことができる。
$ wp wc
受注や商品関連を確認するために権限指定が必要なことが多い。
$ wp --user=1 wc
注文ステータスを変更する
受注メールの開発などで注文ステータスの変更をトリガにして動作させたい場合、WP-CLIで行うと確認が早いです。
114976
は注文番号
$ ORDER_ID=114976
$ wp --user=1 wc shop_order update ${ORDER_ID} --status=processing; \
wp --user=1 wc shop_order update ${ORDER_ID} --status=completed;
在庫数を確認する
SKUを指定して在庫数を確認する。バリエーションの在庫数も確認できる。
$ SKU=abc001
$ wp wc --user=1 product_variation list --fields=sku,stock_quantity \
$(wp wc --user=1 product list --sku=${SKU} --field=id)
固定ページのslugと一致するjsを自動で読み込ませる
上記の応用編。
http://example.com/hoge
という固定ページがある時に
/mytheme/js/hoge.js
を自動的に読み込ませる方法。
「共通じゃなくて固定ページごとにJSを作ったけど毎度読み込ませるのってどうやるんだっけなー」というときにとりあえずページ名でjs/hoge.js
って作っとけば勝手に読み込むので便利。
/**
* 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')
で取得可能。ページごとに切り替えたい場合には
if (get_query_var('pagename') == 'fugapage') {
}
のように記述可能。
時刻の取り方
日本とかで運用する場合、date()
とか使うと九時間ずれてしまうので注意。
$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はよく使うので便利。
$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動かねー」って時。というか$が使えないことについての原因。
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
系のテンプレートタグが追加されたようです。
引数にファイル名をテーマディレクトリからの相対パスで渡すとファイルの存在確認を行いつつ、URLを返してくれるようです。
よく使うbloginfo
公式を見れば乗っているんですが良く忘れるのでメモ
テストコードを下記に。
$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 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;
});
データベースを手元に拾ってくる
そのまま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)
というかあった
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にあるクラスをオートロードする
/**
* @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になる
なぜかテンプレートが選択されない、というのが意外に結構ある。
良くありがちなのは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_posts
もWP_Query
実は(というは必然として)かなり重い。
posts_per_page
を-1とかにすると途中でサクッと落ちてる。しかも静かに落ちてる。
posts_per_page=1
として試してうごくならメモリの可能性大。
ログインした後にリダイレクトさせたい
ユーザがログインしたら普通の管理画面じゃなくて固定ページとかに飛ばしたい場合。
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
のようにするとうまく飛ぶ。
「あれ、変だなと思った時の『表示オプション』」
「リビジョンってないんだっけ」とか「メニューに固定ページ追加できないっけ」という時には右上の「表示オプション」を見てみるべし。