トップページ

2006年01月20日

Seesaa Blogなどのコメントを携帯向け掲示板風に変換

新年お蔵入りPHPスクリプト集そのに。

Seesaa Blog(及びシーサーのBlogServerシステムを利用している他のブログサービス)のコメントを、携帯向け掲示板風に変換表示するPHPスクリプトです。
シーサーのBlogServerシステムは、もちろん携帯端末での利用に対応しているわけですが、現時点のものは、コメントの表示に関しては、すべてのコメントを一度に表示してしまうため、携帯のキャッシュサイズ(メモリ容量)が充分でない場合、コメント数が多いとすべて表示されない場合があります。てなことで、これを分割し、指定件数分だけ表示できるように改竄するというもの。

なんでSeesaa Blogの利用者でもないおいらがこんなものを作ったかというと、コメント欄を掲示板的に利用しておられて、携帯でのコメント表示に困っておられたかたが、PCサイト->携帯変換スクリプトを発見して利用いただいていたのですが、あれはあくまで汎用のものなので、このような定型のドキュメントなら、より見やすく使いやすい表示を、簡単に用意できるだろうと。
てなことでこれは実は、今のところお蔵入りではなく、実際にここで利用できたりしますが、シーサーがBlogServerシステムの携帯向けコメント表示形式を変更すると(少なくともこのままでは)動作しなくなり、そもそもBlogServerシステムが、コメントの表示も携帯にきちんと対応しちゃえば用済みになっちゃうわけです。

利用可能なことを確認しているのは、以下のブログサービスを利用している各ブログ(独自ドメイン名での運用のものもOK)。

なお、シーサーのBlogServerシステムを利用しているブログサービスでも、Erog(blog.shard.jp/*)のように、ブログの識別子がホスト名ではなく、パス名に充てられているものは、このままでは利用できません。アダルトサイトを差別してるわけるではなく、こんなのもあるとは知らんかっただけ(そして、いまさらこのために修正するのもメンドーなだけ)。

注意点とか

このまま利用する場合は、ここにアップしているものとは異なり、設定画面とか端折ってるので、クエリー文字列を付与したスクリプトのURLに直接アクセスします。
クエリー文字列は、例えば「http://www.rcdtokyo.com/sc/sc.php?s=shiraishi.seesaa.net&d=7390945」のように、sキーワードにホスト名(「*.seesaa.net」の場合に限り「.seesaa.net」の部分は省略可で、この例なら「shiraishi」だけでもよい)、dキーワードにエントリー(記事)のID(個別のエントリーを表示するURLが「http://shiraishi.seesaa.net/article/7390945.html」なら「7390945」)を指定します。
さらにpキーワードで、1画面あたりの表示件数を(「p=20」とか。未指定の場合は10件)、fキーワードで表示範囲を(「f=1-100」とか。未指定の場合は最新のものまでを表示件数分)、それぞれ指定できます。fキーワードは「1」、「1-」、「1-100」、「-100」といった形式で、要するに2ちゃんねるのレス番号指定と同じ形式です(ただし「n」や「i」はナシ)。
そもそも以下にある表示画面のテンプレート自体がc.2ch.netのパクリで、accesskeyの割り当ても同じ。ただし、「省」はありません(常にコメントの全文を表示します)。

<?php

// このファイルは、以下で定義する定数INT_ENCと同じ文字エンコーディングにしてね

// --------------------------------------------------------------------
// 設定
// --------------------------------------------------------------------

// コメント中のURLをラップする、サイトを携帯向けに変換するプロキシ
// 未定義ならラップしない(直リンクを表示)
define('REMOTEURL_PROXY', 'http://www.rcdtokyo.com/pc2m/index.php?');

// 各コメントを分割する文字列
define('RES_SEPARATOR', '----------<br>');

// 1画面あたりの表示件数の規定値
define('RES_PERPAGE', 10);

// オリジナルの携帯用コメント表示ページの文字エンコーディング(通常SJIS)
define('INPUT_ENC', 'SJIS');

// 出力文字エンコーディング(通常SJIS)
define('OUTPUT_ENC', 'SJIS');

// スクリプト内部|このファイルの文字エンコーディング(通常EUC-JP)
define('INT_ENC', 'EUC-JP');

// --------------------------------------------------------------------
// メイン
// --------------------------------------------------------------------

mb_language('Japanese');
mb_internal_encoding(INT_ENC);

// サブドメイン名と記事IDの指定が不正なら終了
if (!isset($_GET['s']) or !$_GET['s'] or !preg_match('/^[^\/]+$/', $_GET['s'])
  or !isset($_GET['d']) or !$_GET['d'] or !preg_match('/^\d+$/', $_GET['d'])) {
  exit(mb_convert_encoding('URLが不正な悪寒', OUTPUT_ENC, INT_ENC));
}

// 記事タイトル
$title = null;

// 表示本文
$body = null;

// 表示開始番号
$from = null;

// 表示終了番号
$to = null;

// コメント総数
$res_count = null;

// ホスト名
// ドットがあればそのままで、なければ「.seesaa.net」を付与
$hostname = strpos($_GET['s'], '.')? $_GET['s']: $_GET['s'].'.seesaa.net';

// 以降も引き回すパラメータを付与したこのスクリプトのURL
$script_self = basename($_SERVER['SCRIPT_NAME']).
  '?s='.$_GET['s'].'&d='.$_GET['d'].(isset($_GET['p'])? '&p='.$_GET['p']: null);

// 1画面あたりの表示件数(未指定の場合はRES_PERPAGEの値)
$res_perpage = (isset($_GET['p']) and preg_match('/^\d{1,3}$/', $_GET['p']))? $_GET['p']: RES_PERPAGE;

// オリジナルの携帯用コメント表示ページにアクセス
$_remote_url = 'http://'.$hostname.'/pages/user/m/comments/regist/input?article_id='.$_GET['d'];
if (false === $_fetched = @file_get_contents($_remote_url)) {
  exit(mb_convert_encoding(
    '<a href="'.$_remote_url.'">'.$_remote_url.'</a>にアクセスできませんでした', OUTPUT_ENC, INT_ENC));
}
mb_convert_variables(INT_ENC, INPUT_ENC, $_fetched);
if (!preg_match('/^.+<a\shref="http:\/\/'.quotemeta($hostname).'\/article\/'.$_GET['d'].
  '\.html">"(.+)"<\/a>[^<]+<br>\s*(.+?)('.RES_SEPARATOR.')?\s*<hr>.+$/si', $_fetched, $matches)) {
  exit(mb_convert_encoding(
    '<a href="'.$_remote_url.'">'.$_remote_url.'</a>の内容が想定の範囲外です', OUTPUT_ENC, INT_ENC));
}
$title = $matches[1];

// コメントの分割文字列がなければコメントはゼロ件
if (!isset($matches[3])) {
  $body = 'この記事にはコメントはありません<hr>';
  $from = 1;
} else {

  // 全てのコメントを個別に分割
  $array = explode(RES_SEPARATOR, $matches[2]);
  $res_count = count($array);

  // 各コメントを本文、名前、日時に分割
  for ($i = 0; $i < count($array); $i++) {
    preg_match('/^\s*(.+)\s*<br>\s*&#63826;(.*?)\s*<br>.*&#63838;'.
      '\d{2}(\d{2})[^\d]+?(\d{2})[^\d]+?(\d{2})[^\d]+?\s(\d{2}:\d{2})'.
      '<br>\s*$/si', $array[$i], $matches);
    $_res[$i][0] = isset($matches[2])? $matches[2]: null;
    $_res[$i][1] = (isset($matches[3])
      and isset($matches[4])
      and isset($matches[5])
      and isset($matches[6]))?
      $matches[3].'/'.$matches[4].'/'.$matches[5].' '.$matches[6]: null;
    $_res[$i][2] = isset($matches[1])? $matches[1]: null;
  }

  // コメント番号の指定がある場合
  if (isset($_GET['f']) and $_GET['f']
    and preg_match('/^(\d+)?(-)?(\d+)?$/', $_GET['f'], $matches)) {

    // ハイフンがあれば範囲指定
    if (isset($matches[2])) {

      // 開始番号があれば
      if ($matches[1]) {
        $from = (int) $matches[1];

        // 終了番号があればそこまで
        if (isset($matches[3]) and (int) $matches[3] >= $from) {
          $to = (int) $matches[3];

        // 開始番号のみならそこから表示件数分
        } else {
          $to = $from + $res_perpage -1;
        }

      // 終了番号のみならそこまで表示件数分
      } elseif (isset($matches[3])) {
        $to = (int) $matches[3];
        if ($to > $res_count) {
          $to =& $res_count;
        }
        $from = $to - $res_perpage +1;
      }

    // ハイフンがなければ単独のコメントを表示
    // $fromと$toは同じ値
    } else {
      $from = $matches[1];
      $to =& $from;
    }
  }

  // 開始番号がNULLのままなら
  // 最新まで表示件数分
  if ($from === null) {
    $from = $res_count - $res_perpage +1;
    $to = $from + $res_perpage -1;
  }

  // 開始番号が1未満なら開始番号は1
  if ($from < 1) {
    $from = 1;
  }

  // 開始|終了番号がコメント数を超えていれば
  // 開始|終了番号はコメント数
  if ($from > $res_count) {
    $from =& $res_count;
  }
  if ($to > $res_count) {
    $to =& $res_count;
  }

  // 表示用の本文を整形
  for ($i = $from -1; $i < $to; $i++) {
    $body .= ($i +1).': '.$_res[$i][0].' '.$_res[$i][1].'<br>'.$_res[$i][2].'<hr>';
    if ($i == $res_count -1) {
      $body .= '(最後のコメントです)<hr>';
    }
  }

  // XHTML形式のBRをHTML形式に置換
  $body = str_replace('<br />', '<br>', $body);

  // コメント中のURLにリンクを付ける
  $body = preg_replace_callback(
    '/https?:\/\/[\w\.\/\?\+\-_=&%#]+/si',
    create_function('$matches','
      $_url = $matches[0];
      $_url_parts = parse_url($_url);

      // URLプロキシが定義されていれば
      if (defined("REMOTEURL_PROXY")) {
        return "<a href=\"".REMOTEURL_PROXY.urlencode($_url)."\">".$_url_parts["host"]."</a>";
      } else {
        return "<a href=\"".$_url."\">".$_url_parts["host"]."</a>";
      }
    '), $body);
}

// --------------------------------------------------------------------
// HTML出力
// --------------------------------------------------------------------

// FORM要素
$form = '<form action="'.basename($_SERVER['SCRIPT_NAME']).'" method="get">
<input type="hidden" name="s" value="'.$_GET['s'].'">
<input type="hidden" name="d" value="'.$_GET['d'].'">'.
$res_count.'個のコメント中<input type="text" name="f" size="7" value="'.
(($from == $to)? $from: $from.'-'.$to).'">を表示
<input type="submit" value="移動">
</form>';

// 前・次・1-リンク
$nav_link = (($from != 1)? '<a href="'.
  $script_self.'&f=-'.($from -1).'" accesskey="7">前</a>': null).
'<a href="'.$script_self.'&f='.($to +1).'-" accesskey="9">次</a>'.
(($from != 1)? '<a href="'.$script_self.'&f=1-" accesskey="1">1-</a>': null);

// HTMLテンプレート
$output = <<<HERE
<html>
<title>{$title}へのコメント</title>
<body>

<a name="t" href="#b" accesskey="8">下</a><br>
<a href="http://{$hostname}/article/{$_GET['d']}.html" accesskey="6">{$title}</a>へのコメント<br>
{$form}
<hr>
{$body}
<a name="b" href="#t" accesskey="2">上</a>
{$nav_link}
<a href="{$script_self}" accesskey="3">新</a>
<a href="http://{$hostname}/article/{$_GET['d']}.html" accesskey="6">戻</a>

<form action="http://{$hostname}/pages/user/m/comments/regist/input" method="get">
<label for="article_child__writer">名</label>
<input type="text" name="article_child__writer" size="4"><br>
<textarea name="article_child__body"></textarea><br>
<input type="hidden" name="article_child__article_id" value="{$_GET['d']}">
<input type="hidden" name="article_id" value="{$_GET['d']}">
<input type="submit" name="posted" value="書">
</form>

</body>
</html>
HERE;

// 文字エンコーディングを変換して出力
header('Content-Type:text/html;charset='.OUTPUT_ENC);
echo mb_convert_encoding($output, OUTPUT_ENC, INT_ENC);

?>

お約束

  • このプログラムはGNU GPLでライセンスされます。著作権は放棄されていませんが、なんの保証もありません。利用や改造はGNU GPLの定めに従って、自前のリスクでご自由に。お蔵入りにするくらいなので、ちゃんと検証してたりするわけもなく、あれこれ不具合がある筈ですが、なんらかの不利益を被っても一切関知しません。
  • シーサー株式会社及び各ブログサービスの運営者と、このプログラム及びその作者とはなんの関係もありませんよ。

余談

シーサーのBlogServerシステムでは、エントリーIDは1から始まる連番なのですが、これは同じシステムを共有するすべてのブログで共通になっています。「http://shiraishi.seesaa.net/article/7390945.html」のような個々のエントリーのURLなどでは、エントリーIDと併せて、ホスト名などに見られるような、そのエントリーが属する各ブログの識別子がない限り有効なものとはみなされませんが、携帯向けにコメントを表示するCGIに関しては、存在しているホスト名であればどれでも、エントリーIDの指定のみで、同じシステムを共有するすべてのブログのエントリーのコメントを表示することができます。
要するに、特定のIDのエントリーがどれなのかを知りたければ、「http://blog.seesaa.net/pages/user/m/comments/regist/input?article_id=(エントリーID)」とかでアクセスすればいいわけですが(「blog.seesaa.net」の部分は、存在してるものならなんでも)、これがなにに使えるかというと、キリ番ゲッターさんがわかったりするのでした。いや、それだけ。

Category: ウェブ制作
Posted 2006年01月20日 18:07

トラックバック

このエントリーのトラックバックURL:
http://www.rcdtokyo.com/mt/mt-rcdtokyo5428-tb.cgi/698

コメント

コメントをどうぞ



保存しますか?


Aoaka Style Valid Aoaka