- 2009-05-22 (金) 6:56
- blog

feedのカスタマイズをしてみる。
以前、をかもとさんの助言を得てfeedのテンプレートを自身のテーマ内に持つ方法を知ったので、これをwordpress自身のfeedに加え、コメントと、bbpressのfeedをあわせる方法をやってみる。
注意:今回いろいろ試した結果、挫折したのでこの記事を最後まで読んでも結局どうすればいいかは載ってないのであしからず…!!!
参考:各種フィード用テンプレートの変更 : dogmap.jp
参考:wordpressで非公開コミュニティサイトを作る(4)feedのカスタマイズ – atl*weblog
注意:をかもとさんが提示してくれたコードを、をかもとさん自身が記事にして若干変更されているので、そちらを参考にしたほうがきっと幸せ。
rss2の仕様
atomとかもあるけどまずはrss2から。
恥ずかしながらrss2の仕様についてよく知らないので、せっかくなので調べてみた。
koshigoewiki:feed:rss2.0仕様 [KoshigoeWiki]
今までなんとなく面倒そうでちゃんと理解してこなかったが、案外シンプルでわかりやすい。
詳しい仕様はリンク先を参照してもらうとして、以下個人的めも。
- channel要素は仕様では複数設置できるが、フィード利用状況からひとつにおさえ、複数のコンテンツはchannel要素内のitem要素を増やす形に
- 必須項目・オプション項目に含まれない要素はxmlnsで指定したネームスペースを利用
rss2テンプレートの仕組み(feed-rss2.php)
wordpress2.7.1のfeed-rss2.php
※ただし元のママではなく、本文部分を出力しないよう該当部分を除外したもの、詳しくは”wordpressで非公開コミュニティサイトを作る(4)feedのカスタマイズ – atl*weblog”に。
1: <?php
2: /**
3: * RSS2 Feed Template for displaying RSS2 Posts feed.
4: *
5: * @package WordPress
6: */
7: header('Content-Type: text/xml; charset=' . get_option('blog_charset'), true);
8: $more = 1;
9:
10: ?>
11: <?php echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'; ?>
12:
13: <rss version="2.0"
14: xmlns:content="http://purl.org/rss/1.0/modules/content/"
15: xmlns:wfw="http://wellformedweb.org/CommentAPI/"
16: xmlns:dc="http://purl.org/dc/elements/1.1/"
17: xmlns:atom="http://www.w3.org/2005/Atom"
18: xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
19: <?php do_action('rss2_ns'); ?>
20: >
21:
22: <channel>
23: <title><?php bloginfo_rss('name'); wp_title_rss(); ?></title>
24: <atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
25: <link><?php bloginfo_rss('url') ?></link>
26: <description><?php bloginfo_rss("description") ?></description>
27: <pubDate><?php echo mysql2date('D, d M Y H:i:s +0000', get_lastpostmodified('GMT'), false); ?></pubDate>
28: <?php the_generator( 'rss2' ); ?>
29: <language><?php echo get_option('rss_language'); ?></language>
30: <sy:updatePeriod><?php echo apply_filters( 'rss_update_period', 'hourly' ); ?></sy:updatePeriod>
31: <sy:updateFrequency><?php echo apply_filters( 'rss_update_frequency', '1' ); ?></sy:updateFrequency>
32: <?php do_action('rss2_head'); ?>
33: <?php while( have_posts()) : the_post(); ?>
34: <item>
35: <title><?php the_title_rss() ?></title>
36: <link><?php the_permalink_rss() ?></link>
37: <pubDate><?php echo mysql2date('D, d M Y H:i:s +0000', get_post_time('Y-m-d H:i:s', true), false); ?></pubDate>
38: <dc:creator><?php the_author() ?></dc:creator>
39: <?php the_category_rss() ?>
40: <guid isPermaLink="false"><?php the_guid(); ?></guid>
41: <wfw:commentRss><?php echo get_post_comments_feed_link(); ?></wfw:commentRss>
42: <?php rss_enclosure(); ?>
43: <?php do_action('rss2_item'); ?>
44: </item>
45: <?php endwhile; ?>
46: </channel>
47: </rss>
- 7行目
- headerを送っている、文字コードはxml宣言時に送ってるので不要な気がしたが、両方送っても問題なさそうだしいいのかな。
- 8行目
- 調べたんだけどよくわからないな…おそらく“続きを読む”とかその辺に関連してると思うんだけど。$moreが1だと全文って事な感じなんだけどなー。
- 11行目
- xml宣言
- 13~20行目
- rss要素の開始タグ。何か沢山xmlnsつけてるんだけどxmlns:contentが使われている形跡がない??
- 19行目
- この
do_action('rss2_ns')はどこにもadd_actionがないんだよなぁ、pluginとかで使うようにあるっぽい。 - 22行目
- channel要素開始タグ。
- 23~31行目
- このfeedに関する情報。タイトルとかURLとか。
- 32行目
do_action('rss2_ns')と同じくplugin用ぽい。- 33~45行目
- ここがループ部分。普通のテンプレートでのループと同じやり方。itemを一つづつ出力してる。
- 35~38行目
- ループ中の記事のタイトルとかパーマリンクとかをrss向けに出力するテンプレートタグだったり、関数だったり。
- 39行目
- feedのフォーマットにあわせた形で、ループ中の記事のカテゴリーを出力する。出力はカテゴリーごとに
<category>タグに入れて、カテゴリー名はCDATAとして出している。俺の環境だといまいち法則性の分からない改行とインデントも漏れなく付いてくる。 - 40行目
- これもパーマリンクを出力してるんだけど、25行目と違うのは、こっちはwordpressで設定したパーマリンク設定ではなくデフォルトの設定でURLが出力されてるみたい。けど
isPermaLink=”false”だからfeedreaderはuriとして解釈しないみたい。なんであるかは知らない。 - 41行目
- ループ中の記事のコメントのfeedアドレス。
- 42行目
- ループ中の記事に画像とかポッドキャストがあった場合、feed用に変換し出力するテンプレートタグ、らしい。該当するものがない場合は何も出力されない。
- 43行目
- これもplugin用フックかな、フィードの各記事分が閉じられる直前に何かしたい場合に利用するんだろう。
rss2コメントテンプレートの仕組み(feed-rss2-comments.php)
wordpress2.7.1のfeed-rss2-comments.php
1: <?php
2: /**
3: * RSS2 Feed Template for displaying RSS2 Comments feed.
4: *
5: * @package WordPress
6: */
7:
8: header('Content-Type: text/xml;charset=' . get_option('blog_charset'), true);
9:
10: echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>';
11: ?>
12: <rss version="2.0"
13: xmlns:content="http://purl.org/rss/1.0/modules/content/"
14: xmlns:dc="http://purl.org/dc/elements/1.1/"
15: xmlns:atom="http://www.w3.org/2005/Atom"
16: xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
17: >
18: <channel>
19: <title><?php
20: if ( is_singular() )
21: printf(ent2ncr(__('Comments on: %s')), get_the_title_rss());
22: elseif ( is_search() )
23: printf(ent2ncr(__('Comments for %s searching on %s')), get_bloginfo_rss( 'name' ), attribute_escape($wp_query->query_vars['s']));
24: else
25: printf(ent2ncr(__('Comments for %s')), get_bloginfo_rss( 'name' ) . get_wp_title_rss());
26: ?></title>
27: <atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
28: <link><?php (is_single()) ? the_permalink_rss() : bloginfo_rss("url") ?></link>
29: <description><?php bloginfo_rss("description") ?></description>
30: <pubDate><?php echo gmdate('r'); ?></pubDate>
31: <?php the_generator( 'rss2' ); ?>
32: <sy:updatePeriod><?php echo apply_filters( 'rss_update_period', 'hourly' ); ?></sy:updatePeriod>
33: <sy:updateFrequency><?php echo apply_filters( 'rss_update_frequency', '1' ); ?></sy:updateFrequency>
34: <?php do_action('commentsrss2_head'); ?>
35: <?php
36: if ( have_comments() ) : while ( have_comments() ) : the_comment();
37: $comment_post = get_post($comment->comment_post_ID);
38: get_post_custom($comment_post->ID);
39: ?>
40: <item>
41: <title><?php
42: if ( !is_singular() ) {
43: $title = get_the_title($comment_post->ID);
44: $title = apply_filters('the_title_rss', $title);
45: printf(ent2ncr(__('Comment on %1$s by %2$s')), $title, get_comment_author_rss());
46: } else {
47: printf(ent2ncr(__('By: %s')), get_comment_author_rss());
48: }
49: ?></title>
50: <link><?php comment_link() ?></link>
51: <dc:creator><?php echo get_comment_author_rss() ?></dc:creator>
52: <pubDate><?php echo mysql2date('D, d M Y H:i:s +0000', get_comment_time('Y-m-d H:i:s', true), false); ?></pubDate>
53: <guid isPermaLink="false"><?php comment_guid() ?></guid>
54: <?php if ( post_password_required($comment_post) ) : ?>
55: <description><?php echo ent2ncr(__('Protected Comments: Please enter your password to view comments.')); ?></description>
56: <content:encoded><![CDATA[<?php echo get_the_password_form() ?>]]></content:encoded>
57: <?php else : // post pass ?>
58: <description><?php comment_text_rss() ?></description>
59: <content:encoded><![CDATA[<?php comment_text() ?>]]></content:encoded>
60: <?php endif; // post pass
61: do_action('commentrss2_item', $comment->comment_ID, $comment_post->ID);
62: ?>
63: </item>
64: <?php endwhile; endif; ?>
65: </channel>
66: </rss>
- 8~10行目
- feed-rss2.phpと同じだが
$moreはない。 - 12~17行目
- xmlnsが少なくなっているのと、
do_action('rss2_ns');がなくなっている。 - 19~26行目
- どこのコメントかによってタイトル表記を分岐させているようだ。
- 20~21行目
is_singular()はコンディショナルタグで、When any of the following return true: is_single(), is_page() or is_attachment().
とあるように、記事かページか画像などの添付ファイル表示ページの時のみtrueを返す。
ent2ncr()は文字実態参照を数値文字参照に変換する。- 22~23行目
is_search()はまんま、検索結果表示ページなら、という意味のコンディショナルタグなんだけど、検索結果表示ページにコメントなんかつけて、しかもそれfeedで受信てどうも利用用途がわからないな。テンプレートいじればコメントつけれるようになるとは思うけど…そもそもそんなテーマも少なそう。
get_bloginfo_rss()でfeed用にフォーマットされたblog自体の名称を取得して、検索語句とあわせ出力している。- 24~25行目
- 上記でない場合の処理、けど上記に当てはまらないケースが思いつかない。
- 27~33行目
- 多少差異はあるがfeed-rss2.phpと同様に表示中のfeedに関する詳細情報を出力している。
- 34行目
do_action('rss2_head')と同じくplugin用フックだと思われる。- 36行目
- ループの開始部分。
- 37行目
$comment->comment_post_IDでループ中のコメントのIDを取得し、そのIDをget_post()に渡す事で、そのコメントが含まれる記事やページを取得しているんだと思う。ここで取得した記事やページのオブジェクトから、記事IDや、コメントがついている元の記事タイトルなんかを引っ張り出している。- 38行目
- 上で取得した記事のIDを引数に渡し、なぜかカスタムフィールドを読み込んでいる。
- 40~63行目
- item要素がこの範囲、コメントひとつ分にあたる。
- 41~49行目
- タイトルを先ほど出てきた
is_singular()で分岐させている。is_singular()がfalseの場合はタイトルとコメント投稿者名を、trueの場合はコメントの投稿者名のみを出力。
falseの場合、コメント全体を出力しているのでコメントの元となる記事やページのタイトルが必要になる。 - 50~53行目
- 出現順やテンプレートタグは違うが、基本feed-rss2.phpと同じ。
- 54~60行目
- コメントの元となる記事やページがパスワード保護されているかで条件分岐させている。もし保護されている場合、descriptionにパスワード入れてからコメント見てねというメッセージと入力フォームを表示する。パスワード保護がない場合、descriptionにコメント本文をfeed用に、
<content:encoded>にコメント本文をCDATAで出力。 - 61行目
- feed-rss2.phpと同じくplugin用フック。こちらはコメントIDとコメントのついている記事のIDも引数で渡すようになっている。
通常のfeedにコメントのfeedをあわせる
さて、まずは通常feedにコメントも加えてみる。
具体的にはpostとコメント、両方を区別せず更新日時のみでfeedを生成する形を目指す。
当然そんなテンプレートタグは用意されてないので、どうするのか現時点では皆目検討がつかないが…、うまくいけばwordpressのコアの関数かなにかを使ってもにょもにょしたい。
出来ない場合は…通常のとコメントのをループまわして、それぞれ格納して、ソートしなおして、出力…?
それは避けたいなぁー!!
ループの仕組み
という事でとりあえずwordpressのループ周りの仕組みを追跡してみた。
とりあえずよくわからないけどそう書けと言うんで言われるがままに書いてきた、have_postsとかthe_postについて。
まずwordpressはループやちょっと複雑な処理を担当するWP_Queryってクラスがwp-includes/query.phpで定義されてる。
んで、つかう場合は$wp_queryオブジェクトからメソッドで操作せず、ラッパー関数から同名メソッドを呼び出す形が多い。
しかもこのオブジェクトは色々情報も持ってる、今ループ中ですとか、現ループで扱ってる記事の情報とか、そもそも現在のページはアーカイブページですよ、だとか。
…たぶん、あんま自信ないけどそういう事だと思う。
関数リファレンス/WP Query – WordPress Codex 日本語版
んで、件のhave_postsとかthe_post、あとはコメントfeedで使われてたループ制御のhave_commentsやthe_commentも、WP_Queryのメソッドだったのだ。(正確にはメソッドを呼び出すラッパー関数)
本題のhave_postsとかthe_postの機能はリンク先に既に載っているのでざっと。
have_postsが表示すべき記事がまだあるの?ってのをみてる。記事がまだ3つしかないblogのループの場合、3回ループまわして4回目は当然ないんでこのメソッドはfalseを返す。
the_postはループ中にのみ使えて、現ループ中の対象記事の情報をグローバル変数$postに突っ込んでくれる。
つまりthe_post()と書くだけで$postを参照する様々な関数だとかが、対象となる現ループ中の記事情報を参照してくれる。
コメントのほうも基本同じような事してるっぽい、なるほどなぁ~。
すんげー面倒そうでやってないけど、wp-includes/query.phpの中身をよく見ていくと、きっと何でも出来る気がしてくるんじゃないかな!
俺はまったくしないけど。
七転八倒
とりあえずwhileの条件にhave_posts()かhave_comments()がtrueでまわすようにして、中で再びそれぞれifの条件で出すような感じにしたんだけど、これじゃだめっぽい。
そもそもコメントのみ出てなかったんで、試しにコメントだけ出力するようにしてみたけど、何もでない。
あれー、と思いfeed-rss2.phpにfeed-rss2-comments.phpをまるまる上書きしてみると…うお!でねぇ!
てことは、今表示してるのが普通のfeedかコメントのfeedかをどこかで見てて、それのせいでコメントの分を混ぜれてないのか。
しょうがないのでwp_queryオブジェクトを見てみると、feed-rss2-comments.phpのほうではquery_varsにwithcommentsなんてものが存在してる。あとこれは予想通りだが、is_comment_feedが1(true)だ。
試しに無理やりこの値を設定してみたが、それでもダメくさい、
うーん、feedを読み込む際にwp_queryオブジェクトを生成してて、それがcommentsじゃないからコメントのテキストやらメタ情報が含まれて無いから、もう一回wp_queryオブジェクトを取得しなおせばいい…のか?
何かうまい方法あるんじゃねーの?と思いfeed関係のpluginのソースを眺めてみるが…ゲーッ!クエリ書いてる!
そりゃまぁ何でもできるだろうけど…スマートじゃないなぁ。
wordpressの関数を利用してやるには、今の俺にはちょっとしんどい量のソースを追跡しないといけなそう、かといってSQLクエリを書いてどうこうするのはセキュリティ的な面で非常に不安が残るし、DB構造が今後変更されたら最高に面倒なので、いったんwordpressから離れて考える事に。
別の方法を考える
さて、wordpressの機能を利用するという枷がなくなると選択肢は一気に広がる。
広がりすぎて記事数本になりかねない勢い。
ざっと思いつくのが以下。
- feed-rss2.phpおよびfeed-rss2-comments.phpを、item要素を返す関数があるだけのファイルにし、それを呼び出して使う。
- phpでゴリゴリ作る。
- feedマージ系のwebサービスを使う。
個人的に本命は1なんだけど、今は心が折れ気味なのと、そもそも出来るのかよくわからないのと、そのうちwordpressの機能でやる方法を思いつくなり調べれるのを期待し、もっとも手軽な3で行こうかなとりあえず。
最終的にはbbpressのfeedも合成したいんで、wordpressの機能だけと言いつつどのみち1か2か、もしくは別の方法を混ぜていかないといけなくなるはずなので、そういう意味でもwebサービスは代替方法としては最適だ。
というかなんで最初から使わなかったのかってと、Yahoo! Pipesのレスポンスとかが遅いイメージがあったのと、更新頻度が低いからだったりする。
webサービスでfeedをマージする
[J] 複数のフィード (RSS) を一つにマージする方法 – Jamz
ここに非常に有益なマトメ記事があるので、すでに半分以上目的が達成している。
あとはどれを選ぶかくらいの話だ。
本来なら各サービスの速度測定とかしたり比較したりして余力がありゃ記事化したいところだが、あまりに本筋から脱線してるしそもそもwebサービスは代替方法なのでここはスルー力。とりあえずははてなRSSを非公開で試してみたが、問題なさそうではある…あれ!?bbpressのfeedが構文エラー起こしてる!
bbpressのfeedがおかしい
bbpressのfeedがwordpressと統合をしたせいでおかしな事になっていた。
簡単にまとめると以下の構造のコードが書き出されていた。
<?xml version="1.0" encoding="UTF-8"?><!-- generator="WordPress/2.7.1" -->
<rss version="0.92">
<channel>
<title>wordpressのタイトル » ページが見つかりませんでした</title>
<link>wordpressのURI</link>
<description>wordpressのdescription</description>
<lastBuildDate>日時</lastBuildDate>
<docs>http://backend.userland.com/rss092</docs>
<language>ja</language>
</channel>
</rss>
<br />
<b>Warning</b>: Cannot modify header information - headers already sent by (output started at feedのテンプレートへのパス/feed-rss.php:12) in <b>bbpressのテーマへのパス/rss2.php</b> on line <b>1</b><br />
<?xml version="1.0" encoding="UTF-8"?><!-- generator="bbPress" -->
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title>bbpressの名前: トピック名</title>
<link>bbpressへのパス</link>
<description>bbpressのdescription</description>
<language>ja</language>
<pubDate>日時</pubDate>
<generator>bbpress 1.0-alpha-6</generator>
<textInput>
<title><![CDATA[検索]]></title>
<description><![CDATA[Search all topics from these forums.]]></description>
<name>q</name>
<link>bbpressの検索へのURI</link>
</textInput>
<atom:link href="bbpressのfeedのURI" rel="self" type="application/rss+xml" />
<item>
<title>投稿者名 : "トピック名"</title>
<link>投稿へのリンク</link>
<pubDate>日時</pubDate>
<dc:creator>投稿者名</dc:creator>
<guid isPermaLink="false">1@bbpressへのURI</guid>
<description>投稿内容</description>
</item>
</channel>
</rss>
見てのとおり、最初にwordpressのfeedテンプレートを呼んじゃってて、ページは見つからない扱いになる…まではわかるのだが、なぜかその後bbpressのほうのfeedテンプレートまで呼び出すもんだから、headerがエラー出すわエラーメッセージでbrタグなんか入れるわそもそもwordpress側のrssのヴァージョンがなぜか0.92だわテンヤワンヤになっている。
しかもあれこれしてるうちに、そもそもアクセスしても何もないよとか言われる始末。
とはいえ冷静に見るとbbpressのfeedはちゃんと出てるぽいんでなんとかしてやろうかな。
(ほんと次から次へと問題でるな…)
あれ?というかbbpressのfeedってログインしないと見れないのか。
じゃだめじゃん…_no
ここで今回は力尽きた。(今回、といっても一週間以上かかっているのだが…)
関連するかもしれない他の記事
Comments:0
Trackbacks:0
- Trackback URL for this entry
- http://weblog.atl-r.net/blog/tipstomake_communitysite7/trackback/
- Listed below are links to weblogs that reference
- wordpressで非公開コミュニティサイトを作る(7)feedのカスタマイズ2(失敗) from atl*weblog
