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 ('&amp;lt;p&amp;gt;',  '<p>', $match);
241            $match = str_replace ('&lt;p&gt;',  '<p>', $match);
242            $match = str_replace ('&amp;lt;br&amp;gt;',  '<br>', $match);
243            $match = str_replace ('&lt;br&gt;',  '<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>', '&lt;p&gt;', $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