第5回では、固定ページにショートコードを貼り、NEWS候補一覧を表示する管理画面を作りました。
固定ページに、
というショートコードを貼ることで、登録済みのNEWS候補を一覧で確認できるようになりました。
一覧画面では、表示先NEWS、承認ステータス、表示フラグ、X投稿ステータス、カテゴリ、NEWS表示文、元記事リンク、編集リンクを確認できるようにしました。
ただし、第5回の段階では、固定ページ側はあくまで一覧確認用でした。
NEWS表示文を修正したり、表示先NEWSを変更したり、承認ステータスを変更したりする場合は、一覧の「編集」リンクからWordPressダッシュボード側のNEWS候補編集画面へ移動する必要がありました。
この方法でも、初期運用としては問題ありません。
WordPressの管理画面に慣れている人であれば、ダッシュボード側のNEWS候補編集画面で修正できます。
しかし、NEWS管理を行う担当者が増えてきたり、WordPressの細かい操作に慣れていない人がNEWS候補だけを確認・承認するようになったりすると、ダッシュボード全体を見せずに、専用の固定ページだけで作業できる方が分かりやすくなります。
そこで第6回では、固定ページ側のNEWS候補管理画面を発展させます。
固定ページ上で、NEWS候補の内容を確認するだけでなく、NEWS表示文、表示先NEWS、表示フラグ、承認ステータス、X投稿ステータス、メモを編集・保存できるようにします。
これにより、担当者はWordPressダッシュボードの細かい操作を知らなくても、TECN NEWS候補管理ページだけで日々のNEWS確認作業を進められるようになります。
H2-1 第6回で行うこと
第6回では、固定ページ側のNEWS候補管理画面を、一覧確認画面から編集・承認できる担当者UIへ発展させます。
第5回の固定ページでは、NEWS候補一覧を表示することができました。
しかし、編集そのものはダッシュボード側のNEWS候補編集画面で行う形でした。
第6回では、固定ページ上で次の操作ができるようにします。
NEWS表示文を編集する。
表示先NEWSを選択する。
表示対象にするかどうかを切り替える。
承認ステータスを変更する。
X投稿ステータスを変更する。
メモを編集する。
保存ボタンで更新する。
必要な場合は、ダッシュボード側の詳細編集画面へ移動する。
つまり、第6回では、固定ページ側を単なる確認画面ではなく、担当者が日常的に使えるNEWS管理画面にします。
ただし、今回の初版では、NEWS候補が複数ある場合に、すべてのNEWS候補をカード形式で表示し、それぞれのカード上で編集できる形にします。
たとえばNEWS候補が10件あれば、10件分の編集カードが表示されます。
件数が多くなってきた場合は、将来的に「一覧画面から1件を選んで詳細編集する方式」や、「未確認だけ表示する方式」へ改善できます。
しかし、まずは初版として、固定ページ側で編集・承認・保存までできることを優先します。
H2-2 第5回まででできていること
第5回までで、TECN NEWS候補管理 V1はかなり形になってきました。
第3回では、NEWS候補を保存するためのカスタム投稿タイプを作りました。
WordPress管理画面に「NEWS候補」という専用メニューを追加し、通常の記事とは別にNEWS候補を管理できるようにしました。
第4回では、NEWS候補の編集画面に入力項目を追加しました。
元記事URL、カテゴリ、NEWS表示文、表示先NEWS、表示フラグ、承認ステータス、X投稿文案、X投稿ステータス、メモを入力・保存できるようにしました。
第5回では、固定ページにNEWS候補一覧を表示するショートコードを作りました。
固定ページ「TECN NEWS候補管理」に、
を貼ることで、担当者がNEWS候補を一覧で確認できるようになりました。
ここまでで、次のことができる状態になっています。
NEWS候補を登録できる。
NEWS候補に必要な情報を保存できる。
ダッシュボード側でNEWS候補を編集できる。
固定ページ側でNEWS候補一覧を確認できる。
元記事を開ける。
編集リンクからダッシュボード側のNEWS候補編集画面へ移動できる。
第6回では、ここに固定ページ側での編集・承認機能を追加します。
H2-3 なぜ固定ページ側にも編集UIを作るのか
WordPressには、もともと管理画面があります。
NEWS候補も、ダッシュボード側の「NEWS候補」メニューから編集できます。
そのため、技術的には固定ページ側に編集UIを作らなくても運用はできます。
しかし、実務では、担当者全員がWordPressのダッシュボード操作に慣れているとは限りません。
WordPressの管理画面には、投稿、固定ページ、外観、プラグイン、ユーザー、設定など、さまざまなメニューがあります。
NEWS候補だけを確認したい担当者にとっては、ダッシュボード全体が見えるとかえって分かりにくい場合があります。
そこで、固定ページ側に専用のNEWS管理UIを作ります。
担当者は、固定ページ「TECN NEWS候補管理」を開くだけで、NEWS候補を確認できます。
さらに、第6回では、そのページ上でNEWS表示文や承認ステータスを変更できるようにします。
これにより、担当者はWordPressの細かい操作を覚えなくても、NEWS管理に必要な作業だけを行えるようになります。
一方で、ダッシュボード側の管理機能も残します。
つまり、第6回以降は、次の2つの管理方法が使えるようになります。
1つ目は、ダッシュボード側の管理です。
WordPress操作に慣れている人は、NEWS候補一覧やNEWS候補編集画面から詳細に管理できます。
2つ目は、固定ページ側の管理です。
NEWS担当者は、固定ページの専用UIから、NEWS表示文、表示先NEWS、表示フラグ、承認ステータスなどを編集できます。
このように、ダッシュボード側と固定ページ側の両方から管理できる形にしておくと、運用の幅が広がります。
H2-4 第6回の完成イメージ
第6回の完成イメージは、固定ページ上にNEWS候補ごとのカードが表示される形です。
各カードには、NEWS候補のタイトル、登録日、元記事URL、カテゴリ、NEWS表示文、表示先NEWS、承認ステータス、表示フラグ、X投稿ステータス、メモ、保存ボタンが表示されます。
担当者は、カードごとに内容を確認します。
NEWS表示文に修正が必要であれば、テキストエリアで修正します。
表示先NEWSが違っていれば、プルダウンから正しい表示先を選びます。
表示対象にする場合は、チェックボックスをONにします。
確認が終わったら、承認ステータスを「承認済み」に変更します。
X投稿を行った場合は、X投稿ステータスを「投稿済み」に変更します。
補足事項があれば、メモ欄に入力します。
最後に「このNEWS候補を保存」ボタンを押します。
保存後、画面上に「NEWS候補を更新しました。」というメッセージが表示されます。
ページを再読み込みしても変更内容が残っていれば、固定ページ側からの更新処理が成功しています。
また、ダッシュボード側のNEWS候補編集画面を開いても、同じ値が反映されています。
つまり、第6回では、固定ページ側とダッシュボード側の両方から同じNEWS候補データを管理できるようになります。
H2-5 第6回で更新できる項目
第6回の固定ページ側UIでは、すべての項目を編集対象にするわけではありません。
編集できる項目と、表示確認だけにする項目を分けます。
固定ページ側で編集できる項目は、次の通りです。
NEWS表示文。
表示先NEWS。
表示フラグ。
承認ステータス。
X投稿ステータス。
メモ。
これらは、担当者が日常的に変更する可能性が高い項目です。
たとえば、NEWS表示文は読者に見せる文面なので、担当者が修正することがあります。
表示先NEWSは、TECN NEWS、どこの国NEWS、在庫管理 / DX NEWSなど、どのNEWS枠に表示するかを決める項目です。
表示フラグは、実際に表示対象にするかどうかを決める項目です。
承認ステータスは、未確認、修正中、承認済み、非表示のように、NEWS候補の状態を管理する項目です。
X投稿ステータスは、X投稿を行うか、投稿済みか、投稿しないかを管理する項目です。
メモは、判断理由や注意点を残すために使います。
一方で、固定ページ側では表示確認だけにする項目もあります。
元記事URL。
カテゴリ。
NEWS候補のタイトル。
登録日。
これらは、基本的には元記事情報として扱います。
大きく修正する必要がある場合は、ダッシュボード側のNEWS候補編集画面で変更します。
このように、固定ページ側では日常運用に必要な項目だけを編集できるようにし、詳細編集はダッシュボード側に残します。
H2-6 第6回の実装方針
第6回では、第5回で作ったショートコード
第5回では、NEWS候補を一覧テーブルで表示していました。
第6回では、一覧テーブルではなく、NEWS候補ごとのカード形式に変更します。
カードの中に編集フォームを入れ、それぞれのNEWS候補を固定ページ上で更新できるようにします。
ここで注意が必要なのは、同じショートコード名を使うことです。
第5回と同じ、
を使います。
そのため、新しいスニペットを追加するのではなく、第5回で作成したスニペットの中身を第6回版に丸ごと差し替えます。
同じ関数名や同じショートコードを別スニペットで重複登録すると、エラーになる可能性があります。
安全な手順は次の通りです。
Code Snippetsで「TECN NEWS候補管理 V1:NEWS候補一覧ショートコード」を開く。
既存コードをすべて削除する。
第6回版のコードを貼り付ける。
保存して有効化する。
固定ページ「TECN NEWS候補管理」を再表示する。
これで、同じ固定ページ、同じショートコードのまま、固定ページ側のUIを第6回版に更新できます。
H2-7 第6回版ショートコードのコード
それでは、第6回版のコードを用意します。
Code Snippetsに貼り付ける場合は、先頭の <?php は入れません。
第5回で作ったスニペット、
TECN NEWS候補管理 V1:NEWS候補一覧ショートコード
を開き、中身を以下のコードに差し替えます。
/**
* TECN NEWS候補管理 V1
* 固定ページ側 NEWS候補編集・承認UI
*
* 固定ページに [apice_news_admin] を貼ると、
* ログイン済みで投稿編集権限のある担当者が
* NEWS候補を一覧確認し、その場で編集・承認できます。
*/
function apice_news_admin_shortcode() {
// ログイン確認
if ( ! is_user_logged_in() ) {
return '<div style="padding:16px; border:1px solid #ddd; background:#fafafa;">このページを見るにはログインが必要です。</div>';
}
// 権限確認
if ( ! current_user_can( 'edit_posts' ) ) {
return '<div style="padding:16px; border:1px solid #ddd; background:#fafafa;">このページを表示する権限がありません。</div>';
}
$message = '';
/**
* 固定ページ側からの更新処理
*/
if (
isset( $_POST['apice_news_admin_action'] ) &&
$_POST['apice_news_admin_action'] === 'update_news_candidate'
) {
$post_id = isset( $_POST['apice_news_post_id'] ) ? intval( $_POST['apice_news_post_id'] ) : 0;
if (
$post_id > 0 &&
get_post_type( $post_id ) === 'apice_news_candidate' &&
current_user_can( 'edit_post', $post_id ) &&
isset( $_POST['apice_news_admin_nonce'] ) &&
wp_verify_nonce( $_POST['apice_news_admin_nonce'], 'apice_news_admin_update_' . $post_id )
) {
$allowed_news_targets = array(
'',
'TECN NEWS',
'ダイソーNEWS',
'どこの国NEWS',
'LDAC / Bluetooth NEWS',
'在庫管理 / DX NEWS',
'TECNおすすめ',
'非表示',
);
$allowed_approval_statuses = array(
'未確認',
'修正中',
'承認済み',
'非表示',
);
$allowed_x_post_statuses = array(
'未投稿',
'投稿済み',
'投稿しない',
);
if ( isset( $_POST['news_text'] ) ) {
update_post_meta(
$post_id,
'news_text',
sanitize_textarea_field( $_POST['news_text'] )
);
}
if (
isset( $_POST['news_target'] ) &&
in_array( $_POST['news_target'], $allowed_news_targets, true )
) {
update_post_meta(
$post_id,
'news_target',
sanitize_text_field( $_POST['news_target'] )
);
}
$display_flag = isset( $_POST['display_flag'] ) ? '1' : '0';
update_post_meta( $post_id, 'display_flag', $display_flag );
if (
isset( $_POST['approval_status'] ) &&
in_array( $_POST['approval_status'], $allowed_approval_statuses, true )
) {
update_post_meta(
$post_id,
'approval_status',
sanitize_text_field( $_POST['approval_status'] )
);
}
if (
isset( $_POST['x_post_status'] ) &&
in_array( $_POST['x_post_status'], $allowed_x_post_statuses, true )
) {
update_post_meta(
$post_id,
'x_post_status',
sanitize_text_field( $_POST['x_post_status'] )
);
}
if ( isset( $_POST['memo'] ) ) {
update_post_meta(
$post_id,
'memo',
sanitize_textarea_field( $_POST['memo'] )
);
}
$message = 'NEWS候補を更新しました。';
} else {
$message = '更新できませんでした。権限または確認キーを確認してください。';
}
}
$args = array(
'post_type' => 'apice_news_candidate',
'post_status' => array( 'publish', 'draft', 'pending', 'private' ),
'posts_per_page' => 50,
'orderby' => 'date',
'order' => 'DESC',
);
$news_query = new WP_Query( $args );
ob_start();
?>
<div class="apice-news-admin-wrap">
<style>
.apice-news-admin-wrap {
margin: 24px 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, "Noto Sans JP", sans-serif;
}
.apice-news-admin-title {
font-size: 24px;
font-weight: 800;
margin: 0 0 8px 0;
color: #111827;
}
.apice-news-admin-lead {
margin: 0 0 20px 0;
color: #4b5563;
line-height: 1.8;
}
.apice-news-admin-message {
padding: 12px 14px;
margin: 0 0 18px 0;
border-radius: 10px;
background: #ecfdf5;
border: 1px solid #bbf7d0;
color: #166534;
font-weight: 700;
}
.apice-news-admin-summary {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 0 0 18px 0;
}
.apice-news-admin-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 7px 12px;
border-radius: 999px;
background: #f3f4f6;
color: #374151;
font-size: 13px;
font-weight: 700;
}
.apice-news-card {
border: 1px solid #e5e7eb;
border-radius: 16px;
background: #ffffff;
box-shadow: 0 5px 16px rgba(0,0,0,0.05);
margin: 0 0 18px 0;
overflow: hidden;
}
.apice-news-card-header {
padding: 16px 18px;
background: #f9fafb;
border-bottom: 1px solid #e5e7eb;
}
.apice-news-card-title {
font-size: 17px;
font-weight: 800;
color: #111827;
line-height: 1.6;
margin: 0 0 4px 0;
}
.apice-news-card-meta {
font-size: 12px;
color: #6b7280;
}
.apice-news-card-body {
padding: 18px;
}
.apice-news-form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.apice-news-form-field {
margin: 0 0 14px 0;
}
.apice-news-form-field-full {
grid-column: 1 / -1;
}
.apice-news-form-field label {
display: block;
font-size: 13px;
font-weight: 800;
color: #374151;
margin: 0 0 6px 0;
}
.apice-news-form-field input[type="text"],
.apice-news-form-field select,
.apice-news-form-field textarea {
width: 100%;
max-width: 100%;
padding: 8px 10px;
border: 1px solid #d1d5db;
border-radius: 8px;
background: #ffffff;
font-size: 14px;
box-sizing: border-box;
}
.apice-news-form-field textarea {
min-height: 90px;
line-height: 1.6;
}
.apice-news-readonly {
padding: 8px 10px;
background: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 8px;
color: #374151;
font-size: 14px;
line-height: 1.6;
}
.apice-news-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
margin-top: 10px;
padding-top: 14px;
border-top: 1px solid #e5e7eb;
}
.apice-news-save-button {
appearance: none;
border: none;
border-radius: 999px;
padding: 10px 18px;
background: #2563eb;
color: #ffffff;
font-weight: 800;
cursor: pointer;
}
.apice-news-save-button:hover {
background: #1d4ed8;
}
.apice-news-admin-link {
display: inline-block;
color: #2563eb;
font-weight: 700;
text-decoration: none;
}
.apice-news-admin-link:hover {
text-decoration: underline;
}
.apice-news-checkbox-label {
display: inline-flex !important;
align-items: center;
gap: 8px;
font-weight: 700 !important;
}
.apice-news-empty {
padding: 18px;
border: 1px solid #e5e7eb;
border-radius: 10px;
background: #fafafa;
color: #4b5563;
}
@media (max-width: 768px) {
.apice-news-form-grid {
grid-template-columns: 1fr;
}
}
</style>
<h2 class="apice-news-admin-title">NEWS候補管理</h2>
<p class="apice-news-admin-lead">
登録済みのNEWS候補を確認し、この画面からNEWS表示文、表示先NEWS、表示フラグ、承認ステータス、X投稿ステータス、メモを更新できます。
元記事URLやカテゴリなどを含めて大きく修正する場合は、ダッシュボード側のNEWS候補編集画面も利用できます。
</p>
<?php if ( ! empty( $message ) ) : ?>
<div class="apice-news-admin-message">
<?php echo esc_html( $message ); ?>
</div>
<?php endif; ?>
<div class="apice-news-admin-summary">
<span class="apice-news-admin-badge">表示件数:<?php echo esc_html( $news_query->post_count ); ?>件</span>
<span class="apice-news-admin-badge">固定ページ側編集UI:有効</span>
</div>
<?php if ( $news_query->have_posts() ) : ?>
<?php while ( $news_query->have_posts() ) : ?>
<?php
$news_query->the_post();
$post_id = get_the_ID();
$source_url = get_post_meta( $post_id, 'source_url', true );
$source_category = get_post_meta( $post_id, 'source_category', true );
$news_text = get_post_meta( $post_id, 'news_text', true );
$news_target = get_post_meta( $post_id, 'news_target', true );
$display_flag = get_post_meta( $post_id, 'display_flag', true );
$approval_status = get_post_meta( $post_id, 'approval_status', true );
$x_post_status = get_post_meta( $post_id, 'x_post_status', true );
$memo = get_post_meta( $post_id, 'memo', true );
if ( $approval_status === '' ) {
$approval_status = '未確認';
}
if ( $x_post_status === '' ) {
$x_post_status = '未投稿';
}
$edit_link = get_edit_post_link( $post_id );
?>
<div class="apice-news-card">
<div class="apice-news-card-header">
<div class="apice-news-card-title">
<?php echo esc_html( get_the_title() ); ?>
</div>
<div class="apice-news-card-meta">
登録日:<?php echo esc_html( get_the_date( 'Y/m/d H:i' ) ); ?>
ID:<?php echo esc_html( $post_id ); ?>
</div>
</div>
<div class="apice-news-card-body">
<form method="post">
<input type="hidden" name="apice_news_admin_action" value="update_news_candidate">
<input type="hidden" name="apice_news_post_id" value="<?php echo esc_attr( $post_id ); ?>">
<?php wp_nonce_field( 'apice_news_admin_update_' . $post_id, 'apice_news_admin_nonce' ); ?>
<div class="apice-news-form-grid">
<div class="apice-news-form-field">
<label>元記事URL</label>
<div class="apice-news-readonly">
<?php if ( ! empty( $source_url ) ) : ?>
<a class="apice-news-admin-link" href="<?php echo esc_url( $source_url ); ?>" target="_blank" rel="noopener">
元記事を開く
</a>
<?php else : ?>
URL未設定
<?php endif; ?>
</div>
</div>
<div class="apice-news-form-field">
<label>カテゴリ</label>
<div class="apice-news-readonly">
<?php echo esc_html( $source_category !== '' ? $source_category : '未設定' ); ?>
</div>
</div>
<div class="apice-news-form-field apice-news-form-field-full">
<label for="news_text_<?php echo esc_attr( $post_id ); ?>">NEWS表示文</label>
<textarea id="news_text_<?php echo esc_attr( $post_id ); ?>" name="news_text"><?php echo esc_textarea( $news_text ); ?></textarea>
</div>
<div class="apice-news-form-field">
<label for="news_target_<?php echo esc_attr( $post_id ); ?>">表示先NEWS</label>
<select id="news_target_<?php echo esc_attr( $post_id ); ?>" name="news_target">
<option value="" <?php selected( $news_target, '' ); ?>>選択してください</option>
<option value="TECN NEWS" <?php selected( $news_target, 'TECN NEWS' ); ?>>TECN NEWS</option>
<option value="ダイソーNEWS" <?php selected( $news_target, 'ダイソーNEWS' ); ?>>ダイソーNEWS</option>
<option value="どこの国NEWS" <?php selected( $news_target, 'どこの国NEWS' ); ?>>どこの国NEWS</option>
<option value="LDAC / Bluetooth NEWS" <?php selected( $news_target, 'LDAC / Bluetooth NEWS' ); ?>>LDAC / Bluetooth NEWS</option>
<option value="在庫管理 / DX NEWS" <?php selected( $news_target, '在庫管理 / DX NEWS' ); ?>>在庫管理 / DX NEWS</option>
<option value="TECNおすすめ" <?php selected( $news_target, 'TECNおすすめ' ); ?>>TECNおすすめ</option>
<option value="非表示" <?php selected( $news_target, '非表示' ); ?>>非表示</option>
</select>
</div>
<div class="apice-news-form-field">
<label for="approval_status_<?php echo esc_attr( $post_id ); ?>">承認ステータス</label>
<select id="approval_status_<?php echo esc_attr( $post_id ); ?>" name="approval_status">
<option value="未確認" <?php selected( $approval_status, '未確認' ); ?>>未確認</option>
<option value="修正中" <?php selected( $approval_status, '修正中' ); ?>>修正中</option>
<option value="承認済み" <?php selected( $approval_status, '承認済み' ); ?>>承認済み</option>
<option value="非表示" <?php selected( $approval_status, '非表示' ); ?>>非表示</option>
</select>
</div>
<div class="apice-news-form-field">
<label class="apice-news-checkbox-label">
<input type="checkbox" name="display_flag" value="1" <?php checked( $display_flag, '1' ); ?>>
表示対象にする
</label>
</div>
<div class="apice-news-form-field">
<label for="x_post_status_<?php echo esc_attr( $post_id ); ?>">X投稿ステータス</label>
<select id="x_post_status_<?php echo esc_attr( $post_id ); ?>" name="x_post_status">
<option value="未投稿" <?php selected( $x_post_status, '未投稿' ); ?>>未投稿</option>
<option value="投稿済み" <?php selected( $x_post_status, '投稿済み' ); ?>>投稿済み</option>
<option value="投稿しない" <?php selected( $x_post_status, '投稿しない' ); ?>>投稿しない</option>
</select>
</div>
<div class="apice-news-form-field apice-news-form-field-full">
<label for="memo_<?php echo esc_attr( $post_id ); ?>">メモ</label>
<textarea id="memo_<?php echo esc_attr( $post_id ); ?>" name="memo"><?php echo esc_textarea( $memo ); ?></textarea>
</div>
</div>
<div class="apice-news-actions">
<button type="submit" class="apice-news-save-button">このNEWS候補を保存</button>
<?php if ( $edit_link ) : ?>
<a class="apice-news-admin-link" href="<?php echo esc_url( $edit_link ); ?>">
ダッシュボードで詳細編集
</a>
<?php endif; ?>
</div>
</form>
</div>
</div>
<?php endwhile; ?>
<?php else : ?>
<div class="apice-news-empty">
NEWS候補はまだ登録されていません。WordPress管理画面の「NEWS候補 > 新規追加」から登録してください。
</div>
<?php endif; ?>
</div>
<?php
wp_reset_postdata();
return ob_get_clean();
}
add_shortcode( 'apice_news_admin', 'apice_news_admin_shortcode' );
H2-8 コードのポイント
第6回のコードでは、固定ページ側からNEWS候補を更新するために、フォーム送信処理を追加しています。
まず、フォームから送信されたかどうかを確認します。
apice_news_admin_action という値が update_news_candidate になっている場合、固定ページ側からNEWS候補を更新しようとしていると判断します。
次に、apice_news_post_id から更新対象のNEWS候補IDを取得します。
そのIDが apice_news_candidate の投稿タイプであることを確認します。
さらに、現在のユーザーに、そのNEWS候補を編集できる権限があるか確認します。
そして、nonceを確認します。
nonceは、WordPressでフォーム送信時の安全性を高めるための仕組みです。
今回のコードでは、NEWS候補ごとにnonceを発行し、保存時に正しいnonceかどうかを確認しています。
これにより、意図しない外部からの更新を防ぎます。
更新処理では、選択肢を許可リストで確認しています。
たとえば、表示先NEWSは、あらかじめ許可した値だけを保存します。
TECN NEWS。
ダイソーNEWS。
どこの国NEWS。
LDAC / Bluetooth NEWS。
在庫管理 / DX NEWS。
TECNおすすめ。
非表示。
これ以外の値は保存しません。
承認ステータスも同様です。
未確認。
修正中。
承認済み。
非表示。
X投稿ステータスも、未投稿、投稿済み、投稿しないの中から保存します。
自由入力欄であるNEWS表示文とメモは、sanitize_textarea_field() を使って安全に保存します。
表示フラグは、チェックボックスがONなら1、OFFなら0として保存します。
このように、固定ページ側から保存できるようにしながらも、ログイン確認、権限確認、nonce確認、入力値の整理を行っています。
H2-9 固定ページ側で動作確認する
コードを差し替えたら、固定ページ「TECN NEWS候補管理」を開きます。
第5回の一覧テーブルではなく、NEWS候補ごとのカードが表示されていれば、第6回版に切り替わっています。
各カードには、NEWS候補のタイトル、登録日、ID、元記事URL、カテゴリ、NEWS表示文、表示先NEWS、承認ステータス、表示フラグ、X投稿ステータス、メモ、保存ボタンが表示されます。
まずはテスト用のNEWS候補で確認します。
NEWS表示文を少し変更します。
表示先NEWSを変更します。
表示対象にするチェックをONまたはOFFにします。
承認ステータスを承認済みにします。
X投稿ステータスを投稿済みにします。
メモを追加します。
その後、「このNEWS候補を保存」ボタンを押します。
画面上部に、
NEWS候補を更新しました。
と表示されれば、保存処理は成功です。
さらに、ページを再読み込みしても変更した値が残っていれば、固定ページ側からの更新が正しく保存されています。
H2-10 ダッシュボード側にも反映されることを確認する
固定ページ側で保存した内容は、NEWS候補のカスタムフィールドに保存されています。
そのため、ダッシュボード側のNEWS候補編集画面でも同じ値を確認できます。
固定ページ側でNEWS表示文や承認ステータスを変更したあと、WordPress管理画面の、
NEWS候補 > NEWS候補一覧
を開きます。
該当するNEWS候補を編集します。
第4回で作成した「NEWS候補 詳細情報」の入力欄を確認します。
固定ページ側で変更したNEWS表示文、表示先NEWS、表示フラグ、承認ステータス、X投稿ステータス、メモが反映されていれば成功です。
これにより、次の2つの管理ルートが成立します。
WordPress操作に慣れている人は、ダッシュボード側で編集できます。
NEWS担当者は、固定ページ側の専用UIで編集・承認できます。
どちらから編集しても、同じNEWS候補データを更新します。
この状態になると、TECN NEWS候補管理 V1は、実務運用にかなり近づきます。
H2-11 現在の固定ページUIの注意点
第6回の固定ページUIは、初版としては分かりやすい構成です。
ただし、現在の方式では、取得したNEWS候補をすべてカード形式で表示します。
たとえば、NEWS候補が10件あれば、10件分の編集カードが表示されます。
20件あれば、20件分の編集カードが表示されます。
そのため、NEWS候補の件数が増えてくると、画面が長くなる可能性があります。
ただし、初版では大きな問題ではありません。
第7回以降で作るバッチ処理では、対象を絞ってNEWS候補に取り込む予定です。
たとえば、直近7日または直近30日の公開済み記事だけを対象にする。
通常投稿だけを対象にする。
すでにNEWS候補化されている記事は除外する。
最大件数を決める。
このようにすれば、固定ページ側に表示されるNEWS候補が極端に増えすぎることを避けられます。
また、運用しながら必要になれば、次のような改善もできます。
未確認だけ表示する。
表示先NEWSで絞り込む。
承認済みを非表示にする。
一覧から1件を選んで詳細編集する方式にする。
保存後に一覧上部へ戻る。
一括承認ボタンを付ける。
初版では、まず固定ページ側で編集・承認・保存できることを優先します。
その後、実際の運用を見ながらブラッシュアップしていくのがよいです。
H2-12 第6回でできるようになったこと
第6回では、固定ページ側でNEWS候補を編集・承認できる担当者UIを作りました。
今回できるようになったことを整理します。
固定ページ「TECN NEWS候補管理」に、NEWS候補ごとの編集カードを表示できるようになりました。
NEWS表示文を固定ページ側から編集できるようになりました。
表示先NEWSを固定ページ側から変更できるようになりました。
表示フラグを固定ページ側からON/OFFできるようになりました。
承認ステータスを固定ページ側から変更できるようになりました。
X投稿ステータスを固定ページ側から変更できるようになりました。
メモを固定ページ側から編集できるようになりました。
保存ボタンで更新できるようになりました。
保存後に更新メッセージを表示できるようになりました。
必要に応じて、ダッシュボード側の詳細編集画面へ移動できるようになりました。
これにより、NEWS担当者は、WordPressダッシュボード全体を細かく操作しなくても、固定ページ側のNEWS管理画面で日常的な確認・承認作業を行えるようになりました。
一方で、ダッシュボード側の編集機能も残しています。
そのため、詳細な修正や管理者による確認はダッシュボード側で行い、日常の承認作業は固定ページ側で行う、という使い分けができます。
H2-13 第6回でまだやらないこと
第6回では、固定ページ側でNEWS候補を編集・承認できるようにしました。
ただし、まだ自動取り込みや自動表示までは行っていません。
まず、新規投稿記事を自動的にNEWS候補へ登録する処理は、まだ作っていません。
現時点では、NEWS候補は手動で登録するか、ダッシュボード側から追加する必要があります。
次に、承認済みNEWSを実際の記事本文へ自動差し込みする処理も、まだ作っていません。
たとえば、
で囲んだコメントブロック内に最新NEWSを差し込む処理は、次の段階です。
また、TECN全体NEWS、在庫管理NEWS、どこの国NEWS、LDAC / Bluetooth NEWSなど、表示先NEWSごとに最新7件を表示するショートコードも、まだ第6回では作っていません。
第6回の目的は、あくまで担当者が固定ページ側でNEWS候補を確認・編集・承認できる状態にすることです。
ここまでできたことで、次に新規投稿記事を自動的にNEWS候補へ取り込む準備が整いました。
H2-14 次回は新規投稿記事をNEWS候補へ自動登録する
次回は、新規投稿記事をNEWS候補へ自動登録する処理を作ります。
TECNに新しい記事を投稿したら、その記事情報をNEWS候補に取り込む仕組みです。
たとえば、毎日1回、公開済みの新規投稿を確認します。
まだNEWS候補に登録されていない記事だけを抽出します。
その記事のタイトル、URL、カテゴリ、公開日などを取得します。
そして、NEWS候補として自動登録します。
このとき、承認ステータスは未確認にします。
表示フラグはOFFにしておきます。
表示先NEWSは、カテゴリから仮設定できるようにします。
たとえば、在庫管理カテゴリの記事なら、在庫管理 / DX NEWS。
どこの国カテゴリの記事なら、どこの国NEWS。
LDACやBluetooth関連の記事なら、LDAC / Bluetooth NEWS。
このように仮設定しておくと、担当者は固定ページ側で確認し、必要に応じて修正・承認するだけで済みます。
この処理は、WordPress内部のWP-Cronを使って1日1回実行できます。
WP-Cronは、WordPressに用意されている定期実行の仕組みです。
GASの時間主導トリガーと完全に同じではありませんが、考え方は近いです。
WordPressにアクセスがあったタイミングで、予定時刻を過ぎた処理があれば実行されます。
TECNのように月3万PV前後のアクセスがあるサイトで、1日1回起動すればよい処理であれば、初版ではWP-Cronで十分です。
また、動作確認用として、固定ページ側に「新規投稿をNEWS候補に取り込む」手動実行ボタンを付けると便利です。
通常運用ではWP-Cronで1日1回自動実行。
確認や緊急時には固定ページ側の手動ボタンで実行。
この形にすると、かなり実務的になります。
H2-15 第6回のまとめ
第6回では、固定ページ側でNEWS候補を編集・承認できる担当者UIを作りました。
第5回までは、固定ページ側ではNEWS候補一覧を確認するだけでした。
第6回では、固定ページ側からNEWS表示文、表示先NEWS、表示フラグ、承認ステータス、X投稿ステータス、メモを編集・保存できるようにしました。
これにより、担当者はWordPressダッシュボード全体を細かく操作しなくても、専用のNEWS候補管理ページだけで日々の確認・承認作業を進められます。
また、ダッシュボード側の編集機能も残しているため、WordPressに慣れている管理者は従来通り詳細編集できます。
固定ページ側の専用UIと、ダッシュボード側の管理機能。
この2つを併用できる形にしたことで、TECN NEWS候補管理 V1はかなり実務的な管理システムに近づきました。
現時点では、固定ページ側にNEWS候補をすべてカード形式で表示しています。
件数が増えてきた場合は、一覧から1件を選ぶ方式や、未確認だけを表示する方式に改善できます。
ただし、初版としては、まず固定ページ側で編集・承認・保存できることが重要です。
次回は、新規投稿記事を自動的にNEWS候補へ取り込むバッチ処理を作ります。
WordPressのWP-Cronを使い、1日1回、新規投稿記事を確認し、まだNEWS候補になっていない記事をNEWS候補として登録する仕組みへ進みます。





コメント