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 = '
';
		$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');
?>