キュレーションメディアは、公開してからが本番です。書き手が増えれば「誰が公開してよいか」を決める必要があり、記事が増えれば消えたときの損失も大きくなります。そして WordPress 本体・テーマ・プラグインは、公開後もセキュリティ更新が続きます。
「サイトはできた。でも、ライターが勝手に公開しないようにしたいし、バックアップや更新は誰が、どうやればいいの?」
結論から言うと、「書く人は公開できない」権限と、公開前の自動チェック、レビュー担当への通知で編集フローを仕組みにし、バックアップは「復元できることを確かめる」まで、更新は「ステージングで確認してから本番へ」を手順として決めることです。どれも一度作って終わりではなく、保守契約の中で誰が担当するかを決めておく必要があります。
前回の第10回では、表示速度の改善と計測を行いました。最終回の今回は、権限と編集フロー、バックアップと復元、更新の手順を作り、連載全体を振り返ります。
今回のゴールと、WordPress の権限の考え方
WordPress の権限は、「ロール(役割)」と「権限(capability)」の2段階です。ロールは権限の束で、たとえば標準の「寄稿者」は記事を書けますが公開できず、「編集者」は他人の記事も公開できます。プログラムで判定するときはロール名ではなく、current_user_can( 'publish_posts' ) のように権限で判定するのが基本です。
📰 出典:WordPress Developer Resources「Roles and Capabilities」
今回作る編集フローは次のとおりです。
| 手順 | 誰が | 仕組み |
|---|---|---|
| 1. 下書きを書き、画像を入れる | ライター | 独自ロール「ライター」(公開の権限なし、画像のアップロードは可) |
| 2. レビュー待ちとして提出する | ライター | 「レビュー待ち」で保存。公開しようとしてもレビュー待ちになる |
| 3. 通知を受け取る | 編集長・管理者 | レビュー待ちになった時点でメール通知 |
| 4. 確認して公開する | 編集長・管理者 | 公開時に「公開前チェック」(引用元カードの必須項目、監修日)を自動で実行 |
今回追加・変更したファイル
| ファイル | 内容 |
|---|---|
プラグイン includes/roles.php | 独自ロール「ライター」「編集長」、公開権限のない保存をレビュー待ちにする処理(新規) |
プラグイン includes/review-flow.php | レビュー待ちの通知、公開前チェック(新規) |
プラグイン curation-tools.php | 読み込み、有効化時のロール作成、バージョン 1.0.0 |
scripts/backup.sh・scripts/restore.sh | バックアップと復元(新規) |
.gitignore | バックアップの保存先 backups/ を追加 |
独自ロール「ライター」と「編集長」
wp-content/plugins/curation-tools/includes/roles.php(curation_tools_install_roles() の抜粋)
$writer_caps = array(
'read' => true,
'edit_posts' => true,
'delete_posts' => true,
'upload_files' => true,
);
remove_role( 'curation_writer' );
add_role( 'curation_writer', 'ライター', $writer_caps );
$editor = get_role( 'editor' );
$chief = $editor ? $editor->capabilities : array();
$chief['list_users'] = true;
$chief['curation_review_posts'] = true;
remove_role( 'curation_editor_in_chief' );
add_role( 'curation_editor_in_chief', '編集長', $chief );
$admin = get_role( 'administrator' );
if ( $admin ) {
$admin->add_cap( 'curation_review_posts' );
}
- ライターは標準の寄稿者に近いロールですが、画像のアップロード(upload_files)を許可しています。公開(publish_posts)や公開済み記事の編集の権限はありません。
- 編集長は編集者の権限に、ユーザー一覧の閲覧(list_users)と、独自の権限「レビュー担当」(curation_review_posts)を加えたものです。
- 管理者にもレビュー担当の権限を加えます。通知の送り先は、ロール名ではなくこの権限で決めます。
ロールはデータベースに保存されるため、毎回のページ表示で作り直すのではなく、プラグインの有効化時と、ロール定義の版が変わったときだけ更新します。プラグインを無効化してもロールは削除しません。削除すると、そのロールのユーザーが何もできなくなるためです。
公開の権限がない保存は「レビュー待ち」にする
画面や REST API から公開しようとした場合は、WordPress 本体が権限を確認して拒否します。ただし、プログラムから直接記事を保存する処理(WP-CLI でユーザーを指定した場合など)には、この確認が働きません。そこで、保存直前のデータを確認し、公開の権限がないユーザーの「公開」は「レビュー待ち」に置き換える保険を入れました。
includes/roles.php(curation_tools_force_pending_for_writers() の抜粋)
if ( 'post' !== $data['post_type'] || ! is_user_logged_in() ) {
return $data;
}
if ( in_array( $data['post_status'], array( 'publish', 'future' ), true ) && ! current_user_can( 'publish_posts' ) ) {
$data['post_status'] = 'pending';
}
return $data;
ログインしていない処理(予約投稿の自動公開など)は対象外にしています。予約投稿は、公開の権限を持つ人が予約した時点で確認済みだからです。
レビュー待ちの通知と、公開前チェック
レビュー担当へのメール通知
記事のステータスが変わるときに呼ばれる transition_post_status フックで、「レビュー待ち」になった記事をレビュー担当に知らせます。
includes/review-flow.php(curation_tools_notify_pending_review() の抜粋)
if ( 'pending' !== $new_status || 'pending' === $old_status || 'post' !== $post->post_type ) {
return;
}
$reviewers = get_users(
array(
'capability' => 'curation_review_posts',
'exclude' => array( (int) $post->post_author ),
'fields' => array( 'user_email' ),
)
);
メールには記事のタイトル、執筆者、編集画面の URL を書きます。本番でメールを確実に届けるには、サーバーのメール送信設定(SMTP やメール配信サービス)が別途必要です。サンプルの Docker 環境ではメールは送信されないため、検証では送信内容を記録する仕組みを一時的に入れて確認しました。
公開前チェック
第4回の引用元カードでは、必須項目の入力漏れをエディターで「警告」するだけにしていました。今回、公開するときにだけ、入力漏れがあれば公開を止めるようにしました。
includes/review-flow.php(curation_tools_check_before_publish() の抜粋)
$post_id = isset( $prepared->ID ) ? (int) $prepared->ID : 0;
$status = $prepared->post_status ?? ( $post_id ? get_post_status( $post_id ) : 'draft' );
if ( ! in_array( $status, array( 'publish', 'future' ), true ) ) {
return $prepared;
}
$content = $prepared->post_content ?? ( $post_id ? (string) get_post_field( 'post_content', $post_id, 'raw' ) : '' );
$meta = is_array( $request['meta'] ) ? $request['meta'] : array();
$problems = curation_tools_publish_problems( $content, $post_id, $meta );
if ( empty( $problems ) ) {
return $prepared;
}
return new WP_Error(
'curation_publish_check',
'公開前チェックで問題が見つかりました: ' . implode( ' ', $problems ),
array(
'status' => 400,
'problems' => $problems,
)
);
チェックする項目は次のとおりです。
- 引用元カード:引用文・出典名が空でないこと、出典 URL が http(s) の URL であること、確認日が実在する日付であること
- 監修(第6回):監修者を設定した記事に監修日があること
ブロックエディターは REST API で保存するので、rest_pre_insert_post フィルターでエラーを返すと、エディターに「公開に失敗しました。公開前チェックで問題が見つかりました: …」と理由が表示されます。下書き保存、レビュー待ちへの提出、自動保存は止めません。
このチェックは入力漏れを見つけるもので、引用が適法かどうか、内容が正しいかどうかは判断しません。最終的な確認は、レビュー担当の人が行います。
バックアップと復元
何をバックアップするか
| 対象 | 内容 | このサンプルでの扱い |
|---|---|---|
| データベース | 記事・設定・ユーザー・メタなど | backup.sh で書き出す |
| uploads | アップロードした画像など | backup.sh でアーカイブ |
| languages | 日本語化の翻訳ファイル | backup.sh でアーカイブ(再取得も可能) |
| テーマ・プラグインのコード | 子テーマ・自作プラグイン | Git などのリポジトリで管理し、そこから戻す |
| WordPress 本体 | コアのファイル | 同じバージョンを入れ直す |
scripts/backup.sh(抜粋)
stamp="$(date +%Y%m%d-%H%M%S)"
dir="backups/${stamp}"
mkdir -p "${dir}"
chmod 700 backups "${dir}"
docker compose exec -T db sh -c 'MYSQL_PWD="$MYSQL_PASSWORD" exec mysqldump -u"$MYSQL_USER" --single-transaction --no-tablespaces --default-character-set=utf8mb4 "$MYSQL_DATABASE"' > "${dir}/db.sql"
wpcli sh -c 'cd /var/www/html/wp-content && mkdir -p uploads languages && tar czf - uploads languages' > "${dir}/wp-content.tar.gz"
当初は WP-CLI の wp db export を使う予定でしたが、WP-CLI の Docker イメージに入っているデータベースのクライアントが、MySQL 8.4 の標準の認証方式(caching_sha2_password)に対応しておらず、接続できませんでした。そこで、データベースのコンテナに入っている mysqldump で書き出しています。パスワードはコマンドラインに書かず、コンテナの環境変数から渡しています。
📰 出典:MySQL 8.4 Reference Manual「Caching SHA-2 Pluggable Authentication」
データベースのダンプには、ユーザーのパスワードのハッシュなどが含まれます。backups/ は .gitignore に入れてリポジトリに含めず、フォルダの権限も所有者だけにしています。本番では、サーバーとは別の場所(別のストレージなど)にも保管してください。
復元できることを確かめる
バックアップは、復元できて初めて意味があります。検証では、環境をデータごと削除してから復元しました。
./scripts/backup.sh
docker compose down -v # データベースと WordPress 本体のボリュームごと削除
docker compose up -d
./scripts/restore.sh backups/<日時>
restore.sh は、データベースを取り込み、uploads と翻訳ファイルを戻し、キャッシュとリライトルール(URL の対応表)を作り直します。復元先の URL が違う場合は、復元後に wp search-replace で URL を置き換えます。
📰 出典:WordPress Developer Resources「WordPress Backups」
更新の手順:ステージングで確認してから本番へ
このサンプルは、連載の再現性のために WordPress 6.8 系の特定のバージョンに固定し、自動更新も止めています。実際のサイトでは、セキュリティ更新を止めずに適用し続ける必要があります。WordPress が公式にサポートするのは最新版だけです。
更新は、次の手順を基本にします。
- 本番のバックアップを取る(
backup.shと同じ内容) - ステージング(本番と同じ構成の確認用環境)に本番のバックアップを復元する
- ステージングで更新する(WordPress 本体の最新のマイナー版、テーマ、プラグインの順)
- 確認する:トップ・記事・まとめ・ランキングの表示、ブロックエディターでの編集と公開、引用元カード・監修パネル・PR 表記、閲覧数の記録、構造化データ・OGP の出力
- 本番を更新する。問題があれば、手順1のバックアップから戻す
# 更新できるものの確認(ステージングで)
docker compose run --rm wpcli wp core check-update
docker compose run --rm wpcli wp plugin list --update=available
docker compose run --rm wpcli wp theme list --update=available
Docker で運用する場合は、WordPress 本体を「イメージのタグを上げて入れ替える」のか「WordPress の更新機能で更新する」のかを決めておきます。どちらの方法でも、更新前のバックアップと、ステージングでの確認は同じです。
📰 出典:WordPress.org Documentation「Updating WordPress」
また、サンプルの compose.yaml では、管理画面からテーマ・プラグインのファイルを編集する機能を DISALLOW_FILE_EDIT で止めています。管理者のアカウントが乗っ取られたときの被害を小さくするための設定で、本番でも有効にしておくことをおすすめします。
動作確認の方法
第0回・第2回の手順の後、プラグインをビルドしてから確認しました(筆者の環境で確認済みです)。
- ロール一覧に「ライター」「編集長」が追加され、ライターの権限は read・edit_posts・delete_posts・upload_files だけ
- ライターを指定して
wp post create --post_status=publish --user=<ライター>を実行すると、記事は「レビュー待ち」になる。管理者を指定した場合は公開される - ライターのアプリケーションパスワードで REST API から公開しようとすると 403。レビュー待ちでの提出と画像のアップロードは成功する
- レビュー待ちになると、管理者と編集長(執筆者本人を除く)あてに通知が作られる
- 編集長が REST API で、出典名・URL・確認日に不備のある引用元カードを含む記事を公開しようとすると 400 で、問題点の一覧が返る。下書き保存は成功し、不備のない記事は公開できる。監修者だけを設定して監修日がない記事も 400
- ヘッドレスブラウザで編集長としてブロックエディターから公開すると、公開前チェックのエラーが表示され、記事は下書きのまま
backup.sh→docker compose down -v→docker compose up -d→restore.shで、記事(レビュー待ちを含む)・ユーザー・ロール・画像・日本語化がすべて元に戻る
つまずきやすい点・セキュリティ上の注意
- 権限の棚卸し:退職・契約終了した書き手のアカウントや、使われていない管理者アカウントは定期的に削除・権限変更します。アプリケーションパスワードも同様です。
- 通知メールが届かない:多くのサーバーでは、WordPress 標準のメール送信は迷惑メール扱いや不達になりがちです。送信の仕組みを決めて、実際に届くことを確認します。
- バックアップの保管と保存期間:同じサーバーにしかないバックアップは、サーバー障害で一緒に失われます。何世代残すか、誰がアクセスできるかも決めます。
- ロールの変更は既存ユーザーに影響する:ロールの権限を変えると、そのロールの全員に反映されます。変更はステージングで確認してから行います。
連載のまとめ:全12回の振り返り
この連載では、WordPress 6.8 の標準機能と、子テーマ1つ・小さな自作プラグイン1つで、キュレーションメディアを組み立ててきました。
| 回 | タイトル | 主に作ったもの |
|---|---|---|
| 0 | キュレーションメディアの要件と「低品質まとめ」の教訓 ― Docker で WordPress 6.8 の土台を作る | Docker 環境、子テーマ・プラグインの雛形、編集方針 |
| 1 | theme.json でデザイントークンを定義する(子テーマの基本) | 色・文字・余白、スタイルバリエーション |
| 2 | カテゴリ・タグ・「特集」― タクソノミー設計 | 特集タクソノミー、分類のルール |
| 3 | 記事・まとめ・ランキングのテンプレートとパターン | 3種類の記事テンプレート、初期パターン |
| 4 | 「引用元カード」ブロック ― 著作権法第32条の引用を仕組みで守る | 動的ブロック、必須項目の警告 |
| 5 | 「まとめリスト」と「比較表」をパターンとブロックスタイルで作る | まとめリスト、比較表、スマホ対応 |
| 6 | 著者・監修者プロフィールを表示する(E-E-A-T の考え方) | 著者ボックス、監修パネル |
| 7 | 人気記事ランキングと関連記事を小さなプラグインで作る | 閲覧数の REST API、ランキング・関連記事 |
| 8 | 目次と構造化データ(JSON-LD: Article / BreadcrumbList / ItemList) | 目次、パンくず、JSON-LD |
| 9 | OGP・SNS カードと「広告・PR表記」― ステマ規制への対応 | OGP、PR 表記、rel=”sponsored” |
| 10 | 表示速度の改善 ― 画像サイズ・キャッシュ・計測 | 画像の sizes、キャッシュ設定、計測 |
| 11 | 権限・編集フロー・運用 ― 下書きレビュー、バックアップ、更新(本記事) | ロール、公開前チェック、バックアップと復元 |
振り返ると、どの回にも共通する考え方がありました。
- 見た目は子テーマ、機能はプラグイン:テーマを替えても残すべきもの(特集、引用元カード、PR 表記、構造化データ、ロール)はプラグインに置いた
- 品質は仕組みで支える:出典の書き忘れ、PR 表記の付け忘れ、監修日の入力漏れを、ブロックの入力欄・自動検出・公開前チェックで防ぐ。ただし最終判断は人が行う
- 表示とデータを同じところから作る:パンくずと構造化データ、目次と見出しの id のように、ずれが起きない作りにする
- 保証できないことは保証しない:構造化データはリッチリザルトを、PR 表記は適法性を、閲覧数は正確さを保証しない。その前提を発注者と共有する
本番公開までに残っている作業
連載のサンプルは「仕組みを理解するための最小構成」です。実際にサイトを公開するまでには、次の作業が残っています。
| 項目 | 決めること・やること |
|---|---|
| ホスティングの選択 | レンタルサーバー・マネージド WordPress・クラウドのどれにするか。ページキャッシュ・CDN・バックアップ機能・PHP と MySQL のバージョン・ステージング環境の有無(第10回) |
| ドメインと HTTPS | ドメインの取得・管理者、SSL 証明書の更新 |
| バックアップ | 自動化の方法、保存先、保存期間、復元の練習の頻度 |
| 更新 | WordPress 本体・テーマ・プラグインの更新の担当と頻度、ステージングでの確認項目。サンプルは特定のバージョンに固定しているが、本番では更新し続ける |
| セキュリティ | 管理者の二要素認証、ログインの保護、不要なアカウントとプラグインの削除、監視 |
| メール送信 | 通知メールや問い合わせフォームの送信方法 |
| 法務・運用ルール | プライバシーポリシー(閲覧数の計測、アクセス解析を含む)、引用ルール、PR 表記の文言、編集ルール |
発注者向けメモ:保守契約で「誰がいつ何をするか」を明文化する
公開後の運用は、毎月・毎年続くコストです。発注時には、機能の一覧だけでなく、次の点を保守契約(または運用の取り決め)に書いておきましょう。
- 更新:WordPress 本体・テーマ・プラグインを、誰が、どの頻度で、どこで確認してから更新するか。緊急のセキュリティ更新はどう扱うか
- バックアップと復旧:何を、どこに、どのくらいの期間残すか。障害時に誰が、どのくらいの時間で復旧するか。復元の練習をするか
- 権限の管理:アカウントの発行・削除の手順、権限の棚卸しの頻度
- 編集フロー:誰が書き、誰がレビューし、誰が公開するか。公開前チェックの項目を増やすときの手続き
工数が増えるのは、承認の段階を増やす場合(例:法務の確認を必須にする)、複数サイトをまとめて運用する場合、24時間の監視や短時間での復旧を求める場合です。
打ち合わせでは、次のように聞いてみてください。
- 「バックアップから実際に復元できることを、いつ、どのように確認していますか?」
- 「WordPress やプラグインのセキュリティ更新が出たとき、何日以内に、どんな手順で適用しますか?」
- 「ライターが誤って公開してしまうことは、仕組みとして防げていますか?」
まとめ
第11回では、権限・編集フロー・運用を扱いました。
- 独自ロール「ライター」は公開できず、公開しようとしてもレビュー待ちになる。通知の送り先は権限で決める
- 公開時には引用元カードの必須項目と監修日を自動でチェックし、不備があれば理由を表示して止める
- バックアップはデータベースと uploads などを対象にし、環境を削除してからの復元まで確認する
- 更新はバックアップ → ステージングで確認 → 本番の順で行い、本番ではセキュリティ更新を止めない
全12回にわたってお読みいただき、ありがとうございました。連載のコードは、機能ごとに小さく分けてあります。自社のメディアで使う機能から、少しずつ取り入れてみてください。
この連載の記事一覧
この記事は連載「WordPressで作るキュレーションメディア」の1回です。連載のほかの回は次のとおりです(連載の一覧ページ)。
- 【WordPressで作るキュレーションメディア 第0回】キュレーションメディアの要件と「低品質まとめ」の教訓 ― Docker で WordPress 6.8 の土台を作る
- 【WordPressで作るキュレーションメディア 第1回】theme.json でデザイントークンを定義する(子テーマの基本)
- 【WordPressで作るキュレーションメディア 第2回】カテゴリ・タグ・「特集」― タクソノミー設計
- 【WordPressで作るキュレーションメディア 第3回】記事・まとめ・ランキングのテンプレートとパターン
- 【WordPressで作るキュレーションメディア 第4回】「引用元カード」ブロック ― 著作権法第32条の引用を仕組みで守る
- 【WordPressで作るキュレーションメディア 第5回】「まとめリスト」と「比較表」をパターンとブロックスタイルで作る
- 【WordPressで作るキュレーションメディア 第6回】著者・監修者プロフィールを表示する(E-E-A-T の考え方)
- 【WordPressで作るキュレーションメディア 第7回】人気記事ランキングと関連記事を小さなプラグインで作る
- 【WordPressで作るキュレーションメディア 第8回】目次と構造化データ(JSON-LD: Article / BreadcrumbList / ItemList)
- 【WordPressで作るキュレーションメディア 第9回】OGP・SNS カードと「広告・PR表記」― ステマ規制への対応
- 【WordPressで作るキュレーションメディア 第10回】表示速度の改善 ― 画像サイズ・キャッシュ・計測
- 【WordPressで作るキュレーションメディア 第11回】権限・編集フロー・運用 ― 下書きレビュー、バックアップ、更新(この記事)









