PukiwikiAPIExtension
PukiWiki API Extension
make_inline
/** * Convert only inline plugins unlike make_link() * * This (Precisely, InlineConverter) does htmlspecialchars, too. * * @param $string string * @param $page pagename, default is $vars['page'] * @see make_link * @uses InlineConverter */ function make_inline($string, $page = '') { global $vars; static $converter; if (! isset($converter)) $converter = new InlineConverter(array('plugin')); $clone = $converter->get_clone($converter); return $clone->convert($string, ($page != '') ? $page : $vars['page']); }
parse_options
簡単な割には効果の高いオプション解析
if (! function_exists('parse_options')) { function parse_options(&$args, &$options, $sep = '=') { foreach ($args as $arg) { list($key, $val) = array_pad(explode($sep, $arg, 2), 2, TRUE); if (array_key_exists($key, $options)) { $options[$key] = $val; } } } }
show_iframe
XHTML1.1 Strict のためにできるだけ iframe タグは使用しない。が、IE では object タグではうまくならないので仕方なく iframe タグを使用し、XHTML1.0 Transitional にして逃げる。
// iframe.inc.php, amazonpd.inc.php function show_iframe($url, $style = NULL) { if (ereg("MSIE (3|4|5|6|7)", getenv("HTTP_USER_AGENT"))) { global $pkwk_dtd; //1.4.4 or above global $html_transitional; //1.4.3 $pkwk_dtd = PKWK_DTD_XHTML_1_0_TRANSITIONAL; $html_transitional = 1; $ret = '<iframe frameborder="0" class="iframe"'; $ret .= isset($style) ? ' style="' . $style . '"' : ''; $ret .= ' src="' . $url . '">'; $ret .= '<p>Your borwser is not supporting iframe tag. ' . 'Please use one of the latest browsers.<br />' . 'Go to <a href="' . $url . '">' . $url . '</a></p>'; $ret .= '</iframe>'; return $ret; } else { $ret = '<object class="iframe" type="text/html"'; $ret .= isset($style) ? ' style="' . $style . '"' : ''; $ret .= ' data="' . $url . '">'; $ret .= '<p>Your borwser is not supporting object tag. ' . 'Please use one of the latest browsers.<br />' . 'Go to <a href="' . $url . '">' . $url . '</a></p>'; $ret .= '</object>'; return $ret; } }
remove_multiline_plugin
/** * Remove inside of multiline plugin arguments. * Keys are preserved. * * @param array $lines * @return void */ function remove_multiline_plugin(&$lines) { if(! (defined('PKWKEXP_DISABLE_MULTILINE_PLUGIN_HACK') && PKWKEXP_DISABLE_MULTILINE_PLUGIN_HACK === 0)) { return $lines; } $multiline = 0; foreach ($lines as $i => $line) { $matches = array(); if ($multiline < 2) { if(preg_match('/^#([^\(\{]+)(?:\(([^\r]*)\))?(\{*)/', $line, $matches)) { $multiline = strlen($matches[3]); } } else { if (preg_match('/^\}{' . $multiline . '}$/', $line, $matches)) { $multiline = 0; } unset($lines[$i]); continue; } } }
is_interwiki($wikiname)
オーバーライドしたい。
/** * Check if string is InterWiki syntax or not * * @param string $str * @return boolean */ function is_interwiki($str) { return ! is_url($str) && is_interwiki($str); }
get_interwiki_url($interwiki)
オーバーライドしたい。
/** * Resolve InterWiki name * * @param string $interwiki InterWiki name * @return string url */ function get_interwiki_url($interwiki) { if (is_url($interwiki) || ! is_interwiki($interwiki)) return FALSE; list($interwiki, $page) = explode(':', $interwiki, 2); $url = get_interwiki_url($interwiki, $page); return $url; }
pkwk_noskin_output
/** * Output contents without skin * * @param string $body html * @param string $content_type e.g., 'text/html', 'text/css', 'text/javascript' * @return void exit */ function pkwk_output_noskin($body, $content_type = 'text/html') // text/css, text/javascript { pkwk_common_headers(); header('Content-Type: ' . $content_type); print $body; exit; }
get_page_uri
/** * get uri of a page * * @param string $page * @param string $query query word if needs * @return string uri */ function get_page_uri($page, $query = '') { if (function_exists('get_script_uri')) { // from pukiwiki 1.4 $url = get_script_uri() . '?' . rawurlencode($page); } else { global $script; $url = $script . '?' . rawurlencode($page); } if ($query != '') { $url .= '&' . $query; } return $url; }
get_existpages($prefix = '')
オ-バーライドしたい
/** * get existing pages with prefix restriction (instead of directory name) * * @param string $prefix * @return array */ function get_existpages($prefix = '') { $pages = get_existpages(); if ($prefix === '') return $pages; foreach ($pages as $i => $page) { if (strpos($page, $prefix) !== 0) { unset($pages[$i]); } } return $pages; }
is_edit_restrict
requires below is_edit_auth
function is_edit_restrict($page) { return PKWK_READONLY > 0 or is_freeze($page) or self::is_edit_auth($page); }
is_edit_auth
/** * Check if a page is configured to require authentication * * @param string $page * @param string $user if want to check whether this user possibly can get permission * @return boolean */ function is_edit_auth($page, $user = '') { global $edit_auth, $edit_auth_pages, $auth_method_type; if (! $edit_auth) { return FALSE; } // Checked by: $target_str = ''; if ($auth_method_type == 'pagename') { $target_str = $page; // Page name } else if ($auth_method_type == 'contents') { $target_str = join('', get_source($page)); // Its contents } foreach($edit_auth_pages as $regexp => $users) { if (preg_match($regexp, $target_str)) { if ($user == '' || in_array($user, explode(',', $users))) { return TRUE; } } } return FALSE; }
exec_existpages
tag.inc.php など、ページが参照されて始めてログが作成されるプラグインのために、全ページ実行する。 正規表現で必要な行だけに絞り込むことが可能。
if (! function_exists('exec_existpages')) { /** * Execute (convert_html) all pages * * PukiWiki API Extension * * @param string $regexp execute only matched lines (preg_grep) * @return array executed pages */ function exec_existpages($regexp = null) { global $vars, $get, $post; $pages = get_existpages(); $exec_pages = array(); $tmp_page = $vars['page']; $tmp_cmd = $vars['cmd']; $vars['cmd'] = $get['cmd'] = $post['cmd'] = 'read'; foreach ($pages as $page) { $vars['page'] = $get['page'] = $post['page'] = $page; $lines = get_source($page); if (isset($regexp)) { $lines = preg_grep($regexp, $lines); } if (empty($lines)) continue; convert_html($lines); $exec_pages[] = $page; } $vars['page'] = $get['page'] = $post['page'] = $tmp_page; $vars['cmd'] = $get['cmd'] = $post['cmd'] = $tmp_cmd; return $exec_pages; } }
exec_page
if (! function_exists('exec_page')) { /** * Execute (convert_html) this page * * PukiWiki API Extension * * @param string $page * @param string $regexp execute only matched lines (preg_grep) * @return boolean executed */ function exec_page($page, $regexp = null) { global $vars, $get, $post; $lines = get_source($page); if (isset($regexp)) { $lines = preg_grep($regexp, $lines); } if (empty($lines)) return FALSE; $tmp_page = $vars['page']; $tmp_cmd = $vars['cmd']; $vars['cmd'] = $get['cmd'] = $post['cmd'] = 'read'; $vars['page'] = $get['page'] = $post['page'] = $page; convert_html($lines); $vars['page'] = $get['page'] = $post['page'] = $tmp_page; $vars['cmd'] = $get['cmd'] = $post['cmd'] = $tmp_cmd; return TRUE; } }
is_human
requires below is_admin too
if (! function_exists('is_human')) { /** * Human recognition using PukiWiki Auth methods * * @param boolean $is_human Tell this is a human (Use TRUE to store into session) * @param boolean $use_session Use Session log * @param int $use_rolelevel accepts users whose role levels are stronger than this * @return boolean */ if (! defined('ROLE_AUTH')) define('ROLE_AUTH', 5); // define for PukiWiki Official if (! defined('ROLE_ENROLLEE')) define('ROLE_ENROLLEE', 4); if (! defined('ROLE_ADM_CONTENTS')) define('ROLE_ADM_CONTENTS', 3); if (! defined('ROLE_ADM')) define('ROLE_ADM', 2); if (! defined('ROLE_GUEST')) define('ROLE_GUEST', 0); function is_human($is_human = FALSE, $use_session = FALSE, $use_rolelevel = 0) { if (! $is_human) { if ($use_session) { session_start(); $is_human = isset($_SESSION['pkwk_is_human']) && $_SESSION['pkwk_is_human']; } } if (! $is_human) { if (ROLE_GUEST < $use_rolelevel && $use_rolelevel <= ROLE_ENROLLEE) { if (is_callable(array('auth', 'check_role'))) { // Plus! $is_human = ! auth::check_role('role_enrollee'); } else { // In PukiWiki Official, enrollees are all auth_users (ROLE_AUTH && BasicAuth in Plus!) $is_human = isset($_SERVER['PHP_AUTH_USER']); } } } if (! $is_human) { if (ROLE_GUEST < $use_rolelevel && $use_rolelevel <= ROLE_ADM_CONTENTS) { $is_human = is_admin(NULL, $use_session, TRUE); // In PukiWiki Official, username 'admin' is the Admin } } if ($use_session) { session_start(); $_SESSION['pkwk_is_human'] = $is_human; } else { global $vars; $vars['pkwk_is_human'] = $is_human; } return $is_human; } }
is_admin
if (! function_exists('is_admin')) { /** * PukiWiki admin login with session * * @param string $pass Password. Use NULL when to get current session state. * @param boolean $use_session Use Session log * @param boolean $use_authlog Use Auth log. * Username 'admin' is deemed to be Admin in PukiWiki Official. * PukiWiki Plus! has role management, roles ROLE_ADM and ROLE_ADM_CONTENTS are deemed to be Admin. * @return boolean */ function is_admin($pass = NULL, $use_session = FALSE, $use_authlog = FALSE) { $is_admin = FALSE; if (! $is_admin) { if ($use_session) { session_start(); $is_admin = isset($_SESSION['pkwk_is_admin']) && $_SESSION['pkwk_is_admin']; } } // BasicAuth (etc) login if (! $is_admin) { if ($use_authlog) { if (is_callable(array('auth', 'check_role'))) { // Plus! $is_admin = ! auth::check_role('role_adm_contents'); } else { $is_admin = (isset($_SERVER['PHP_AUTH_USER']) && $_SERVER['PHP_AUTH_USER'] === 'admin'); } } } // PukiWiki Admin login if (! $is_admin) { if (isset($pass)) { $is_admin = function_exists('pkwk_login') ? pkwk_login($pass) : md5($pass) === $GLOBALS['adminpass']; // 1.4.3 } } if ($use_session) { session_start(); if ($is_admin) $_SESSION['pkwk_is_admin'] = TRUE; } else { global $vars; $vars['pkwk_is_admin'] = $is_admin; } return $is_admin; } }
Plus! の check_role は auth.ini.php にてユーザ作成、パスワード登録時に
'hoge' => array('password', 3);
のようにすると、role_adm_contents になれる。2だと admin(最高権限), 4だと enrollee などなど
get_pkwk_baseuri
if (! function_exists('get_pkwk_baseuri')) { /** * Get PukiWiki Base(Top) URI (without index.php) * * @return string baseuri */ function get_pkwk_baseuri() { static $baseuri = ''; if ($baseuri !== '') return $baseuri; $baseuri = get_script_uri(); if (($pos = strrpos($baseuri, '/')) !== FALSE) { $baseuri = substr($baseuri, 0, $pos + 1); } return $baseuri; } }
is_read_auth
if (! function_exists('is_read_auth')) {
/**
* Check if the page requires the read-authentication
*
* @param $page pagename
* @param $user check if it is possible for this page to be read by the user
* @return boolean
*/
function is_read_auth($page, $user = '')
{
global $read_auth, $read_auth_pages, $auth_method_type;
if (! $read_auth) {
return FALSE;
}
// Checked by:
$target_str = '';
if ($auth_method_type == 'pagename') {
$target_str = $page; // Page name
} else if ($auth_method_type == 'contents') {
$target_str = join('', get_source($page)); // Its contents
}
foreach($read_auth_pages as $regexp => $users) {
if (preg_match($regexp, $target_str)) {
if ($user == '' || in_array($user, explode(',', $users))) {
return TRUE;
}
}
}
return FALSE;
}
}
is_page_newer
if (! function_exists('is_page_newer')) { /** * Check if the page timestamp is newer than the file timestamp * * PukiWiki API Extension * * @param string $page pagename * @param string $file filename * @param bool $ignore_notimestamp Ignore notimestamp edit and see the real time editted * @return boolean */ function is_page_newer($page, $file, $ignore_notimestamp = TRUE) { $filestamp = file_exists($file) ? filemtime($file) : 0; if ($ignore_notimestamp) { // See the diff file. PukiWiki Trick. $pagestamp = is_page($page) ? filemtime(DIFF_DIR . encode($page) . '.txt') : 0; } else { $pagestamp = is_page($page) ? filemtime(get_filename($page)) : 0; } return $pagestamp > $filestamp; } }
get_filecreatetime
if (! function_exists('get_filecreatetime')) { /** * Get page created time * * PukiWiki API Extension * * @param string $page pagename * @return int timestamp * @see get_filetime($page) */ function get_filecreatetime($page) { if (_backup_file_exists($page)) { // PukiWiki Trick // This is not a created time exactly, but the closest time $backup = get_backup($page, 1); // 1st age return $backup['time']; } else { return get_filetime($page); } } }
is_newpage
function is_newpage($page) { // pukiwiki trick return ! _backup_file_exists($page); }
backup はとめることもできるので、そうなると無理。 ちなみに、diff ファイルは最初の編集でも作られるので、それも無理。
ctime と mtime が同じかどうか、も無理。 pukiwiki は「タイムスタンプを更新しない」編集で touch してしまうので、そのときに ctime が更新されてしまう。
備忘録:backup ファイルは page_write のタイミングで作成される。ただし lib/backup.php で UTIME - $lastmod > 60 * 60 * $cycle のようなチェックがされており、デフォルトでは 1 時間以降の更新タイミングで作成される。
get_heading
if (! function_exists('get_heading')) { /** * Get heading strings from a wiki source line * * *** Heading Strings ((footnotes)) [id] * -> array("Heading Strings", "id") * * @param string $line a wiki source line * @param bool $strip cut footnotes * @return array [0] heading string [1] a fixed-heading anchor * @uses lib/html.php#make_heading */ function get_heading($line, $strip = TRUE) { global $NotePattern; $id = make_heading($line, FALSE); // $line is modified inside if ($strip) { $line = preg_replace($NotePattern, '', $line); // cut footnotes } return array($line, $id); } }
make_pagelink_nopg
no passage。経過時間加工、timestamp 取得をしなくなるので格段に早くなる(timestamp の取得などファイルシステムにアクセスするものはかなりのろい)。
if (! function_exists('make_pagelink_nopg')) { /** * Make a hyperlink to the page without passage * * @param string $page pagename * @param string $alias string to be displayed on the link * @param string $anchor anchor * @param string $refer reference pagename. query '&refer=' is added. * @param bool $isautolink flag if this link is created via autolink or not * @return string link html * @uses make_pagelink */ function make_pagelink_nopg($page, $alias = '', $anchor = '', $refer = '', $isautolink = FALSE) { // no passage global $show_passage; $tmp = $show_passage; $show_passage = 0; $link = make_pagelink($page, $alias, $anchor, $refer, $isautolink); $show_passage = $tmp; return $link; } }
PukiWiki Syntax Definitions
$def_freeze = '/^(?:#freeze(?!\w)\s*)+/im'; // define('PLUGIN_EDIT_FREEZE_REGEX', '/^(?:#freeze(?!\w)\s*)+/im'); // edit.inc.php $def_headline = '/^(\*{1,3})/'; $def_include = '/^#include.*\((.+)\)/';
PukiWiki Version Adapter
update_recent
if (! function_exists('update_recent')) { /** * Update recent * * PukiWiki Version Adapter * * @param string $page */ function update_recent($page) { if (is_page($page) && function_exists('lastmodified_add')) { lastmodified_add($page); // 1.4.7 or higher } elseif (function_exists('put_lastmodified')) { put_lastmodified(); } } }
PukiWiki Plus Adapter
get_plugin_dir
if (! function_exists('get_plugin_dir')) { /** * Get plugin directory name where a given plugin exists * * PukiWiki Plus! <-> PukiWiki Adapter * * @param string $plugin_name plugin name * @return string plugin directory name */ function get_plugin_dir($plugin_name) { static $plugin_dir = array(); if (isset($plugin_dir[$plugin_name])) return $plugin_dir[$plugin_name]; $p_dirs = defined('EXT_PLUGIN_DIR') ? array(EXT_PLUGIN_DIR, PLUGIN_DIR) : array(PLUGIN_DIR); foreach ($p_dirs as $p_dir) { if (file_exists($p_dir . $plugin_name . '.inc.php')) { $plugin_dir[$plugin_name] = $p_dir; break; } } return $plugin_dir[$plugin_name]; } }
auth::get_existpages
if (! class_exists('auth')) { class auth { function get_existpages($dir = DATA_DIR, $ext = '.txt') { return get_existpages($dir, $ext); } } }
メモ
auth::get_existpages は権限がない人にはそもそも存在自体を隠して、リストに出さないようにしてしまえというもの。
一覧表示時、すでにログインをしていることが必須となる。ので、lsx には使用していない。
制限ページアクセス時に認証を求めるのが pukiwiki 的なので、一覧でとりあえず出してもらわないとそのページに飛べない。
悩ましいところではある。
Pukiwiki API Tips
exist_plugin
exist_plugin は関数名からは、 そのプラグインが存在しているかのチェックだけと見えるが、 実は require_once もしている。
require_once(PLUGIN_DIR . $plugin_name . '.inc.php);
とするよりは、
exist_plugin($plugin_name);
のほうが楽なので require 目的にも使える。
exist_plugin_[inline|convert|action] は対応する型の関数があるかどうかのチェックもしている。
do_plugin
do_plugin_convert, do_plugin_inline, do_plugin_action は function plugin_[plugin_name]_init の部分も実行してくれる。
プラグイン multilang を l という名前で実行するラッパープラグインの作成を例に使い方を説明する。
function plugin_l_inline() { if (! exist_plugin_inline('multilang')) return '<span>l(): multilang plugin does not exist.</span>'; $args = func_get_args(); $body = array_pop($args); return do_plugin_inline('multilang', csv_implode(',', $args), $body); } function plugin_l_convert() { if (! exist_plugin_convert('multilang')) return '<p>l(): multilang plugin does not exist.</p>'; $args = func_get_args(); return do_plugin_convert('multilang', csv_implode(',', $args)); } function plugin_l_action() { if (! exist_plugin_inline('action')) return array('msg'=>'l', 'body'=>'<p>l(): multilang plugin does not exist.</p'); return do_plugin_action('multilang'); }
意味の理解を深めるためにこれとほぼ同等のものを標準 PHP 関数を用いて記述してみる。
function plugin_l_inline() { if (! file_exists(PLUGIND_DIR . 'multilang' . '.inc.php')) return '<span>l(): multilang plugin does not exist.</span>'; require_once(PLUGIN_DIR . 'multilang' . '.inc.php'); if (! function_exists('plugin_multilang_inline')) return '<span>l(): multilang plugin does not exist.</span>'; $args = func_get_args(); if (function_exists('plugin_multilang_init')) call_user_func('plugin_multilang_init'); return call_user_func_array('plugin_multilang_inline', $args); } function plugin_l_convert() { if (! file_exists(PLUGIND_DIR . 'multilang' . '.inc.php')) return '<p>l(): multilang plugin does not exist.</p>'; require_once(PLUGIN_DIR . 'multilang' . '.inc.php'); if (! function_exists('plugin_multilang_convert')) return '<p>l(): multilang plugin does not exist.</p>'; $args = func_get_args(); if (function_exists('plugin_multilang_init')) call_user_func('plugin_multilang_init'); return call_user_func_array('plugin_multilang_convert', $args); } function plugin_l_action() { if (! file_exists(PLUGIND_DIR . 'multilang' . '.inc.php')) return array('msg'=>'l', 'body'=>'<p>l(): multilang plugin does not exist.</p'); require_once(PLUGIN_DIR . 'multilang' . '.inc.php'); if (! function_exists('plugin_multilang_action')) return array('msg'=>'l', 'body'=>'<p>l(): multilang plugin does not exist.</p'); if (function_exists('plugin_multilang_init')) call_user_func('plugin_multilang_init'); return call_user_func('plugin_multilang_action'); }
プラグインの関数そのものは、call_user_func_array のほうが呼びやすい・・・(csv_implode($args) しなくてよい)


