できるようになること — プレビュー画面の管理バーから1クリックで公開
下書き記事はフロントのプレビューで最終確認するのに、公開するには管理画面へ戻ってエディタの公開ボタンを探す——この往復、地味に面倒です。本記事では、下書きのプレビュー画面の管理バー(画面上部の黒いバー)に「この記事を公開」ボタンを追加し、AJAXでその場で公開して公開後のURLへ移動する実装を紹介します。実際に当社のサイトで使っているコードです。
動作の流れはこうです。
- 下書き(draft / pending)をログイン中にプレビューすると、管理バーに緑の「この記事を公開」ボタンが出る
- クリック→確認ダイアログ→AJAXで公開処理
- 成功すると公開後の本URLへ自動で移動する(プレビューURLは公開後に意味を失うため)
公開済みの記事や、公開権限のないユーザーには何も表示されません。
functions.phpに追加するコード全文
子テーマのfunctions.php、または自作プラグインに以下を追加します。コピペで動きます。
// 下書きプレビューの管理バーに「この記事を公開」ボタンを出す。
// ボタンを出すべき投稿を返す(対象外ならnull)。
function mysite_publishbar_target() {
if ( is_admin() || ! is_singular() ) {
return null;
}
$post = get_queried_object();
if ( ! ( $post instanceof WP_Post ) ) {
return null;
}
if ( ! in_array( $post->post_status, array( 'draft', 'pending' ), true ) ) {
return null;
}
$pto = get_post_type_object( $post->post_type );
if ( ! $pto || ! current_user_can( $pto->cap->publish_posts ) || ! current_user_can( 'edit_post', $post->ID ) ) {
return null;
}
return $post;
}
// 管理バーにボタンを追加(priority 81 = 標準の「編集」リンクの隣)。
add_action( 'admin_bar_menu', function ( $bar ) {
if ( ! mysite_publishbar_target() ) {
return;
}
$bar->add_node( array(
'id' => 'mysite-publish-now',
'title' => '<span style="background:#00a32a;color:#fff;padding:3px 10px;border-radius:3px;font-weight:bold;">この記事を公開</span>',
'href' => '#',
) );
}, 81 );
// ボタンの動作(AJAX呼び出し)。
add_action( 'wp_footer', function () {
$post = mysite_publishbar_target();
if ( ! $post ) {
return;
}
$nonce = wp_create_nonce( 'mysite_publish_now_' . $post->ID );
?>
<script>
(function() {
// 管理バーのHTMLはwp_footerのpriority 1000で描画されるため、
// このスクリプトが先に実行されるとボタンがまだ存在しない。
// DOM構築完了を待ってから探すことで、出力位置に依存させない。
function bind() {
var btn = document.getElementById('wp-admin-bar-mysite-publish-now');
if (!btn) return;
btn.addEventListener('click', function(e) {
e.preventDefault();
if (!confirm('この記事を公開しますか?')) return;
var body = new URLSearchParams();
body.append('action', 'mysite_publish_now');
body.append('post_id', '<?php echo (int) $post->ID; ?>');
body.append('nonce', '<?php echo esc_js( $nonce ); ?>');
fetch('<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>', {
method: 'POST', credentials: 'same-origin', body: body
}).then(function(r){ return r.json(); }).then(function(res) {
if (res && res.success) {
location.href = res.data.url;
} else {
alert('公開に失敗しました: ' + (res && res.data ? res.data : '不明なエラー'));
}
});
});
}
if ('loading' === document.readyState) {
document.addEventListener('DOMContentLoaded', bind);
} else {
bind();
}
})();
</script>
<?php
} );
// AJAX: 下書きを公開する。
add_action( 'wp_ajax_mysite_publish_now', function () {
$post_id = isset( $_POST['post_id'] ) ? (int) $_POST['post_id'] : 0;
if ( ! $post_id || ! check_ajax_referer( 'mysite_publish_now_' . $post_id, 'nonce', false ) ) {
wp_send_json_error( '不正なリクエストです。' );
}
$post = get_post( $post_id );
if ( ! $post || ! in_array( $post->post_status, array( 'draft', 'pending' ), true ) ) {
wp_send_json_error( 'この記事は公開待ちの状態ではありません。' );
}
$pto = get_post_type_object( $post->post_type );
if ( ! $pto || ! current_user_can( $pto->cap->publish_posts ) || ! current_user_can( 'edit_post', $post_id ) ) {
wp_send_json_error( '公開する権限がありません。' );
}
$result = wp_update_post( array( 'ID' => $post_id, 'post_status' => 'publish' ), true );
if ( is_wp_error( $result ) ) {
wp_send_json_error( $result->get_error_message() );
}
wp_send_json_success( array( 'url' => get_permalink( $post_id ) ) );
} );
コードの要点 — 権限チェックとnonceを省略しない
この実装の本体はボタンではなく安全装置です。押さえるべき点は4つあります。
表示条件と実行条件を同じ関数で二重に確認する
ボタンの表示判定(mysite_publishbar_target)とAJAX側の実行判定で、同じ条件(下書きであること・公開権限があること)を両方チェックしています。フロントの表示条件はいくらでも偽装できるので、サーバー側のAJAX処理が単体で安全でなければなりません。「表示していないから押されない」は防御になりません。
nonceに投稿IDを含める
wp_create_nonce( 'mysite_publish_now_' . $post->ID )と投稿IDを含めているのは、ある記事のプレビューで取得したnonceを別の記事の公開に流用されないようにするためです。汎用の合言葉ではなく「この記事を公開してよい」という個別の許可証にします。
権限はpublish_postsとedit_postの両方を見る
カスタム投稿タイプでも正しく動くよう、get_post_type_object()から投稿タイプごとのpublish_postsケーパビリティを取って確認しています。「投稿は公開できるがこのカスタム投稿タイプは公開できない」という権限設計のサイトでも安全です。
公開後はプレビューURLに留まらない
下書きのプレビューURL(?preview=true付き)は、公開後は不要なパラメータになります。成功レスポンスでget_permalink()の本URLを返し、そこへ移動させています。公開直後の見た目を最終確認する動線としても自然です。
スクリプトの実行タイミング — 管理バーはwp_footerの最後に描画される
ひとつ実際にハマった点を共有します。管理バーのHTMLはwp_footerのpriority 1000という遅いタイミングで描画されるため、同じwp_footerに既定のpriorityで出力したスクリプトが先に実行され、まだ存在しないボタンを探して何も起きない——という「表示はされるのにクリックが無反応」な状態になります。上のコードでDOMContentLoadedを待ってからボタンを探しているのはこのためです。フックの出力順に依存しなくなるので、この形にしておくのが安全です。
注意点 — テーマのfunctions.php直書きよりプラグイン化を推奨
このコードはテーマに依存しない機能なので、テーマのfunctions.phpに書くとテーマを差し替えたときに一緒に消えます。長く使うなら自作プラグイン(またはmu-plugins)に置くのが安全です。当社でも運用プラグインの一機能として入れています。
また、ブロックエディタ(Gutenberg)の編集画面の公開ボタンとは経路が別なので干渉しません。あくまで「プレビュー画面から戻らずに公開したい」という動線を1つ足すだけの実装です。
EC-CUBEゴールドパートナー
WordPress構築・保守