SHOEISHA iD

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

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

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

PerlでデスクトップCGI

デスクトップCGIにinnerHTMLを使ったAjaxを導入する

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


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の配列として出力します。それぞれtitlescategoriesupdatedslinks配列となります。

 content要素はdocument.getElementById('r1').innerHTML = links[i];のようにして、HTML出力の<div name="r1" id="r1"></div>の部分に表示させます。

content要素の実体参照のデコード

 AtomフィードデータベースのcontentカラムにはAtomのcontent要素が格納されています。ただし、単に取り出しただけでは正常に表示できません。「atom2msrdb.cgi」がAtomフィードを解析するために実体参照エンコードしたデータを格納している場合があるからです。その場合には実体参照のデコードが必要です。二重にエンコードされている場合は、デコードを二重に行います。その判断基準は、「実体参照があって、しかもpreタグが存在しなければデコードする」というものです。

atom2msrdb_read_js3.cgi
#!/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 ();">
  &lt;-----&gt;
  <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/(&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;
}

問題点

 「atom2msrdb_read_js3.cgi」の検討において、さまざまなサイトのAtomフィードを表示させてみると、うまく表示できない場合があります。特に、scriptタグがcontent要素に含まれる場合には、CGIが出力するHTMLのscriptタグ内にcontent要素を含ませると誤動作してしまいます。

 JavaScript内のデータとしてHTMLを記述するのは、クォートのエスケープや改行の再現など、それでなくとも複雑で面倒です。このような方法はHTMLが簡単なものは別としても、基本的には避けた方がよさそうです。

次のページ
Atomフィードリーダー(atom2msrdb_read_ajax3.cgi)

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

PerlでデスクトップCGI連載記事一覧

もっと読む

この記事の著者

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

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

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1342 2008/08/19 18:57

イベント

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

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

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

メールバックナンバー