&pgid;

**目次 [#mf3a2e3b]

#contents

** はじめに [#p3cfc5a1]

[[ARK-Web SandboxWiki>http://www.ark-web.jp/sandbox/wiki/]]ではフッタ情報を表現するために、PukiWikiの[[blikifooterプラグイン>http://pukiwiki.sourceforge.jp/?%E8%87%AA%E4%BD%9C%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%2Fbliki.inc.php]]を利用して

 #blikifooter(進地);

とコマンドを記述することで

 投稿者 進地|パーマリンク|trackback(10)|comment(5)

のように投稿者、Permalink、Trackback、コメント数を表示していましたが、追加ではてブへの登録ボタン、はてブ数カウント、はてブ詳細情報を表示するためにblikifooterプラグインをカスタマイズしました。(追加後のイメージは下の画像の通り)

&ref(hatebufooter_with_blikifooter.JPG);

特にはてブ数カウントとはてブ詳細情報の取得/表示には[[JSONP(JSON with Padding)>http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/]]を利用できる[[はてなブックマークエントリー情報取得API>http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A4%CA%A5%D6%A5%C3%A5%AF%A5%DE%A1%BC%A5%AF%A5%A8%A5%F3%A5%C8%A5%EA%A1%BC%BE%F0%CA%F3%BC%E8%C6%C0API?kid=184075]]を使用して取得しています。

また、blikifooterと切り離して、はてブ登録ボタン、はてブ数カウント、はてブ詳細情報の表示機能のみを持たせたhatebufooterプラグインを作成しました。

以下、このエントリーでは

-はてブfooterの外部仕様
-ソース解説
--JSONPの解説

を行います。


** はてブfooterの外部仕様 [#k6274407]

はてブfooterの外部仕様となる機能は下記になります。

-はてブ登録ボタンの表示
-はてブ数カウントの表示
-はてブ詳細情報の表示

はてブ登録ボタンは[B!]のボタンで、クリックすると当該Wikiページをはてブ登録できます。

はてブ数カウントの表示と、はてブ詳細情報の表示は、当該Wikiページが1件以上はてブされている場合にのみ有効になります。

今、n(n=1〜N)人によって当該Wikiページがはてブされているとすると、

はてブ数カウントは

 このエントリーをブックマークしているユーザー( n users )

と表示され、

はてブ詳細情報ははてブ数カウントに続いて

          <div>
            ユーザ1のはてブ登録日時(yyyy/mm/dd hh:mm:ss) <a href="ユーザ1のHatena::BookmarkのURL">ユーザ1</a><br/>
            - ユーザ1のコメント(ユーザ1のコメントがもしあれば表示される)
          </div>
          <div>
            ユーザ2のはてブ登録日時(yyyy/mm/dd hh:mm:ss) <a href="ユーザ2のHatena::BookmarkのURL">ユーザ2</a><br/>
            - ユーザ2のコメント(ユーザ2のコメントがもしあれば表示される)
          </div>
           :
           :
          <div>
            ユーザNのはてブ登録日時(yyyy/mm/dd hh:mm:ss) <a href="ユーザNのHatena::BookmarkのURL">ユーザN</a><br/>
            - ユーザNのコメント(ユーザNのコメントがもしあれば表示される)
          </div>

という構造で生成、表示されます。


** ソース解説 [#m5800f2d]

はてブfooterのメイン部のソースは下記の通りです(定数部の宣言等は載せていません)

 01: function plugin_hatebufooter_convert() {
 02: 
 03:     global $script, $vars;
 04: 
 05:     $args   = func_get_args();
 06:     $retval = "";
 07: 
 08:     if ( empty($args[1]) || ( ( !empty($args[1]) ) && $args[1] == true ) ) {
 09: 
 10: 	$permalink = $script . '?' . urlencode($vars['page']);
 11: 	$permalink4bookmark = urlencode($permalink);
 12: 
 13:    // 「このエントリをはてなブックマークに登録」ボタンの表示
 14:    $retval .= sprintf('<a href="http://b.hatena.ne.jp/append?"><img alt="append.gif" src="" alt="このエントリをはてなブックマークに登録" width="" height="" border="0" /></a>', $permalink4bookmark, HATEBUFOOTER_HATENA_BOOKMARK_APPEND_IMG_URL, HATEBUFOOTER_HATENA_BOOKMARK_APPEND_IMG_WIDTH, HATEBUFOOTER_HATENA_BOOKMARK_APPEND_IMG_HEIGHT);
 15: 
 16:    // はてブ数カウントとブックマークしているユーザの詳細表示(JSONPを利用)
 17:    $md5_page = md5($vars['page']);
 18:    $retval .=<<< HATENA_BOOKMARK_COUNT
 19: <script type="text/javascript" src="./js/jsr_class.js"></script>
 20: <div id="hatena_bookmark_count_detail_$md5_page">
 21: </div>
 22: <script type="text/javascript">
 23: var hatena_jsonp_base = 'http://b.hatena.ne.jp/entry/json/';
 24: var jsr = new JSONscriptRequest(hatena_jsonp_base + '?url=$permalink4bookmark&callback=hatena_footer_hundler_$md5_page');
 25: jsr.buildScriptTag();
 26: jsr.addScriptTag();
 27: 
 28: function hatena_footer_hundler_$md5_page(data) {
 29: 
 30:     if (data != null && data.count > 0) {
 31:         var parent_div = document.getElementById('hatena_bookmark_count_detail_$md5_page');
 32: 
 33:         // カウンタ画像を取得
 34:         var counter_a = document.createElement('a');
 35:         counter_a.setAttribute('href', 'http://b.hatena.ne.jp/entry/' + '$permalink');
 36:         var counter_img = document.createElement('img');
 37:         counter_img.setAttribute('src', 'http://b.hatena.ne.jp/entry/image/' + '$permalink');
 38:         counter_a.appendChild(counter_img);
 39: 
 40: 	    // このエントリーをブックマークしているユーザー(xx users)
 41:         // のフォーマットでfooterの下に表示する
 42:         parent_div.appendChild(document.createTextNode('このエントリーをブックマークしているユーザー('));
 43:         parent_div.appendChild(counter_a);
 44:         parent_div.appendChild(document.createTextNode(')'));
 45: 
 46: 	    // 各ブックマークの詳細をリスト表示させる
 47:         var bookmarks = data.bookmarks;
 48:         for (var i=0, bookmark; bookmark = bookmarks[i]; i++) {
 49: 
 50:             var user_div = document.createElement('div');
 51: 
 52:             // ブックマークタイムスタンプの表示
 53:             user_div.appendChild(document.createTextNode(bookmark.timestamp + ' '));
 54: 
 55:             // ブックマークしたユーザの表示
 56:             var user_a = document.createElement('a');
 57:             // yyyy/mm/dd hh:mm:ssからyyyymmdd文字列を作成する
 58:             var user_a_timestamp = bookmark.timestamp.substring(0, bookmark.timestamp.indexOf(' ', 0));
 59:             while (true) {
 60:                 var tmp_timestamp = user_a_timestamp;
 61:                 user_a_timestamp = tmp_timestamp.replace('/', '');
 62:                 if (user_a_timestamp == tmp_timestamp) {
 63:                     break;
 64:                 }
 65:             }
 66:             user_a.setAttribute('href', 'http://b.hatena.ne.jp/' + bookmark.user + '/' + user_a_timestamp + '#bookmark-' + data.eid);
 67:             user_a.appendChild(document.createTextNode(bookmark.user));
 68:             user_div.appendChild(user_a);
 69: 
 70:             // コメントの表示
 71:             if ( bookmark.comment != '' ) {
 72:                 user_div.appendChild(document.createElement('br'));
 73:                 user_div.appendChild(document.createTextNode(' - ' + bookmark.comment));
 74:             }
 75: 
 76:             parent_div.appendChild(user_div);
 77:         }
 78:     }
 79:     jsr.removeScriptTag();
 80: }
 81: </script>
 82: HATENA_BOOKMARK_COUNT;
 83:     }
 84: 
 85:     return sprintf(HATEBUFOOTER_TEMPLATE, $retval);
 86: }

簡単に解説します。

:10,11行目|はてなが提供する[[自分のブログに被ブックマーク数を表示する>http://b.hatena.ne.jp/help/count]]機能、および[[はてなブックマークエントリー情報取得API>http://d.hatena.ne.jp/keyword/%a4%cf%a4%c6%a4%ca%a5%d6%a5%c3%a5%af%a5%de%a1%bc%a5%af%a5%a8%a5%f3%a5%c8%a5%ea%a1%bc%be%f0%ca%f3%bc%e8%c6%c0API]]を利用する為に、当該WikiページのPermalinkを生成しています(両機能、APIは対象ページのPermalinkを引数として要求するため)。はてなブックマークエントリー情報取得APIは文字%もurlencodeされたURLを要求するため、Permalinkを2回urlencode()にかけています。

:13〜15行目|はてブ登録ボタンのタグを生成しています。はてブ登録機能は~
 http://b.hatena.ne.jp/append?$permalink4bookmark(←Permalinkを2回urlencode()にかけたURL)
にリンクすることで実現されます。

:16〜83行目|JSONPを利用して当該Wikiページに対して1件以上はてブがされていた場合にはてブ数カウント表示と詳細情報の表示を行います。[[JSONP利用部の解説>#v8aee48a]]に詳細を記述します。

:85行目|生成したタグ(=はてブfooterの出力)を返します。

**JSONP利用部の解説 [#v8aee48a]

[[ソース解説>#m5800f2d]]の16〜83行目は下記のHTMLタグとJavaScriptコードを生成して返す為のロジックになっています。$md5_pageは実際には当該Wikiページのページ名をPHPのmd5()関数でハッシュ化した値が指定されます。これはトップページなどで複数Wikiページが並んで表示される場合に、はてブfooterによって各Wikiページ単位に出力されるdivのidやJSONPコールバック関数名がバッティングしないようにするための仕組みです。

 <script type="text/javascript" src="./js/jsr_class.js"></script>
 <div id="hatena_bookmark_count_detail_$md5_page">
 </div>
 <script type="text/javascript">
   var hatena_jsonp_base = 'http://b.hatena.ne.jp/entry/json/';
   var jsr = new JSONscriptRequest(hatena_jsonp_base + '?url=$permalink4bookmark&callback=hatena_footer_hundler_$md5_page');
   jsr.buildScriptTag();
   jsr.addScriptTag();
 
   function hatena_footer_hundler_$md5_page(data) {
 
       if (data != null && data.count > 0) {
           var parent_div = document.getElementById('hatena_bookmark_count_detail_$md5_page');
 
           // カウンタ画像を取得
           var counter_a = document.createElement('a');
           counter_a.setAttribute('href', 'http://b.hatena.ne.jp/entry/' + '$permalink');
           var counter_img = document.createElement('img');
           counter_img.setAttribute('src', 'http://b.hatena.ne.jp/entry/image/' + '$permalink');
           counter_a.appendChild(counter_img);
 
           // このエントリーをブックマークしているユーザー(xx users)
           // のフォーマットでfooterの下に表示する
           parent_div.appendChild(document.createTextNode('このエントリーをブックマークしているユーザー('));
           parent_div.appendChild(counter_a);
           parent_div.appendChild(document.createTextNode(')'));
 
           // 各ブックマークの詳細をリスト表示させる
           var bookmarks = data.bookmarks;
           for (var i=0, bookmark; bookmark = bookmarks[i]; i++) {
 
               var user_div = document.createElement('div');
 
               // ブックマークタイムスタンプの表示
               user_div.appendChild(document.createTextNode(bookmark.timestamp + ' '));
 
               // ブックマークしたユーザの表示
               var user_a = document.createElement('a');
               // yyyy/mm/dd hh:mm:ssからyyyymmdd文字列を作成する
               var user_a_timestamp = bookmark.timestamp.substring(0, bookmark.timestamp.indexOf(' ', 0));
               while (true) {
                   var tmp_timestamp = user_a_timestamp;
                   user_a_timestamp = tmp_timestamp.replace('/', '');
                   if (user_a_timestamp == tmp_timestamp) {
                       break;
                   }
               }
               user_a.setAttribute('href', 'http://b.hatena.ne.jp/' + bookmark.user + '/' + user_a_timestamp + '#bookmark-' + data.eid);
               user_a.appendChild(document.createTextNode(bookmark.user));
               user_div.appendChild(user_a);
 
               // コメントの表示
               if ( bookmark.comment != '' ) {
                   user_div.appendChild(document.createElement('br'));
                   user_div.appendChild(document.createTextNode(' - ' + bookmark.comment));
               }
 
               parent_div.appendChild(user_div);
           }
       }
       jsr.removeScriptTag();
   }
 </script>

構造は大きく分けて2つです。
-JSONscriptRequestクラスを使ったJSONPコールバック関数の実行
-JSONPコールバック関数の定義

***JSONscriptRequestクラスを使ったJSONPコールバック関数の実行 [#d2278895]

JSONPはフォーマットとしては[[JSON(JavaScript Object Notation)>http://www.json.org/]]フォーマットのデータ(テキスト)をコールバック関数名で括ったものにすぎません。例えば、

 "bindings": [
         {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"},
         {"ircEvent": "PRIVMSG", "method": "deleteURI", "regex": "^delete.*"},
         {"ircEvent": "PRIVMSG", "method": "randomURI", "regex": "^random.*"}
 ]

のようなJSONデータをコールバック関数名をhundlerでJSONP化すると

 hundler("bindings": [
             {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"},
             {"ircEvent": "PRIVMSG", "method": "deleteURI", "regex": "^delete.*"},
             {"ircEvent": "PRIVMSG", "method": "randomURI", "regex": "^random.*"}
          ]
 );

となります。~
はてなが提供する[[はてなブックマークエントリー情報取得API>http://d.hatena.ne.jp/keyword/%a4%cf%a4%c6%a4%ca%a5%d6%a5%c3%a5%af%a5%de%a1%bc%a5%af%a5%a8%a5%f3%a5%c8%a5%ea%a1%bc%be%f0%ca%f3%bc%e8%c6%c0API]]はJSON形式ではてブ情報を返してくれます。そのフォーマットは下記の例になります。

  {
    "count":"1",
    "url":"http://www.ark-web.jp/sandbox/wiki/index.php?%A1%D6%A5%D7%A5%ED%A5%B8%A5%A7%A5%AF%A5%C8%C0%AE%B8%F9%A4%CE%A4%BF%A4%E1%A4%CE%A5%BD%A5%D5%A5%C8%A5%A6%A5%A7%A5%A2%A5%C6%A5%B9%A5%C8%A4%CE%B4%AA%BD%EA%A1%D7%BB%B2%B2%C3%A5%EC%A5%DD%A1%BC%A5%C8",
    "bookmarks":
      [
        {
          "timestamp":"2006/10/08 09:55:44",
          "comment":"",
          "user":"shinchi_xx",
          "tags":[]
        }
      ],
     "title":"\u300c\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u6210\u529f\u306e\u305f\u3081\u306e\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u30c6\u30b9\u30c8\u306e\u52d8\u6240\u300d\u53c2\u52a0\u30ec\u30dd\u30fc\u30c8 - ARK-Web SandBox Wiki",
     "eid":"2950166",
     "entry_url":"http://b.hatena.ne.jp/entry/http://www.ark-web.jp/sandbox/wiki/index.php?%A1%D6%A5%D7%A5%ED%A5%B8%A5%A7%A5%AF%A5%C8%C0%AE%B8%F9%A4%CE%A4%BF%A4%E1%A4%CE%A5%BD%A5%D5%A5%C8%A5%A6%A5%A7%A5%A2%A5%C6%A5%B9%A5%C8%A4%CE%B4%AA%BD%EA%A1%D7%BB%B2%B2%C3%A5%EC%A5%DD%A1%BC%A5%C8",
     "screenshot":"http://screenshot.hatena.ne.jp/images/120x90/c/4/2/3/3/95c3fdc54d5a13047ce29c77f7a78df13aa.jpg"
  }

また、引数でコールバック関数名を指定することでJSONP化したテキストが返されます。仮にこの例でコールバック関数名をhundlerとすると

 hundler(
  {
    "count":"1",
    "url":"http://www.ark-web.jp/sandbox/wiki/index.php?%A1%D6%A5%D7%A5%ED%A5%B8%A5%A7%A5%AF%A5%C8%C0%AE%B8%F9%A4%CE%A4%BF%A4%E1%A4%CE%A5%BD%A5%D5%A5%C8%A5%A6%A5%A7%A5%A2%A5%C6%A5%B9%A5%C8%A4%CE%B4%AA%BD%EA%A1%D7%BB%B2%B2%C3%A5%EC%A5%DD%A1%BC%A5%C8",
    "bookmarks":
      [
        {
          "timestamp":"2006/10/08 09:55:44",
          "comment":"",
          "user":"shinchi_xx",
          "tags":[]
        }
      ],
     "title":"\u300c\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u6210\u529f\u306e\u305f\u3081\u306e\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u30c6\u30b9\u30c8\u306e\u52d8\u6240\u300d\u53c2\u52a0\u30ec\u30dd\u30fc\u30c8 - ARK-Web SandBox Wiki",
     "eid":"2950166",
     "entry_url":"http://b.hatena.ne.jp/entry/http://www.ark-web.jp/sandbox/wiki/index.php?%A1%D6%A5%D7%A5%ED%A5%B8%A5%A7%A5%AF%A5%C8%C0%AE%B8%F9%A4%CE%A4%BF%A4%E1%A4%CE%A5%BD%A5%D5%A5%C8%A5%A6%A5%A7%A5%A2%A5%C6%A5%B9%A5%C8%A4%CE%B4%AA%BD%EA%A1%D7%BB%B2%B2%C3%A5%EC%A5%DD%A1%BC%A5%C8",
     "screenshot":"http://screenshot.hatena.ne.jp/images/120x90/c/4/2/3/3/95c3fdc54d5a13047ce29c77f7a78df13aa.jpg"
  }
 );

が返されます。

JSONPはそのままでは単にテキストに過ぎないため、コールバック関数を起動してJSONデータをコールバック関数に渡す仕組みが必要です。この仕事をするために、Jason Levitt氏が作成、公開されている[[JSONscriptRequestクラス>http://www.xml.com/pub/a/2005/12/21/json-dynamic-script-tag.html]]を利用しています。

その箇所が

 <script type="text/javascript" src="./js/jsr_class.js"></script>
 <div id="hatena_bookmark_count_detail_$md5_page">
 </div>
 <script type="text/javascript">
   var hatena_jsonp_base = 'http://b.hatena.ne.jp/entry/json/';
   var jsr = new JSONscriptRequest(hatena_jsonp_base + '?url=$permalink4bookmark&callback=hatena_footer_hundler_$md5_page');
   jsr.buildScriptTag();
   jsr.addScriptTag();

になります。

jsr_class.jsにJSONscriptRequestクラスは定義されているので、これをまずロードします。~
次に、JSONscriptRequestオブジェクトを[[はてなブックマークエントリー情報取得API>http://d.hatena.ne.jp/keyword/%a4%cf%a4%c6%a4%ca%a5%d6%a5%c3%a5%af%a5%de%a1%bc%a5%af%a5%a8%a5%f3%a5%c8%a5%ea%a1%bc%be%f0%ca%f3%bc%e8%c6%c0API]]へのリクエストURLを引数にして作成します。~
引数に与えたURLのcallbackパラメータに指定した値がコールバック関数名になります。~

JSONscriptRequestオブジェクト(jsr)はbuildScriptTag()でコールバック関数を実行するscriptタグを生成し、addScriptTag()でページのhead内にそのscriptタグを埋め込みます。

以上によって、[[はてなブックマークエントリー情報取得API>http://d.hatena.ne.jp/keyword/%a4%cf%a4%c6%a4%ca%a5%d6%a5%c3%a5%af%a5%de%a1%bc%a5%af%a5%a8%a5%f3%a5%c8%a5%ea%a1%bc%be%f0%ca%f3%bc%e8%c6%c0API]]が返すJSONデータを指定したコールバック関数で受けて処理することが可能になります。

***JSONPコールバック関数の定義 [#vcc2398c]

コールバック関数の引数には[[はてなブックマークエントリー情報取得API>http://d.hatena.ne.jp/keyword/%a4%cf%a4%c6%a4%ca%a5%d6%a5%c3%a5%af%a5%de%a1%bc%a5%af%a5%a8%a5%f3%a5%c8%a5%ea%a1%bc%be%f0%ca%f3%bc%e8%c6%c0API]]が返すJSONデータが格納されます。先の例で言えば、

  {
    "count":"1",
    "url":"http://www.ark-web.jp/sandbox/wiki/index.php?%A1%D6%A5%D7%A5%ED%A5%B8%A5%A7%A5%AF%A5%C8%C0%AE%B8%F9%A4%CE%A4%BF%A4%E1%A4%CE%A5%BD%A5%D5%A5%C8%A5%A6%A5%A7%A5%A2%A5%C6%A5%B9%A5%C8%A4%CE%B4%AA%BD%EA%A1%D7%BB%B2%B2%C3%A5%EC%A5%DD%A1%BC%A5%C8",
    "bookmarks":
      [
        {
          "timestamp":"2006/10/08 09:55:44",
          "comment":"",
          "user":"shinchi_xx",
          "tags":[]
        }
      ],
     "title":"\u300c\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u6210\u529f\u306e\u305f\u3081\u306e\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u30c6\u30b9\u30c8\u306e\u52d8\u6240\u300d\u53c2\u52a0\u30ec\u30dd\u30fc\u30c8 - ARK-Web SandBox Wiki",
     "eid":"2950166",
     "entry_url":"http://b.hatena.ne.jp/entry/http://www.ark-web.jp/sandbox/wiki/index.php?%A1%D6%A5%D7%A5%ED%A5%B8%A5%A7%A5%AF%A5%C8%C0%AE%B8%F9%A4%CE%A4%BF%A4%E1%A4%CE%A5%BD%A5%D5%A5%C8%A5%A6%A5%A7%A5%A2%A5%C6%A5%B9%A5%C8%A4%CE%B4%AA%BD%EA%A1%D7%BB%B2%B2%C3%A5%EC%A5%DD%A1%BC%A5%C8",
     "screenshot":"http://screenshot.hatena.ne.jp/images/120x90/c/4/2/3/3/95c3fdc54d5a13047ce29c77f7a78df13aa.jpg"
  }

が格納されることになります。例では引数名dataで受けているので、例えば、当該Wikiページに対するはてブ数を取得したい場合はdata.countで取得できます。

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS

アークウェブのサービスやソリューションはこちら