#topicpath

* PukiWiki/1.4/ちょっと便利に/固定アンカーをユニークなIDに(エイリアスも可能に) by [[Cue]] [#f60cc420]

**関連 [#g0135018]
[[BugTrack2/96]]

**注意事項 [#m08037f4]
-固定アンカーの書き換えを行なう性質上、データファイルの変更を伴います。適用に際しては十分な注意をお願いします。
-固定アンカーのデータディレクトリに anchor/ を使用します。パッチを適用する時に新しくディレクトリを作成し適切なパーミッションを設定してください。(counter/を丸ごとコピーするとか)

**暫定仕様 [#nf67b1c7]
+固定アンカーが割り当てられるのはページ書き込み時(プレビューでは割り当てられない)
+anameプラグインの第一引数に ''*'' を設定すると自動割当の固定アンカーが書きこまれる
+%%自動生成される固定アンカーは /[A-Za-z][A-Za-z0-9_-]{8}/ (従来は /[a-z][0-9a-f]{7}/)%%
--固定アンカー生成は従来通りに戻しました。また、大文字小文字は同一視されます(IE6の仕様に合わせた為)。
+見出しの固定アンカーより''前の''部分と、anameのボディ部は、固定アンカーへのアンカーエイリアスとして登録される(前後の空白文字や各種Wiki装飾・footnote等は除外)
+[[ページ名#"アンカーエイリアス名"]]で固定アンカーへのリンクが可能(変換はページ生成時)
--''"'' での囲み方はプラグイン引数やCSV形式テーブルで ''"'' を用いる時と同じ方法になっています。
+anameプラグインにボディ部を表示しない ''hide'' オプション追加(アンカーエイリアス登録だけを可能にするため)
+アンカーエイリアスはページ内でユニークならば十分で、日本語を含む自由な文字が使用可能(重複はエラーにはならないが飛び先が異なる)
--%%アンカーエイリアスに<>&が含まれる場合、BracketNameに記述するアンカーエイリアスにはそれぞれ&lt;&gt;&amp;と記述しないといけない。(make_link.phpの正規表現では無理みたい)%%
--''"'' で囲む方法に変えたので上の書き換えは不要になりました。
 *不等号"<>"とは? [#Anchor] となっている場合、[[#"不等号""<>""とは?"]]がこの行自身へのリンクに

***重複固定アンカーの処理など [#x82cc54a]
+基本的に先行取得優先(先に更新されたページ、同一ページ内は先に記述された方が優先)
+編集で変更された場合は以下の順で同一性を確認
++固定アンカー・アンカーエイリアス共に変更のない物を確保
++固定アンカーのみ一致する物はアンカーエイリアス書き換えとして確保
++アンカーエイリアスのみ一致する物は固定アンカーが改変・削除されたとして可能なら元に戻す
++残りは新規として新たな固定アンカーを割り当て(可能なら書かれた固定アンカーをそのまま使う)
+固定アンカーの大文字小文字の違いは無視されますので、Anchorとanchorがあった場合は一方が書き換えられます。が、BracketNameに固定アンカーを記述した場合にこの違いをどう解釈するかはブラウザに依存するようです(Firefoxはcase sensitive、IE6はinsensitive)。なお、アンカーエイリアスで記述した場合には登録された固定アンカーは大文字小文字を保存していますので適切に処理されます。

***既知の問題 [#lc407be2]
+%%固定アンカーとアンカーエイリアスの文字列が一致し得る%% (対応済み)
+%%BracketNameで書かれたアンカーが固定アンカーなのかアンカーエイリアスなのかが判別できない%% (対応済み)
--現在は、登録済み固定アンカーならば固定アンカー、登録済みアンカーエイリアスなら対応する固定アンカー、未確認はアンカー文字列として適当か判別して適当ならそのまま・不適当なら空文字列を返す
+%%アンカーエイリアスに>が含まれるとmake_linkでBracketNameが誤判別される%% (対応済み)
 [[Alias>BracketName#Anchor>Alias]]
--%%現在は、&gt;と書く事で回避%% (''"''で囲む書式に変更)
+edit.inc.phpの衝突検出を通り抜けた書きこみデータが複数あると、重複アンカー処理が期待通りに働かない可能性がある(破綻はしない)
+%%1022 - (固定アンカーのバイト数) を超えるアンカーエイリアスがあると困る%% (対応済み)
+%%ページ削除・renameに未対応%% (対応済み)
+プラグイン等が生成するIDと一致する固定アンカーを文書に埋めこむ事ができる。
--例えば&aname(notefoot_1);と書いた場合、重複固定アンカーのチェックを通り抜けてしまいますが、paraeditに悪影響は無い様です。

**コメント [#fa7197d5]
-XSS脆弱性ありました。今から対策します。 -- [[Cue]] &new{2005-07-31 (日) 16:52:02};
--いちおう対策終わりましたが、aname(){body};がaname(body){};になるのは手付かず -- [[Cue]] &new{2005-07-31 (日) 20:03:21};
-anchor_explodeの対応、バグ取りなどしてみましたが、まだ目の届かないところがあるかもしれません((PukiWikiの全てはまだ到底理解できていないので))。&br;ID重複対策はできたと思いますが、BracketNameのアンカーへのエイリアス周りには色々問題がありそうです。 -- [[Cue]] &new{2005-08-07 (日) 01:20:07};
-anchor_explode周りの処理にバグがあって、重複チェック済みか確認できていませんでした。今回のパッチではチェック済みでないページでparaeditが使えなくなります。アンカーエイリアスでの指定が可能・固定アンカーが/^[a-z][a-f0-9]{7}$/に制限されない等の機能が追加される反面、ページ生成が若干重くなりますので、全ページの重複チェックを済ませたからチェックは要らないといった方はanchor_explodeを元に戻すなど弄ってみて下さい。 -- [[Cue]] &new{2005-08-12 (金) 02:51:56};
- バグ取りと保存データの追加・変更しました。前のを試していた方はアンカーキャッシュの更新をお願いします。保存データの変更は、#ls2等でページをスキャンせず見出し構造が取得できるよう汎用化してみるテストです。(これで最後にしたいなぁ…続きは固定アンカーを極力隠蔽してみようかと。リダイレクト乱発になりますがスッキリするような気がする(影響範囲が読めないけど)) -- [[Cue]] &new{2005-08-16 (火) 21:27:16};

#comment
//#comment

**差分 [#t14ced14]
1.4.6rcからの差分

 --- /lib/file.php	Sun Jul 03 23:16:24 2005
 +++ /lib/file.php	Tue Aug 16 19:36:04 2005
 @@ -30,11 +30,14 @@
  // Put a data(wiki text) into a physical file(diff, backup, text)
  function page_write($page, $postdata, $notimestamp = FALSE)
  {
 -	global $trackback;
 +	global $trackback, $fixed_heading_anchor;
  
  	if (PKWK_READONLY) return; // Do nothing
  
 -	$postdata = make_str_rules($postdata);
 +	if($fixed_heading_anchor)
 +		$postdata = make_str_rules($postdata, $page);
 +	else
 +		$postdata = make_str_rules($postdata);
  
  	// Create and write diff
  	$oldpostdata = is_page($page) ? join('', get_source($page)) : '';
 @@ -58,11 +61,291 @@
  	links_update($page);
  }
  
 +function generate_anchor_id($page, $title, $query_id)
 +{
 +	$filename = ANCHOR_DIR . 'global.anchor.ids';
 +	$ids = array();
 +	$fp = fopen($filename, 'a+b')
 +		or die_message('cannot open ' . htmlspecialchars($filename));
 +	flock($fp, LOCK_EX);
 +	$ids = file($filename);
 +	if (!preg_match('/^(?!xml)[A-Z][\w-]*$/i', $query_id) ||
 +		in_array(strtolower($query_id)."\n", $ids)){
 +		do{
 +			$top = mt_rand(ord('a'), ord('z'));
 +			$md5_sub = substr(md5($page . $title . uniqid($top)), mt_rand(0, 24), 7);
 +			$query_id = chr($top) . $md5_sub;
 +		}while(in_array($query_id."\n", $ids));
 +	}
 +	fseek($fp, 0, SEEK_END);
 +	fputs($fp, strtolower($query_id)."\n");
 +	fflush($fp);				//	普通fcloseでまとめて
 +	flock($fp, LOCK_UN);		//	やってくれると思うが
 +	fclose($fp);
 +	return $query_id;
 +}
 +
 +function remove_anchor_ids($lists)
 +{
 +	$lists = preg_replace('/$/', "\n", $lists);
 +	$lists[] = '';
 +	$filename = ANCHOR_DIR . 'global.anchor.ids';
 +	$ids = array();
 +	$fp = fopen($filename, 'a+b')
 +		or die_message('cannot open ' . htmlspecialchars($filename));
 +	flock($fp, LOCK_EX);
 +	$ids = array_diff(file($filename), $lists);
 +	ftruncate($fp, 0);
 +	if(!empty($ids))
 +		fputs($fp, implode('', $ids));
 +	fflush($fp);
 +	flock($fp, LOCK_UN);
 +	fclose($fp);
 +}
 +
 +class anchors
 +{
 +	var $page;
 +	var $filename;
 +
 +	var $ids     = array();
 +	var $line_no = array();
 +	var $heading = array();
 +	var $titles  = array();
 +	var $others  = array();
 +
 +	function anchors($page){
 +		$this->page = $page;
 +		$this->filename = ANCHOR_DIR . encode($page) . '.ids';
 +	}
 +
 +	function _unset($key){
 +		unset($this->ids[$key]);
 +		unset($this->line_no[$key]);
 +		unset($this->heading[$key]);
 +		unset($this->titles[$key]);
 +		unset($this->others[$key]);
 +	}
 +}
 +
 +class read_anchors extends anchors
 +{
 +	function read_anchors($page){
 +		parent::anchors($page);
 +
 +		if(($fp = @fopen($this->filename, 'rb')) === FALSE)
 +			return;
 +		flock($fp, LOCK_SH);
 +		$data = fread($fp, filesize($this->filename));
 +		flock($fp, LOCK_UN);
 +		fclose($fp);
 +		$mathces = array();
 +		if(preg_match_all('/^([^\t\n]*)\t(\d+)\t(\d)\t([^\t\n]*)\t(.*)$/m', $data, $matches)
 +			!= substr_count($data, "\n")){
 +				error_log('PukiWiki Warning: broken anchor file detected -> ' .
 +					htmlspecialchars($page), 0);
 +		}else{
 +			unset($data, $matches[0]);
 +			$this->ids     = $matches[1];
 +			$this->line_no = $matches[2];
 +			$this->heading = $matches[3];
 +			$this->titles  = str_replace('>', "\t", $matches[4]);
 +			$this->others  = $matches[5];
 +		}
 +	}
 +
 +	function get_data(){
 +		return array(
 +			'ids'     => $this->ids,
 +			'line_no' => $this->line_no,
 +			'heading' => $this->heading,
 +			'titles'  => $this->titles,
 +			'others'  => $this->others
 +		);
 +	}
 +}
 +
 +class write_anchors extends anchors
 +{
 +	var $current_line;
 +	var $count = 0;
 +	var $lines = array();
 +
 +	function write_anchors($page){
 +		require_once(PLUGIN_DIR . 'aname.inc.php');
 +		function_exists('plugin_aname_getargs')
 +			or die_message('plugin <em>aname.inc.php</em> has not been updated.');
 +		parent::anchors($page);
 +	}
 +
 +	function write(){
 +		if(!empty($this->lines)){
 +			ksort($this->lines);
 +			$fp = fopen($this->filename, 'ab')
 +				or die_message('cannot write ' . htmlspecialchars($this->filename));
 +			flock($fp, LOCK_EX);
 +			ftruncate($fp, 0);
 +			fputs($fp, implode('', $this->lines));
 +			fflush($fp);
 +			flock($fp, LOCK_UN);
 +			fclose($fp);
 +		}else if(file_exists($this->filename))
 +			@unlink($this->filename);
 +	}
 +
 +	function set($key){
 +		$this->lines[$key] =
 +			$this->ids[$key] . "\t" .
 +			$this->line_no[$key] . "\t" .
 +			$this->heading[$key] . "\t" .
 +			str_replace("\t", '>', $this->titles[$key]) . "\t" .
 +			$this->others[$key] . "\n";
 +		$this->_unset($key);
 +	}
 +
 +	function splice_aname_id_callback($matches){
 +		if(!preg_match('/^[&#]aname$/i', $matches[1])){
 +			if(!isset($matches[3]))
 +				return $matches[0];
 +			$body = preg_replace_callback(
 +				'/(&\w+)(?:\(((?:(?!\)[;{]).)*)\))?(?:\{((?:(?R)|(?!};).)*)\})?;/', 
 +				array(&$this, 'splice_aname_id_callback'),
 +				$matches[3]
 +			);
 +			return substr_replace($matches[0], $body, -(strlen($matches[3])+2), -2);
 +		}
 +		$args = csv_explode(',', $matches[2]);
 +		if(empty($args))
 +			return $matches[0];
 +
 +		$_args = plugin_aname_getargs(
 +			$matches[1]{0} == '#' ? $args : array_merge($args, @(array)$matches[3]), FALSE);
 +		if(!is_array($_args) || $_args['noid'])
 +			return $matches[0];
 +
 +		$this->ids[$this->count]     = $_args['id'] != '*' ? $_args['id'] : '';
 +		$this->line_no[$this->count] = $this->current_line;
 +		$this->heading[$this->count] = '0';		//	ここ悩む…
 +		$this->titles[$this->count]  = trim(strip_decoration($_args['body']));
 +		$this->others[$this->count]  = '';
 +
 +		return substr_replace($matches[0], "\x08".$this->count++."\x08", 7, strlen($args[0]));
 +	}
 +
 +	function splice_anchor(&$line, $i){
 +		$this->current_line = $i;
 +		$matches = array();
 +
 +		if(preg_match('/^(#aname)\((.*)\)(.*)$/i', $line, $matches)){
 +			$line = $this->splice_aname_id_callback($matches);
 +			return;
 +		}
 +		if(preg_match('/^(\*{1,3}(.*?))(?:\[#([A-Za-z][\w-]*)\](.*))?$/', $line, $matches)){
 +			$this->ids[$this->count]     = isset($matches[3]) ? $matches[3] : '';
 +			$this->line_no[$this->count] = $this->current_line;
 +			$this->heading[$this->count] = (string)min(strspn($matches[1], '*'), 3);
 +			$this->titles[$this->count]  = trim(strip_decoration($matches[2]));
 +			$this->others[$this->count]  = isset($matches[4]) ? trim(strip_decoration($matches[4])) : '';
 +
 +			$line = $matches[1] . (isset($matches[3]) ? '' : ' ') .
 +				"[#\x08" . $this->count++ . "\x08]" . @$matches[4];
 +		}
 +		$line = preg_replace_callback(
 +			'/(&\w+)(?:\(((?:(?!\)[;{]).)*)\))?(?:\{((?:(?R)|(?!};).)*)\})?;/', 
 +			array(&$this, 'splice_aname_id_callback'),
 +			$line
 +		);
 +	}
 +
 +	function reassign_id(&$source){
 +		$org = &new read_anchors($this->page);
 +
 +		foreach($org->ids as $org_key=>$id){
 +			if(is_int($new_key = array_search($id, $this->ids)) &&
 +				$this->heading[$new_key] == $org->heading[$org_key] &&
 +				$this->titles[$new_key] == $org->titles[$org_key]){
 +					$this->set($new_key);
 +					$org->_unset($org_key);
 +			}
 +		}
 +		//	id一致・title不一致
 +		foreach($org->ids as $org_key=>$id){
 +			if(is_int($new_key = array_search($id, $this->ids))){
 +				$this->set($new_key);
 +				$org->_unset($org_key);
 +			}
 +		}
 +		//	title一致・id不一致
 +		foreach($org->titles as $org_key=>$title){
 +			if(is_int($new_key = array_search($title, $this->titles))){
 +				$this->ids[$new_key] = $org->ids[$org_key];
 +				$this->set($new_key);
 +				$org->_unset($org_key);
 +			}
 +		}
 +		if(!empty($org->ids))
 +			remove_anchor_ids($org->ids);
 +		unset($org);
 +
 +		foreach($this->ids as $key=>$id){
 +			$assigned_id = generate_anchor_id($this->page, $this->titles[$key], $id);
 +			if($assigned_id != $id)
 +				$this->ids[$key] = $assigned_id;
 +			$this->set($key);
 +		}
 +
 +		$source = preg_replace(
 +			'/\x08(\d+)\x08/e',
 +			'substr($this->lines[$1], 0, strpos($this->lines[$1], "\t"))',
 +			$source
 +		);
 +		$this->write();
 +	}
 +}
 +
 +function get_anchors($page = '', $nocache = FALSE)
 +{
 +	global $vars;
 +	static $cache;
 +
 +	if($page == '')
 +		$page = $vars['page'];
 +	if(!$nocache)
 +		$retval = &$cache[$page];
 +	if(!isset($retval)){
 +		$anchors = &new read_anchors($page);
 +		$retval = $anchors->get_data();
 +	}
 +	return $retval;
 +}
 +
 +function search_anchors(&$search, $page, $heading_only = FALSE)
 +{
 +	$is_id = preg_match('/^#?[A-Za-z][\w-]*$/', $search);
 +	$search = preg_replace('/(?:^#?"?|"(")|"$)/', '$1', $search);
 +	$s_search = htmlspecialchars(trim($search));
 +	if($s_search == '')
 +		return '';
 +	$anchors = get_anchors($page);
 +	if($is_id){
 +		$key = array_search($s_search, $anchors['ids']);
 +		if(is_int($key) && (!$heading_only || $anchors['heading'][$key]))
 +			return $s_search;
 +	}else{
 +		$key = array_search($s_search, $anchors['titles']);
 +		if(is_int($key) && (!$heading_only || $anchors['heading'][$key]))
 +			return $anchors['ids'][$key];
 +	}
 +	return (!$heading_only && $is_id) ? $s_search : '';
 +}
 +
  // Modify ogirinal text with user-defined / system-defined rules
 -function make_str_rules($source)
 +function make_str_rules($source, $page = NULL)
  {
 -	global $str_rules, $fixed_heading_anchor;
 +	global $str_rules;
  
 +	if(isset($page))
 +		$anchors = &new write_anchors($page);
  	$lines = explode("\n", $source);
  	$count = count($lines);
  
 @@ -98,25 +381,19 @@
  		// Replace with $str_rules
  		foreach ($str_rules as $pattern => $replacement)
  			$line = preg_replace('/' . $pattern . '/', $replacement, $line);
 -		
 -		// Adding fixed anchor into headings
 -		if ($fixed_heading_anchor &&
 -			preg_match('/^(\*{1,3}(.(?!\[#[A-Za-z][\w-]+\]))+)$/', $line, $matches))
 -		{
 -			// Generate ID:
 -			// A random alphabetic letter + 7 letters of random strings from md()
 -			$anchor = chr(mt_rand(ord('a'), ord('z'))) .
 -				substr(md5(uniqid(substr($matches[1], 0, 100), 1)), mt_rand(0, 24), 7);
 -			$line = rtrim($matches[1]) . ' [#' . $anchor . ']';
 -		}
 -	}
  
 +		if(isset($anchors) && substr($line, 0, 2) != '//')
 +			$anchors->splice_anchor($line, $i);
 +	}
  	// Multiline part has no stopper
  	if (! PKWKEXP_DISABLE_MULTILINE_PLUGIN_HACK &&
  	    $modify === FALSE && $multiline != 0)
  		$lines[] = str_repeat('}', $multiline);
  
 -	return implode("\n", $lines);
 +	$source = implode("\n", $lines);
 +	if(isset($anchors))
 +		$anchors->reassign_id($source);
 +	return $source;
  }
  
  // Output to a file

 --- /lib/html.php	Sun Jul 03 23:51:18 2005
 +++ /lib/html.php	Mon Aug 15 20:16:56 2005
 @@ -329,7 +329,7 @@
  // Remove AutoLink marker with AutLink itself
  function strip_autolink($str)
  {
 -	return preg_replace('#<!--autolink--><a [^>]+>|</a><!--/autolink-->#', '', $str);
 +	return preg_replace('#(?:<!--autolink-->)?<a [^>]+>|</a>(?:<!--/autolink-->)?#', '', $str);
  }
  
  // Make a backlink. searching-link of the page name, by the page name, for the page name
 @@ -347,8 +347,6 @@
  // Make heading string (remove heading-related decorations from Wiki text)
  function make_heading(& $str, $strip = TRUE)
  {
 -	global $NotePattern;
 -
  	// Cut fixed-heading anchors
  	$id = '';
  	$matches = array();
 @@ -361,32 +359,31 @@
  
  	// Cut footnotes and tags
  	if ($strip === TRUE)
 -		$str = strip_htmltag(make_link(preg_replace($NotePattern, '', $str)));
 +		$str = strip_decoration($str);
  
  	return $id;
  }
  
 +// remove decorations from Wiki text
 +function strip_decoration($str)
 +{
 +	global $NotePattern;
 +
 +	return strip_htmltag(make_link(preg_replace($NotePattern, '', $str)));
 +}
 +
  // Separate a page-name(or URL or null string) and an anchor
  // (last one standing) without sharp
  function anchor_explode($page, $strict_editable = FALSE)
  {
 -	$pos = strrpos($page, '#');
 -	if ($pos === FALSE) return array($page, '', FALSE);
 -
 -	// Ignore the last sharp letter
 -	if ($pos + 1 == strlen($page)) {
 -		$pos = strpos(substr($page, $pos + 1), '#');
 -		if ($pos === FALSE) return array($page, '', FALSE);
 -	}
 +	$pos = strpos($page, '#');
 +	if ($pos === FALSE || strlen($page) == $pos + 1)
 +		return array($page, '', FALSE);
  
  	$s_page = substr($page, 0, $pos);
 -	$anchor = substr($page, $pos + 1);
 -
 -	if($strict_editable === TRUE &&  preg_match('/^[a-z][a-f0-9]{7}$/', $anchor)) {
 -		return array ($s_page, $anchor, TRUE); // Seems fixed-anchor
 -	} else {
 -		return array ($s_page, $anchor, FALSE);
 -	}
 +	$anchor = substr($page, $pos);
 +	$id = search_anchors($anchor, $s_page, $strict_editable);
 +	return array($s_page, $id, $strict_editable && $id != '');
  }
  
  // Check HTTP header()s were sent already, or

 --- /lib/make_link.php	Mon Jun 27 23:18:08 2005
 +++ /lib/make_link.php	Mon Aug 15 19:58:36 2005
 @@ -551,7 +551,11 @@
   |
   (?:$BracketName)
  )?
 -(\#(?:[a-zA-Z][\w-]*)?)? # (4) Anchor
 +(\#(?:                   # (4) Anchor
 + [A-Za-z][\w-]*          # ID
 + |
 + (?:"[^"]*")+            # Alias for ID
 +)?)?
  (?($s2)\]\])             # Close bracket if (2)
  \]\]                     # Close bracket
  EOD;
 @@ -569,12 +573,14 @@
  		list(, $alias, , $name, $this->anchor) = $this->splice($arr);
  		if ($name == '' && $this->anchor == '') return FALSE;
  
 -		if ($name == '' || ! preg_match('/^' . $WikiName . '$/', $name)) {
 -			if ($alias == '') $alias = $name . $this->anchor;
 -			if ($name != '') {
 -				$name = get_fullname($name, $page);
 -				if (! is_pagename($name)) return FALSE;
 -			}
 +		if ($name != '' && ! preg_match('/^' . $WikiName . '$/', $name)) {
 +			$name = get_fullname($name, $page);
 +			if (! is_pagename($name)) return FALSE;
 +		}
 +		if ($this->anchor != ''){
 +			$id = search_anchors($this->anchor, $name);
 +			if ($alias == '') $alias = $name . '#' . $this->anchor;
 +			$this->anchor = '#' . $id;
  		}
  
  		return parent::setParam($page, $name, '', 'pagename', $alias);

 --- /plugin/aname.inc.php	Fri Jun 17 00:04:08 2005
 +++ /plugin/aname.inc.php	Sat Aug 06 03:30:02 2005
 @@ -27,6 +27,8 @@
  // #aname
  function plugin_aname_convert()
  {
 +	if(func_num_args() < 1)
 +		return plugin_aname_usage(TRUE);
  	$args = func_get_args(); // Zero or more
  	return plugin_aname_tag($args);
  }
 @@ -34,6 +36,8 @@
  // &aname;
  function plugin_aname_inline()
  {
 +	if(func_num_args() < 2)
 +		return plugin_aname_usage(FALSE);
  	$args = func_get_args(); // ONE or more
  
  	$body = strip_autolink(array_pop($args));
 @@ -49,7 +53,7 @@
  		if ($convert) {
  			return '#aname(anchorID[[,super][,full][,noid],Link title])';
  		} else {
 -			return '&amp;aname(anchorID[,super][,full][,noid]){[Link title]}';
 +			return '&amp;aname(anchorID[,super][,full][,noid]){[Link title]};';
  		}
  	} else {
  		if ($convert) {
 @@ -60,42 +64,52 @@
  	}
  }
  
 -// Aname plugin itself
 -function plugin_aname_tag($args = array(), $convert = TRUE)
 +function plugin_aname_getargs($args = array(), $check_dup_id)
  {
 -	global $vars;
  	static $_id = array();
  
 -	if (empty($args) || $args[0] == '') return plugin_aname_usage($convert);
 +	if ($args[0] == '') return '';
  	$id = array_shift($args);
  	$body = '';
  	if (! empty($args)) $body = array_pop($args);
 -	$f_noid  = in_array('noid',  $args); // Option: Without id attribute
 -	$f_super = in_array('super', $args); // Option: CSS class
 -	$f_full  = in_array('full',  $args); // Option: With full(absolute) URI
 +	$noid  = in_array('noid',  $args); // Option: Without id attribute
 +	$super = in_array('super', $args); // Option: CSS class
 +	$full  = in_array('full',  $args); // Option: With full(absolute) URI
 +	$hide  = in_array('hide',  $args);
  
  	if ($body == '') {
 -		if ($f_noid)  return plugin_aname_usage($convert, 'Meaningless(No link-title with \'noid\')');
 -		if ($f_super) return plugin_aname_usage($convert, 'Meaningless(No link-title with \'super\')');
 -		if ($f_full)  return plugin_aname_usage($convert, 'Meaningless(No link-title with \'full\')');
 +		if ($noid)  return 'Meaningless(No link-title with \'noid\')';
 +		if ($super) return 'Meaningless(No link-title with \'super\')';
 +		if ($full)  return 'Meaningless(No link-title with \'full\')';
  	}
  
 -	if (PLUGIN_ANAME_ID_MUST_UNIQUE && isset($_id[$id]) && ! $f_noid) {
 -		return plugin_aname_usage($convert, 'ID already used: '. $id);
 +	if ($check_dup_id && isset($_id[$id]) && ! $noid) {
 +		return 'ID already used: '. $id;
  	} else {
  		if (strlen($id) > PLUGIN_ANAME_ID_MAX)
 -			return plugin_aname_usage($convert, 'ID too long');
 -		if (! preg_match(PLUGIN_ANAME_ID_REGEX, $id))
 -			return plugin_aname_usage($convert, 'Invalid ID string: ' .
 -				htmlspecialchars($id));
 -		$_id[$id] = TRUE; // Set
 +			return 'ID too long';
 +		if ($id != '*' && ! preg_match(PLUGIN_ANAME_ID_REGEX, $id))
 +			return 'Invalid ID string: ' . htmlspecialchars($id);
 +		if ($check_dup_id) $_id[$id] = TRUE; // Set
  	}
  
 -	if ($convert) $body = htmlspecialchars($body);
 -	$id = htmlspecialchars($id); // Insurance
 -	$class   = $f_super ? 'anchor_super' : 'anchor';
 -	$attr_id = $f_noid  ? '' : ' id="' . $id . '"';
 -	$url     = $f_full  ? get_script_uri() . '?' . rawurlencode($vars['page']) : '';
 +	return compact('id', 'super', 'full', 'noid', 'hide', 'body');
 +}
 +
 +// Aname plugin itself
 +function plugin_aname_tag($args = array(), $convert = TRUE)
 +{
 +	global $vars;
 +
 +	$args = plugin_aname_getargs($args, PLUGIN_ANAME_ID_MUST_UNIQUE);
 +	if (! is_array($args))
 +		return plugin_aname_usage($convert, $args);
 +
 +	$body = $args['hide'] ? '' : ($convert ? htmlspecialchars($args['body']) : $args['body']);
 +	$id = htmlspecialchars($args['id']); // Insurance
 +	$class   = $args['super'] ? 'anchor_super' : 'anchor';
 +	$attr_id = $args['noid']  ? '' : ' id="' . $id . '"';
 +	$url     = $args['full']  ? get_script_uri() . '?' . rawurlencode($vars['page']) : '';
  	if ($body != '') {
  		$href  = ' href="' . $url . '#' . $id . '"';
  		$title = ' title="' . $id . '"';

 --- /plugin/rename.inc.php	Sun Feb 27 16:57:26 2005
 +++ /plugin/rename.inc.php	Fri Aug 05 23:31:18 2005
 @@ -297,7 +297,7 @@
  function plugin_rename_get_files($pages)
  {
  	$files = array();
 -	$dirs  = array(BACKUP_DIR, DIFF_DIR, DATA_DIR);
 +	$dirs  = array(BACKUP_DIR, DIFF_DIR, DATA_DIR, ANCHOR_DIR);
  	if (exist_plugin_convert('attach'))  $dirs[] = UPLOAD_DIR;
  	if (exist_plugin_convert('counter')) $dirs[] = COUNTER_DIR;
  	// and more ...

 --- /pukiwiki.ini.php	Sun Jul 03 23:16:24 2005
 +++ /pukiwiki.ini.php	Sat Aug 06 16:31:50 2005
 @@ -78,6 +78,7 @@
  define('COUNTER_DIR',   DATA_HOME . 'counter/'  ); // Counter plugin's counts
  define('TRACKBACK_DIR', DATA_HOME . 'trackback/'); // TrackBack logs
  define('PLUGIN_DIR',    DATA_HOME . 'plugin/'   ); // Plugin directory
 +define('ANCHOR_DIR',    DATA_HOME . 'anchor/'   ); // Fixed anchor data
  
  /////////////////////////////////////////////////
  // Directory settings II (ended with '/')

**ツール [#we2d18d5]
 <?php
 // PukiWiki - Yet another WikiWikiWeb clone
 //	anchors.inc.php, v0.02
 //
 // Update anchor cache plugin
 
 // Message setting
 function plugin_anchors_init()
 {
 	$messages = array(
 		'_anchors_messages'=>array(
 			'title_update'   => 'アンカーキャッシュ更新',
 			'msg_adminpass'  => '管理者パスワード',
 			'msg_update'     => '重複IDの書き換え結果は画面表示のみ',
 			'msg_diff'       => '書き換えたページの差分にも残す',
 			'btn_submit'     => '実行',
 			'msg_done'       => 'キャッシュの更新が完了しました。',
 			'msg_no_dup_id'  => '重複IDはありませんでした。',
 			'msg_usage'      => '
 * 処理内容
 
 :キャッシュを更新|
 凍結ページを含む全てのページをスキャンし、重複アンカーIDの調査・再割り当てを行ない、
 アンカーのエイリアス名をキャッシュします。
 
 * 注意
 実行には数分かかる場合もあります。実行ボタンを押したあと、しばらくお待ちください。
 
 * 実行
 管理者パスワードを入力して、[実行]ボタンをクリックしてください。
 '
 		)
 	);
 	set_plugin_messages($messages);
 }
 
 function plugin_anchors_action()
 {
 	global $script, $post, $vars, $fixed_heading_anchor;
 	global $_anchors_messages;
 
 	$msg  = & $_anchors_messages['title_update'];
 	$body = '';
 	if (PKWK_READONLY) die_message('PKWK_READONLY prohibits this');
 	if (!$fixed_heading_anchor) die_message('$fixed_heading_anchor disabled');
 
 	if (empty($vars['action']) || empty($post['adminpass']) || ! pkwk_login($post['adminpass'])) {
 		$body  = convert_html($_anchors_messages['msg_usage']);
 		$body .= <<<EOD
 <form method="POST" action="$script">
  <div>
   <input type="hidden" name="plugin" value="anchors" />
   <input type="radio" name="action" value="update" id="_p_anchors_update" checked="checked" />
   <label for="_p_anchors_update">{$_anchors_messages['msg_update']}</label><br />
   <input type="radio" name="action" value="diff" id="_p_anchors_diff" />
   <label for="_p_anchors_diff">{$_anchors_messages['msg_diff']}</label><br />
   <label for="_p_anchors_adminpass">{$_anchors_messages['msg_adminpass']}</label>
   <input type="password" name="adminpass" id="_p_anchors_adminpass" size="20" value="" />
   <input type="submit" value="{$_anchors_messages['btn_submit']}" />
  </div>
 </form>
 EOD;
 
 	}else if(preg_match('/^(?:update|diff)$/', $vars['action'])) {
 		$retval = plugin_anchors_update($vars['action']);
 		if($retval == '') $retval = $_anchors_messages['msg_no_dup_id'];
 		$body = $_anchors_messages['msg_done'] . "<br />\n" . $retval;
 	}else{
 		$body = $_anchors_messages['err_invalid' ];
 	}
 	return compact('msg', 'body');
 }
 
 function plugin_anchors_update($action)
 {
 	$fp = @fopen(ANCHOR_DIR . 'global.anchor.ids', 'w')
 		or die_message('cannot open ' . htmlspecialchars(ANCHOR_DIR . 'global.anchor.ids'));
 	fclose($fp);
 	foreach(get_existpages(ANCHOR_DIR, '.ids') as $page)
 		@unlink(ANCHOR_DIR . encode($page) . '.ids');
 	$pages = array();
 	foreach(get_existpages() as $page)
 		$pages[$page] = get_filetime($page);
 	asort($pages);
 	$retval = '';
 	foreach(array_keys($pages) as $page){
 		$old_data = implode('', get_source($page));
 		$new_data = make_str_rules($old_data, $page);
 		$diff_data = do_diff($old_data, $new_data);
 		$diffs = preg_grep('/^[+-]/', explode("\n", htmlspecialchars($diff_data)));
 		if(!empty($diffs)){
 			file_write(DATA_DIR, $page, $new_data, TRUE);
 			if($action == 'diff')
 				file_write(DIFF_DIR, $page, $diff_data);
 			$diffs = preg_replace('/^-(.*)$/', '<span class="diff_removed"> $1</span>', $diffs);
 			$diffs = preg_replace('/^\+(.*)$/', '<span class="diff_added"  > $1</span>', $diffs);
 			$retval .= '<p>' . htmlspecialchars($page) . "</p>\n";
 			$retval .= "<pre>\n" . implode("\n", $diffs) . "\n</pre>\n";
 		}
 	}
 	return $retval;
 }
 ?>

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Site admin: PukiWiki Development Team

PukiWiki 1.5.3+ © 2001-2020 PukiWiki Development Team. Powered by PHP 5.6.40-0+deb8u12. HTML convert time: 0.086 sec.

OSDN