1<?php 2/** 3 * DokuWiki Plugin const (Action Component) 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author lisps 7 */ 8 9if (!defined('DOKU_INC')) 10 define('DOKU_INC', realpath(dirname(__FILE__) . '/../../') . '/'); 11if (!defined('DOKU_PLUGIN')) 12 define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 13 14require_once(DOKU_PLUGIN . 'action.php'); 15 16/** 17 * All DokuWiki plugins to extend the parser/rendering mechanism 18 * need to inherit from this class 19 */ 20class action_plugin_const extends DokuWiki_Action_Plugin { 21 /** 22 * return variable 23 */ 24 private $autoindexer = 0; 25 26 27 /** 28 * offsets-at-character-position. recorded as tuples page:{charpos,offset}, where charpos is the 29 * position in the MODIFIED data, not the position in the original data, and offset is the 30 * CUMULATIVE offset at that position, not the offset relative to the previous location. 31 */ 32 private $offsets = array(); 33 34 //hook before rendering starts 35 function register( Doku_Event_Handler $controller) { 36 $controller->register_hook('PARSER_WIKITEXT_PREPROCESS', 'BEFORE', $this, '_doreplace'); 37 $controller->register_hook('PARSER_HANDLER_DONE', 'BEFORE', $this, '_fixsecedit'); 38 $controller->register_hook('PARSER_CACHE_USE', 'BEFORE', $this, '_cache_control'); 39 } 40 41 42 function _replacecallback($hits) { 43 return ($this->autoindexer++); 44 } 45 46 //trigger 47 function _doreplace(&$event, $param) { 48 global $ID; 49 50 require_once(dirname(__FILE__) . "/class.evalmath.php"); 51 $math = new evalmath(); 52 53 $this->autoindexer = 0; 54 $invalidate = false; 55 56 $original = $event->data; 57 $wikified = $original; 58 59 //catch anonymous access 60 $username=isset($_SERVER['REMOTE_USER'])?$_SERVER['REMOTE_USER']:"anonymous"; 61 62 //get const definitions 63 $data = array(); 64 if (preg_match('/<const[^>]*>([^<]*)<\/const>/', $event->data, $data) > 0) { 65 //split entries 66 //var_dump($data);exit; 67 $data = array_pop($data); 68 $data = preg_split('/[\r\n]+/', $data, -1, PREG_SPLIT_NO_EMPTY); 69 70 //process wiki-data 71 $autoindex = 0; 72 foreach ($data as $entry) { 73 //normal const 74 $item = explode("=", trim($entry), 2); 75 if (count($item) === 2) { 76 //special string-replace 77 switch ($item[1]) { 78 case "%USER%": 79 $item[1] = $username; 80 $invalidate = true; 81 break; //pagename 82 case "%ID%": 83 $item[1] = noNS(cleanID(getID())); 84 break; //pagename 85 case "%NAMESPACE%": 86 $item[1] = getNS(cleanID(getID())); 87 break; //namespace 88 case "%RANDOM%": 89 $item[1] = strval(rand()); 90 $invalidate = true; 91 break; //random number 92 case "%YEAR%": 93 $item[1] = date("Y"); 94 break; //current year 95 case "%MONTH%": 96 $item[1] = date("m"); 97 break; //current month 98 case "%MONTHNAME%": 99 $item[1] = date("F"); 100 break; //current month 101 case "%WEEK%": 102 $item[1] = date("W"); 103 $invalidate = true; 104 break; //current week (iso) 105 case "%DAY%": 106 $item[1] = date("d"); 107 $invalidate = true; 108 break; //current day 109 case "%DAYNAME%": 110 $item[1] = date("l"); 111 $invalidate = true; 112 break; //current day 113 case "%HOUR%": 114 $item[1] = date("H"); 115 $invalidate = true; 116 break; //current hour 117 case "%MINUTE%": 118 $item[1] = date("i"); 119 $invalidate = true; 120 break; //current minute 121 case "%SECOND%": 122 $item[1] = date("s"); 123 $invalidate = true; 124 break; //current second 125 case "%UNIXEPOCH%": 126 $item[1] = date("U"); 127 $invalidate = true; 128 break; //current unix timestamp 129 case "%AUTOINDEX%": 130 $item[1] = "%%INDEX#" . (++$autoindex) . "%%"; //special automatic indexer 131 break; 132 case "%REVISION%": 133 $tmp_info = pageinfo(); 134 $item[1] = $tmp_info['lastmod']; 135 break; //current revision number (unix timestamp) 136 case "%LASTMOD%": 137 $tmp_info = pageinfo(); 138 $item[1] = strftime("%Y%m%d_%H%M%S", $tmp_info['lastmod']); 139 break; //last modification date 140 default: 141 $item[1] = trim($item[1]); 142 break; 143 } 144 145 //replace in wiki 146 $wikified = str_replace("%%" . trim($item[0]) . "%%", $item[1], $wikified); 147 $wikified = str_replace("§§" . trim($item[0]) . "§§", $item[1], $wikified); 148 149 //load evaluator 150 @$math->assign_and_evaluate($item[0]."=".$item[1]); 151 } else { 152 //evaluate expression 153 $item = explode(":", $entry); 154 if (count($item) === 2) { 155 $wikified = str_replace("%%" . trim($item[0]) . "%%", @$math->assign_and_evaluate($item[1]), $wikified); 156 $wikified = str_replace("§§" . trim($item[0]) . "§§", @$math->assign_and_evaluate($item[1]), $wikified); 157 } 158 } 159 } 160 161 //autoindex? 162 while ($autoindex > 0) { 163 $this->autoindexer = 1; 164 //replace all 165 $wikified = preg_replace_callback("|%%INDEX#" . $autoindex . "%%|", array( 166 $this, 167 "_replacecallback" 168 ), $wikified); 169 $autoindex--; 170 } 171 172 $event->data = $wikified; 173 174 $original = explode("\n", $original); 175 $wikified = explode("\n", $wikified); 176 177 178 $this->offsets[$ID] = array(); 179 // fill offset array to deal with section editing issues 180 for ($l = 0; $l < count($wikified); $l++) { 181 // record offsets at the start of this line 182 $this->offsets[$ID][] = array( 183 'pos' => $char_pos, 184 'offset' => $text_offset 185 ); 186 // calculate position / offset for next line 187 $char_pos += strlen($wikified[$l]) + 1; 188 $text_offset += strlen($wikified[$l]) - strlen($original[$l]); 189 //echo '(' . $char_pos . '/' . $text_offset . ')' . ' '; 190 } 191 } 192 193 194 //save invalidation info to metadata 195 p_set_metadata($ID, array( 196 'plugin_const' => array( 197 'invalidate' => $invalidate 198 ) 199 ), false, true); 200 } 201 202 /** 203 * force cache invalidation for certain constants 204 */ 205 function _cache_control(&$event, $param) { 206 global $conf; 207 208 $cache =& $event->data; 209 210 if ((isset($cache->page) === true) && ($cache->mode === "i")) { 211 //cache purge requested? 212 $const = p_get_metadata($cache->page, 'plugin_const'); 213 214 //force initial purge 215 if (!isset($const['invalidate'])) { 216 $const['invalidate'] = true; 217 } 218 219 $cache->depends["purge"] = $const["invalidate"]; 220 } 221 } 222 223 /** 224 * modifying the raw data has as side effect that the sectioning is based on the 225 * modified data, not the original. This means that after processing, we need to 226 * adjust the section start/end markers so that they point to start/end positions 227 * in the original data, not the modified data. 228 */ 229 function _fixsecedit(&$event, $param) { 230 $calls =& $event->data->calls; 231 $count = count($calls); 232 233 // iterate through the instruction list and set the file offset values 234 // back to the values they would be if no const syntax has been added by this plugin 235 for ($i = 0; $i < $count; $i++) { 236 if (in_array($calls[$i][0],array( 237 'section_open', 238 'section_close', 239 'header', 240 'p_open', 241 //'table_close', 242 //'table_open', 243 ))) { 244 $calls[$i][2] = $this->_convert($calls[$i][2]); 245 } 246 if (in_array($calls[$i][0],array('header','table_open'))) { 247 $calls[$i][1][2] = $this->_convert($calls[$i][1][2]); 248 } 249 if(in_array($calls[$i][0],array('table_close'))) { 250 $calls[$i][1][0] = $this->_convert($calls[$i][1][0]); 251 } 252 // be aware of headernofloat plugin 253 if ($calls[$i][0] === 'plugin' && $calls[$i][1][0] === 'headernofloat') { 254 $calls[$i][1][1]['pos'] = $this->_convert($calls[$i][1][1]['pos']); 255 $calls[$i][2] = $this->_convert($calls[$i][2]); 256 } 257 //if($calls[$i][0] == 'table_close') 258 } 259 } 260 261 /** 262 * Convert modified raw wiki offset value pos back to the unmodified value 263 */ 264 function _convert($pos) { 265 global $ID; 266 if(!array_key_exists($ID,$this->offsets)) return $pos; 267 // find the offset that applies to this character position 268 $offset = 0; 269 foreach ($this->offsets[$ID] as $tuple) { 270 if ($pos >= $tuple['pos']) { 271 $offset = $tuple['offset']; 272 } else { 273 break; 274 } 275 } 276 277 // return corrected position 278 return $pos - $offset; 279 } 280} 281