先日、f-dexというフリーペーパーのサイトをリリースした。f-dexとはうちの嫁さんが友達と作っている、デザイン、オモシロ系のネタを扱ったA6サイズの小冊子のこと。フリーペーパーながら有名人に突撃インタビューを敢行したりして、特集の内容が妙に豪華だったりする。
特集のほかに、友人知人のツテでいろんな人に記事を書いてもらったり、長く付き合いのある印刷会社の協力で凝った印刷を試したり、そのお礼に印刷がらみの小ネタを載せたりしていて、手に取ってくれている人たちからご好評をいただいている。

f-dexのサイトは以前からあったが、ニュースやイベント情報(f-dexのではなく、記事を寄稿してもらったりしてお世話になっている方々のイベント情報)をちょこっと載せているだけだった。バックナンバーをWeb上で見せるという話も出たことがあったが、忙しかったりタイミングが合わなかったりして、なかなか実行に移せないでいた。

ちょうど個人的にWordpressを触っていたこともあり、ブログ以外のものを作りたいなーと思っていたので、承諾を得て仕事の合間にサイトを作ってみることにした。(ちなみに、Wordpressを使った他の事例は嫁さんのサイト(デザインはやってもらった)。案件では今の所機会に恵まれていないので、使ったことがありません。随時大募集中です

サイトの全体像

まず、サイトの全体像から。主なコンテンツとして「ニュース、配布場所、バックナンバーの閲覧、バックナンバーの請求、特集(プレゼントとか)、イベント紹介」があり、補助的な内容として「f-dexについて、メールフォーム」がある。

f-dexのサイトマップ

この中で、頻繁に更新しブログ的な機能と相性が良いのは「ニュース、特集、イベント情報」の3つ。これとは反対に、一度作ってしまえば後はあまり触らなくていいモノ、WordpressのPAGE機能と相性が良いのは「配布場所」と補助内容の3つ。
残った「バックナンバーの閲覧」と「バックナンバーの請求」のうち、請求の方は別で作ったフォームがあるので、今回はWordpressで管理しない。「バックナンバーの閲覧」はややこしい事をしてしまったので、分けて書きます。

ブログと相性のいいページ/ PAGEと相性のいいページ

ブログと相性のいいページは、特に何も考えず普通のブログっぽく作成。一覧ページのページ遷移用にWP-PAGENAVIプラグインを使用したくらいで、特筆する箇所はなし。

PAGEと相性のいいページも特に変わった事はしていないので、あまり書くことはない・・・かも。メールフォームの所にContact Form][というプラグインを使ったことくらい。

基本的に変なカスタマイズはせず、Wordpressの基本機能だけで作成。ここまでは本に載ってるサンプルで出来ました。

バックナンバーの閲覧

ここがサイトの一番重要なところなので、見せ方を工夫した。
f-dexの内容はインタビューに始まり、写真、イラスト、各種連載…というような順番になっている。インタビューもさることながら、連載記事も有名なイラストレーターさんだったり、クリエーターさんだったり、噺家さんだったりと多岐にわたっていて面白いので、Vol.001、Vol.002などの号数に関係なく読めるようにした。

1. バックナンバー閲覧画面の、最初の画面

f-dexのアーカイブトップグローバルナビゲーションのArchiveを押したら、バックナンバーの最初のページが出るようになっている。ここでは「本や連載記事がこれだけあるよ!」というのを見せたかったので、本のサムネールや連載のバナーを並べてにぎやかなデザインにした。
楽をしたかったので、このページは内容が追加されたら自動で表示されるようになっている。

まず表紙の方の説明。
一冊の本の中身について、それぞれこんな風にカテゴリをつけている。

カテゴリー付け

「表紙」というカテゴリはf-dexの表紙になる部分にしかつけていないので、query_postsで表紙カテゴリのエントリだけを取得。各記事のタイムスタンプを発行日にしているので、日付の古いものから順に並べればVol.001、Vol.002…と綺麗に並んでくれる。

1
query_posts('showposts=-1&category_name=cover&order=asc');

取得した表紙エントリのかたまりをループで回す際、get_the_category()を使うと各エントリにつけてあるカテゴリのリストが得られる(戻り値はカテゴリID)ので、ここからVol名を取得。

1
2
3
4
5
6
// vol名取得
foreach((get_the_category()) as $cat){
if(strstr($cat->slug, "vol")){
$newestVol = $cat->slug;
}
}

表紙の画像はvol001_thumb_l.jpgというような名前で統一してあらかじめアップロードしてあるので、取得したvol名をファイル名に指定すれば、その号の表紙が表示される。

1
2
// 表紙ファイル名
$cover = get_bloginfo('template_url') . "/images/cover/" . $newestVol . "_thumb_l.jpg";

表紙サムネールの下に出す特集の内容をどうするか悩んだ末、カスタムフィールドに入れることにした。featureというキーで登録しておいたので、その値を表示するようにして完成。(困ったときのカスタムフィールドだなぁ…、と実感しました)

1
特集:< ?php echo post_custom("feature"); ?>

連載のバナーの表示部分も同じような感じで表示。
※サイトのトップページも同じ要領でやっているので、自動で表示されます。

2. 左メニューに、Vol名、連載記事、著者別のリストを表示

これはVol名、連載記事、著者を格納するための親カテゴリを作り、具体的な内容のカテゴリをその中に登録し、wp_list_cats(‘child_of=xx’);を使って表示。

Wordpressはver2.3.2日本語版からタグが標準装備されたので使いたかったけど、これをやりたかったので断念。

3. 各号のトップページで表紙を出す

左メニューのVol.001というリンクや表紙のサムネールをクリックしたら、各号の表紙が出るようにした。archive/vol001/index.html を用意するようなイメージ。

f-dex各号のトップページ

Vol.001などのVol名はカテゴリなので、通常は管理画面で設定した件数だけ、このカテゴリに属したエントリが表示される。一般的にこういったカテゴリートップでは、記事へのリンクが付いた見出しと概要を表示することが多い。例:http://wiredvision.jp/news/culture/

f-dexは各号ごとに紙や印刷加工が違ったりするので、表紙の画像以外に説明文を入れたかった。各号のカテゴリートップでそれをやりたかったので、ちょっと変な事をした。

1. で書いたように、各エントリの日付は発行日にしてある。さらに、エントリの内容が本の順番どおりに並ぶよう時間をずらして登録してある。表紙は23時、特集は22時、その次の記事は21時・・・といった感じ。Wordpressのループはデフォルトで新しいものから順に取り出すので、カウント用の変数を用意して先頭のエントリだけは本文を表示、それ以外はリスト表示にして目次として使った。

1
2
3
4
5
6
7
8
9
10
11
if(have_posts()) : while(have_posts()) : the_post();
$entryCount++;

if($entryCount == 1){
// 先頭のエントリだけ本文表示
the_content();
}else{
// それ以外は目次としてタイトルだけ表示
the_title();
}
endwhile; endif;

表紙の画像はカスタムフィールドに入れても良かったけど、ここも楽したかったのでカテゴリにつけたVol名から自動で表示するようにした。なので、ここで入力しているのは表紙の説明文のみ。
テンプレートを使っても良かったかな・・・と思ったけど、新刊ごとにテンプレートを作るのもどうかなと思ったのでこうしました。

4. 記事ページ間の遷移

f-dexは本なので、Vol.001ならVol.001内だけで「<前のページへ / 次のページへ>」とリンクをつけて、現物と同じように読めるようにした。
Wordpressのテンプレートタグにnext_post_link()、previous_post_link()というものがあり、引数に” in_same_cat=true”を与えると同じカテゴリの前後エントリだけにリンクを張ってくれる。エントリにつけるカテゴリが1つだけなら、このタグが使えたので簡単だった。

今回はカテゴリを複数つけていたので、このタグがうまく機能しなかった。各エントリのカテゴリがget_the_category()で出力した時の最後に来るカテゴリとして認識されてしまうようで、「Vol.001カテゴリ」を見ていたと思ったらいつの間にか「筆っこカテゴリ」を見ていた…というような事が起こってしまった。
2番にあるように今回はタグ機能が使えないので、next_post_link()、previous_post_link()を改造することにした。

Wordpressのインストールフォルダ内で「next_post_link」を含むファイルを検索すると、link-template.phpに入っていることがわかる。Ver2.3.2では450行目にnext_post_linkの定義があるので見てみると…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function next_post_link($format='%link &raquo;', $link='%title', $in_same_cat = false, $excluded_categories = '') {
$post = get_next_post($in_same_cat, $excluded_categories);

if ( !$post )
return;

$title = $post->post_title;

if ( empty($post->post_title) )
$title = __('Next Post');

$title = apply_filters('the_title', $title, $post);
$string = '[ID).'">';
$link = str_replace('%title', $title, $link);
$link = $string . $link . ']()';
$format = str_replace('%link', $link, $format);

echo $format;
}

この関数はリンクの見栄えを担当しているだけで、エントリが同じカテゴリかどうかの判別はget_next_postという関数でやっていることが分かった。get_next_postの定義は少し上の391行目にあるので見てみる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function get_next_post($in_same_cat = false, $excluded_categories = '') {
global $post, $wpdb;

if( empty($post) || !is_single() || is_attachment() )
return null;

$current_post_date = $post->post_date;

$join = '';
if ( $in_same_cat ) {
$join = " INNER JOIN $wpdb->term_relationships AS tr ON p.ID = tr.object_id ";
$cat_array = wp_get_object_terms($post->ID, 'category', 'fields=tt_ids');
$join .= ' AND (tr.term_taxonomy_id = ' . intval($cat_array[0]);
for ( $i = 1; $i < (count($cat_array)); $i++ ) {
$join .= ' OR tr.term_taxonomy_id = ' . intval($cat_array[$i]);
}
$join .= ')';
}

$sql_exclude_cats = '';
if ( !empty($excluded_categories) ) {
$blah = explode(' and ', $excluded_categories);
$posts_in_ex_cats = get_objects_in_term($blah, 'category');
$posts_in_ex_cats_sql = 'AND p.ID NOT IN (' . implode($posts_in_ex_cats, ',') . ')';
}

$join = apply_filters( 'get_next_post_join', $join, $in_same_cat, $excluded_categories );
$where = apply_filters( 'get_next_post_where', "WHERE p.post_date > '$current_post_date' AND p.post_type = 'post' AND p.post_status = 'publish' $posts_in_ex_cats_sql AND p.ID != $post->ID", $in_same_cat, $excluded_categories );
$sort = apply_filters( 'get_next_post_sort', 'ORDER BY p.post_date ASC LIMIT 1' );

return @$wpdb->get_row("SELECT p.ID, p.post_title FROM $wpdb->posts AS p $join $where $sort");
}

if ( $in_same_cat ) { … }の中がカテゴリの判別をしているところなので、そこを中心に見る。

if文の2行下の$cat_arrayに、エントリが属しているカテゴリの番号が入る。例としてVol.001の特集ページだと、

1
2
3
4
5
Array
(
[0] => 8
[1] => 11
)

のようになっている。変数名が$cat_arrayなので、これがカテゴリIDかな・・・と思っていたら違っていた。DBのwp_term_taxonomyテーブルを見てみると、カテゴリIDであるterm_idをterm_taxonomy_idという値とマッピングしている。そして$cat_arrayの値はterm_taxonomy_idの数字が出ていた。

まずはterm_idが欲しいので、term_taxonomy_idをterm_idに変換する関数をfunction.phpに作る。

1
2
3
4
function conv_taxonomyID2termID($id){
global $wpdb;
return $wpdb->get_results("SELECT term_id FROM $wpdb->term_taxonomy WHERE term_taxonomy_id=$id");
}

ついでにカテゴリIDからスラッグ名に変換する関数も作った。これはcategory.phpにget_cat_IDという似たような関数があったので、返す値をIDからスラッグ名に変えただけ。

1
2
3
4
5
function get_cat_slug($cat_id) {
$cat_id = (int) $cat_id;
$category = &get_category($cat_id);
return $category->slug;
}

これらを使って、カテゴリIDからスラッグ名を取得し、volで始まるカテゴリだけを入れるようにした。そうすると、各エントリは同じVol名のエントリにしかリンクを張らなくなるので、晴れて目的達成。ただ、コアファイルに書き足してしまうとWordpressの再インストール時に上書きしてしまいそうなので、これもfunction.phpにget_next_post_volという名前で保存しておいた。また、next_post_linkもnext_post_link_volという名前に変えて、こちらで追記した関数を読むように変更した。

今回使用したプラグイン


Akismet

標準で入っているコメントスパム対策プラグイン。

[Contact Form]

コンタクトフォーム設置用。手軽で便利。

Search Everything

サイト内検索の範囲を広げる。(デフォルトが記事本文のみなので。狭いよね…。)

Top Level Categories

Wordpressのパーマリンク設定で%category%を使った際、デフォルトだとhttp://hoge.com/category/news/とかhttp://hoge.com/category/event/ のように、「category」が入ってしまう。ここのcategoryの部分を消してくれるプラグイン。カテゴリーベースの所に/.と入れても消せるけど、どこかで不具合があったのでこっちを使うことにした。(WP: 設置ディレクトリと異なるページをホームにするをやったときだったかも)
via.WordPress › WordPress Ideas — Remove ‘Category Base’ From Permalinks

Upload Unzipper

画像をzipに圧縮して、まとめてアップロードできる。ちゃんと画像をエントリに関連付けてくれる。一つのエントリに画像をたくさん使う際に便利。

WP-AddQuicktag

エントリ投稿画面にクイックタグボタンを増やせる。エントリ内でいろいろタグを使う際に便利。(なぜか新規投稿時だけ機能していない)

WP-PageNavi

ページナビゲーションを「Page 1 2 »」という形で出してくれる。

FeedBurner FeedSmith

RSSはfeedburnerを通して公開しているので、転送するために。

Google XML Sitemaps

アーカイブ内のページ数が結構あるので使ってみました。

小ネタ

画像アップロードの初期設定を変える方法(ver.2.3.3以前)

fdex_upload.gifエントリに画像を入れる際、デフォルトだと表示:サムネイル、リンク先:ファイルが選択されており、「エディタに送る」ボタンを押すとサムネールのタグが書き出されてしまう。個人的に表示:フルサイズ、リンク先:なしが好みなので、最初からこうなるようコアファイルを以下のように変更した。

ver2.3.2の場合、wp-admin/js/upload.js の中の

  1. 124行目 checked = ‘display-thumb’; をコメントアウト
  2. 138行目 value=’file’となっているinput要素から、checked=’checked’を切り取る
  3. 140行目 value=’none’となっているinput要素に貼り付け

隠れてしまっているアップロードボタンを直す(ver.2.3.3以前)

wp-admin/wp-admin.css の適当な場所に、以下のコードを記述する。

1
2
3
form#upload-file th label {
white-space: nowrap;
}

WordPress Japan :: トピックを表示 - 画像アップロード画面でアップロードボタンが隠れる
これと同じ話題。

カスタムフィールドの使い方

名前が悪いのでいまいち何に使うのかわからないカスタムフィールド。これはエントリにメタ情報を自由に追加できる機能で、記事に変数が使えるようなもの。(DBもwp_postmetaというテーブルに入っていて、meta_key、meta_valueというフィールド名で保存されている)

普通のブログをやっている時は出番があまりないが、何かしら定型の値をエントリに持たせたいときに威力を発揮する。Wordpressサイト構築スタイルブックでは航空券の値段や訪問都市、航空会社などの情報を入れていたし、f-dexのサイトでは特集の内容を入れて使った。バイクのツーリングだったら、行き先、気温、所要時間、走行距離なんかを入れてもいい。
値は普通にDBに保存されるので、「走行距離が200km以上」とか「気温10度以下」といった条件でエントリの抽出ができたりする…かも(試してません)。

値を取り出すときは post_custom('キーの値’) とするだけでOK。アンダースコアで始まるキーはシステムが使用するので、重複させないように注意。カスタムフィールドを使ったプラグインもいくつかあるようなので、使いようによっては面白いことが出来そうです。

参考にしたサイト

power source* » WordPress
逆引きリストが便利。設置ディレクトリと…の記事も解説が詳しくてわかりやすかったです。

TRANS [hatena]
WordPressテーマ(テンプレート)カスタマイズのまとめ(日本語訳)他、Wordpress関連のエントリは色々と参考になりました。Yahoo! UI LibraryのCSSを使っている…とのエントリもあったので、まねして使ってみたりしました。

Main Page « WordPress Codex
日本のフォーラムはまだまだ情報量が少ないので、他の方もやってるように本家の中を検索して回りました。英語は辛い…。

参考にした書籍


Wordpressのフォーラムなどでよく見かける、マクラケン直子さんの著書。解説が丁寧なので、まったくの初心者だった自分は助かりました。基本から学べるので初めての人におすすめ。
3月末にWordPressビジネスブログ標準ガイドブックが出たようなので、こちらも要チェック。


上記のTRANSに紹介されていた本。こちらは基礎から応用まで、ブログからサイトの作り方までを解説している。カテゴリースラッグを使って画像を自動で表示する方法なんかは、ブログの作り方しか知らなかった自分には目からうろこでした。タグリファレンスもわかりやすくて◎。エントリの日付をいじって好きな順に並べるアイデアも、この本から得ました。

最後に

なかば勢いと思いつきで作ってしまったので、もっと便利な方法があるよ!とか、このページがわかりにくいよ!といった意見に飢えています。なにかありましたら、こちらのコメント欄やf-dexのほうのコメントフォームからご連絡ください。