DokuWiki

It's better when it's simple

User Tools

Site Tools


plugin:condition

condition Plugin

Compatible with DokuWiki

No compatibility info given!

plugin Render a block if a condition is fulfilled, user custom tests can be easily added

Last updated on
2014-01-22
Provides
Syntax
Repository
Source

This extension has not been updated in over 2 years. It may no longer be maintained or supported and may have compatibility issues.

Security warning (please read plugin security guidelines): Hiding parts of a DokuWiki page is not supported by the core. Most attempts to introduce ACL control for parts of a page will leak information through RSS feed, search or other core functionality.

Similar to ifauth, nodisp

Tagged with condition, if, syntax

Description

The Syntax Plugin allows to parse a content only if a specific condition (or multiple condition) is fulfilled. Some basic tests are provided and users can add new tests just by implementing a file in the template directory.

Syntax

<if [condition_list]>doku code</if>
or
<if [condition_list]>doku code<else>doku code</if>

[condition_list] is a set of [condition] records separated by logical operators (&&, and, ||, or, ^, xor for now), use of parenthesis is allowed, negation is achieved by using heading ! (ex !foo=bar or !(a=b || c<d) )

[condition] is formed from a [key], followed by an [operator] (optional) and then a [value] (optional)

[key] is in the list (defined in base_tester.php as of 2009/09/15):

  1. user: refers to the user “login” (like in $_SERVER['REMOTE_USER'])
  2. group: refers to the user group-set
  3. nsread: refers to the ability of the user to read a namespace
  4. nsedit: refers to the ability of the user to edit a namespace
  5. time: refers to the current server time
  6. IP: refers to the client's IP address

[operator] signification is [key] dependent, for example:

  • = or ==: equality, membership, read/edit ability on…
  • !=: non-equality, non-member, no read/edit ability on…

(browse base_tester.php for test_* methods for more information)

[value] can be a string (whitespace, ) and > free) or a " delimited string (whitespaces, ) and > are then allowed)

DokuWiki code can contain DokuWiki syntax but be careful with high-level syntax (containers, table …) as there may be priority issue, bug reports are welcome…

It is now sure that using the plugin inside a table cell won't work, working on it now, any help is welcome…

Example:

<if user>You are **connected**<else>You must identify yourself to do something</if>
<if group!=manager>You are not a manager</if>
<if time=31d12m>It's New Year's Eve</if>
<if IP~=192.168>You are in a local network</if>
<if !(group=admin || group=manager)>not VIP</if> (equivalent of <if !group=admin && !group=manager>not VIP</if>)
...

Download and Installation

Download and install the plugin using the Plugin Manager using the following URL. Refer to Plugins on how to install plugins manually.

Sources

Attention: there is also a third-party source at GitHub

syntax.php

syntax.php
<?php
/**
 * Condition Plugin: render a block if a condition if fulfilled
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Etienne Meleard <etienne.meleard@free.fr>
 * 
 * 2009/06/08 : Creation
 * 2009/06/09 : Drop of the multi-value tests / creation of tester class system
 * 2009/06/10 : Added tester class override to allow user to define custom tests
 * 2009/09/15 : Added better renderer handling with respect of conditions for TOC generation
 */
 
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
 
/**
 * All DokuWiki plugins to extend the parser/rendering mechanism
 * need to inherit from this class
 */
class syntax_plugin_condition extends DokuWiki_Syntax_Plugin {
	// To be used by _processblocks to mix the test results together
	var $allowedoperators = array('\&\&', '\|\|', '\^', 'and', 'or', 'xor'); // plus '!' specific operator
 
	// Allowed test operators, their behavior is defined in the tester class, they are just defined here for recognition during parsing
	var $allowedtests = array();
 
	// Allowed test keys
	var $allowedkeys = array();
 
	// To store the tester object
	private $tester = null;
 
	// return some info
	function getInfo() {
		return confToHash(dirname(__FILE__).'/INFO');
	}
 
	/*function accepts($mode) { return true; }
	function getAllowedTypes() {
		return array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs'); // quick hack
		}*/
 
	function getType() { return 'container';}
	function getPType() { return 'normal';}
	function getSort() { return 5; } // condition is top priority
 
	// Connect pattern to lexer
	function connectTo($mode){
		$this->Lexer->addEntryPattern('<if(?=.*?</if>)', $mode, 'plugin_condition');
	}
 
	function postConnect() {
		$this->Lexer->addExitPattern('</if>', 'plugin_condition');
	}
 
	// Handle the match
	function handle($match, $state, $pos, Doku_Handler $handler) {
		if($state != DOKU_LEXER_UNMATCHED) return false;
 
		// Get allowed test operators
		$this->_loadtester();
		if(!$this->tester) return array(array(), '');
		$this->allowedtests = $this->tester->getops();
		$this->allowedkeys = $this->tester->getkeys();
 
		$blocks = array();
		$content = '';
		$this->_parse($match, $blocks, $content);
 
		return array($blocks, $content);
	}
 
	// extracts condition / content
	function _parse(&$match, &$b, &$ctn) {
		$match = preg_replace('`^\s+`', '', $match); // trim heading whitespaces
		$b = $this->_fetch_block($match, 0);
		if($match != '') $ctn = preg_replace('`\n+$`', '', preg_replace('`^\n+`', '', preg_replace('`^>`', '', $match)));
		return true;
	}
 
	// fetch a condition block from buffer
	function _fetch_block(&$match, $lvl=0) {
		$match = preg_replace('`^\s+`', '', $match); // trim heading whitespaces
		$instrs = array();
		$continue = true;
		while(($match{0} != '>') && ($match != '') && (($lvl == 0) || ($match{0} != ')')) && $continue) {
			$i = array('type' => null, 'key' => '', 'test' => '', 'value' => '', 'next' => '');
			if($this->_fetch_op($match, true)) { // ! heading equals block descending for first token
				$i['type'] = 'nblock';
				$match = substr($match, 1); // remove heading !
				$i['value'] = $this->_fetch_block($match, $lvl+1);
			}else if($this->_is_block($match)) {
				$i['type'] = 'block';
				$match = substr($match, 1); // remove heading (
				$i['value'] = $this->_fetch_block($match, $lvl+1);
			}else if($this->_is_key($match, $key)) {
				$i['type'] = 'test';
				$i['key'] = $key;
				$match = substr($match, strlen($key)); // remove heading key
				if($this->_is_test($match, $test)) {
					$i['test'] = $test;
					$match = substr($match, strlen($test)); // remove heading test
					if(($v = $this->_fetch_value($match)) !== null) $i['value'] = $v;
				}
			}else $match = preg_replace('`^[^>\s\(]+`', '', $match); // here dummy stuff remains
			if($i['type']) {
				if(($op = $this->_fetch_op($match, false)) !== null) {
					$match = substr($match, strlen($op)); // remove heading op
					$i['next'] = $op;
				}else $continue = false;
				$instrs[] = $i;
			}
		}
		return $instrs;
	}
 
	// test if buffer starts with new sub-block
	function _is_block(&$match) {
		$match = preg_replace('`^\s+`', '', $match); // trim heading whitespaces
		return preg_match('`^\(`', $match);
	}
 
	// test if buffer starts with a key ref
	function _is_key(&$match, &$key) {
		$match = preg_replace('`^\s+`', '', $match); // trim heading whitespaces
		if(preg_match('`^([a-zA-Z0-9_-]+)`', $match, $r)) {
			if(preg_match('`^'.$this->_preg_build_alternative($this->allowedkeys).'$`', $r[1])) {
				$key = $r[1];
				return true;
			}
		}
		return false;
	}
 
	// build a pcre alternative escaped test from array
	function _preg_build_alternative($choices) {
		//$choices = array_map(create_function('$e', 'return preg_replace(\'`([^a-zA-Z0-9])`\', \'\\\\\\\\$1\', $e);'), $choices);
		return '('.implode('|', $choices).')';
	}
 
	// tells if buffer starts with a test operator
	function _is_test(&$match, &$test) {
		$match = preg_replace('`^\s+`', '', $match); // trim heading whitespaces
		if(preg_match('`^'.$this->_preg_build_alternative($this->allowedtests).'`', $match, $r)) { $test = $r[1]; return true; }
		return false;
	}
 
	// fetch value from buffer, handles value quoting
	function _fetch_value(&$match) {
		$match = preg_replace('`^\s+`', '', $match); // trim heading whitespaces
		if($match{0} == '"') {
			$match = substr($match, 1);
			$value = substr($match, 0, strpos($match, '"'));
			$match = substr($match, strlen($value) + 1);
		}else{
			$psp = strpos($match, ')');
			$wsp = strpos($match, ' ');
			$esp = strpos($match, '>');
			$sp = 0;
			$bug = false;
			if(($wsp === false) && ($esp === false) && ($psp === false)) {
				return null; // BUG
			}else if(($wsp === false) && ($esp === false)) {
				$sp = $psp;
			}else if(($wsp === false) && ($psp === false)) {
				$sp = $esp;
			}else if(($psp === false) && ($esp === false)) {
				$sp = $wsp;
			}else if($wsp === false) {
				$sp = min($esp, $psp);
			}else if($esp === false) {
				$sp = min($wsp, $psp);
			}else if($psp === false) {
				$sp = min($esp, $wsp);
			}else $sp = min($wsp, $esp, $psp);
 
			$value = substr($match, 0, $sp);
			$match = substr($match, strlen($value));
		}
		return $value;
	}
 
	// fetch a logic operator from buffer
	function _fetch_op(&$match, $head=false) {
		$match = preg_replace('`^\s+`', '', $match); // trim heading whitespaces
		$ops = $this->allowedoperators;
		if($head) $ops = array('!');
		if(preg_match('`^'.$this->_preg_build_alternative($ops).'`', $match, $r)) return $r[1];
		return null;
	}
 
	/**
	 * Create output
	 */
	function render($mode, Doku_Renderer $renderer, $data) {
		global $INFO;
		if(count($data) != 2) return false;
		if($mode == 'xhtml') {
			global $ID;
			// prevent caching to ensure good user data detection for tests
			$renderer->info['cache'] = false;
 
			$blocks = $data[0];
			$content = $data[1];
 
			// parsing content for a <else> statement
			$else = '';
			if(strpos($content, '<else>') !== false) {
				$i = explode('<else>', $content);
				$content = $i[0];
				$else = implode('', array_slice($i, 1));
			}
 
			// Process condition blocks
			$bug = false;
			$this->_loadtester();
			$ok = $this->_processblocks($blocks, $bug);
 
			// Render content if all went well
			$toc = $renderer->toc;
			if(!$bug) {
			  $instr = p_get_instructions($ok ? $content : $else);
			  foreach($instr as $instruction) {
			    call_user_func_array(array(&$renderer, $instruction[0]), $instruction[1]);
			  }
			}
			$renderer->toc = array_merge($toc, $renderer->toc);
 
			return true;
		}
		if($mode == 'metadata') {
			global $ID;
			// prevent caching to ensure good user data detection for tests
			$renderer->info['cache'] = false;
 
			$blocks = $data[0];
			$content = $data[1];
 
			// parsing content for a <else> statement
			$else = '';
			if(strpos($content, '<else>') !== false) {
				$i = explode('<else>', $content);
				$content = $i[0];
				$else = implode('', array_slice($i, 1));
			}
 
			// Process condition blocks
			$bug = false;
			$this->_loadtester();
			$ok = $this->_processblocks($blocks, $bug);
			// Render content if all went well
			$metatoc = $renderer->meta['description']['tableofcontents'];
			if(!$bug) {
			  $instr = p_get_instructions($ok ? $content : $else);
			  foreach($instr as $instruction) {
			    call_user_func_array(array(&$renderer, $instruction[0]), $instruction[1]);
			  }
			}
			$renderer->meta['description']['tableofcontents'] = array_merge($metatoc, $renderer->meta['description']['tableofcontents']); 
 
			return true;
		}
		return false;
	}
 
	// Strips the heading <p> and trailing </p> added by p_render xhtml to acheive inline behavior
	function _stripp($data) {
		$data = preg_replace('`^\s*<p[^>]*>\s*`', '', $data);
		$data = preg_replace('`\s*</p[^>]*>\s*$`', '', $data);
		return $data;
	}
 
	// evaluates the logical result from a set of blocks
	function _processblocks($b, &$bug) {
		for($i=0; $i<count($b); $i++) {
			if(($b[$i]['type'] == 'block') || ($b[$i]['type'] == 'nblock')) {
				$b[$i]['r'] = $this->_processblocks($b[$i]['value'], $bug);
				if($b[$i]['type'] == 'nblock') $b[$i]['r'] = !$b[$i]['r'];
			}else{
				$b[$i]['r'] = $this->_evaluate($b[$i], $bug);
			}
		}
		if(!count($b)) $bug = true; // no condition in block
		if($bug) return false;
 
		// assemble conditions
		/* CUSTOMISATION :
		 * You can add custom mixing operators here, don't forget to add them to
		 * the "allowedoperators" list at the top of this file
		 */
		$r = $b[0]['r'];
		for($i=1; $i<count($b); $i++) {
			if($b[$i-1]['next'] == '') {
				$bug = true;
				return false;
			}
			switch($b[$i-1]['next']) {
				case '&&' :
				case 'and' :
					$r &= $b[$i]['r'];
					break;
				case '||' :
				case 'or' :
					$r |= $b[$i]['r'];
					break;
				case '^' :
				case 'xor' :
					$r ^= $b[$i]['r'];
					break;
			}
		}
		return $r;
	}
 
	// evaluates a single test, loads custom tests if class exists, default test set otherwise
	function _evaluate($b, &$bug) {
		if(!$this->tester) {
			$bug = true;
			return false;
		}
		return $this->tester->run($b, $bug);
	}
 
	// tries to load user defined tester, then base tester if previous failed
	function _loadtester() {
		global $conf;
		$this->tester = null;
		include_once(DOKU_PLUGIN.'condition/base_tester.php');
		if(@file_exists(DOKU_INC.'lib/tpl/'.$conf['template'].'/condition_plugin_custom_tester.php')) {
			include_once(DOKU_INC.'lib/tpl/'.$conf['template'].'/condition_plugin_custom_tester.php');
			if(class_exists('condition_plugin_custom_tester')) {
				$this->tester = new condition_plugin_custom_tester();
			}
		}
		if(!$this->tester) {
			if(class_exists('condition_plugin_base_tester')) {
				$this->tester = new condition_plugin_base_tester();
			}
		}
	}
} //class
?>

base_tester.php

base_tester.php
<?php
	/*
	 * The condition_plugin_custom_tester class defined in <tpl>/condition_plugin_custom_tester.php
	 * MUST implements this class
	 * 
	 * To add a custom test in condition_plugin_custom_tester you just have to add a method like :
	 * 
	 *	function test_dummy($b, &$bug, $lop=false) { if($lop) return array(); return true; }
	 *		this test will react to <if dummy></if> of <if dummy=3></if>
	 * 
	 * or
	 * 
	 *	function test_IP($b, &$bug, $lop=false) {
	 *		if($lop) return array('\=\=?', 'eq', '\!\=', 'neq?', '\~\='); // pcre regexp list of allowed test operators
	 *		$ip = clientIP(true);
	 *		if(!$b['test'] || ($b['test'] == '') || !$b['value'] || ($b['value'] == '') || ($ip == '0.0.0.0')) {
	 *			$bug = true;
	 *			return false;
	 *		}
	 *		switch($b['test']) {
	 *			case '=' :
	 *			case 'eq' :
	 *			case '==' :
	 *				return ($ip == $b['value']); break;
	 *			case '!=' :
	 *			case 'ne' :
	 *			case 'neq' :
	 *				return ($ip != $b['value']); break;
	 *			case '~=' : // such new test operators must be added in syntax.php
	 *				return (strpos($ip, $b['value']) !== false); break;
	 *			default: // non allowed operators for the test must lead to a bug flag raise
	 *				$bug = true;
	 *				return false;
	 *		}
	 *	}
	 *		this test will react to <if IP=127.0.0.1></if>
	 */
 
	class condition_plugin_base_tester {
		function __construct() {}
 
		// Wrapper for all tests
		function run($b, &$bug) {
			if(method_exists($this, 'test_'.$b['key'])) {
				return call_user_func(array($this, 'test_'.$b['key']), $b, $bug);
			}
			$bug = true;
			return false;
		}
 
		// Get allowed keys
		function getkeys() {
			$keys = array();
			foreach(get_class_methods($this) as $m) {
				if(preg_match('`^test_(.+)$`', $m, $r)) $keys[] = $r[1];
			}
			return $keys;
		}
 
		// Get test operators
		function getops() {
			$ops = array();
			foreach($this->getkeys() as $m) $ops = array_merge($ops, call_user_func(array($this, 'test_'.$m), null, $dummy, true));
			return array_unique($ops);
		}
 
		// Tests follows
		// -------------
 
		// user based tests
		function test_user($b, &$bug, $lop=false) {
			if($lop) return array('\=\=?', 'eq', '\!\=', 'neq?');
			$rh = isset($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'] : '';
			if(!$b['test'] || ($b['test'] == '')) return ($rh && ($rh != ''));
			switch($b['test']) {
				case '=' :
				case 'eq' :
				case '==' :
					return $rh == $b['value']; break;
				case '!=' :
				case 'ne' :
				case 'neq' :
					return $rh != $b['value']; break;
				default:
					$bug = true;
					return false;
			}
		}
 
		function test_group($b, &$bug, $lop=false) {
			if($lop) return array('\=\=?', 'eq', '\!\=', 'neq?');
			global $INFO;
			$grps = isset($INFO['userinfo']) ? $INFO['userinfo']['grps'] : array();
			if(!$b['test'] || ($b['test'] == '')) return (count($grps) != 0);
			switch($b['test']) {
				case '=' :
				case 'eq' :
				case '==' :
					if(!$b['value'] || ($b['value'] == '')) return (count($grps) == 0);
					return in_array($b['value'], $grps); break;
				case '!=' :
				case 'ne' :
				case 'neq' :
					if(!$b['value'] || ($b['value'] == '')) return (count($grps) != 0);
					return !in_array($b['value'], $grps); break;
				default:
					$bug = true;
					return false;
			}
		}
 
		// namespace based tests
		function test_nsread($b, &$bug, $lop=false) {
			if($lop) return array('\=\=?', 'eq', '\!\=', 'neq?');
			if(!$b['test'] || ($b['test'] == '')) {
				$bug = true;
				return false;
			}
			if(!$b['value'] || ($b['value'] == '')) $b['value'] = '.';
			switch($b['test']) {
				case '=' :
				case 'eq' :
				case '==' :
					return (auth_quickaclcheck($b['value']) >= AUTH_READ); break;
				case '!=' :
				case 'ne' :
				case 'neq' :
					return (auth_quickaclcheck($b['value']) < AUTH_READ); break;
				default:
					$bug = true;
					return false;
			}
		}
 
		function test_nsedit($b, &$bug, $lop=false) {
			if($lop) return array('\=\=?', 'eq', '\!\=', 'neq?');
			if(!$b['test'] || ($b['test'] == '')) {
				$bug = true;
				return false;
			}
			if(!$b['value'] || ($b['value'] == '')) $b['value'] = '.';
			switch($b['test']) {
				case '=' :
				case 'eq' :
				case '==' :
					return (auth_quickaclcheck($b['value']) >= AUTH_EDIT); break;
				case '!=' :
				case 'ne' :
				case 'neq' :
					return (auth_quickaclcheck($b['value']) < AUTH_EDIT); break;
				default:
					$bug = true;
					return false;
			}
		}
 
		// time based tests
		function test_time($b, &$bug, $lop=false) {
			if($lop) return array('\=\=?', 'eq', '\!\=', 'neq?', '\<\=?', 'lt', '\>\=?', 'gt');
			global $INFO;
			if(!$b['test'] || ($b['test'] == '') || !$b['value'] || ($b['value'] == '')) {
				$bug = true;
				return false;
			}
			switch($b['test']) {
				case '=' :
				case 'eq' :
				case '==' :
					return $this->_bt_cmptimeandstr($b['value']); break;
				case '!=' :
				case 'ne' :
				case 'neq' :
					return !$this->_bt_cmptimeandstr($b['value']); break;
				case '<' :
				case 'lt' :
					$t = time();
					return ($t < $this->_bt_strtotime($b['value'], $t)); break;
				case '>' :
				case 'gt' :
					$t = time();
					return ($t > $this->_bt_strtotime($b['value'], $t)); break;
				case '<=' :
					$t = time();
					return ($t <= $this->_bt_strtotime($b['value'], $t)); break;
				case '>=' :
					$t = time();
					return ($t >= $this->_bt_strtotime($b['value'], $t)); break;
				default:
					$bug = true;
					return false;
			}
		}
		function _bt_strtotime($value, $default) {
			if(preg_match('`^([0-9]{4})(-|/)([0-9]{2})(-|/)([0-9]{2})(\s+([0-9]{2}):([0-9]{2})(:([0-9]{2}))?)?$`', $value, $reg)) $value = mktime($reg[7], $reg[8], $reg[10], $reg[3], $reg[5], $reg[1]); // YYYY(-|/)MM(-|/)DD (HH:II(:SS)?)?
			if(preg_match('`^(([0-9]{2}):([0-9]{2})(:([0-9]{2}))?\s+)?([0-9]{4})(-|/)([0-9]{2})(-|/)([0-9]{2})$`', $value, $reg)) $value = mktime($reg[2], $reg[3], $reg[5], $reg[8], $reg[10], $reg[6]); // (HH:II(:SS)?)? YYYY(-|/)MM(-|/)DD
			if(preg_match('`^([0-9]{2})(-|/)([0-9]{2})(-|/)([0-9]{4})(\s+([0-9]{2}):([0-9]{2})(:([0-9]{2}))?)?$`', $value, $reg)) $value = mktime($reg[7], $reg[8], $reg[10], $reg[3], $reg[1], $reg[5]); // DD(-|/)MM(-|/)YYYY (HH:II(:SS)?)?
			if(preg_match('`^(([0-9]{2}):([0-9]{2})(:([0-9]{2}))?\s+)?([0-9]{2})(-|/)([0-9]{2})(-|/)([0-9]{4})$`', $value, $reg)) $value = mktime($reg[2], $reg[3], $reg[5], $reg[8], $reg[6], $reg[10]); // (HH:II(:SS)?)? DD(-|/)MM(-|/)YYYY
			if(!is_numeric($value)) $value = $default;
			return $value;
		}
		function _bt_cmptimeandstr($str) {
			$matched = false;
			$t = time();
			$time = array('y' => date('Y', $t), 'm' => date('m', $t), 'd' => date('d', $t), 'h' => date('H', $t), 'i' => date('i', $t), 's' => date('s', $t));
			$d = array('y' => '', 'm' => '', 'd' => '', 'h' => '', 'i' => '', 's' => '');
 
			// full date y, m and d, time is optionnal
			if(preg_match('`^([0-9]{4})(-|/)([0-9]{2})(-|/)([0-9]{2})(\s+([0-9]{2}):([0-9]{2})(:([0-9]{2}))?)?$`', $str, $reg)) {
				$d = array('y' => $reg[1], 'm' => $reg[3], 'd' => $reg[5], 'h' => $reg[7], 'i' => $reg[8], 's' => $reg[10]); // YYYY(-|/)MM(-|/)DD (HH:II(:SS)?)?
				$matched = true;
			}
			if(preg_match('`^(([0-9]{2}):([0-9]{2})(:([0-9]{2}))?\s+)?([0-9]{4})(-|/)([0-9]{2})(-|/)([0-9]{2})$`', $str, $reg)) {
				$d = array('y' => $reg[6], 'm' => $reg[8], 'd' => $reg[10], 'h' => $reg[2], 'i' => $reg[3], 's' => $reg[5]); // (HH:II(:SS)?)? YYYY(-|/)MM(-|/)DD
				$matched = true;
			}
			if(preg_match('`^([0-9]{2})(-|/)([0-9]{2})(-|/)([0-9]{4})(\s+([0-9]{2}):([0-9]{2})(:([0-9]{2}))?)?$`', $str, $reg)) {
				$d = array('y' => $reg[5], 'm' => $reg[3], 'd' => $reg[1], 'h' => $reg[7], 'i' => $reg[8], 's' => $reg[10]); // DD(-|/)MM(-|/)YYYY (HH:II(:SS)?)?
				$matched = true;
			}
			if(preg_match('`^(([0-9]{2}):([0-9]{2})(:([0-9]{2}))?\s+)?([0-9]{2})(-|/)([0-9]{2})(-|/)([0-9]{4})$`', $str, $reg)) {
				$d = array('y' => $reg[10], 'm' => $reg[8], 'd' => $reg[6], 'h' => $reg[2], 'i' => $reg[3], 's' => $reg[5]); // (HH:II(:SS)?)? DD(-|/)MM(-|/)YYYY
				$matched = true;
			}
 
			// only month and year
			if(preg_match('`^([0-9]{2})(-|/)([0-9]{4})$`', $str, $reg)) {
				$d = array('y' => $reg[3], 'm' => $reg[1], 'd' => '', 'h' => '', 'i' => '', 's' => '');
				$matched = true;
			}
			if(preg_match('`^([0-9]{4})(-|/)([0-9]{2})$`', $str, $reg)) {
				$d = array('y' => $reg[1], 'm' => $reg[3], 'd' => '', 'h' => '', 'i' => '', 's' => '');
				$matched = true;
			}
 
			// only year
			if(preg_match('`^([0-9]{4})$`', $str, $reg)) {
				$d = array('y' => $reg[1], 'm' => '', 'd' => '', 'h' => '', 'i' => '', 's' => '');
				$matched = true;
			}
 
			// full time hours, minutes (opt) and seconds (opt)
			// 11 : 11h
			// 11:30 : 11h30min
			// 11:30:27 : 11h30min27sec
			if(preg_match('`^([0-9]{2})(:([0-9]{2})(:([0-9]{2}))?)?$`', $str, $reg)) {
				$d = array('y' => '', 'm' => '', 'd' => '', 'h' => $reg[7], 'i' => $reg[8], 's' => $reg[10]); // YYYY(-|/)MM(-|/)DD (HH:II(:SS)?)?
				$matched = true;
			}
 
			// custom datetime format : (XX(XX)?i\s?)+
			if(preg_match('`^[0-9]{2}([0-9]{2})?\s?[ymdhis](\s?[0-9]{2}([0-9]{2})?\s?[ymdhis])*$`', $str, $reg)) {
				while(preg_match('`^(([0-9]{2}([0-9]{2})?)\s?([ymdhis]))`', $str, $reg)) {
					$v = $reg[2];
					$i = $reg[4];
					$str = substr($str, strlen($reg[1]));
					if(($i != 'y') || (strlen($v) == 4)) $d[$i] = $v;
				}
				$matched = true;
			}
 
			if(!$matched) return false;
			$same = true;
			foreach($time as $k => $v) if(($d[$k] != '') && ($d[$k] != $v)) $same = false;
			return $same;
		}
 
		// test IP
		function test_IP($b, &$bug, $lop=false) {
			if($lop) return array('\=\=?', 'eq', '\!\=', 'neq?', '\~\=');
			$ip = clientIP(true);
			if(!$b['test'] || ($b['test'] == '') || !$b['value'] || ($b['value'] == '') || ($ip == '0.0.0.0')) {
				$bug = true;
				return false;
			}
			switch($b['test']) {
				case '=' :
				case 'eq' :
				case '==' :
					return ($ip == $b['value']); break;
				case '!=' :
				case 'ne' :
				case 'neq' :
					return ($ip != $b['value']); break;
				case '~=' :
					return (strpos($ip, $b['value']) !== false); break;
				default:
					$bug = true;
					return false;
			}
		}
	}
?>

How can I implement some other tests?

You must create the condition_plugin_custom_tester.php in your template directory and start with this base:

<?php
include_once(DOKU_PLUGIN.'condition/base_tester.php');
class condition_plugin_custom_tester extends condition_plugin_base_tester {
	// Here the tests methods ...
}
?>

To add the test with the key “foo” just add the test method:

function test_foo($b, &$bug, $lop=false) {
	if($lop) return array('\=\=?', '\!\=', '\:'); // list of accepted test operators, pcre style regexp
	// Here we accept test operators : =, ==, !=, :
	switch($b['test']) {
		case '=' :
		case '==' :
			return ($b['value'] == 'bar'); break;
		case '!=' :
			return ($b['value'] != 'bar'); break;
		case ':' :
			// ...
		default :
			$bug = true;
			return false;
	}
}

So that test can be used as:

<if foo=bar>foo = bar<else>foo != bar</if>
<if foo==bar>foo = bar<else>foo != bar</if>
<if !foo!=bar>foo = bar<else>foo != bar</if>
...

If your test don't take a parameter you can define it as follows:

function test_foo($b, &$bug, $lop=false) {
	if($lop) return array(); // no test operators
	global $ID;
	return preg_match('`^([^\:]+\:)?bar(\:[^\:]+)?$`', $ID);
}

This test is valid if “bar” is a parent namespace of the page:

<if foo>Page is under "bar" namespace</if>

TODO

  • Allow multiple user custom test files (plugin style)?
  • Ensure this works well with top-level containers
  • clean-up and simplify code (mainly reduce calls to preg_replace/match) because it is a bit slow…

While discussion isn't working

I installed Condition in our production wiki, version 2009-02-14 running on RedHat Enterprise Linux ES 4.0u6 with PHP 4.3.9, and the plugin doesn't seem to work in that environment. Basically, the wiki breaks down immediately after installation and no pages are rendered at all, even before using Condition in any page. Removing the plugin (manually from the file system) restored functionality.

I tested the plugin in a CentOS 5.5/PHP 5.1.6 environment before adding it to our production wiki and it worked without problems.

Is there a known problem with older DokuWiki and/or PHP versions?

Dunno, can you get apache's error log ? There could be some hints about the bug in it.

PHP Errors after saving a changed page with conditions

I get the following warnings on all pages with conditions:

Warning: array_merge() [function.array-merge]: Argument #1 is not an array in /hp/ab/ac/va/www/Wiki/lib/plugins/condition/syntax.php on line 261

Warning: array_merge() [function.array-merge]: Argument #2 is not an array in /hp/ab/ac/va/www/Wiki/lib/plugins/condition/syntax.php on line 261

Warning: Cannot modify header information - headers already sent by (output started at /hp/ab/ac/va/www/Wiki/lib/plugins/condition/syntax.php:261) in /hp/ab/ac/va/www/Wiki/inc/common.php on line 1543

The page is actually saved correctly. It obviously happens when the changed page is to be rendered. Is this a known issue?

2010-10-06 Rainer

This error exists in ~~NOTOC~~ pages.

For fixed: into syntax.php search 261 line and add condition before:

if (is_array( $renderer->meta['description']['tableofcontents']))

2010-11-22 Zefir

:!: I have the same error, but this code doesn´t solve the problem. If I use the condition-plugin some where in the page all headings get lost for the TOC up to the line where you use the condition plugin. The next heading is used as first heading. If no heading is left, filename is used.

2011-07-03 anonymous

:!: The plugin does not work with my installation. I traced the issue in github.

2014-09-23 11:01 cinlloccinlloc

plugin/condition.txt · Last modified: 2016-02-10 09:13 by Aleksandr