Atomフィードリーダー(atom2msrdb_read_js3.cgi)
動作の概要
データベースから読み出されたカテゴリとタイトルと更新日付がtext型の入力フォームで表示されるところなど、前回の「atom2msrdb_read_js2.cgi」と基本的な仕組みはほぼ一緒ですが、記事本体はentry主要素のcontent要素を、JavaScriptの配列としてCGIが出力するHTML内に格納し、innerHTMLを使用して1件ずつ表示しています。また、[PREV]ボタン(戻る)、[NEXT]ボタン(次へ)で時系列で記事の表示を切り替えることができます。[NEXT]ボタンを押した場合は、時系列降順に記事が表示されます。
スクリプト
設定項目
CGIパラメータ
パラメータとして、AtomフィードのURLを受け取り動作します。コマンドラインの最初の引数からでもパラメータの受け取りができます。また、入力がない場合は、著者のサイトのAtomフィードを使います。従って、単にブラウザで本CGIを実行すると著者のサイトのAtomフィードデータをMySQLのAtomフィードデータベースから読み出して1件ずつ表示します。「feeds2.cgi」と併用すれば、FirefoxのブックマークのATOMフォルダからAtomフィードのURLを読み出してパラメータとして設定し、Feedsリストとして表示します。
環境変数
環境変数CGIDIRが設定されている必要があります。
JavaScript
スクリプトのコメント部分「CGI 最初の部分の出力」の終わりの部分から、「CGI 最後の部分の出力」の部分を参照してください。
Atomフィードデータベースから読み取った、title要素、category要素、updated要素、content要素をJavaScriptの配列として出力します。それぞれtitles、categories、updateds、links配列となります。
content要素はdocument.getElementById('r1').innerHTML = links[i];のようにして、HTML出力の<div name="r1" id="r1"></div>の部分に表示させます。
content要素の実体参照のデコード
AtomフィードデータベースのcontentカラムにはAtomのcontent要素が格納されています。ただし、単に取り出しただけでは正常に表示できません。「atom2msrdb.cgi」がAtomフィードを解析するために実体参照エンコードしたデータを格納している場合があるからです。その場合には実体参照のデコードが必要です。二重にエンコードされている場合は、デコードを二重に行います。その判断基準は、「実体参照があって、しかもpreタグが存在しなければデコードする」というものです。
#!/Perl5.8/bin/perl.exe use strict; use warnings; use DBI; use CGI qw(:cgi); use URI::Escape; my $cgidir = $ENV{'CGIDIR'}; my $atomurl = ""; # Atom URL データの取得 if(param('atom')){ # CGI で URL を取得する $atomurl = param('atom'); }elsif($ARGV[0]){ # コマンドラインから URL を取得する $atomurl = $ARGV[0]; }else{ $atomurl = "http://homepage1.nifty.com/kazuf/renewal_atom.xml"; # print "Atom URL データが存在しない。\n";exit;# 終了する } # CGI 最初の部分の出力 print <<HEADER; Content-type: text/html; charset=UTF-8 <html> <head> <title>ATOM DB Browser</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="stylesheet" type="text/css" href="/mystyle.css" /> <script language="Javascript"> <!-- if(navigator.appCodeName.indexOf("Mozilla") != -1){ document.write( "<link rel='stylesheet' type='text/css' href='/firefox.css' />"); } //--> </script> <script> <!-- HEADER my $dbh = DBI->connect( "DBI:mysql:database=atomfeeds;host=localhost;port=3306", "root", "password", {'RaiseError' => 1}); my $sth = $dbh->prepare(qq{ SELECT atom,link,ver,title,tagline,modified,info,logo, icon,subtitle,updated FROM feeds WHERE atom = \"$atomurl\" }); $sth->execute(); my $feed; while(my $row = $sth->fetchrow_hashref()){ $feed .= "<p><a href=\"$row->{link}\">$row->{title}</a></p><p>"; if($row->{logo}){ $feed .= "<img src=\"$row->{logo}\">"; }elsif($row->{icon}){ $feed .= "<img src=\"$row->{icon}\">"; } $feed .= "$row->{subtitle}</p><p>$row->{updated}</p>"; } $sth = $dbh->prepare(qq{ SELECT atom, issued, published, link, title, content, name, modified, created, id, updated, summary, category FROM entries WHERE atom = \"$atomurl\" ORDER BY updated DESC }); $sth->execute(); my $dec_title; my $title = "var titles = new Array("; my $category = "var categories = new Array("; my $updated = "var updateds = new Array("; my $link = "var links = new Array("; my $count = 0; my $esc_id; my $linkstr; my $firstlink; my $content; my $summary; my $year;my $cyear = (localtime(time))[5] + 1900; my $mon;(my $cmon = (localtime(time))[4] + 1) =~ s/^(\d)$/0$1/; while(my $row = $sth->fetchrow_hashref()){ $dec_title = entities_decode($row->{title}); chomp $dec_title; $dec_title =~ s/(["'])/\\$1/g; $title .= "\"$dec_title\","; $category .= "\"$row->{category}\","; my $esc_atomurl = uri_escape($atomurl); if($row->{updated}){ $updated .= "\"$row->{updated}\","; $esc_id = uri_escape($row->{updated}); }elsif($row->{modified}){ $updated .= "\"$row->{modified}\","; $esc_id = uri_escape($row->{modified}); }else{ $esc_id = uri_escape($row->{title}); } if($row->{content}){ if($row->{content} =~ /(&[a-z0-9]{2};|&#\d+;)/i && $row->{content} !~ /<pre>/i){ $content = &entities_decode($row->{content}); if($content =~ /(&[a-z0-9]{2};|&#\d+;)/i && $content !~ /<pre>/i){ $linkstr = &entities_decode($content); }else{ $linkstr = $content; } }else{ $linkstr = $row->{content}; } }elsif($row->{summary}){ if($row->{summary} =~ /(&[a-z0-9]{2};|&#\d+;)/i && $row->{summary} !~ /<pre>/i){ $summary = &entities_decode($row->{summary}); if($summary =~ /(&[a-z0-9]{2};|&#\d+;)/i && $summary !~ /<pre>/i){ $linkstr = &entities_decode($summary); }else{ $linkstr = $summary; } }else{ $linkstr = $row->{summary}; } } $count++; if($count == 1){ $firstlink = $linkstr; } $linkstr =~ s/([\\'"])/\\$1/g;# エスケープ処理 $linkstr =~ s/([^\\]?)[\r\n]+/$1\\n"+"/g;# 改行の処理 $link .= "\"$linkstr\","; } chop $title;print $title . ");\n"; chop $category;print $category . ");\n"; chop $updated;print $updated . ");\n"; chop $link;print $link . ");\n"; $sth->finish(); $dbh->disconnect(); # CGI 最後の部分の出力 print <<FOOTER; var i = 0; function nextLink () { i++; if(i >= links.length) i = 0; msg(i); } function prevLink () { i--; if(i < 0) i = links.length - 1; msg(i); } function msg(i) { document.form.title.value = titles[i]; document.form.category.value = categories[i]; document.form.updated.value = updateds[i]; document.getElementById('r1').innerHTML = links[i]; } //--> </script> </head> <body onLoad="msg(0);"> <div class="emph">Atom DB Browser</div> $feed <form name="form"> <p> <input type="button" value="PREV" onclick="prevLink ();"> <-----> <input type="button" value="NEXT" onclick="nextLink ();"> </p> <p> <input type="text" name="category" size="20"> <input type="text" name="title" size="80"> </p> <p> <input type="text" name="updated" size="40"> </p> <div name="r1" id="r1"></div> </form> </body> </html> FOOTER exit(0); # マークアップ記号の実体参照デコード # 文字実体参照と数値文字参照の両方に対応 sub entities_decode{ my($str) = @_; $str =~ s/(<|<|<)/</g; $str =~ s/(>|>|>)/>/g; $str =~ s/('|'|')/'/g; $str =~ s/("|"|")/"/g; $str =~ s/(&|&|&)/&/g; return $str; }
問題点
「atom2msrdb_read_js3.cgi」の検討において、さまざまなサイトのAtomフィードを表示させてみると、うまく表示できない場合があります。特に、scriptタグがcontent要素に含まれる場合には、CGIが出力するHTMLのscriptタグ内にcontent要素を含ませると誤動作してしまいます。
JavaScript内のデータとしてHTMLを記述するのは、クォートのエスケープや改行の再現など、それでなくとも複雑で面倒です。このような方法はHTMLが簡単なものは別としても、基本的には避けた方がよさそうです。
