array('number', ''),
'except' => array('string', ''),
'filter' => array('string', ''),
'title' => array('enum', 'on', array('on', 'off', 'nolink', 'basename')), // obsolete
'titlestr' => array('enum', 'title', array('name', 'off', 'basename', 'title', 'relname')),
'titlelink' => array('bool', true),
'section' => array('array', array()),
'permalink' => array('string', false),
'head' => array('bool', true),
);
static $default_section_options = array(
'num' => array('number', ''),
'depth' => array('number', ''),
'except' => array('string', ''),
'filter' => array('string', ''),
'cache' => array('enum', 'on', array('on', 'off', 'reset')),
'inclsub' => array('bool', false), // not yet
);
$this->default_options = &$default_options;
$this->default_section_options = &$default_section_options;
// init
static $visited = array();
$this->visited = &$visited;
$this->options = $this->default_options;
$this->section_options = $this->default_section_options;
}
function PluginIncludex() {
$this->__construct();
}
// static
var $default_options;
var $default_section_options;
var $visited;
// var
var $error = "";
var $plugin = "includex";
var $options;
var $section_options;
var $inclpage;
var $lines;
var $headlines;
var $narrowed_headlines;
function convert()
{
$args = func_get_args();
$body = $this->body($args);
if ($this->error != "" ) { return "
#$this->plugin(): $this->error
"; }
return $body;
}
function body($args)
{
global $vars, $get, $post;
$this->visited[$vars['page']] = TRUE;
$this->inclpage = array_shift($args);
$this->check_page();
if ($this->error != "") { return; }
$parser = new PluginIncludexOptionParser();
$this->options = $parser->parse_options($args, $this->options);
if ($parser->error != "") { $this->error = $parser->error; return; }
$this->check_options();
if ($this->error != "") { return; }
$this->init_lines();
if ($this->error !== "") { return; }
$this->narrow_lines();
if ($this->error !== "") { return; }
$body = $this->frontend();
if ($this->error !== "") { return; }
$this->visited[$this->inclpage] = TRUE;
return $body;
}
function check_options()
{
// support lower version
if ($this->options['title'][1] != 'on') {
if ($this->options['title'][1] == 'nolink') {
$this->options['titlelink'][1] = false;
$this->options['titlestr'][1] = 'name';
} else {
$this->options['titlestr'][1] = $this->options['title'][1];
}
}
if ($this->options['permalink'][1] === '') {
$this->options['permalink'][1] = _('Permalink');
}
}
function check_page()
{
global $vars;
if (empty($this->inclpage)) {
$this->error = "No page is specified.";
return;
}
$current = $vars['page'];
$this->inclpage = get_fullname($this->inclpage, $current);
if (! $this->is_page($this->inclpage)) {
$this->error = "$this->inclpage does not eixst.";
return;
}
if (! $this->check_readable($this->inclpage, false, false)) {
$this->error = "$this->inclpage is not readable.";
return;
}
if (isset($this->visited[$this->inclpage])) {
$this->error = "$this->inclpage is already included.";
return;
}
}
function frontend()
{
global $vars, $get, $post;
$titlestr = PluginIncludex::get_titlestr($this->inclpage, $this->options['titlestr'][1]);
$title = PluginIncludex::get_title($this->inclpage, $titlestr, $this->options['title'][1]);
if ($this->error != "") { return; }
// because included page would use these variables.
$tmp = $vars['page'];
$get['page'] = $post['page'] = $vars['page'] = $this->inclpage;
if (function_exists('convert_filter')) {
$this->lines = convert_filter($this->lines); // plus
}
$body = convert_html($this->lines);
$get['page'] = $post['page'] = $vars['page'] = $tmp;
if ($this->error != "") { return; }
$footer = '';
if ($this->options['permalink'][1] !== false) {
$linkstr = $this->make_inline($this->options['permalink'][1]);
$footer = '' .
make_pagelink($this->inclpage, $linkstr) . '
';
}
return $title . "\n" . $body . $footer;
}
// static
function get_titlestr($inclpage, $option = null, $current = null)
{
switch ($option) {
case 'off':
$titlestr = '';
break;
case 'name':
$titlestr = htmlsc($inclpage);
break;
case 'basename':
$titlestr = htmlsc(basename($inclpage));
break;
case 'relname':
if (! isset($current)) $current = $GLOBALS['vars']['page'];
if (($i = strpos($inclpage, $current . '/')) === 0) {
$titlestr = htmlsc(substr($inclpage, strlen($current)+1));
} else {
$titlestr = htmlsc($inclpage);
}
break;
case 'on':
case 'title':
default:
if (exist_plugin('contentsx')) {
$contentsx = new PluginContentsx();
if (method_exists($contentsx, 'get_title')) {
$titlestr = $contentsx->get_title($inclpage);
$titlestr = strip_htmltag(make_link($titlestr));
}
}
if ($titlestr == '') $titlestr = htmlsc($inclpage);
break;
}
return $titlestr;
}
// static
function get_title($inclpage, $titlestr, $option = true)
{
global $fixed_heading_edited;
$anchorlink = ' ' . PluginIncludex::get_page_anchorlink($inclpage);
$editlink = $fixed_heading_edited ? ' ' . PluginIncludex::get_page_editlink($inclpage) : '';
if ($titlestr == '') {
//return $ret = '' . $anchorlink . '
';
return '';
}
switch ($option) {
case false:
$ret = '' . $titlestr . $editlink . $anchorlink . '
';
break;
case true:
default:
$link = make_pagelink($inclpage, $titlestr);
$ret = '' . $link . $editlink . $anchorlink . '
';
break;
}
return $ret;
}
function narrow_lines()
{
$parser = new PluginIncludexOptionParser();
if (! empty($this->options['section'][1]) && exist_plugin('contentsx')) {
$this->section_lines();
}
$this->filter_lines();
$this->except_lines();
$num = sizeof($this->lines);
$this->options['num'][1] = $parser->parse_numoption($this->options['num'][1], 1, $num);
if ($parser->error !== "") { $this->error = $parser->error; return; }
$this->num_filter_lines();
$this->cut_head_lines();
}
function cut_head_lines()
{
// cut the headline on the first line
if ($this->options['head'][1] === FALSE) {
$def_headline = '/^(\*{1,3})/';
if (preg_match($def_headline, $this->lines[0])) {
unset($this->lines[0]);
}
}
}
function section_lines()
{
if (empty($this->options['section'][1])) {
return;
}
$parser = new PluginIncludexOptionParser();
$this->section_options = $parser->parse_options($this->options['section'][1], $this->section_options);
if ($parser->error != "") { $this->error = $parser->error; return; }
// what a public class! hehehe
$contentsx = new PluginContentsx();
$contentsx->options['include'][1] = false;
$contentsx->options['fromhere'][1] = false;
$contentsx->options['page'][1] = $contentsx->check_page($this->inclpage);
$this->headlines = $contentsx->get_metalines($this->inclpage);
if ($contentsx->error != "") { $this->error = $contentsx->error; return; }
foreach ($this->section_options as $key => $val) {
$contentsx->options[$key] = $val;
}
$contentsx->narrow_metalines();
if ($contentsx->error != "") { $this->error = $contentsx->error; return; }
$this->narrowed_headlines = $contentsx->metalines;
$size = sizeof($this->headlines);
$this->section_options['num'][1] = $parser->parse_numoption($this->section_options['num'][1], 0, $size);
$lines = array();
if (in_array(0, $this->section_options['num'][1])) {
$linenum = $this->headlines[0]['linenum'];
$lines = array_merge($lines, array_splice($this->lines, 0, $linenum));
}
// FutureWork: Do no rely on contentsx's cache as much as possible.
$i = 0; $size = sizeof($this->headlines);
foreach ($this->narrowed_headlines as $narrowed_headline) {
$linenum = $narrowed_headline['linenum'];
for (; $i < $size; $i++ ) {
$current = $i;
if ($linenum != $this->headlines[$current]['linenum']) {
continue;
}
$next = $i + 1;
if ($next < $size) {
$len = $this->headlines[$next]['linenum'] - $linenum;
$lines = array_merge($lines, array_slice($this->lines, $linenum, $len));
} else {
$lines = array_merge($lines, array_slice($this->lines, $linenum));
}
break;
}
}
$this->lines = $lines;
}
function num_filter_lines()
{
if ($this->options['num'][1] === '') {
return;
}
$lines = array();
foreach ($this->options['num'][1] as $num) {
$lines[] = $this->lines[$num - 1];
}
$this->lines = $lines;
}
function filter_lines()
{
if ($this->options['filter'][1] === "") {
return;
}
$lines = array();
foreach ($this->lines as $line) {
if (ereg($this->options['filter'][1], $line)) {
$lines[] = $line;
}
}
$this->lines = $lines;
}
function except_lines()
{
if ($this->options['except'][1] === "") {
return;
}
$lines = array();
foreach ($this->lines as $line) {
if (! ereg($this->options['except'][1], $line)) {
$lines[] = $line;
}
}
$this->lines = $lines;
}
function init_lines()
{
$this->lines = $this->get_source($this->inclpage);
}
// static
function get_page_anchor($page) {
// anchor must be '^[A-Za-z][A-Za-z0-9_-]*'
return 'z' . md5($page);
}
// PukiWiki API Extension
// convert inline plugins
// PukiWiki API InlineConverter does htmlsc, too.
// refer plugin/make_link.php#make_link
// static
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']);
}
// refer plugin/aname.inc.php
// static
function get_page_anchorlink($page) {
global $_symbol_anchor;
global $pkwk_dtd;
$id = $this->get_page_anchor($page);
$id = htmlsc($id);
// aname allows only fiexed_anchors such as x83dvkd8
//if (exist_plugin_inline('aname')) {
//$link = do_plugin_inline('aname', "$anchor,super,$_symbol_anchor");
//}
if (isset($pkwk_dtd) && $pkwk_dtd < PKWK_DTD_XHTML_1_1) {
$attr_id = ' id="' . $id . '" name="' . $id . '"';
} else {
$attr_id = ' id="' . $id . '"';
}
$attr_href = ' href="#' . $id . '"';
$attr_title = ' title="' . $id . '"';
$attr_class = ' class="anchor_super"';
$link = '' . $_symbol_anchor . '';
return $link;
}
// static
function get_page_editlink($page) {
$r_page = rawurlencode($page);
$link = '';
$link .= '
';
$link .= '';
return $link;
}
function get_source($page)
{
return get_source($page);
}
function is_page($page)
{
return is_page($page);
}
function check_readable($page, $flag, $flag2)
{
return check_readable($page, $flag, $flag2);
}
}
///////////////////////////////////////
class PluginIncludexOptionParser
{
var $error = "";
function parse_options($args, $options)
{
if (! $this->is_associative_array($args)) {
$args = $this->associative_args($args, $options);
if ($this->error != "") { return; }
}
foreach ($args as $key => $val) {
if ( !isset($options[$key]) ) { continue; } // for action ($vars)
$type = $options[$key][0];
switch ($type) {
case 'bool':
if($val == "" || $val == "on" || $val == "true") {
$options[$key][1] = true;
} elseif ($val == "off" || $val == "false" ) {
$options[$key][1] = false;
} else {
$this->error = htmlsc("$key=$val") . " is invalid. ";
$this->error .= "The option, $key, accepts only a boolean value.";
$this->error .= "#$this->plugin($key) or #$this->plugin($key=on) or #$this->plugin($key=true) for true. ";
$this->error .= "#$this->plugin($key=off) or #$this->plugin($key=false) for false. ";
return;
}
break;
case 'string':
$options[$key][1] = $val;
break;
case 'sanitize':
$options[$key][1] = htmlsc($val);
break;
case 'number':
// Do not parse yet, parse after getting min and max. Here, just format checking
if ($val === '') {
$options[$key][1] = '';
break;
}
if ($val[0] === '(' && $val[strlen($val) - 1] == ')') {
$val = substr($val, 1, strlen($val) - 2);
}
foreach (explode(",", $val) as $range) {
if (preg_match('/^-?\d+$/', $range)) {
} elseif (preg_match('/^-?\d*\:-?\d*$/', $range)) {
} elseif (preg_match('/^-?\d+\+-?\d+$/', $range)) {
} else {
$this->error = htmlsc("$key=$val") . " is invalid. ";
$this->error .= "The option, " . $key . ", accepts number values such as 1, 1:3, 1+3, 1,2,4. ";
$this->error .= "Specify options as \"$key=1,2,4\" or $key=(1,2,3) when you want to use \",\". ";
$this->error .= "In more details, a style like (1:3,5:7,9:) is also possible. 9: means from 9 to the last. ";
$this->error .= "Furtermore, - means backward. -1:-3 means 1,2,3 from the tail. ";
return;
}
}
$options[$key][1] = $val;
break;
case 'enum':
if($val == "") {
$options[$key][1] = $options[$key][2][0];
} elseif (in_array($val, $options[$key][2])) {
$options[$key][1] = $val;
} else {
$this->error = htmlsc("$key=$val") . " is invalid. ";
$this->error .= "The option, " . $key . ", accepts values from one of (" . join(",", $options[$key][2]) . "). ";
$this->error .= "By the way, #$this->plugin($key) equals to #$this->plugin($key=" . $options[$key][2][0] . "). ";
return;
}
break;
case 'array':
if ($val == '') {
$options[$key][1] = array();
break;
}
if ($val[0] === '(' && $val[strlen($val) - 1] == ')') {
$val = substr($val, 1, strlen($val) - 2);
}
$val = explode(',', $val);
//$val = $this->support_paren($val);
$options[$key][1] = $val;
break;
case 'enumarray':
if ($val == '') {
$options[$key][1] = $options[$key][2];
break;
}
if ($val[0] === '(' && $val[strlen($val) - 1] == ')') {
$val = substr($val, 1, strlen($val) - 2);
}
$val = explode(',', $val);
//$val = $this->support_paren($val);
$options[$key][1] = $val;
foreach ($options[$key][1] as $each) {
if (! in_array($each, $options[$key][2])) {
$this->error = "$key=" . htmlsc(join(",", $options[$key][1])) . " is invalid. ";
$this->error .= "The option, " . $key . ", accepts sets of values from (" . join(",", $options[$key][2]) . "). ";
$this->error .= "By the way, #$this->plugin($key) equals to #$this->plugin($key=(" . join(',',$options[$key][2]) . ")). ";
return;
}
}
break;
default:
}
}
return $options;
}
/**
* Handle associative type option arguments as
* ["prefix=Hoge/", "contents=(hoge", "hoge", "hoge)"] => ["prefix"=>"hoge/", "contents"=>"(hoge,hoge,hoge)"]
* This has special supports for parentheses type arguments (number, array, enumarray)
* Check option in along with.
* @access public
* @param Array $args Original option arguments
* @return Array $result Converted associative option arguments
*/
function associative_args($args, $options)
{
$result = array();
while (($arg = current($args)) !== false) {
list($key, $val) = array_pad(explode("=", $arg, 2), 2, '');
if (! isset($options[$key])) {
$this->error = 'No such a option, ' . htmlsc($key);
return;
}
// paren support
if ($val[0] === '(' && ($options[$key][0] == 'number' ||
$options[$key][0] == 'array' || $options[$key][0] == 'enumarray')) {
while(true) {
if ($val[strlen($val)-1] === ')' && substr_count($val, '(') == substr_count($val, ')')) {
break;
}
$arg = next($args);
if ($arg === false) {
$this->error = "The # of open and close parentheses of one of your arguments did not match. ";
return;
}
$val .= ',' . $arg;
}
}
$result[$key] = $val;
next($args);
}
return $result;
}
function parse_numoption($optionval, $min, $max)
{
if ($optionval === '') {
return '';
}
$result = array();
foreach (explode(",", $optionval) as $range) {
if (preg_match('/^-?\d+$/', $range)) {
$left = $right = $range;
} elseif (preg_match('/^-?\d*\:-?\d*$/', $range)) {
list($left, $right) = explode(":", $range, 2);
if ($left == "" && $right == "") {
$left = $min;
$right = $max;
} elseif($left == "") {
$left = $min;
} elseif ($right == "") {
$right = $max;
}
} elseif (preg_match('/^-?\d+\+-?\d+$/', $range)) {
list($left, $right) = explode("+", $range, 2);
$right += $left;
}
if ($left < 0) {
$left += $max + 1;
}
if ($right < 0) {
$right += $max + 1;
}
$result = array_merge($result, range($left, $right));
// range allows like range(5, 3) also
}
// filter
foreach (array_keys($result) as $i) {
if ($result[$i] < $min || $result[$i] > $max) {
unset($result[$i]);
}
}
sort($result);
$result = array_unique($result);
return $result;
}
function option_debug_print($options) {
foreach ($options as $key => $val) {
$type = $val[0];
$val = $val[1];
if(is_array($val)) {
$val=join(',', $val);
}
$body .= "$key=>($type, $val),";
}
return $body;
}
// php extension
function is_associative_array($array)
{
if (!is_array($array) || empty($array))
return false;
$keys = array_keys($array);
return array_keys($keys) !== $keys;
// or
//return is_array($array) && !is_numeric(implode(array_keys($array)));
}
}
// php extension
if (! function_exists('_')) {
function &_($str)
{
return $str;
}
}
////////////////////////////////////////////
function plugin_includex_common_init()
{
global $plugin_includex;
if (class_exists('PluginIncludexUnitTest')) {
$plugin_includex = new PluginIncludexUnitTest();
} elseif (class_exists('PluginIncludexUser')) {
$plugin_includex = new PluginIncludexUser();
} else {
$plugin_includex = new PluginIncludex();
}
}
function plugin_includex_convert()
{
global $plugin_includex;
plugin_includex_common_init();
$args = func_get_args();
return call_user_func_array(array(&$plugin_includex, 'convert'), $args);
}
if (! defined('INIT_DIR')) // if not Plus!
if (file_exists(DATA_HOME . 'init/includex.ini.php'))
include_once(DATA_HOME . 'init/includex.ini.php');
?>