Failed Conditions
Push — psr2 ( de3699...36dc94 )
by Andreas
06:50 queued 03:31
created

inc/parserutils.php (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Utilities for accessing the parser
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Harry Fuecks <[email protected]>
7
 * @author     Andreas Gohr <[email protected]>
8
 */
9
10
/**
11
 * How many pages shall be rendered for getting metadata during one request
12
 * at maximum? Note that this limit isn't respected when METADATA_RENDER_UNLIMITED
13
 * is passed as render parameter to p_get_metadata.
14
 */
15
if (!defined('P_GET_METADATA_RENDER_LIMIT')) define('P_GET_METADATA_RENDER_LIMIT', 5);
16
17
/** Don't render metadata even if it is outdated or doesn't exist */
18
define('METADATA_DONT_RENDER', 0);
19
/**
20
 * Render metadata when the page is really newer or the metadata doesn't exist.
21
 * Uses just a simple check, but should work pretty well for loading simple
22
 * metadata values like the page title and avoids rendering a lot of pages in
23
 * one request. The P_GET_METADATA_RENDER_LIMIT is used in this mode.
24
 * Use this if it is unlikely that the metadata value you are requesting
25
 * does depend e.g. on pages that are included in the current page using
26
 * the include plugin (this is very likely the case for the page title, but
27
 * not for relation references).
28
 */
29
define('METADATA_RENDER_USING_SIMPLE_CACHE', 1);
30
/**
31
 * Render metadata using the metadata cache logic. The P_GET_METADATA_RENDER_LIMIT
32
 * is used in this mode. Use this mode when you are requesting more complex
33
 * metadata. Although this will cause rendering more often it might actually have
34
 * the effect that less current metadata is returned as it is more likely than in
35
 * the simple cache mode that metadata needs to be rendered for all pages at once
36
 * which means that when the metadata for the page is requested that actually needs
37
 * to be updated the limit might have been reached already.
38
 */
39
define('METADATA_RENDER_USING_CACHE', 2);
40
/**
41
 * Render metadata without limiting the number of pages for which metadata is
42
 * rendered. Use this mode with care, normally it should only be used in places
43
 * like the indexer or in cli scripts where the execution time normally isn't
44
 * limited. This can be combined with the simple cache using
45
 * METADATA_RENDER_USING_CACHE | METADATA_RENDER_UNLIMITED.
46
 */
47
define('METADATA_RENDER_UNLIMITED', 4);
48
49
/**
50
 * Returns the parsed Wikitext in XHTML for the given id and revision.
51
 *
52
 * If $excuse is true an explanation is returned if the file
53
 * wasn't found
54
 *
55
 * @author Andreas Gohr <[email protected]>
56
 *
57
 * @param string $id page id
58
 * @param string|int $rev revision timestamp or empty string
59
 * @param bool $excuse
60
 * @param string $date_at
61
 *
62
 * @return null|string
63
 */
64
function p_wiki_xhtml($id, $rev='', $excuse=true,$date_at=''){
65
    $file = wikiFN($id,$rev);
66
    $ret  = '';
67
68
    //ensure $id is in global $ID (needed for parsing)
69
    global $ID;
70
    $keep = $ID;
71
    $ID   = $id;
72
73
    if($rev || $date_at){
74
        if(file_exists($file)){
75
            //no caching on old revisions
76
            $ret = p_render('xhtml',p_get_instructions(io_readWikiPage($file,$id,$rev)),$info,$date_at);
77
        }elseif($excuse){
78
            $ret = p_locale_xhtml('norev');
79
        }
80
    }else{
81
        if(file_exists($file)){
82
            $ret = p_cached_output($file,'xhtml',$id);
83
        }elseif($excuse){
84
            $ret = p_locale_xhtml('newpage');
85
        }
86
    }
87
88
    //restore ID (just in case)
89
    $ID = $keep;
90
91
    return $ret;
92
}
93
94
/**
95
 * Returns the specified local text in parsed format
96
 *
97
 * @author Andreas Gohr <[email protected]>
98
 *
99
 * @param string $id page id
100
 * @return null|string
101
 */
102
function p_locale_xhtml($id){
103
    //fetch parsed locale
104
    $html = p_cached_output(localeFN($id));
105
    return $html;
106
}
107
108
/**
109
 * Returns the given file parsed into the requested output format
110
 *
111
 * @author Andreas Gohr <[email protected]>
112
 * @author Chris Smith <[email protected]>
113
 *
114
 * @param string $file filename, path to file
115
 * @param string $format
116
 * @param string $id page id
117
 * @return null|string
118
 */
119
function p_cached_output($file, $format='xhtml', $id='') {
120
    global $conf;
121
122
    $cache = new cache_renderer($id, $file, $format);
123
    if ($cache->useCache()) {
124
        $parsed = $cache->retrieveCache(false);
125
        if($conf['allowdebug'] && $format=='xhtml') {
126
            $parsed .= "\n<!-- cachefile {$cache->cache} used -->\n";
127
        }
128
    } else {
129
        $parsed = p_render($format, p_cached_instructions($file,false,$id), $info);
130
131
        if ($info['cache'] && $cache->storeCache($parsed)) {              // storeCache() attempts to save cachefile
132
            if($conf['allowdebug'] && $format=='xhtml') {
133
                $parsed .= "\n<!-- no cachefile used, but created {$cache->cache} -->\n";
134
            }
135
        }else{
136
            $cache->removeCache();                     //try to delete cachefile
137
            if($conf['allowdebug'] && $format=='xhtml') {
138
                $parsed .= "\n<!-- no cachefile used, caching forbidden -->\n";
139
            }
140
        }
141
    }
142
143
    return $parsed;
144
}
145
146
/**
147
 * Returns the render instructions for a file
148
 *
149
 * Uses and creates a serialized cache file
150
 *
151
 * @author Andreas Gohr <[email protected]>
152
 *
153
 * @param string $file      filename, path to file
154
 * @param bool   $cacheonly
155
 * @param string $id        page id
156
 * @return array|null
157
 */
158
function p_cached_instructions($file,$cacheonly=false,$id='') {
159
    static $run = null;
160
    if(is_null($run)) $run = array();
161
162
    $cache = new cache_instructions($id, $file);
163
164
    if ($cacheonly || $cache->useCache() || (isset($run[$file]) && !defined('DOKU_UNITTEST'))) {
165
        return $cache->retrieveCache();
166
    } else if (file_exists($file)) {
167
        // no cache - do some work
168
        $ins = p_get_instructions(io_readWikiPage($file,$id));
169
        if ($cache->storeCache($ins)) {
170
            $run[$file] = true; // we won't rebuild these instructions in the same run again
171
        } else {
172
            msg('Unable to save cache file. Hint: disk full; file permissions; safe_mode setting.',-1);
173
        }
174
        return $ins;
175
    }
176
177
    return null;
178
}
179
180
/**
181
 * turns a page into a list of instructions
182
 *
183
 * @author Harry Fuecks <[email protected]>
184
 * @author Andreas Gohr <[email protected]>
185
 *
186
 * @param string $text  raw wiki syntax text
187
 * @return array a list of instruction arrays
188
 */
189
function p_get_instructions($text){
190
191
    $modes = p_get_parsermodes();
192
193
    // Create the parser
194
    $Parser = new Doku_Parser();
195
196
    // Add the Handler
197
    $Parser->Handler = new Doku_Handler();
198
199
    //add modes to parser
200
    foreach($modes as $mode){
201
        $Parser->addMode($mode['mode'],$mode['obj']);
202
    }
203
204
    // Do the parsing
205
    trigger_event('PARSER_WIKITEXT_PREPROCESS', $text);
206
    $p = $Parser->parse($text);
207
    //  dbg($p);
208
    return $p;
209
}
210
211
/**
212
 * returns the metadata of a page
213
 *
214
 * @param string $id      The id of the page the metadata should be returned from
215
 * @param string $key     The key of the metdata value that shall be read (by default everything)
216
 *                        separate hierarchies by " " like "date created"
217
 * @param int    $render  If the page should be rendererd - possible values:
218
 *     METADATA_DONT_RENDER, METADATA_RENDER_USING_SIMPLE_CACHE, METADATA_RENDER_USING_CACHE
219
 *     METADATA_RENDER_UNLIMITED (also combined with the previous two options),
220
 *     default: METADATA_RENDER_USING_CACHE
221
 * @return mixed The requested metadata fields
222
 *
223
 * @author Esther Brunner <[email protected]>
224
 * @author Michael Hamann <[email protected]>
225
 */
226
function p_get_metadata($id, $key='', $render=METADATA_RENDER_USING_CACHE){
227
    global $ID;
228
    static $render_count = 0;
229
    // track pages that have already been rendered in order to avoid rendering the same page
230
    // again
231
    static $rendered_pages = array();
232
233
    // cache the current page
234
    // Benchmarking shows the current page's metadata is generally the only page metadata
235
    // accessed several times. This may catch a few other pages, but that shouldn't be an issue.
236
    $cache = ($ID == $id);
237
    $meta = p_read_metadata($id, $cache);
238
239
    if (!is_numeric($render)) {
240
        if ($render) {
241
            $render = METADATA_RENDER_USING_SIMPLE_CACHE;
242
        } else {
243
            $render = METADATA_DONT_RENDER;
244
        }
245
    }
246
247
    // prevent recursive calls in the cache
248
    static $recursion = false;
249
    if (!$recursion && $render != METADATA_DONT_RENDER && !isset($rendered_pages[$id])&& page_exists($id)){
250
        $recursion = true;
251
252
        $cachefile = new cache_renderer($id, wikiFN($id), 'metadata');
253
254
        $do_render = false;
255
        if ($render & METADATA_RENDER_UNLIMITED || $render_count < P_GET_METADATA_RENDER_LIMIT) {
256
            if ($render & METADATA_RENDER_USING_SIMPLE_CACHE) {
257
                $pagefn = wikiFN($id);
258
                $metafn = metaFN($id, '.meta');
259
                if (!file_exists($metafn) || @filemtime($pagefn) > @filemtime($cachefile->cache)) {
260
                    $do_render = true;
261
                }
262
            } elseif (!$cachefile->useCache()){
263
                $do_render = true;
264
            }
265
        }
266
        if ($do_render) {
267
            if (!defined('DOKU_UNITTEST')) {
268
                ++$render_count;
269
                $rendered_pages[$id] = true;
270
            }
271
            $old_meta = $meta;
272
            $meta = p_render_metadata($id, $meta);
273
            // only update the file when the metadata has been changed
274
            if ($meta == $old_meta || p_save_metadata($id, $meta)) {
275
                // store a timestamp in order to make sure that the cachefile is touched
276
                // this timestamp is also stored when the meta data is still the same
277
                $cachefile->storeCache(time());
278
            } else {
279
                msg('Unable to save metadata file. Hint: disk full; file permissions; safe_mode setting.',-1);
280
            }
281
        }
282
283
        $recursion = false;
284
    }
285
286
    $val = $meta['current'];
287
288
    // filter by $key
289
    foreach(preg_split('/\s+/', $key, 2, PREG_SPLIT_NO_EMPTY) as $cur_key) {
290
        if (!isset($val[$cur_key])) {
291
            return null;
292
        }
293
        $val = $val[$cur_key];
294
    }
295
    return $val;
296
}
297
298
/**
299
 * sets metadata elements of a page
300
 *
301
 * @see http://www.dokuwiki.org/devel:metadata#functions_to_get_and_set_metadata
302
 *
303
 * @param String  $id         is the ID of a wiki page
304
 * @param Array   $data       is an array with key ⇒ value pairs to be set in the metadata
305
 * @param Boolean $render     whether or not the page metadata should be generated with the renderer
306
 * @param Boolean $persistent indicates whether or not the particular metadata value will persist through
307
 *                            the next metadata rendering.
308
 * @return boolean true on success
309
 *
310
 * @author Esther Brunner <[email protected]>
311
 * @author Michael Hamann <[email protected]>
312
 */
313
function p_set_metadata($id, $data, $render=false, $persistent=true){
314
    if (!is_array($data)) return false;
315
316
    global $ID, $METADATA_RENDERERS;
317
318
    // if there is currently a renderer change the data in the renderer instead
319
    if (isset($METADATA_RENDERERS[$id])) {
320
        $orig =& $METADATA_RENDERERS[$id];
321
        $meta = $orig;
322
    } else {
323
        // cache the current page
324
        $cache = ($ID == $id);
325
        $orig = p_read_metadata($id, $cache);
326
327
        // render metadata first?
328
        $meta = $render ? p_render_metadata($id, $orig) : $orig;
329
    }
330
331
    // now add the passed metadata
332
    $protected = array('description', 'date', 'contributor');
333
    foreach ($data as $key => $value){
334
335
        // be careful with sub-arrays of $meta['relation']
336
        if ($key == 'relation'){
337
338
            foreach ($value as $subkey => $subvalue){
339
                if(isset($meta['current'][$key][$subkey]) && is_array($meta['current'][$key][$subkey])) {
340
                    $meta['current'][$key][$subkey] = array_replace($meta['current'][$key][$subkey], (array)$subvalue);
341
                } else {
342
                    $meta['current'][$key][$subkey] = $subvalue;
343
                }
344
                if($persistent) {
345
                    if(isset($meta['persistent'][$key][$subkey]) && is_array($meta['persistent'][$key][$subkey])) {
346
                        $meta['persistent'][$key][$subkey] = array_replace(
347
                            $meta['persistent'][$key][$subkey],
348
                            (array) $subvalue
349
                        );
350
                    } else {
351
                        $meta['persistent'][$key][$subkey] = $subvalue;
352
                    }
353
                }
354
            }
355
356
            // be careful with some senisitive arrays of $meta
357
        } elseif (in_array($key, $protected)){
358
359
            // these keys, must have subkeys - a legitimate value must be an array
360
            if (is_array($value)) {
361
                $meta['current'][$key] = !empty($meta['current'][$key]) ?
362
                    array_replace((array)$meta['current'][$key],$value) :
363
                    $value;
364
365
                if ($persistent) {
366
                    $meta['persistent'][$key] = !empty($meta['persistent'][$key]) ?
367
                        array_replace((array)$meta['persistent'][$key],$value) :
368
                        $value;
369
                }
370
            }
371
372
            // no special treatment for the rest
373
        } else {
374
            $meta['current'][$key] = $value;
375
            if ($persistent) $meta['persistent'][$key] = $value;
376
        }
377
    }
378
379
    // save only if metadata changed
380
    if ($meta == $orig) return true;
381
382
    if (isset($METADATA_RENDERERS[$id])) {
383
        // set both keys individually as the renderer has references to the individual keys
384
        $METADATA_RENDERERS[$id]['current']    = $meta['current'];
385
        $METADATA_RENDERERS[$id]['persistent'] = $meta['persistent'];
386
        return true;
387
    } else {
388
        return p_save_metadata($id, $meta);
389
    }
390
}
391
392
/**
393
 * Purges the non-persistant part of the meta data
394
 * used on page deletion
395
 *
396
 * @author Michael Klier <[email protected]>
397
 *
398
 * @param string $id page id
399
 * @return bool  success / fail
400
 */
401
function p_purge_metadata($id) {
402
    $meta = p_read_metadata($id);
403
    foreach($meta['current'] as $key => $value) {
404
        if(is_array($meta[$key])) {
405
            $meta['current'][$key] = array();
406
        } else {
407
            $meta['current'][$key] = '';
408
        }
409
410
    }
411
    return p_save_metadata($id, $meta);
412
}
413
414
/**
415
 * read the metadata from source/cache for $id
416
 * (internal use only - called by p_get_metadata & p_set_metadata)
417
 *
418
 * @author   Christopher Smith <[email protected]>
419
 *
420
 * @param    string   $id      absolute wiki page id
421
 * @param    bool     $cache   whether or not to cache metadata in memory
422
 *                             (only use for metadata likely to be accessed several times)
423
 *
424
 * @return   array             metadata
425
 */
426
function p_read_metadata($id,$cache=false) {
427
    global $cache_metadata;
428
429
    if (isset($cache_metadata[(string)$id])) return $cache_metadata[(string)$id];
430
431
    $file = metaFN($id, '.meta');
432
    $meta = file_exists($file) ?
433
        unserialize(io_readFile($file, false)) :
434
        array('current'=>array(),'persistent'=>array());
435
436
    if ($cache) {
437
        $cache_metadata[(string)$id] = $meta;
438
    }
439
440
    return $meta;
441
}
442
443
/**
444
 * This is the backend function to save a metadata array to a file
445
 *
446
 * @param    string   $id      absolute wiki page id
447
 * @param    array    $meta    metadata
448
 *
449
 * @return   bool              success / fail
450
 */
451
function p_save_metadata($id, $meta) {
452
    // sync cached copies, including $INFO metadata
453
    global $cache_metadata, $INFO;
454
455
    if (isset($cache_metadata[$id])) $cache_metadata[$id] = $meta;
456
    if (!empty($INFO) && ($id == $INFO['id'])) { $INFO['meta'] = $meta['current']; }
457
458
    return io_saveFile(metaFN($id, '.meta'), serialize($meta));
459
}
460
461
/**
462
 * renders the metadata of a page
463
 *
464
 * @author Esther Brunner <[email protected]>
465
 *
466
 * @param string $id    page id
467
 * @param array  $orig  the original metadata
468
 * @return array|null array('current'=> array,'persistent'=> array);
469
 */
470
function p_render_metadata($id, $orig){
471
    // make sure the correct ID is in global ID
472
    global $ID, $METADATA_RENDERERS;
473
474
    // avoid recursive rendering processes for the same id
475
    if (isset($METADATA_RENDERERS[$id])) {
476
        return $orig;
477
    }
478
479
    // store the original metadata in the global $METADATA_RENDERERS so p_set_metadata can use it
480
    $METADATA_RENDERERS[$id] =& $orig;
481
482
    $keep = $ID;
483
    $ID   = $id;
484
485
    // add an extra key for the event - to tell event handlers the page whose metadata this is
486
    $orig['page'] = $id;
487
    $evt = new Doku_Event('PARSER_METADATA_RENDER', $orig);
488
    if ($evt->advise_before()) {
489
490
        // get instructions
491
        $instructions = p_cached_instructions(wikiFN($id),false,$id);
492
        if(is_null($instructions)){
493
            $ID = $keep;
494
            unset($METADATA_RENDERERS[$id]);
495
            return null; // something went wrong with the instructions
496
        }
497
498
        // set up the renderer
499
        $renderer = new Doku_Renderer_metadata();
500
        $renderer->meta =& $orig['current'];
501
        $renderer->persistent =& $orig['persistent'];
502
503
        // loop through the instructions
504
        foreach ($instructions as $instruction){
505
            // execute the callback against the renderer
506
            call_user_func_array(array(&$renderer, $instruction[0]), (array) $instruction[1]);
507
        }
508
509
        $evt->result = array('current'=>&$renderer->meta,'persistent'=>&$renderer->persistent);
510
    }
511
    $evt->advise_after();
512
513
    // clean up
514
    $ID = $keep;
515
    unset($METADATA_RENDERERS[$id]);
516
    return $evt->result;
517
}
518
519
/**
520
 * returns all available parser syntax modes in correct order
521
 *
522
 * @author Andreas Gohr <[email protected]>
523
 *
524
 * @return array[] with for each plugin the array('sort' => sortnumber, 'mode' => mode string, 'obj'  => plugin object)
525
 */
526
function p_get_parsermodes(){
527
    global $conf;
528
529
    //reuse old data
530
    static $modes = null;
531
    if($modes != null && !defined('DOKU_UNITTEST')){
532
        return $modes;
533
    }
534
535
    //import parser classes and mode definitions
536
    require_once DOKU_INC . 'inc/parser/parser.php';
537
538
    // we now collect all syntax modes and their objects, then they will
539
    // be sorted and added to the parser in correct order
540
    $modes = array();
541
542
    // add syntax plugins
543
    $pluginlist = plugin_list('syntax');
544
    if(count($pluginlist)){
545
        global $PARSER_MODES;
546
        $obj = null;
547
        foreach($pluginlist as $p){
548
            /** @var DokuWiki_Syntax_Plugin $obj */
549
            if(!$obj = plugin_load('syntax',$p)) continue; //attempt to load plugin into $obj
550
            $PARSER_MODES[$obj->getType()][] = "plugin_$p"; //register mode type
0 ignored issues
show
The method getType() does not seem to exist on object<DokuWiki_PluginInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
551
            //add to modes
552
            $modes[] = array(
553
                    'sort' => $obj->getSort(),
0 ignored issues
show
The method getSort() does not seem to exist on object<DokuWiki_PluginInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
554
                    'mode' => "plugin_$p",
555
                    'obj'  => $obj,
556
                    );
557
            unset($obj); //remove the reference
558
        }
559
    }
560
561
    // add default modes
562
    $std_modes = array('listblock','preformatted','notoc','nocache',
563
            'header','table','linebreak','footnote','hr',
564
            'unformatted','php','html','code','file','quote',
565
            'internallink','rss','media','externallink',
566
            'emaillink','windowssharelink','eol');
567
    if($conf['typography']){
568
        $std_modes[] = 'quotes';
569
        $std_modes[] = 'multiplyentity';
570
    }
571
    foreach($std_modes as $m){
572
        $class = 'dokuwiki\\ParserMode\\'.ucfirst($m);
573
        $obj   = new $class();
574
        $modes[] = array(
575
                'sort' => $obj->getSort(),
576
                'mode' => $m,
577
                'obj'  => $obj
578
                );
579
    }
580
581
    // add formatting modes
582
    $fmt_modes = array('strong','emphasis','underline','monospace',
583
            'subscript','superscript','deleted');
584
    foreach($fmt_modes as $m){
585
        $obj   = new \dokuwiki\ParserMode\Formatting($m);
586
        $modes[] = array(
587
                'sort' => $obj->getSort(),
588
                'mode' => $m,
589
                'obj'  => $obj
590
                );
591
    }
592
593
    // add modes which need files
594
    $obj     = new \dokuwiki\ParserMode\Smiley(array_keys(getSmileys()));
595
    $modes[] = array('sort' => $obj->getSort(), 'mode' => 'smiley','obj'  => $obj );
596
    $obj     = new \dokuwiki\ParserMode\Acronym(array_keys(getAcronyms()));
597
    $modes[] = array('sort' => $obj->getSort(), 'mode' => 'acronym','obj'  => $obj );
598
    $obj     = new \dokuwiki\ParserMode\Entity(array_keys(getEntities()));
599
    $modes[] = array('sort' => $obj->getSort(), 'mode' => 'entity','obj'  => $obj );
600
601
    // add optional camelcase mode
602
    if($conf['camelcase']){
603
        $obj     = new \dokuwiki\ParserMode\Camelcaselink();
604
        $modes[] = array('sort' => $obj->getSort(), 'mode' => 'camelcaselink','obj'  => $obj );
605
    }
606
607
    //sort modes
608
    usort($modes,'p_sort_modes');
609
610
    return $modes;
611
}
612
613
/**
614
 * Callback function for usort
615
 *
616
 * @author Andreas Gohr <[email protected]>
617
 *
618
 * @param array $a
619
 * @param array $b
620
 * @return int $a is lower/equal/higher than $b
621
 */
622
function p_sort_modes($a, $b){
623
    if($a['sort'] == $b['sort']) return 0;
624
    return ($a['sort'] < $b['sort']) ? -1 : 1;
625
}
626
627
/**
628
 * Renders a list of instruction to the specified output mode
629
 *
630
 * In the $info array is information from the renderer returned
631
 *
632
 * @author Harry Fuecks <[email protected]>
633
 * @author Andreas Gohr <[email protected]>
634
 *
635
 * @param string $mode
636
 * @param array|null|false $instructions
637
 * @param array $info returns render info like enabled toc and cache
638
 * @param string $date_at
639
 * @return null|string rendered output
640
 */
641
function p_render($mode,$instructions,&$info,$date_at=''){
642
    if(is_null($instructions)) return '';
643
    if($instructions === false) return '';
644
645
    $Renderer = p_get_renderer($mode);
646
    if (is_null($Renderer)) return null;
647
648
    $Renderer->reset();
649
650
    if($date_at) {
651
        $Renderer->date_at = $date_at;
652
    }
653
654
    $Renderer->smileys = getSmileys();
655
    $Renderer->entities = getEntities();
656
    $Renderer->acronyms = getAcronyms();
657
    $Renderer->interwiki = getInterwiki();
658
659
    // Loop through the instructions
660
    foreach ( $instructions as $instruction ) {
661
        // Execute the callback against the Renderer
662
        if(method_exists($Renderer, $instruction[0])){
663
            call_user_func_array(array(&$Renderer, $instruction[0]), $instruction[1] ? $instruction[1] : array());
664
        }
665
    }
666
667
    //set info array
668
    $info = $Renderer->info;
669
670
    // Post process and return the output
671
    $data = array($mode,& $Renderer->doc);
672
    trigger_event('RENDERER_CONTENT_POSTPROCESS',$data);
673
    return $Renderer->doc;
674
}
675
676
/**
677
 * Figure out the correct renderer class to use for $mode,
678
 * instantiate and return it
679
 *
680
 * @param string $mode Mode of the renderer to get
681
 * @return null|Doku_Renderer The renderer
682
 *
683
 * @author Christopher Smith <[email protected]>
684
 */
685
function p_get_renderer($mode) {
686
    /** @var Doku_Plugin_Controller $plugin_controller */
687
    global $conf, $plugin_controller;
688
689
    $rname = !empty($conf['renderer_'.$mode]) ? $conf['renderer_'.$mode] : $mode;
690
    $rclass = "Doku_Renderer_$rname";
691
692
    // if requested earlier or a bundled renderer
693
    if( class_exists($rclass) ) {
694
        $Renderer = new $rclass();
695
        return $Renderer;
696
    }
697
698
    // not bundled, see if its an enabled renderer plugin & when $mode is 'xhtml', the renderer can supply that format.
699
    /** @var Doku_Renderer $Renderer */
700
    $Renderer = $plugin_controller->load('renderer',$rname);
701
    if ($Renderer && is_a($Renderer, 'Doku_Renderer')  && ($mode != 'xhtml' || $mode == $Renderer->getFormat())) {
702
        return $Renderer;
703
    }
704
705
    // there is a configuration error!
706
    // not bundled, not a valid enabled plugin, use $mode to try to fallback to a bundled renderer
707
    $rclass = "Doku_Renderer_$mode";
708
    if ( class_exists($rclass) ) {
709
        // viewers should see renderered output, so restrict the warning to admins only
710
        $msg = "No renderer '$rname' found for mode '$mode', check your plugins";
711
        if ($mode == 'xhtml') {
712
            $msg .= " and the 'renderer_xhtml' config setting";
713
        }
714
        $msg .= ".<br/>Attempting to fallback to the bundled renderer.";
715
        msg($msg,-1,'','',MSG_ADMINS_ONLY);
716
717
        $Renderer = new $rclass;
718
        $Renderer->nocache();     // fallback only (and may include admin alerts), don't cache
719
        return $Renderer;
720
    }
721
722
    // fallback failed, alert the world
723
    msg("No renderer '$rname' found for mode '$mode'",-1);
724
    return null;
725
}
726
727
/**
728
 * Gets the first heading from a file
729
 *
730
 * @param   string   $id       dokuwiki page id
731
 * @param   int      $render   rerender if first heading not known
732
 *                             default: METADATA_RENDER_USING_SIMPLE_CACHE
733
 *                             Possible values: METADATA_DONT_RENDER,
734
 *                                              METADATA_RENDER_USING_SIMPLE_CACHE,
735
 *                                              METADATA_RENDER_USING_CACHE,
736
 *                                              METADATA_RENDER_UNLIMITED
737
 * @return string|null The first heading
738
 *
739
 * @author Andreas Gohr <[email protected]>
740
 * @author Michael Hamann <[email protected]>
741
 */
742
function p_get_first_heading($id, $render=METADATA_RENDER_USING_SIMPLE_CACHE){
743
    return p_get_metadata(cleanID($id),'title',$render);
744
}
745
746
/**
747
 * Wrapper for GeSHi Code Highlighter, provides caching of its output
748
 *
749
 * @param  string   $code       source code to be highlighted
750
 * @param  string   $language   language to provide highlighting
751
 * @param  string   $wrapper    html element to wrap the returned highlighted text
752
 * @return string xhtml code
753
 *
754
 * @author Christopher Smith <[email protected]>
755
 * @author Andreas Gohr <[email protected]>
756
 */
757
function p_xhtml_cached_geshi($code, $language, $wrapper='pre', array $options=null) {
758
    global $conf, $config_cascade, $INPUT;
759
    $language = strtolower($language);
760
761
    // remove any leading or trailing blank lines
762
    $code = preg_replace('/^\s*?\n|\s*?\n$/','',$code);
763
764
    $optionsmd5 = md5(serialize($options));
765
    $cache = getCacheName($language.$code.$optionsmd5,".code");
766
    $ctime = @filemtime($cache);
767
    if($ctime && !$INPUT->bool('purge') &&
768
            $ctime > filemtime(DOKU_INC.'vendor/composer/installed.json') &&  // libraries changed
769
            $ctime > filemtime(reset($config_cascade['main']['default']))){ // dokuwiki changed
770
        $highlighted_code = io_readFile($cache, false);
771
    } else {
772
773
        $geshi = new GeSHi($code, $language);
774
        $geshi->set_encoding('utf-8');
775
        $geshi->enable_classes();
776
        $geshi->set_header_type(GESHI_HEADER_PRE);
777
        $geshi->set_link_target($conf['target']['extern']);
778
        if($options !== null) {
779
            foreach ($options as $function => $params) {
780
                if(is_callable(array($geshi, $function))) {
781
                    $geshi->$function($params);
782
                }
783
            }
784
        }
785
786
        // remove GeSHi's wrapper element (we'll replace it with our own later)
787
        // we need to use a GeSHi wrapper to avoid <BR> throughout the highlighted text
788
        $highlighted_code = trim(preg_replace('!^<pre[^>]*>|</pre>$!','',$geshi->parse_code()),"\n\r");
789
        io_saveFile($cache,$highlighted_code);
790
    }
791
792
    // add a wrapper element if required
793
    if ($wrapper) {
794
        return "<$wrapper class=\"code $language\">$highlighted_code</$wrapper>";
795
    } else {
796
        return $highlighted_code;
797
    }
798
}
799
800