これまでこのメモ置き場は WordPress で運用してきたが、本体もプラグインもしょっちゅうアップデートがあって面倒になったこともあり、Hugo に移行することにした。
wordpress-to-hugo-exporter を使えば楽ができそうだと思ったが、案外そうでもなかった。
- 動作させるのに PHP の zip extension が必要
extension=zip.so
これは結構動作環境を限定すると思う。
- 出力される .md の frontmatter が YAML。TOML なら良かったのに。しかたないので全部直した。
- 出力される markdown が汚い。
- さらに当サイトでは電力料金比較表があり、これは WordPress の固定ページとして作ったので、PHP であり静的 HTML ではない。
しかたないのでこれは Hugo 用の .md (と言いつつ中身はほとんど HTML) を出力するように改造した。
PHP CLI で実行すれば .md ができる。
Hugo で WordPress 的な年月日別アーカイブ
WordPress では パーマリンク設定を /%year%/%monthnum%/%day%/%postname%/ にしていた。
この場合、親ディレクトリ (/2018/07/10/) に日別アーカイブ、さらに遡れば月別、年別アーカイブがあった。
Hugo らしいやり方ではタクソノミーを作ることになる。この場合パスが /archives/2018/07/10/ 等のように、冒頭にタクソノミーの slug を含むパスになってしまう。
WordPress 時代のパーマリンクをそのまま維持したいが、Hugo にはそうした機能がないため、泥臭い形で実装した。
- 記事は content/post/ 下に
- content/archive_yearly/ に年別アーカイブ用ダミーファイルを配置
- content/archive_monthly/ に月別アーカイブ用ダミーファイルを配置
- content/archive_daily/ に日別アーカイブ用ダミーファイルを配置
config.tomlでパーマリンク設定。
キモは post と archive_* のパーマリンク構造を合わせること。
[permalinks]
post = "/:year/:month/:day/:slug/"
archive_yearly = "/:year/"
archive_monthly = "/:year/:month/"
archive_daily = "/:year/:month/:day/"
次に各アーカイブのテンプレート。私はテーマ下に入れているが、まあ適宜よしなに。
年別
themes/example/layouts/archive_yearly/single.html
{{define "main"}}
{{$archive_year := .Date.Year}}
<h1>{{$archive_year}}年</h1>
<ul>
{{range (where .Site.Pages "Section" "post").ByDate}}
{{if eq .Date.Year $archive_year}}
<li>{{printf "%04d/%02d/%02d" .Date.Year .Date.Month .Date.Day}}<br /><a href="{{.Permalink}}">{{.Title}}</a></li>
{{end}}
{{end}}
</ul>
{{end}}
月別
themes/example/layouts/archive_monthly/single.html
{{define "main"}}
{{$archive_year := .Date.Year}}
{{$archive_month := .Date.Month}}
<h1>{{$archive_year}}年{{printf "%02d" $archive_month}}月</h1>
<ul>
{{range (where .Site.Pages "Section" "post").ByDate}}
{{if eq .Date.Year $archive_year}}{{if eq .Date.Month $archive_month}}
<li>{{printf "%04d/%02d/%02d" .Date.Year .Date.Month .Date.Day}}<br /><a href="{{.Permalink}}">{{.Title}}</a></li>
{{end}}{{end}}
{{end}}
</ul>
{{end}}
日別
themes/example/layouts/archive_daily/single.html
{{define "main"}}
{{$archive_year := .Date.Year}}
{{$archive_month := .Date.Month}}
{{$archive_day := .Date.Day}}
<h1>{{$archive_year}}年{{printf "%02d" $archive_month}}月{{$archive_day}}日</h1>
<ul>
{{range (where .Site.Pages "Section" "post").ByDate}}
{{if eq .Date.Year $archive_year}}{{if eq .Date.Month $archive_month}}{{if eq .Date.Day $archive_day}}
<li><a href="{{.Permalink}}">{{.Title}}</a></li>
{{end}}{{end}}{{end}}
{{end}}
</ul>
{{end}}
これで archive_daily/2018-07-10.md があれば /2018/07/10/index.html に2018年07月10日の記事一覧が出力される。
このダミーファイルも手作業でいちいち作ってたら面倒でかなわないので、自動化する。
content/post/*.md を読んで、
date = “2018-07-10T00:00:00+09:00”
みたいな行を見つけたら、
- content/archive_yearly/2018.md
- content/archive_monthly/2018-07.md
- content/archive_daily/2018-07-10.md
をそれぞれ作成。というもの。
content-src/archives.php
<?php
// make archives
$dir = dirname(dirname(__FILE__)) . '/content/post/';
$archive_yearly = dirname(dirname(__FILE__)) . '/content/archive_yearly';
$archive_monthly = dirname(dirname(__FILE__)) . '/content/archive_monthly';
$archive_daily = dirname(dirname(__FILE__)) . '/content/archive_daily';
cleandir($archive_yearly);
cleandir($archive_monthly);
cleandir($archive_daily);
$dh = opendir($dir);
while(($file = readdir($dh)) != false) {
if(preg_match('/\.md$/', $file) === 1) {
$matches = array();
$str = file_get_contents(sprintf('%s/%s', $dir, $file));
$array = explode("\n", $str);
for($i = 0; $i < count($array); $i++) {
if(preg_match('/date\s*=\s*"(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})T/', $str, $matches) === 1) {
file_put_contents(sprintf('%s/%04d.md', $archive_yearly, $matches['year']), sprintf("+++\ndate = \"%04d-01-01T00:00:00+09:00\"\ntitle = \"%04d年\"\n+++\n%04d", $matches['year'], $matches['year'], $matches['year']));
file_put_contents(sprintf('%s/%04d-%02d.md', $archive_monthly ,$matches['year'], $matches['month']), sprintf("+++\ndate = \"%04d-%02d-01T00:00:00+09:00\"\ntitle = \"%04d年%02d月\"\n+++\n%04d-%02d", $matches['year'], $matches['month'], $matches['year'], $matches['month'], $matches['year'], $matches['month']));
file_put_contents(sprintf('%s/%04d-%02d-%02d.md', $archive_daily , $matches['year'], $matches['month'], $matches['day']), sprintf("+++\ndate = \"%04d-%02d-%02dT00:00:00+09:00\"\ntitle = \"%04d年%02d月%02d日\"\n+++\n%04d-%02d-%02d", $matches['year'], $matches['month'], $matches['day'], $matches['year'], $matches['month'], $matches['day'], $matches['year'], $matches['month'], $matches['day']));
break;
}
}
}
}
closedir($dh);
function cleandir($d) {
if(file_exists($d)) {
$dh = opendir($d);
while(($file = readdir($dh)) != false) {
if(preg_match('/\.md$/', $file) === 1) {
unlink($d . '/' . $file);
}
}
}
else {
mkdir($d);
}
}
(ぶっちゃけ CI 使う場合 cleandir() は必要ないが、手元でビルドして public/ をアップロードするケースも考えられるので一応……)
php content-src/archives.php
すると、各アーカイブ用ダミーファイルが生成される。
ここで PHP を使っているのは、すでに電力料金比較表の生成で PHP を使っているから。どうせ1個使ってるんだから PHP に揃えてしまえと。
Hugo を使う場合ほとんどの人が CI を使うだろうし、Netlify ではビルドで PHP を使えるので、まあまあ使い物になるのではないか。
Netlify のビルドコマンド:
php content-src/archives.php; hugo
WordPress では年月日別アーカイブで本文を表示しているが、その点だけは追従していない。
日付とタイトルがあってリンクされればそれで用は済むと思うので。
当サイトは記事数が少ないので問題にならないが、記事数が多い場合は年別アーカイブぐらいにはページネーションを入れた方がいいかも知れない。
Hugo でこういうアーカイブを作る場合、普通はアーカイブ用のタクソノミーを作って list.html で出力するのが大半だと思うが、それでは WordPress 同様の /YYYY/MM/DD/ ではなく /archives/YYYY/MM/DD/ のようなパスになってしまう。
既存サイトを Hugo に移行する場合、パーマリンク構造を変化させずに移行することが重要だと思われるので、年月日別アーカイブのページを single.html で post のパーマリンク構造内に出力する形にした。