HNFファイルを表示するスクリプト

MovableTypeなどのブログシステムより前に利用していた「ハイパー日記システム」。今はサーバーのPerlに対応できなくなり動作していませんが,サイト内全文検索(Namazu)の検索対象には入れているので,検索結果のリンクがエラーにならないよう対策を講じました。Geminiとやり取りしながら.nhfファイルを表示するPHPのスクリプトを生成。該当日付のファイル内容を表示するだけでなく,その月の日記の一覧をリンク表示したり,前後の月に移動したりする機能も持たせました。また,アクセスの際にURLパラメータがなければ,最新の日記を表示するようにもしました。だだし,かなり昔に使用していたシステムなので,最新のデータとして表示されるは2004年07月23日になります。

◆サイト内全文検索(Namazu)の検索結果からアクセス
http://freeside.skr.jp/diary/?200204a#200204050 (126 bytes)
→ /home/(ユーザー名)/tdiary/2004/d20020405.hnf の内容を表示

URLパラメータなしで,スクリプト(index.php)にアクセス
http://freeside.skr.jp/diary/
→ 最新の日記(2004/07/23)を表示

index.php(.hnfファイルを読み込み,カレンダーと本文を表示するPHPスクリプト)
<?php
/**
 * HNF Diary System (Simplified Version)
 * .hnfファイルを読み込み、カレンダーと本文を表示するPHPスクリプト
 */

// 出力はすべてEUC-JPとしてブラウザに通知
header('Content-Type: text/html; charset=EUC-JP');

/**
 * UTF-8の文字列をEUC-JPに変換して出力する補助関数
 */
function e($str) {
    return mb_convert_encoding($str, "EUC-JP", "UTF-8");
}

$baseDir = "/home/(ユーザー名)/diary";
$query = $_SERVER['QUERY_STRING'] ?? '';

// --- 年月の特定ロジック ---
if (empty($query) || !preg_match('/^[0-9]{6}/', $query)) {
    $foundYM = "";
    $years = glob($baseDir . '/[0-9][0-9][0-9][0-9]', GLOB_ONLYDIR);
    if ($years) {
        rsort($years);
        foreach ($years as $yPath) {
            $y = basename($yPath);
            for ($m = 12; $m >= 1; $m--) {
                $mStr = sprintf('%02d', $m);
                $files = glob("{$yPath}/d{$y}{$mStr}[0-9][0-9].hnf");
                if (!empty($files)) {
                    $foundYM = $y . $mStr;
                    break 2;
                }
            }
        }
    }
    $yearMonth = $foundYM ?: date('Ym');
} else {
    $yearMonth = substr($query, 0, 6);
}

$year = substr($yearMonth, 0, 4);
$month = substr($yearMonth, 4, 2);

// --- 1. JavaScriptによる日付自動判定 ---
echo "<html><head>";
// スマホ用メタタグの挿入
echo '<meta name="viewport" content="width=device-width,initial-scale=1.0">';
echo "<script>
const h = window.location.hash;
if (h && h.length >= 9) {
    const hContent = h.replace('#', '');
    const correctDay = hContent.substring(6, 8); 
    const urlParams = new URLSearchParams(window.location.search);
    if (urlParams.get('day') !== correctDay) {
        urlParams.set('day', correctDay);
        const baseQ = window.location.search ? window.location.search.substring(1).split('&')[0] : '{$yearMonth}c';
        window.location.replace(window.location.pathname + '?' + baseQ + '&day=' + correctDay + h);
    }
}
</script>";

// --- 2. 表示対象日の決定 ---
$day = $_GET['day'] ?? '';
$day = substr($day, 0, 2);

// その月のファイルが一つでもあるか確認
$monthFiles = glob("{$baseDir}/{$year}/d{$yearMonth}[0-9][0-9].hnf");
$hasDiary = !empty($monthFiles);

if (empty($day)) {
    for ($d = 31; $d >= 1; $d--) {
        $checkDay = sprintf('%02d', $d);
        if (file_exists("{$baseDir}/{$year}/d{$yearMonth}{$checkDay}.hnf")) {
            $day = $checkDay;
            break;
        }
    }
}

$filePath = "{$baseDir}/{$year}/d{$yearMonth}{$day}.hnf";

// --- HTML出力(headの続きとbody) ---
echo "<title>" . e("{$year}{$month}" . ($day ? "{$day}" : "")) . "</title></head>";
// スマホで文字が小さくなりすぎないよう body に padding 調整
echo "<body style='font-family:sans-serif; background:#fff; line-height:1.6; padding:15px; color:#333; max-width:800px; margin:0 auto;'>";

// --- タイトルの追加(下線を他のhrと同じ1px #cccに設定) ---
echo "<h1 style='font-size:1.5em; text-align:center; border-bottom:1px solid #ccc; padding-bottom:10px; margin-bottom:20px;'>HNF Diary System (Simplified Version)</h1>";

// --- 3. 日記本文の表示セクション ---
if (!$hasDiary) {
    // その月の日記が一つもない場合
    echo "<div style='margin-bottom:20px; font-weight:bold; color:#666;'>" . e("日記なし") . "</div>";
} elseif (!empty($day) && file_exists($filePath)) {
    $content = file_get_contents($filePath);
    
    // URLをリンクに変換(EUC-JP環境を考慮)
    $content = preg_replace('{https?://[\w?./%&=+#~!-]+}', '<a href="$0" target="_blank" style="color:#00e; text-decoration:underline;">$0</a>', $content);

    $anchorId = $yearMonth . $day . "0";
    $content = preg_replace('/^(OK|NEW|LNEW)/m', '<a id="' . $anchorId . '" name="' . $anchorId . '"></a>$1', $content);

    echo "<div id='diary-content' style='margin-bottom:20px;'>";
    echo "<strong style='font-size:1.1em; color:#000;'>" . e("{$year}{$month}{$day}日の日記") . "</strong>";
    // preのフォントサイズをスマホで見やすい1rem(約16px)程度に調整
    echo "<pre style='white-space: pre-wrap; word-wrap: break-word; font-family: monospace; font-size:1rem; border-top:1px solid #ccc; padding-top:15px; margin-top:10px;'>{$content}</pre>";
    echo "</div>";
}

echo "<hr style='border:0; border-top:1px solid #ccc; margin:20px 0;'>";

// --- 4. 月間ナビゲーションと一覧 ---
$currentDate = strtotime("{$year}-{$month}-01");
$prevYM = date('Ym', strtotime("-1 month", $currentDate));
$nextYM = date('Ym', strtotime("+1 month", $currentDate));

echo "<div style='margin-bottom:15px; font-size:0.9em;'>";
echo "<a href='?{$prevYM}c' style='text-decoration:none;'><< " . e(date('Y年m月', strtotime("-1 month", $currentDate))) . "</a> | ";
echo "<strong>" . e("{$year}{$month}") . "</strong> | ";
echo "<a href='?{$nextYM}c' style='text-decoration:none;'>" . e(date('Y年m月', strtotime("+1 month", $currentDate))) . " >></a>";
echo "</div><ul style='list-style-type: none; padding-left: 0; font-size:1rem;'>";

if ($hasDiary) {
    for ($d = 1; $d <= 31; $d++) {
        $dayStr = sprintf('%02d', $d);
        $targetFile = "{$baseDir}/{$year}/d{$yearMonth}{$dayStr}.hnf";
        if (file_exists($targetFile)) {
            $lines = file($targetFile);
            $title = "";
            foreach ($lines as $line) {
                $line = trim($line);
                if ($line === "" || $line === "~" || $line === "^") continue;
                $title = preg_replace('/^(OK|NEW|LNEW|CAT|SUB|TITLE)\s*/i', '', $line);
                if (!empty($title)) break;
            }
            $title = $title ?: e("無題");
            $fw = ($day === $dayStr) ? "font-weight:bold; color:#d33;" : "color:#00e;";
            // リンクエリアを広げてスマホでタップしやすく
            echo "<li style='margin-bottom:10px; {$fw}'><a href='?{$yearMonth}c&day={$dayStr}#{$yearMonth}{$dayStr}0' style='text-decoration:none; color:inherit; display:block;'>[" . e("{$d}") . "] {$title}</a></li>";
        }
    }
}
echo "</ul><hr style='border:0; border-top:1px solid #ccc; margin:20px 0;'></body></html>";
PHP

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

Time limit is exhausted. Please reload CAPTCHA.