編集記 / CMS改善ログ

この記事は、新びんえい通信の軽量CMSを実際に使いながら磨いた制作記録です。タブ幅、表示切替、記事選択、画像の扱い、保存履歴、そして公開ページの見え方まで、記事を書く流れそのものをテストケースにして改善を重ねました。

今回の狙いは、CMSを「作った」で止めず、記事制作の現場で本当に使えるかどうかを確かめることでした。本文を書き、画像を配置し、プレビューで確認し、公開ページを見て、違和感をコードへ戻す。この一巡のサイクルが回るようになると、編集画面はかなり頼れる道具に育ちます。

新びんえい通信CMSの編集改善イメージ
記事用に作成したメインビジュアル。編集画面、本文、プレビュー、画像管理、AI補助が同じ作業台に乗るイメージです。
書く本文HTMLをコードレベルで触りつつ、プレビューで即座に読める状態にしました。
見る公開記事、CMSプレビュー、トップ一覧、スマホ幅をスクリーンショットで確認しました。
戻す保存時に旧記事、CSV、キャッシュを履歴化し、復元用の材料を残す構造にしました。

実際の画面で見た状態

以下は、今回の検証で保存したスクリーンショットです。記事の出来上がり、CMS上での選択状態、幅調整後の編集画面、トップ一覧への反映を、同じ記事の中に記録しています。

公開記事のデスクトップ表示スクリーンショット
公開記事のデスクトップ表示。ローカル画像と記事カードの基本レイアウトが崩れないことを確認。
CMSで記事を選択している画面
CMSでこの記事を選択した状態。記事名プルダウン、メタ情報、本文HTML、プレビューが同時に見えます。
CMSの表示切替と幅調整を試した画面
表示切替と幅調整を試した画面。本文重視、プレビュー重視をその場で切り替えられます。
トップ一覧に記事が反映された画面
トップ一覧にも新規記事が反映。外部画像が読めない場合の欠落表示も破綻しないよう調整しました。
公開記事のスマホ幅スクリーンショット
スマホ幅の公開記事。横スクロールなしで、本文と画像が縦に自然に流れる状態を確認。

触って分かった違和感

最初に気になったのは、編集画面のプルダウンでした。カテゴリ選択と記事選択の役割が近く見え、今どの記事を触っているのかが一瞬ぼやけてしまいます。そこで、記事名の選択欄を独立させ、カテゴリ欄とは別の意味を持つUIとして整理しました。

もうひとつは横幅の問題です。本文HTMLを集中して直したいときもあれば、プレビューを広く見たいときもあります。固定幅の3ペインは見た目こそ整っていますが、作業の重心に合わせるのが難しいと感じました。そこで、一覧・メタ情報・HTML・プレビューをそれぞれオンオフでき、境界をドラッグして幅を変えられる構造に改修しています。

コードレベルで見た改善点

今回いちばん効果が大きかったのは、画像URLの扱いを公開テンプレートとCMSプレビューの両方で統一したことです。外部画像は従来どおりに使いつつ、今回のようなthumbs/...の同一サイト画像も自然に扱えるようにしました。

公開記事テンプレート i.php: 相対画像とOGP画像を分けて解決
function article_asset_url($value, $absolute = false) {
    if (preg_match('#^https?://#i', $value)) return $value;
    if (strpos($value, '//') === 0) return $absolute ? 'https:' . $value : $value;
    if ($value[0] === '/') return $absolute ? article_origin_url() . $value : $value;
    if (preg_match('/^[A-Za-z0-9.-]+\.[A-Za-z]{2,}(\/|$)/', $value)) {
        return $absolute ? 'https://' . $value : '//' . $value;
    }
    return $absolute ? article_origin_url() . $base . '/' . ltrim($value, '/') : $value;
}

記事本文に表示する画像は相対パスのまま使い、OGPのように絶対URLが必要な場所でだけ絶対URL化しています。これにより、ローカル検証ではhttp://localhost/xn--c5r/thumbs/...として、本番環境ではサイトURLとして正しく扱えるようになりました。

CMSプレビュー app.js: _cms から一段上のサイト画像として解決
function absoluteImage(src) {
  const value = String(src || "").trim();
  if (/^https?:\/\//i.test(value)) return value;
  if (value.startsWith("//")) return `https:${value}`;
  if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../")) return value;
  if (/^[A-Za-z0-9.-]+\.[A-Za-z]{2,}(\/|$)/.test(value)) return `https://${value}`;
  return new URL(value, new URL("../", window.location.href)).href;
}

CMSは/_cms/の下で動くため、単にthumbs/foo.pngと書くと/_cms/thumbs/foo.pngを見に行ってしまいます。そこでnew URL("../", window.location.href)を基準URLとして使い、サイト直下の画像として正しくプレビューできるようにしました。

トップ一覧 index.php: 外部画像が落ちてもカードを崩さない
document.querySelectorAll('.card-img img').forEach((img) => {
    const markMissing = () => img.parentElement.classList.add('is-missing-image');
    if (img.complete && img.naturalWidth === 0) markMissing();
    img.addEventListener('error', markMissing, { once: true });
});

XAMPPの検証環境では、外部画像や外部CSSがネットワーク制限によって読み込めないことがあります。その場合でもカードの高さや余白が壊れないよう、画像が欠落した際にはカード内に「新びんえい通信」のプレースホルダーを表示する処理を入れました。

バージョン管理の仕組み

CMSの保存処理は、単純な上書きではありません。保存前のファイルを_cms_data/versions/に退避してから、新しい内容を反映する仕組みです。記事ファイル、メタCSV、関連キャッシュがすべて履歴に残るため、編集で崩してしまっても戻すための材料が常に確保されています。

保存時の主な流れ lib.php
$stamp = cms_version_stamp();
$manifest = ['action' => 'article_save', 'stamp' => $stamp];

cms_backup_existing($oldPath, $stamp, $manifest, 'article');
cms_backup_existing(cms_meta_path(), $stamp, $manifest, 'metadata');
cms_move_cache_files([$oldPath . '-cache.html', $path . '-cache.html', 'index-cache.html'], $stamp, $manifest);

$manifest['sha256_after'] = hash_file('sha256', $path);
file_put_contents($versionDir . '/manifest.json', json_encode($manifest, JSON_PRETTY_PRINT));
保存履歴保存のたびにYYYYMMDD-HHMMSS-xxxxxx形式のスタンプが付与されます。
対象記事本文、blog-articles.csv、記事キャッシュ、トップキャッシュがバックアップの対象です。
検証保存後の本文にはsha256_afterが記録され、どの内容が反映されたかを追跡できます。
復元復元する際にも、現在のファイルをbefore_restoreとして残してから過去のバックアップを戻します。

今回の検証結果

公開記事デスクトップ、スマホ幅とも表示OK。ローカル記事画像は自然幅 1672 x 941 で読み込み完了。
CMS操作記事プルダウンに表示、プレビュー画像OK、プレビューON/OFF、幅ドラッグを確認。
トップ一覧新規記事が一覧に出て、ローカル画像も読み込みOK。外部画像失敗時の見た目崩れも抑制。

AIアシストの位置づけ

AIボタンは、本文を丸投げするためではなく、編集者の迷いを短くする補助輪として位置づけるのがよいと考えています。概要案、見出し案、SEO確認、HTML化、文体の整え直し――人間が最終判断を下し、AIが候補を出す形にすれば、軽量CMSであっても十分に実用的です。

ただし、APIキーやモデル設定はCMSの設定画面に閉じ込め、記事本文や公開ページには一切表示されないようにしています。便利さを足すほどに、秘密情報を表に漏らさない設計の重要性が増してきます。

次に育てたいところ

  • 画像アップロードをCMS内で完結させ、ファイル名、alt、OGP設定まで一気に扱えるようにしたいと考えています。
  • AI提案は上書きではなく差分表示にして、採用前に比較できる形を目指します。
  • 保存後に作成されたバージョンスタンプを、画面上でもっと目立たせたいところです。
  • 記事一覧の検索を、カテゴリ、優先度、更新日でも絞れるようにする予定です。

今回の結論。軽量CMSであっても、スクリーンショットで出来上がりを確認し、コードレベルで改善理由を説明でき、保存履歴でいつでも戻れる状態まで持っていけば、記事制作の安心感はかなり高まります。この記事自体が、そのままCMSの実地テストになりました。