1<?php 2/** 3 * 4 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 5 * @author Myron Turner <turnermm02@shaw.ca> 6 */ 7 8// must be run within Dokuwiki 9if(!defined('DOKU_INC')) die(); 10 11if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 12require_once(DOKU_PLUGIN.'syntax.php'); 13require_once(DOKU_INC . 'inc/io.php'); 14 15/** 16 * All DokuWiki plugins to extend the parser/rendering mechanism 17 * need to inherit from this class 18 */ 19class syntax_plugin_fckg_dwplugin extends DokuWiki_Syntax_Plugin { 20 var $plugin_ref; 21 var $escaped_pattern; 22 var $missing_middle = array(); 23 var $plugin_name; 24 var $our_keys = array(); 25 26 function syntax_plugin_fckg_dwplugin() { 27 global $EVENT_HANDLER; 28 $EVENT_HANDLER->register_hook('PARSER_CACHE_USE', 'AFTER', $this, 'cache_bypass_after'); 29 } 30 31 32 33 function getType(){ return 'formatting'; } 34 function getAllowedTypes() { return array('formatting','substition'); } 35 function getSort(){ return 80; } 36 function connectTo($mode) { 37 $this->Lexer->addSpecialPattern('<plugin.*?</plugin>',$mode,'plugin_fckg_dwplugin'); 38 } 39 40 function handle($match, $state, $pos, Doku_Handler $handler){ 41 42 43 $retv = $this->is_stet($match); 44 if($retv) { 45 return array($state,"$retv "); 46 } 47 $match = preg_replace('/\\\\\\\\/',"", $match); 48 if(isset($_REQUEST['do']) && $_REQUEST['do'] == 'edit') { 49 50 list($title, $pattern) = explode('>',substr($match,7,-9),2); 51 $pattern=trim($pattern); 52 53 // escape '<' and '>' using ~ for escape character 54 $pattern = preg_replace('/(?<!~)(<+)(?!~)/', '~' . '\\1' . '~',$pattern); 55 $pattern = preg_replace('/(?<!~)(>+)(?!~)/','~' . '\\1' . '~',$pattern); 56 $match = "<plugin $title>$pattern</plugin>"; 57 58 return array($state,$match); 59 } 60 61 62 global $DOKU_PLUGINS; 63 global $INFO; 64 65 $this->syntax = $DOKU_PLUGINS['syntax']['info']->Lexer; 66 67 $this->escaped_pattern = false; 68 if(preg_match('/~(<+)~/',$match)) { 69 $this->escaped_pattern = true; 70 } 71 $match = preg_replace('/[~]+(<+)[~]+/','\\1',$match); 72 $match = preg_replace('/[~]+(>+)[~]+/','\\1',$match); 73 74 $mode = 'xhtml'; 75 $file = DOKU_INC."inc/parser/$mode.php"; 76 @require_once($file); 77 78 list($title, $pattern) = explode('>',substr($match,7,-9),2); 79 if(!$title) return array($state,""); 80 81 82 list($t,$plugin) = explode('=',$title); 83 $plugin=trim($plugin); 84 $pattern=trim($pattern); 85 86 $match = preg_replace('/<\/plugin>/',"", $match); 87 $match = preg_replace('/^\s*<plugin\s+title=.*?>/',"", $match); 88 89 $plugin = $this->getPluginName($match, $plugin); 90 if(!$plugin) return array($state,""); 91 92 $this->meta_io(false,array('dwplugin'=>time())); 93 94 $id = $$INFO['id']; 95 96 97 $match = $pattern; 98 99 if(isset($plugin)) { 100 $plugin_name = $plugin; 101 $this->plugin_ref = $this->setup_plugin($plugin,$pattern); 102 103 if($this->plugin_ref) { 104 list($entry_patterns, $middle_patterns, $_exit) = $this->getPatterns($plugin); 105 if(!$entry_patterns) { 106 return array($state,""); 107 } 108 $entry_match = $this->getEntryMatch($match, $entry_patterns); 109 if(!$entry_match) { 110 $match = preg_replace('/<\s*/','<', $match); 111 $entry_match = $this->getEntryMatch($match, $entry_patterns); 112 } 113 $middle = $this->getMiddleMatches($match,$entry_match, $middle_patterns, $_exit); 114 if($_exit) { 115 preg_match("/($_exit)/", $match, $exit_matches); 116 $_exit = $exit_matches[1]; 117 } 118 119 $regex = '/' . preg_quote($entry_match, '/') . '(.*?)' . preg_quote($_exit, '/') . '/'; 120 121 if(preg_match( $regex, $match, $matches)) { 122 $plugin_name = $this->get_key($matches[1], $plugin); 123 124 // $str = substr($matches[1],0,25); 125 // $plugin_name = $plugin . str_replace(' ', '', $str); 126 127 $this->missing_middle[$plugin_name] = $matches[1]; 128 129 } 130 131 return $this->write_plugin($entry_match, $middle, $_exit, $plugin_name); 132 } 133 } 134 135 return array($state,""); 136 } 137 138 function is_stet($match) { 139 list($title, $pattern) = explode('>',substr($match,7,-9),2); 140 $pattern=trim($pattern); 141 142 list($name,$value)=explode('=', $title); 143 global $ACT; 144 145 $value=trim($value); 146 $value=trim($value,'"\''); 147 if($value == 'stet') { 148 $pattern = preg_replace('/<\s+/', "<", $pattern); 149 $pattern = preg_replace('/\s+>/', ">", $pattern); 150 $pattern = str_replace('%%',"",$pattern); 151 if ($ACT == 'edit'){ 152 $pattern = str_replace('<',"< ",$pattern); 153 $pattern = str_replace('>'," >",$pattern); 154 } 155 $pattern = htmlspecialchars($pattern); 156 $pattern = str_replace("\n","<br />",$pattern); 157 $match = "<plugin $title>$pattern</plugin>"; 158 159 return $match; 160 } 161 162 return false; 163 } 164 165 function write_plugin($entry_match, $middle, $_exit, $plugin_name) { 166 167 168 $save_state = array(); 169 $save_match = array(); 170 171 $enter_state = $_exit ? DOKU_LEXER_ENTER : DOKU_LEXER_SPECIAL; 172 list($state, $match) = $this->plugin_ref->handle($entry_match, $enter_state, $pos, $handler); 173 $save_state[] = $state; 174 $save_match[] = $match; 175 176 $data = $middle[0]; 177 $states = $middle[1]; 178 for($i=0; $i < count($data); $i++) { 179 list($state, $match) = $this->plugin_ref->handle($data[$i], $states[$i], $pos, $handler); 180 $save_state[] = $state; 181 $save_match[] = $match; 182 } 183 184 185 if($_exit) { 186 list($state, $match) = $this->plugin_ref->handle($_exit, DOKU_LEXER_EXIT, $pos, $handler); 187 $save_state[] = $state; 188 $save_match[] = $match; 189 } 190 191 $Renderer = new Doku_Renderer_xhtml(); 192 for($i=0; $i < count($save_state); $i++) { 193 $this->plugin_ref->render('xhtml', $Renderer, array($save_state[$i], $save_match[$i])); 194 } 195 196 197 198 return array($state, $Renderer->doc . ":::$plugin_name"); 199 200 } 201 202 203 204 function & setup_plugin($plugin, $pattern) { 205 global $DOKU_PLUGINS; 206 207 $plugin_name = ltrim($plugin, '_'); 208 $plugin_name = substr($plugin_name,7); // remove 'plugin_' 209 210 $p_ref = & plugin_load('syntax', $plugin_name); 211 212 if(!$p_ref) 213 { 214 $p_ref = &plugin_load('syntax', $plugin); 215 216 if(!$p_ref) { // if above fails create class name and try to instantiate it 217 $func = 'syntax_plugin_'.$plugin_name; 218 if(class_exists($func,false)) { 219 $p_ref = new $func(); 220 } 221 } 222 } 223 224 225 return $p_ref; 226 } 227 228 229 /** 230 * Create output 231 */ 232 function render($mode, Doku_Renderer $renderer, $data) { 233 234 235 if($mode == 'xhtml'){ 236 global $INFO; 237 list($state, $match_open) = $data; 238 list($match,$plugin_name) = explode(':::',$match_open); 239 240 $match = str_replace ('&lt;p&gt;', '<p>', $match); 241 $match = str_replace ('<p>', '<p>', $match); 242 $match = str_replace ('&lt;br&gt;', '<br>', $match); 243 $match = str_replace ('<br>', '<br>', $match); 244 $m_middle = $this->missing_middle[$plugin_name] ? $this->missing_middle[$plugin_name] : false; 245 if($m_middle && preg_match('/(.*?)>[\n\s]+<(.*)/', $match, $matches)) { 246 $match = $matches[1] . '>' . $m_middle . '<' . $matches[2]; 247 } 248 elseif($m_middle && preg_match('/(.*?)><(.*)/', $match, $matches)) { 249 $match = $matches[1] . '>' . $m_middle . '<' . $matches[2]; 250 } 251 $renderer->doc .= $match; 252 253 254 return true; 255 } 256 257 258 return false; 259 } 260 261 262 263 function regex_esc($str) { 264 return preg_replace('/([\/])/', '\\\\' . "$1", $str); 265 } 266 267 function getEntryMatch($text, $regexes) { 268 269 foreach ($regexes as $regex) { 270 $regex = "/($regex)/"; 271 if(preg_match( $regex, $text, $matches) ) { 272 return $matches[0]; 273 } 274 } 275 276 return null; 277 } 278 279 function getPatterns($plugin) { 280 281 global $syntax; 282 283 $syntax = $this->syntax; 284 $regexes = $this->syntax->_regexes; 285 $base_patterns = $syntax->_regexes['base']->_patterns; 286 $base_labels= $syntax->_regexes['base']->_labels; 287 288 289 $base_keys = array_keys($base_labels,$plugin, true); 290 if(!$base_keys) { 291 return array(null,null,null); 292 } 293 // save base patterns 294 for($i=0; $i<count($base_keys); $i++) { 295 $patterns[] = $base_patterns[$base_keys[$i]]; 296 } 297 298 // check to see if there is a separate entry for this plugin in the regexes array 299 if(!isset($syntax->_regexes[$plugin])) { 300 return array($patterns,array(), ""); 301 } 302 303 // load the plugin's patterns and labels 304 $plugin_patterns = $syntax->_regexes[$plugin]->_patterns; 305 $plugin_labels= $syntax->_regexes[$plugin]->_labels; 306 307 $plugin_keys = array_keys($plugin_labels,$plugin, true); 308 309 for($i=0; $i<count($plugin_keys); $i++) { 310 $patterns[] = '(' . $this->regex_esc($plugin_patterns[$plugin_keys[$i]]) . ')'; 311 } 312 313 $patterns = array_unique($patterns); 314 315 $plugin_internal_keys = array_keys($plugin_labels,TRUE, true); 316 $plugin_exitkey = array_search('__exit', $plugin_labels, true); 317 318 $internals = array(); 319 for($i=0; $i<count($plugin_internal_keys); $i++) { 320 $internals[] = '(' . $this->regex_esc($plugin_patterns[$plugin_internal_keys[$i]]) . ')'; 321 } 322 323 if($plugin_exitkey) { 324 $_exit_pattern = $plugin_patterns[$plugin_exitkey]; 325 $_exit_pattern = $this->regex_esc($_exit_pattern); 326 } 327 else { 328 $_exit_pattern = ""; 329 } 330 return array($patterns, $internals, $_exit_pattern); 331 } 332 333 function getPluginName($text, $input_name="") { 334 335 $syntax = $this->syntax; 336 $patterns = array(); 337 $base_patterns = $syntax->_regexes['base']->_patterns; 338 $base_labels= $syntax->_regexes['base']->_labels; 339 340 for($i=0; $i < count($base_labels); $i++) { 341 if(preg_match('/plugin/', $base_labels[$i])) { 342 $patterns[] = array($i => $base_patterns[$i]); 343 } 344 } 345 346 foreach($patterns as $pattern) { 347 list($index, $regex) = each($pattern); 348 if(preg_match('/' . $regex . '/', $text)) { 349 return $base_labels[$index]; 350 } 351 352 } 353 354 if(!$input_name) return null; 355 $input_name=str_replace ('"', "", $input_name); 356 $needle = 'plugin_' . $input_name; 357 $key = array_search ($needle, $syntax->_regexes['base']->_labels, true); 358 if($key === false) { 359 $needle = '_' . $needle; 360 $key = array_search ($needle, $syntax->_regexes['base']->_labels, true); 361 if($key === false) { 362 return null; 363 } 364 } 365 366 367 if($key === false) return null; 368 369 return $base_labels[$key]; 370 } 371 372 373 function getMiddleMatches($text,$entry_match, $patterns, $exit_pattern) { 374 375 if(!$patterns) return array(null, null); 376 377 $len = strlen($entry_match); 378 $remainder = substr($text,$len); 379 $remainder = preg_replace('/'. $exit_pattern .'$/',"", $remainder); 380 $remainder = str_replace ( '<p>', '<p>', $remainder); 381 382 $regexes = '/'. implode('|',$patterns) ."/"; 383 $split = preg_split($regexes,$remainder,-1,PREG_SPLIT_DELIM_CAPTURE); 384 385 $matches = array(); 386 for($i=0; $i<count($split); $i++) { 387 388 if($split[$i]) { 389 if(preg_match($regexes,$split[$i])) { 390 $matches[$i] = DOKU_LEXER_MATCHED; 391 } 392 else { 393 $matches[$i] = DOKU_LEXER_UNMATCHED; 394 } 395 } 396 else { 397 $matches[$i] = DOKU_LEXER_UNMATCHED; 398 } 399 } 400 401 return array($split,$matches); 402 } 403 404function meta_io ($read_only,$data=array()) { 405 global $INFO, $conf; 406 407 $meta_path = str_replace(':', '/', $INFO['id']); 408 $meta_path = $conf['metadir'] . '/' . $meta_path . '.meta'; 409 410 411 if(file_exists($meta_path)) { 412 $meta = file_get_contents($meta_path); 413 } 414 415 $meta = unserialize($meta); 416 if($read_only) return $meta; 417 418 require_once(DOKU_INC . 'inc/io.php'); 419 $data_added = false; 420 foreach ($data as $key=>$datum) { 421 if(isset($meta[$key]) && $meta[$key] == $datum) { 422 continue; 423 } 424 else { 425 $meta ['persistent'][$key]=$datum; 426 $meta [$key]=$datum; 427 $data_added = true; 428 } 429 } 430 431 432 if(!$data_added) return; 433 434 $data = serialize($meta); 435 io_saveFile($meta_path, $data); 436 437} 438 439 440 function cache_bypass_after(&$event, $param) 441 { 442 443 444 445 global $INFO; 446 447 $meta_data = $this->meta_io (true); 448 449 $timestamp = time() + (5000*24*60*60); 450 451 if(isset($meta_data['persistent']['dwplugin']) && $meta_data ['persistent']['dwplugin'] > 0) { 452 $timestamp = $meta_data ['persistent']['dwplugin']; 453 } 454 else if(isset($meta_data['dwplugin']) && $meta_data['dwplugin'] > 0) { 455 $timestamp = $meta_data ['dwplugin']; 456 } 457 458 $tmp = time() + (24*60*60); // one day 459 460 if($timestamp < $tmp) { 461 $event->result=false; 462 463 } 464 465 466 } 467 468 function cache_bypass(&$event, $param) 469 { 470 471 472 } 473 474 475 /* create unique key for each plugin on page */ 476 function get_key($text, $plugin="") { 477 478 $replace = array(); 479 480 $str = substr($text,0,25); 481 $str = preg_replace('/[\/\'\"]/',"",$str); 482 $key = $plugin . str_replace(' ', '', $str); 483 $key = strtolower($key); 484 485 486 $a = rand(97, 104); 487 $b = rand(105, 112); 488 $c = rand(113, 120); 489 $replace[chr($a)] = chr($a+1); 490 $replace[chr($b)] = chr($b+1); 491 $replace[chr($c)] = chr($c+1); 492 493 $key = strtr($key, $replace); 494 if(isset($this->our_keys[$key])) { 495 $key = str_replace(chr($a+1),chr($a),$key); 496 $key = strtoupper($key); 497 } 498 $this->our_keys[$key] = $key; 499 500 return $key; 501 } 502 503 504 function write_debug($what) { 505 // return; 506 $handle = fopen("dwplugin_debug.txt", "a"); 507 fwrite($handle, "$what\n"); 508 fclose($handle); 509 } 510} 511 512 513