「公開」ボタンを押したあとに、本文中のリンクがテストサイト環境のURLのままだったことに気づく——。サイトを日常的に更新していると、一度は経験がある事故ではないでしょうか。今回は、claude code をつかって、WordPressのブロックエディタに「公開前チェック」の仕組みを仕込んでみたので、その考え方と実装をご紹介します。

こんな“事故”、身に覚えありませんか?

投稿を公開したあとになって発覚する、よくある見落としです。

・本文中のリンクが、確認用のテストサイト環境のURLのままだった

・外部サイトへのリンクが、同じタブで開く設定のままだった

・SEO用のカノニカルURLを設定し忘れていた

・カテゴリーを選び忘れて、一覧ページに表示されていなかった

どれも「あとで気づいて修正すれば済む」話ではありますが、公開直後に気づかなければ、その間は本番サイトに事故った状態で表示され続けてしまいます。

WordPressの「公開」ボタンは、実は何もチェックしていない

ブロックエディタの「公開」ボタンが実際に確認しているのは、基本的に「本文が空でないか」程度です。タイトルの有無、カテゴリーの設定、リンクの設定など、運用上気にしたい項目は、WordPress標準の仕組みではまったくチェックされません。

つまり、こうした見落としを防ぐには、公開ボタンを押す前に投稿者自身が目視で確認する運用ルールを作るか、それをエディタ側の仕組みとして組み込むかの、どちらかが必要になります。

そこで「公開前チェック」パネルを作ってみた

ブロックエディタには、PluginPrePublishPanelという拡張ポイントが用意されています。これを使うと、「公開」ボタンを押したときに開く確認パネルの中に、自分で定義した項目を追加できます。

今回は、次の5項目を確認する仕組みにしました。

項目判定方法確認内容
タイトル自動タイトルが入力されているか
※ほとんど入力を忘れることはないですが、、
本文自動本文が入力されている、かつテストサイト環境のURLが残っていないか
カテゴリー自動カテゴリーが1つ以上選択されているか
※下書き保存してしまうと、デフォルトのカテゴリになってしまいますが、、
外部リンク自動外部サイトへのリンクすべてに、別タブで開く設定(target=”_blank”)がされているか
カノニカル手動SEOプラグインでカノニカルURLを設定済みか、投稿者自身がチェックを入れて確認

「自動で判定できるもの」と「人の目でしか確認できないもの」が混在するのがポイントで、無理にすべてを自動化しようとせず、チェックボックスで済ませる項目は割り切って手動にしています。

▶ 導入方法:どこに何を置くか

使用中のテーマの中に2つのファイルを用意するだけのシンプルな方法にしています。

1. テーマのフォルダの中に js フォルダがなければ作り、その中に prepublish-check.js というファイルを作って、後述のJavaScriptコード全文を保存する

2. テーマの functions.php の末尾に、後述のPHPコードを追記する(子テーマを使っている場合は子テーマ側のfunctions.php)

3. 保存すれば、それだけで投稿編集画面に反映されます(プラグインの有効化などの操作は不要です)

(補足)このあと出てくる registerPlugin という関数名の「プラグイン」は、ブロックエディタの拡張機能を指すJavaScript側の用語で、WordPressの「プラグイン」機能そのものとは別物です。名前が似ているだけなので、混同しないよう注意してください。

PHP(functions.php に追記)

// 「カノニカル確認済み」を投稿メタとして保存できるように登録
add_action( 'init', function () {
    register_post_meta( '', '_check_canonical_confirmed', [
        'show_in_rest'  => true,
        'single'        => true,
        'type'          => 'boolean',
        'default'       => false,
        'auth_callback' => function () {
            return current_user_can( 'edit_posts' );
        },
    ] );
} );
 
// ブロックエディタにJavaScriptファイルを読み込む
add_action( 'enqueue_block_editor_assets', function () {
    $path = get_theme_file_path( 'js/prepublish-check.js' );
    $uri  = get_theme_file_uri( 'js/prepublish-check.js' );
 
    wp_enqueue_script(
        'my-prepublish-check',
        $uri,
        [ 'wp-plugins', 'wp-edit-post', 'wp-element', 'wp-components', 'wp-data', 'wp-i18n' ],
        filemtime( $path ),
        true
    );
} );

functions.phpを保存すると、投稿編集画面でJavaScriptファイルが読み込まれるようになります。続いて、そのJavaScript(js/prepublish-check.js)の中身を見ていきます。

▶ 【実装】仕組みはたった2つのAPI

実装のコアになるAPIは2つだけです。

1つ目はregisterPlugin + PluginPrePublishPanelこれで確認パネルにオリジナルの項目を追加できます。

JavaScriptjs/prepublish-check.js

registerPlugin( 'prepublish-checks', {
    render: () => {
        // useSelect で title / content / categories などを取得し、
        // 5項目それぞれの ✓ / ✕ を判定する
        return wp.element.createElement(
            wp.editPost.PluginPrePublishPanel,
            { title: '公開前チェック' },
            /* チェックしたい内容コードを書いてください */
        );
    },
} );


2つ目はlockPostSaving / unlockPostSaving。5項目のいずれかが未確認の間、このAPIで「公開」「更新」ボタンを無効化します。

JavaScriptjs/prepublish-check.js の続き)

const { lockPostSaving, unlockPostSaving } = wp.data.dispatch( 'core/editor' );

if ( allChecksPassed ) {
    unlockPostSaving( 'prepublish-check-lock' );
} else {
    lockPostSaving( 'prepublish-check-lock' );

このロックが効くのは「公開」「更新」ボタンだけで、下書き保存やオートセーブには影響しません。作業を中断しても下書き保存できるので、執筆中にボタンが押せなくてストレスになる、ということはありません。

参考までに、5項目の中でも少しロジックが込み入っている「外部リンクの target=”_blank” チェック」の部分だけ、実装例を載せておきます。本文のHTMLをブラウザに解析させて、外部サイトへのリンクのうち target=”_blank” が付いていないものを探し出しています。

JavaScript(js/prepublish-check.js の一部)

// #アンカー、mailto:、tel:、javascript: は外部リンク判定の対象外
function isExternalHref( href ) {
    if ( ! href ) {
        return false;
    }
    var trimmed = href.trim();
    if ( /^(#|mailto:|tel:|javascript:)/i.test( trimmed ) ) {
        return false;
    }
    try {
        var url = new URL( trimmed, window.location.href );
        return url.hostname !== window.location.hostname;
    } catch ( e ) {
        return false;
    }
}
 
// target="_blank" が設定されていない外部リンクのhref一覧を返す
function getExternalLinksMissingBlank( html ) {
    var doc = new DOMParser().parseFromString( html, 'text/html' );
    var anchors = doc.querySelectorAll( 'a[href]' );
    var missing = [];
    anchors.forEach( function ( a ) {
        var href = a.getAttribute( 'href' );
        if ( ! isExternalHref( href ) ) {
            return;
        }
        if ( a.getAttribute( 'target' ) !== '_blank' ) {
            missing.push( href );
        }
    } );
    return missing;
}

「今のドメイン(window.location.hostname)と違うリンクだけを外部リンクとみなす」という判定にしているので、ドメインをハードコードする必要がないのがポイントです。

ここで紹介したのは要点のみです。実際に動かす際は、registerPlugin + PluginPrePublishPanel  とlockPostSaving / unlockPostSaving の2つの関数の使い方を軸に、ご自身のチェック項目に合わせて実装を組み立ててみてください

※実際にコードを反映した画像※

記事を公開しようとしても、「公開前チェックをしていないので、「公開」ボタンが押せないようになっています。

▶ 使ってみて分かった落とし穴 ⚠️

実装してから気づいたのですが、この仕組みには抜け道があります。ブロックエディタの「エディタ設定」には、標準で「公開前チェックを常に表示する」というオプションがあり、これをOFFにすると、投稿者は確認パネルを開かずに「公開」ボタンひとつで即座に公開できてしまいます。

つまり、今回作った仕組みはあくまでエディタ画面上のチェックであって、この設定をOFFにされたり、REST API経由で直接投稿を公開されたりすると、チェックをすり抜けてしまう可能性があります。

運用ルールとしての“うっかり防止”には十分役立ちますが、「絶対に守らせたい」項目がある場合は、サーバー側(transition_post_statusフックなど)でも同じ条件をチェックし、条件を満たさない投稿は強制的に下書きへ差し戻す、といった二重の仕組みを検討したほうが安心です。

▶ 余談:Claude Codeとの“壁打ち”で作った

実は今回の仕組み、最初から仕様が固まっていたわけではありません。「投稿の確認画面に表示項目を増やしたい」という漠然とした相談から始まり、Claude Codeとの壁打ちを重ねながら、チェックする項目や、公開ボタンをロックする範囲(下書き保存は妨げない、など)を少しずつ固めていきました。

「本文にテストサイト URLが残っていないか」「外部リンクにtarget=”_blank”が付いているか」といった自動判定と、「カノニカルはSEOプラグインの画面で確認する」といった人の目に頼る部分の切り分けも、会話の中で「これは自動化できそうか?」を一つずつ検証しながら決めていった結果です。

コードを書く作業そのものより、「何を、どこまで自動化するか」を言語化していく過程のほうが、時間としては長かったかもしれません。

まとめ

WordPressの「公開」ボタンは、思っている以上に何もチェックしてくれません。逆に言えば、PluginPrePublishPanelとlockPostSavingを組み合わせるだけで、自分たちの運用に合わせたチェックリストを比較的簡単に組み込めます。

「毎回同じミスをうっかりやってしまう」という項目があるなら、注意喚起の張り紙をするより先に、エディタ側に組み込んでしまうほうが確実かもしれません。