トラックバック機能を作る

Daisukeh 2009/01/27 19:43 投稿
Daisukeh 2009/01/27 23:11 解説
Daisukeh 2009/08/05 13:43 移設

記事一覧

サーバー移管に伴い、この機能は一時的に削除されました。
コンテンツの配備が整い次第、 CGI 関連の復旧作業をしようと思います。
2009/08/05

 自分が立ち上げているウェブシステムではplugin:linkbackがうまく動作しない。プラグインのソースコードを改造したり、デバッグコードを入れてトラックバックを送信して、挙動を確かめたりしたのだが、不具合の原因がつかめなかったため、思い切って作ることにした。幸いにもplugin:linkbackは読みやすいソースコードだった(と思う)ので、これをベースに基本的な部分だけ抽出して、plugin:phpincによる表示と外部からのトラックバック受信&メタデータ記録の両方の処理をするスクリプトを作った。

 急造なのでコメントが一切ないが、PHPは関数名などがわかりやすいので、このくらいのソースコードであれば読解は難しくないと思う。前半部分が CGI で POST された場合の処理、すなわちトラックバックを受け付ける処理で、受信データの解析とメタデータの更新、ロギング処理を行っている。後半部分はブログやウィキで表示する部分で、トラックバックの URL 表示と当該ページにトラックバックされたリンクアイテムを表示する処理になっている。

/phpincludes/trackback.php 改訂版

<?php
 
  // トラックバック処理   2.00 09/02/02 Daisukeh
 
  // 基本情報取得
  $server = $_SERVER['SERVER_NAME'];
  $method = $_SERVER['REQUEST_METHOD'];
//  $dokuwk = '\dokuwiki';
  $path   = $_SERVER['APPL_PHYSICAL_PATH'].$dokuwk.'\data\\';
  $script = eregi_replace('/[^/]+$', '', $_SERVER['SCRIPT_NAME']);
 
  // トラックバック受付
  if(($method == 'POST') && ($_REQUEST['url'] != ''))
  {
    // ページ名取得
    $query = $_SERVER['QUERY_STRING'];
    if($query == '') $query = substr($_SERVER['PATH_INFO'], 1);
 
    // リンク情報取得
    $info = array(
      'id'                  => md5($_REQUEST['url']),
      'url'                 => $_REQUEST['url'],
      'title'               => strip_tags($_REQUEST['title']),
      'excerpt'             => strip_tags($_REQUEST['excerpt']),
      'blog_name'           => strip_tags($_REQUEST['blog_name']),
      'submitter_ip'        => $_SERVER['REMOTE_ADDR'],
      'submitter_useragent' => $_SERVER['HTTP_USER_AGENT'],
      'submitter_referer'   => $_SERVER['HTTP_REFERER'],
      'datetime'            => date('Y/m/d H:i'),
    );
 
    // リンク有効判定
    $ok = true;
    if(!eregi('^[htps]+://.*', $info['url'])) $ok = false;
    if(($info['title'] == '') || ($info['blog_name'] == '')) $ok = false;
    if($ok)
    {
      $data = array();
      $file = $path.'meta\\'.eregi_replace(':', '\\', $query).'.linkbacks';
      if(@file_exists($file)) $data = unserialize(@file_get_contents($file));
      if(!array_key_exists('trackbacks', $data))
        $data = array(
          'trackbacks' => 0,
          'linkinfo'   => array(),
        );
      if(array_key_exists($info['id'], $data['linkinfo'])) $ok = false;
      if($data['trackbacks'] >= 10) $ok = false;
    }
 
    // リンク無効
    if(!$ok)
      exit("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n<response>\n<error>1</error>\n</response>");
 
    // トラックバック情報追加
    $data['linkinfo'][$info['id']] = $info;
    $data['trackbacks'] = count($data['linkinfo']);
    @file_put_contents($file, serialize($data));
 
    // トラックバックログ追加
    $data = array();
    $log  = $path.'trackback.log';
    if(@file_exists($log)) $data = unserialize(@file_get_contents($log));
    $info['pagename']  = $query;
    $data[$info['id']] = $info;
    @file_put_contents($log, serialize($data));
 
    // リンク有効
    exit("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n<response>\n<error>0</error>\n</response>");
  }
 
  // トラックバック審査
  if(($method == 'GET') && ($_GET['cmd'] != ''))
  {
    // リンク情報取得
    $cmd  = $_GET['cmd'];
    $page = str_replace('%3A', ':', str_replace('%2F', '/', urlencode($_GET['pg'])));
    $id   = $_GET['id'];
 
    // リンク削除
    if($cmd == 'delete')
    {
      $file = $path.'meta\\'.eregi_replace(':', '\\', $page).'.linkbacks';
      $data = array();
      if(@file_exists($file)) $data = unserialize(@file_get_contents($file));
      if(!array_key_exists('trackbacks', $data))
        $data = array(
          'trackbacks' => 0,
          'linkinfo'   => array(),
        );
      if(array_key_exists($id, $data['linkinfo']))
      {
        unset($data['linkinfo'][$id]);
        $data['trackbacks'] = count($data['linkinfo']);
      }
      @file_put_contents($file, serialize($data));
      $cmd = 'accept';
    }
 
    // リンク承認(トラックバックログ更新)
    if($cmd == 'accept')
    {
      $data = array();
      $log  = $path.'trackback.log';
      if(@file_exists($log)) $data = unserialize(@file_get_contents($log));
      if(array_key_exists($id, $data)) unset($data[$id]);
      @file_put_contents($log, serialize($data));
    }
 
    // トラックバックページ遷移
    $script = eregi_replace('/phpincludes', '', $script);
    exit('<html><head><meta http-equiv="refresh" content="0;url=http://'.$server.$script.'/doku.php?id=wiki:trackback"></head></html>');
  }
 
  // 不正アクセス回避
  if(!array_key_exists('conf', $GLOBALS))
    exit('<html><head><meta http-equiv="refresh" content="0;url=http://'.$server.'/"></head></html>');
 
  // トラックバック表示
  global $conf;
  global $ID;
  global $INFO;
  $link  = $conf['baseurl'].$script.'/phpincludes/trackback.php';
  $back  = eregi_replace('/', ':', eregi_replace('^.+/data/meta/', '', metaFN($ID)));
  $admin = (md5($INFO['userinfo']['name']) == md5('ADMINISTRATOR'));
  $style = 'padding:0;border:1px dotted gray;color:red;background-color:yellow;';
  $data  = array();
  $file  = metaFN($ID).'.linkbacks';
  if(@file_exists($file)) $data = unserialize(@file_get_contents($file));
  if(!array_key_exists('trackbacks', $data))
  {
    $data = array(
      'trackbacks' => 0,
      'linkinfo'   => array(),
    );
    @file_put_contents($file, serialize($data));
  }
  $trackbacks = $data['trackbacks'];
 
  // リンク一覧整形
  $html = '<div><a class="urlextern" href="'.$link.'?'.$back.'"> トラックバック';
  if($trackbacks > 0) 
  {
    $html .= "ス ({$trackbacks})<ul>";
    foreach($data['linkinfo'] as $info)
    {
      $url   = $info['url'];
      $title = $info['title'];
      $blog  = $info['blog_name'];
      $date  = $info['datetime'];
      $excpt = $info['excerpt'];
      $html .= "<li><a href=\"{$url}\" title=\"{$excpt}\">{$blog} - {$title} ({$date})</a>";
      if($admin) $html .= ' - <a href="'.$link.'?cmd=delete&pg='.$back.'&id='.$info['id'].'" style="'.$style.'">削除</a>';
      $html .= '</li>';
    }
    $html .= '</ul>';
  }
  else $html .= '</a>';
  $html .= '</div>';
 
  // リンク表示
  echo $html;
 
?>

 使い方は非常に簡単である。まず、自分のDokuWikiplugin:phpincが入っている事を確認してから、このプログラムを所定の場所へ作成する。そしてブログでもウィキでも何でもいいのだが、要するに編集しているドキュメントの中で、

  <phpinc=trackback.php>

と書くだけだ。プログラム中の $ID というのがページ名で、自動的に最適なトラックバック用 URL が生成される。下のリンクはこのページへのトラックバックリンクである。

と、plugin:linkbackが使えないから作っちゃえという、非常に無謀な計画にしては、短時間で作成できた。PHPDokuWikiの柔軟性に感謝!

Daisukeh 2009/01/29 15:26 追記 1.10

 その後、トラックバックの承認と削除をする機能を追加してみた。その処理中で POST と GET を使いわけているのだが、文字列のデコード影響かそれとも他の要因なのか、正しくメタデータをとってこれないことがある。何故なのかは調査中だが、おそらく日本語関係のデコードに何か指定を忘れている部分があるのかと思う。承認と削除には簡単なロギング処理を用意して、それで行うようにしているが、こちらはDokuWikiのページ内に<php>タグで直接記述してしまった。(このあたり、簡単に動的なページを作れるのが、DokuWikiのすばらしいところだと思う。)トラックバックのページを参考にされたい。

trackback.phpの追加箇所(抜粋)

  if(($method == 'GET') && ($_GET['cmd'] != ''))
  {
    $cmd  = $_GET['cmd'];
    $page = $_GET['pg'];
    $id   = $_GET['id'];
 
    if($cmd == 'delete')
    {
      $path = $_SERVER['APPL_PHYSICAL_PATH'];
      $file = $path.'\data\meta\\'.eregi_replace(':', '\\', $page).'.linkbacks';
      $data = array();
      if(@file_exists($file)) $data = unserialize(@file_get_contents($file));
      if(!array_key_exists('trackbacks', $data))
        $data = array(
          'trackbacks' => 0,
          'linkinfo'   => array(),
        );
      if(array_key_exists($id, $data['linkinfo']))
      {
        unset($data['linkinfo'][$id]);
        $data['trackbacks'] = count($data['linkinfo']);
      }
      @file_put_contents($file, serialize($data));
      $cmd = 'accept';
    }
 
    if($cmd == 'accept')
    {
      $data = array();
      $log  = $path.'\data\trackback.log';
      if(@file_exists($log)) $data = unserialize(@file_get_contents($log));
      if(array_key_exists($id, $data)) unset($data[$id]);
      @file_put_contents($log, serialize($data));
    }
 
    exit('<html><head><meta http-equiv="refresh" content="0;url=http://'.$server.'/doku.php?id=wiki:trackback"></head></html>');
  }

 md5('ADMINISTRATOR')の部分は、それぞれの環境で書き換える必要があるので注意が必要だ。1)

Daisukeh 2009/02/02 10:21 2.00

 上記の不具合に関して調査した結果、$_GETで得たページ名の文字コードに問題があることが判明した。渡されるのがUTF-8デコード(エンコード?)済みの、読める状態の文字列であったので、ページ名をファイル名に変換している関数metaFN()などが利用しているurlencode()で生成される名称と合致していないことがわかった。ページ名が合致していることがわかった後、承認と削除も正しく動作することが確認できた。(確認は、CGIで渡される$_GETの内容をファイルに保存することで行った。画面に表示したりすると、自動的にエンコードを認識して表示されるので、この手の問題はわかりにくい。ファイルへの保存はバイナリ保存に近い感覚なので、変数の内容がたとえ文字列であっても、自動的にデコードされることがないという点で、原始的なデバッグには必要な手順だと思う。)

trackback.php リンク情報取得 部分

  $cmd  = $_GET['cmd'];
  $page = str_replace('%3A', ':', str_replace('%2F', '/', urlencode($_GET['pg'])));
  $id   = $_GET['id'];

 また、随所にちりばめてしまっていた$_SERVER['APPL_PHYSICAL_PATH']や'\data\\'といった記述も、なるべく一元化できるように変更し、スクリプトパスを認識させた上で、再描画時のウェブルートパスを正常に設定できるように改造した。あわせて、コメントも追加したので、かなり判りやすくなった。

ダウンロードtrackback-090202.zip2.10

 トラックバックに関しては、これで一通りの機能がインプリメントできたと思う。トラックバックの受付こそ自動で行われるが、後日審査できるというシステムでも、実質的には問題はない。(少なくとも自分は。)今後の展開として、Javascriptと組ませてクイックリンクバック(仮)機能を実現したい。これは、ポップアップでリンク情報を入力する簡易トラックバックである。トラックバックは通常ブログなどから送信しなければならないため、トラックバックの窓口を持たないCMSなどではリンクを張りにくい。トラックバックは勝手に相手のページへリンクを作ることができるという点が優れている訳で、性善説に基づいた上で、そういった機能は面白いと思うのだ。

Daisukeh 2009/02/02 14:10 2.10

 頭の中でクイックリンクのことが離れなかったので、即刻製作することにした。ストラクチャは単純で、DokuWikiでフォームを作成して、そこからtrackback.phpをPOSTで呼び出す。その時にhiddenでquicklinkの指定をしておくが、これは見た目をよくするだけの処理である。すなわち、ブログエンジンなどからトラックバックを送るのと同じことを、フォームで代行させているだけである。この機能によって、ログインしていなくても(非ユーザーであっても)関連するサイトやページを、このサイトのそれぞれのトピックにリンクできるようになった。フォーム側はクイックリンクページのソースを参照して欲しい。プログラムの変更は以下の部分を中心に行った。

trackback.php リンク一覧整形 部分

  $html = '<div>'
        . '<a class="urlextern" href="'.$link.'?'.$back.'"> トラックバック</a> '
        . '<a class="urlextern" href="'.$base.$script.'/doku.php?id=wiki:quicklink&pagename='.$back.'"> クイックリンク</a> ';

 DokuWikiのページと、既存のtrackback.phpの吐くコードとの複合動作になるので、ウェブページの表示と更新との関係が煩雑に感じるが、えてしてウェブアプリケーションとはそういうものだろう。JavascriptとDOMを操る人にとっては、こういったコード生成のロジックは朝飯前なんだろうな。

1) くれぐれもmd5(' … なんて書かないように!

掲示板

, 2009/01/27 19:49
自己レスです。 ブログじゃなくても、しかも記事の途中でもトラックバックを表示させることができます。
, 2012/08/05 20:44
Stay with this guys, you're helping a lot of popele.
, 2018/08/08 23:46
Meet a guy to communicate and more
write I won't bite you +79967254947
boys write WhatsApp +79967254947
boys write WhatsApp +79967254947
boys write WhatsApp +79967254947
Enter your comment
 
 
programming/dokuwiki/トラックバック機能を作る.txt · 最終更新: 2009/08/06 14:59 by daisukeh
 

Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS
Driven by DokuWiki Powered by Google do yourself a favour and use a real browser - get firefox ! GIMP is the GNU Image Manipulation Program. Adobe Flex smarty : Template Engine