SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

PerlでデスクトップCGI

デスクトップCGIフレームワークでDHTMLを実現する

第6回 デスクトップCGIでWebとデスクトップを融合する


  • X ポスト
  • このエントリーをはてなブックマークに追加

Atomフィードリーダー - atom2msrdb_read_js.cgi

動作の概要と外観

 図2は、著者のサイトのAtomフィードデータをAtomフィードデータベースから読み出して表示したFirefoxの画面です。データベースから読み出されたカテゴリとタイトルと更新日付がtext型の入力フォームで表示され、記事本体はIFRAMEで表示しており、中身はentry主要素のlink要素に設定されているURLで参照先される文章そのものとなっています。また、[PREV]ボタン(戻る)、[NEXT]ボタン(次へ)で時系列で記事の表示を切り替えることができます。記事は時系列降順で表示されます。

図2 atom2msrdb_read_js.cgi実行画面
図2 atom2msrdb_read_js.cgi実行画面

スクリプト

 連載第5回の「atom2msrdb_read.cgi」と比べて、スクリプトの新しい構成要素は以下のようになっています。

CGIパラメータ

 パラメータとして、AtomフィードのURLを受け取り動作します。コマンドラインの最初の引数からでもパラメータの受け取りができます。また、入力がない場合は、著者のサイトのAtomフィードを使います。従って、単にブラウザで本CGIを実行すると著者のサイトを表示します。

SQLデータベースへの問い合わせ

 「feeds」テーブルへのクエリ部分は簡単なので省略します。「entries」テーブルへのSQLクエリ部分を次に示します。ORDER BY updated DESCによりカラム名降順で結果を取得します。

atom2msrdb_read_js.cgiの一部分
SELECT atom, issued, published, link, title, content, 
       name, modified, created, id, updated, summary, category
  FROM entries 
 WHERE atom = \"$atomurl\" 
ORDER BY updated DESC

JavaScript

 ソースの「### JavaScript部分のCGI出力生成」と「# CGI 最後の部分の出力」の部分を参照してください。

 Atomフィードデータベースから読み取った、title要素、category要素、updated要素、link要素をJavaScriptの配列として出力します。それぞれtitlescategoriesupdatedslinks配列となります。

 link要素のURLが示すページへジャンプしてIFRAME(self.frames['r1']オブジェクトで示される)に表示させるためには、location.hrefプロパティを用います。JavaScript関数のスクリプトがどのIFRAMEやFORMのどの部分で動作するかは、IFRAMEタグやFORMタグのname属性で結び付けられています。

link要素設定の例外
 著者サイトの場合、AtomフィードのURLは当月であれば同一の「http://homepage1.nifty.com/kazuf/renewal_atom.xml」を使いますが、古い月のAtomフィードは、「renewal_atom_YYYY_MM.xml」というファイル名に変えて保存されるルールで運用しています。しかし、新規記事を読み込んだ場合には常に「renewal_atom.xml」という名前でデータベースに記録されるために、そのまま読み出そうとすると存在しない記事になってしまいます。これを回避するために、スクリプト実行時点の日時から、古い記事を読み出すためにファイル名を変換するルールをスクリプトに組み込んでいます。
atom2msrdb_read_js.cgi
#!/Perl5.8/bin/perl.exe
use strict;
use warnings;
use DBI;
use CGI qw(:cgi);

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 Reader</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>
</head>
<body onLoad="msg(0);">
<div class="emph">Atom DB Reader</div>
<script>
HEADER

# Atomフィードデータベース(atomfeeds)へのアクセス
my $db = DBI->connect(
  "DBI:mysql:database=atomfeeds;host=localhost;port=3306",
                       "root", "password",
                       {'RaiseError' => 1});

## feedsテーブルへのアクセス
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>";
}

## entriesテーブルへのアクセス
$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();

### JavaScript部分のCGI出力生成
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 $firstlink;
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()){
  $title .= "\"$row->{title}\",";
  $category .= "\"$row->{category}\",";
  $updated .= "\"$row->{updated}\",";
  $count++;
  if($atomurl eq 'http://homepage1.nifty.com/kazuf/renewal_atom.xml'){
    if($row->{updated} =~ /^(\d{4})\-(\d{2})/){
      $year = $1;$mon = $2;
      if($cyear != $year or $cmon != $mon){
        $row->{link} =~ s/(renewal)(\.html)/$1_${year}_$mon$2/;
      }
    }
  }
  if($count == 1){ $firstlink = $row->{link}; }
  $link .= "\"$row->{link}\",";
}
chop $title;print $title . ");\n";
chop $category;print $category . ");\n";
chop $updated;print $updated . ");\n";
chop $link;print $link . ");\n";

# Atomデータベースの終了
$sth->finish();
$dbh->disconnect();

# CGI 最後の部分の出力
print <<FOOTER;
var i = 0;
function nextLink () {
    i++; if(i >= links.length) i = 0;
    self.frames['r1'].location.href = links[i];
    msg(i);
}
function prevLink () {
    i--; if(i < 0) i = links.length - 1;
    self.frames['r1'].location.href = links[i];
    msg(i);
}
function msg(i) {
    document.form.title.value = titles[i];
    document.form.category.value = categories[i];
    document.form.updated.value = updateds[i];
}
//-->
</script>
$feed
<form name="form">
<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>
<p>
  <iframe name="r1" src="$firstlink" width="95%" height="65%">
  </iframe>
</p>
<p>
  <input type="button" value="PREV" onclick="prevLink ();">
   &lt;-----&gt;
  <input type="button" value="NEXT" onclick="nextLink ();">
</p>
</form>
</body>
</html>
FOOTER

exit(0);
# マークアップ記号の実体参照デコード
# 文字実体参照と数値文字参照(注1)の両方に対応
sub entities_decode{
    my($str) = @_;
    $str =~ s/(&lt;|&#60;|&#x3C;)/</g;
    $str =~ s/(&gt;|&#62;|&#x3E;)/>/g;
    $str =~ s/(&apos;|&#39;|&#x27;)/'/g;
    $str =~ s/(&quot;|&#34;|&#x22;)/"/g;
    $str =~ s/(&amp;|&#38;|&#x26;)/&/g;
    return $str;
}
※注意
 「数値文字参照」という用語はこれまで「数値実体参照」という用語を使っていました。英語では、「Numeric character reference」ですので、「数値文字参照」に修正します。また、entities_decodeのサブルーチンは、10進数で表現された数値文字参照だけでなく16進数で表されたものもデコードできるように対応しました。

問題点と対策

 「atom2msrdb_read_js.cgi」の検討において、さまざまなサイトのAtomフィードを表示させてみると、entry主要素のlink要素のURLは、必ずしもentry要素に収録された元の記事を指し示すとは限らないことが分かりました。大体、次の3通りの応答があります。

  1. 元記事のURL
  2. 該当記事のenrty要素のXMLを返す
  3. 「No authentication info」のエラーを返す

 期待通りに元記事のURLを示していたとしても該当記事が見やすく表示されない場合もありますので、まったく実用的とは言えません。entry要素のlink要素から元記事にアクセスする方法は本稿の目的に合致しないので、content要素そのものを表示する方法を考えてみましょう。次項では、content要素を埋め込んだHTMLを生成するCGIの出力を、IFRAMEに表示するように書き換えます。

次のページ
Atomフィードブラウザ - atom2msrdb_read_js2.cgi

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
PerlでデスクトップCGI連載記事一覧

もっと読む

この記事の著者

jscripter(ジェイスクリプター)

NIFTY SERVEのFGALEL(エントリーラボ)、FGALTS(テキスト&スクリプト)に参画し、FGALTSでシスオペを務める。現在、TSNETメンバー。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/1189 2007/05/22 11:44

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング