Top/PukiWiki/改造/階層化とAutoLinkを両立

階層化とAutoLinkを両立はてなブックマーク

以下の内容はあくまで実験です。実環境に適用するのはお勧めできません (^^;

階層化の弊害

階層化されていると見やすい。見やすいけれど、その基準は編集者の主観であるため、弊害もある。

  1. 閲覧者が求めるカテゴライズになっているとは限らない
  2. AutoLinkの恩恵を受けにくい

AutoLinkとは、既存のページ名が記述された場合にそれを自動リンクする機能であり、個人的にはPukiWikiには必須の機能であると思っている。*1

ところが、階層化されている環境では、文中に階層化された長いページ名を書くのは非常に不自然なため、AutoLinkではなくBracketName+エイリアスを使わざるを得ない*2

AutoLinkの挙動を変える

ここで、階層化しても文章上自然にAutoLinkが働いたらどうだろう。弊害2は解消、弊害1はちょっとマシになるんではないだろうか。
具体的には次のような方法を取る。

  1. AutoLink辞書(autolink.dat)にページ名の葉の部分*3を登録。
  2. その辞書を使ってAutoLink。

ただし、登録された単語にマッチするページは複数ある可能性があるため、次のようにしなければならない。

  1. 辞書にページ名の葉の部分を登録。
  2. 辞書で全ページ名の葉の部分を検索する。
  3. 検索結果が単一なら直行、複数ならリスト表示。

問題はどの段階で検索を行うかだ。2つの選択肢がある。

  1. AutoLink時
  2. リンクから飛んだ後

1を選んだ場合、検索結果が単一なら直行、複数ならリストとリンク先を変える必要があるし、リストへのリンクを張るにしても検索結果を受け渡す方法がなさそうなので、再度検索しなければならないかも。

2を選んだ場合は楽そう。検索結果が単一ならリダイレクト、複数ならリストを表示。

修正作業 (PukiWiki1.4.4)

上記2を選択して、作業を進める。

  1. lib/file.inc.php
    1. AutoLink辞書への登録時に葉のみに加工するように修正。
      --- file.php.orig	2004-08-01 23:23:20.000000000 +0900
      +++ file.php	2004-11-10 21:24:14.000000000 +0900
      @@ -245,6 +245,7 @@
       
       	// for autolink
       	if ($autolink) {
      +		$pages = preg_replace('/.+\//', '', $pages);
       		list($pattern, $pattern_a, $forceignorelist) = get_autolink_pattern($pages);
       
       		$fp = fopen(CACHE_DIR . 'autolink.dat', 'w') or
  2. lib/make_link.inc.php
    1. AutoLink先の修正。
    2. 自分を除外するように修正。
      --- make_link.php.orig	2004-08-01 10:54:35.000000000 +0900
      +++ make_link.php	2004-11-10 21:09:00.000000000 +0900
      @@ -615,8 +615,8 @@
       		global $WikiName;
       
       		list($name) = $this->splice($arr);
      -		// 無視リストに含まれている、あるいは存在しないページを捨てる
      -		if (in_array($name,$this->forceignorepages) or !is_page($name))
      +		// 無視リストに含まれているページ、あるいは自分自身を捨てる
      +		if (in_array($name,$this->forceignorepages) or (preg_replace('/.+\//','',$page)==$name))
       		{
       			return FALSE;
       		}
      @@ -624,12 +624,8 @@
       	}
       	function toString()
       	{
      -		return make_pagelink(
      -			$this->name,
      -			$this->alias,
      -			'',
      -			$this->page
      -		);
      +		$url = rawurlencode($this->name);
      +		return "<a href=\"$script?plugin=search_redirect&word=$url\">{$this->name}</a>";
       	}
       }
       class Link_autolink_a extends Link_autolink
  3. plugin/search_redirect.inc.php
    1. do_search_redirect()*4に丸投げするプラグインを新規作成。
      <?php
      function plugin_search_redirect_action()
      {
          global $script,$vars;
          global $_title_choice;
      
          $s_word = array_key_exists('word',$vars) ? htmlspecialchars($vars['word']) : '';
      
          if ($s_word != '')
          {
              $msg = str_replace('$1',$s_word,$_title_choice);
              $body = do_search_redirect($vars['word']);
          }
      
          return array('msg'=>$msg,'body'=>$body);
      }
      ?>
  4. lib/func.php
    1. 候補を検索して 表示/リダイレクト するdo_search_redirect()を追加。
      --- func.php.orig	2004-08-07 00:39:52.000000000 +0900
      +++ func.php	2004-11-10 20:56:19.000000000 +0900
      @@ -232,6 +232,58 @@
       	return $retval;
       }
       
      +// AutoLink用検索
      +function do_search_redirect($word)
      +{
      +	global $script, $whatsnew, $non_list, $non_autolink, $search_non_list;
      +	global $_msg_notfoundresult;
      +	global $vars;
      +
      +	$retval = array();
      +
      +	$_pages = get_existpages();
      +	$pages = array();
      +
      +	foreach ($_pages as $page)
      +	{
      +		if ($page == $whatsnew or (! $search_non_list and (preg_match("/$non_list/", $page) or preg_match("/$non_autolink/", $page))))
      +			continue;
      +
      +		if (preg_match("/(^$word$|\/$word$)/", $page)) {
      +			$pages[$page] = get_filetime($page);
      +		}
      +	}
      +
      +	$s_word = htmlspecialchars($word);
      +	if (count($pages) == 0) {
      +		return str_replace('$1', $s_word, $_msg_notfoundresult);
      +	}
      +
      +	if (count($pages) == 1) {
      +		foreach ($pages as $page=>$time)
      +		{
      +			header('HTTP/1.0 301 Moved Permanently');
      +			header("Location: $script?".rawurlencode($page));
      +			exit;
      +		}
      +	}
      +
      +	ksort($pages);
      +	$retval = "<ul>\n";
      +	foreach ($pages as $page=>$time)
      +	{
      +		$r_page = rawurlencode($page);
      +		$s_page = htmlspecialchars($page);
      +		$passage = get_passage($time);
      +		$retval .= " <li><a href=\"$script?$r_page\">$s_page</a>$passage</li>\n";
      +	}
      +	$retval .= "</ul>\n";
      +
      +	unset($vars['word']);
      +
      +	return $retval;
      +}
      +
       // プログラムへの引数のチェック
       function arg_check($str)
       {

備考

  • ページが大量にある環境では重そう。

静的コンテンツに偽装

愚行をさらに重ねる。
example.com/*/foo/ でfooを検索語とした前述の動作をするようにする。

RewriteRule ^\*/(.+)/$ index.php?plugin=search_redirect&word=$1 [L]

lib/make_link.inc.phpのLink_autolinkクラスのtoStringをいじってリンク先を次のようにする。

$url = rawurlencode($this->name);
return "<a href=\"/*/$url/\">{$this->name}</a>";

参考

Amazon

*1 WikiNameは単語間の区切りが難しい日本語のような言語と相性が悪い
*2 不自然さについてはPukiWiki:続・質問箱/441で示されているように、BracketName内の親階層名を自動的に除去することでも解決できる。
*3 最後の/以降
*4 do_search()をコピーしたのでfunc内にあるが、よくないかも
差分 一覧