Failed Conditions
Push — psr2-config ( c6639e )
by Andreas
06:39 queued 03:33
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
use dokuwiki\Parsing\Parser;
11
12
/**
13
 * How many pages shall be rendered for getting metadata during one request
14
 * at maximum? Note that this limit isn't respected when METADATA_RENDER_UNLIMITED
15
 * is passed as render parameter to p_get_metadata.
16
 */
17
if (!defined('P_GET_METADATA_RENDER_LIMIT')) define('P_GET_METADATA_RENDER_LIMIT', 5);
18
19
/** Don't render metadata even if it is outdated or doesn't exist */
20
define('METADATA_DONT_RENDER', 0);
21
/**
22
 * Render metadata when the page is really newer or the metadata doesn't exist.
23
 * Uses just a simple check, but should work pretty well for loading simple
24
 * metadata values like the page title and avoids rendering a lot of pages in
25
 * one request. The P_GET_METADATA_RENDER_LIMIT is used in this mode.
26
 * Use this if it is unlikely that the metadata value you are requesting
27
 * does depend e.g. on pages that are included in the current page using
28
 * the include plugin (this is very likely the case for the page title, but
29
 * not for relation references).
30
 */
31
define('METADATA_RENDER_USING_SIMPLE_CACHE', 1);
32
/**
33
 * Render metadata using the metadata cache logic. The P_GET_METADATA_RENDER_LIMIT
34
 * is used in this mode. Use this mode when you are requesting more complex
35
 * metadata. Although this will cause rendering more often it might actually have
36
 * the effect that less current metadata is returned as it is more likely than in
37
 * the simple cache mode that metadata needs to be rendered for all pages at once
38
 * which means that when the metadata for the page is requested that actually needs
39
 * to be updated the limit might have been reached already.
40
 */
41
define('METADATA_RENDER_USING_CACHE', 2);
42
/**
43
 * Render metadata without limiting the number of pages for which metadata is
44
 * rendered. Use this mode with care, normally it should only be used in places
45
 * like the indexer or in cli scripts where the execution time normally isn't
46
 * limited. This can be combined with the simple cache using
47
 * METADATA_RENDER_USING_CACHE | METADATA_RENDER_UNLIMITED.
48
 */
49
define('METADATA_RENDER_UNLIMITED', 4);
50
51
/**
52
 * Returns the parsed Wikitext in XHTML for the given id and revision.
53
 *
54
 * If $excuse is true an explanation is returned if the file
55
 * wasn't found
56
 *
57
 * @author Andreas Gohr <[email protected]>
58
 *
59
 * @param string $id page id
60
 * @param string|int $rev revision timestamp or empty string
61
 * @param bool $excuse
62
 * @param string $date_at
63
 *
64
 * @return null|string
65
 */
66
function p_wiki_xhtml($id, $rev='', $excuse=true,$date_at=''){
67
    $file = wikiFN($id,$rev);
68
    $ret  = '';
69
70
    //ensure $id is in global $ID (needed for parsing)
71
    global $ID;
72
    $keep = $ID;
73
    $ID   = $id;
74
75
    if($rev || $date_at){
76
        if(file_exists($file)){
77
            //no caching on old revisions
78
            $ret = p_render('xhtml',p_get_instructions(io_readWikiPage($file,$id,$rev)),$info,$date_at);
79
        }elseif($excuse){
80
            $ret = p_locale_xhtml('norev');
81
        }
82
    }else{
83
        if(file_exists($file)){
84
            $ret = p_cached_output($file,'xhtml',$id);
85
        }elseif($excuse){
86
            $ret = p_locale_xhtml('newpage');
87
        }
88
    }
89
90
    //restore ID (just in case)
91
    $ID = $keep;
92
93
    return $ret;
94
}
95
96
/**
97
 * Returns the specified local text in parsed format
98
 *
99
 * @author Andreas Gohr <[email protected]>
100
 *
101
 * @param string $id page id
102
 * @return null|string
103
 */
104
function p_locale_xhtml($id){
105
    //fetch parsed locale
106
    $html = p_cached_output(localeFN($id));
107
    return $html;
108
}
109
110
/**
111
 * Returns the given file parsed into the requested output format
112
 *
113
 * @author Andreas Gohr <[email protected]>
114
 * @author Chris Smith <[email protected]>
115
 *
116
 * @param string $file filename, path to file
117
 * @param string $format
118
 * @param string $id page id
119
 * @return null|string
120
 */
121
function p_cached_output($file, $format='xhtml', $id='') {
122
    global $conf;
123
124
    $cache = new cache_renderer($id, $file, $format);
125
    if ($cache->useCache()) {
126
        $parsed = $cache->retrieveCache(false);
127
        if($conf['allowdebug'] && $format=='xhtml') {
128
            $parsed .= "\n<!-- cachefile {$cache->cache} used -->\n";
129
        }
130
    } else {
131
        $parsed = p_render($format, p_cached_instructions($file,false,$id), $info);
132
133
        if ($info['cache'] && $cache->storeCache($parsed)) {              // storeCache() attempts to save cachefile
134
            if($conf['allowdebug'] && $format=='xhtml') {
135
                $parsed .= "\n<!-- no cachefile used, but created {$cache->cache} -->\n";
136
            }
137
        }else{
138
            $cache->removeCache();                     //try to delete cachefile
139
            if($conf['allowdebug'] && $format=='xhtml') {
140
                $parsed .= "\n<!-- no cachefile used, caching forbidden -->\n";
141
            }
142
        }
143
    }
144
145
    return $parsed;
146
}
147
148
/**
149
 * Returns the render instructions for a file
150
 *
151
 * Uses and creates a serialized cache file
152
 *
153
 * @author Andreas Gohr <[email protected]>
154
 *
155
 * @param string $file      filename, path to file
156
 * @param bool   $cacheonly
157
 * @param string $id        page id
158
 * @return array|null
159
 */
160
function p_cached_instructions($file,$cacheonly=false,$id='') {
161
    static $run = null;
162
    if(is_null($run)) $run = array();
163
164
    $cache = new cache_instructions($id, $file);
165
166
    if ($cacheonly || $cache->useCache() || (isset($run[$file]) && !defined('DOKU_UNITTEST'))) {
167
        return $cache->retrieveCache();
168
    } else if (file_exists($file)) {
169
        // no cache - do some work
170
        $ins = p_get_instructions(io_readWikiPage($file,$id));
171
        if ($cache->storeCache($ins)) {
172
            $run[$file] = true; // we won't rebuild these instructions in the same run again
173
        } else {
174
            msg('Unable to save cache file. Hint: disk full; file permissions; safe_mode setting.',-1);
175
        }
176
        return $ins;
177
    }
178
179
    return null;
180
}
181
182
/**
183
 * turns a page into a list of instructions
184
 *
185
 * @author Harry Fuecks <[email protected]>
186
 * @author Andreas Gohr <[email protected]>
187
 *
188
 * @param string $text  raw wiki syntax text
189
 * @return array a list of instruction arrays
190
 */
191
function p_get_instructions($text){
192
193
    $modes = p_get_parsermodes();
194
195
    // Create the parser and handler
196
    $Parser = new Parser(new Doku_Handler());
197
198
    //add modes to parser
199
    foreach($modes as $mode){
200
        $Parser->addMode($mode['mode'],$mode['obj']);
201
    }
202
203
    // Do the parsing
204
    trigger_event('PARSER_WIKITEXT_PREPROCESS', $text);
205
    $p = $Parser->parse($text);
206
    //  dbg($p);
207
    return $p;
208
}
209
210
/**
211
 * returns the metadata of a page
212
 *
213
 * @param string $id      The id of the page the metadata should be returned from
214
 * @param string $key     The key of the metdata value that shall be read (by default everything)
215
 *                        separate hierarchies by " " like "date created"
216
 * @param int    $render  If the page should be rendererd - possible values:
217
 *     METADATA_DONT_RENDER, METADATA_RENDER_USING_SIMPLE_CACHE, METADATA_RENDER_USING_CACHE
218
 *     METADATA_RENDER_UNLIMITED (also combined with the previous two options),
219
 *     default: METADATA_RENDER_USING_CACHE
220
 * @return mixed The requested metadata fields
221
 *
222
 * @author Esther Brunner <[email protected]>
223
 * @author Michael Hamann <[email protected]>
224
 */
225
function p_get_metadata($id, $key='', $render=METADATA_RENDER_USING_CACHE){
226
    global $ID;
227
    static $render_count = 0;
228
    // track pages that have already been rendered in order to avoid rendering the same page
229
    // again
230
    static $rendered_pages = array();
231
232
    // cache the current page
233
    // Benchmarking shows the current page's metadata is generally the only page metadata
234
    // accessed several times. This may catch a few other pages, but that shouldn't be an issue.
235
    $cache = ($ID == $id);
236
    $meta = p_read_metadata($id, $cache);
237
238
    if (!is_numeric($render)) {
239
        if ($render) {
240
            $render = METADATA_RENDER_USING_SIMPLE_CACHE;
241
        } else {
242
            $render = METADATA_DONT_RENDER;
243
        }
244
    }
245
246
    // prevent recursive calls in the cache
247
    static $recursion = false;
248
    if (!$recursion && $render != METADATA_DONT_RENDER && !isset($rendered_pages[$id])&& page_exists($id)){
249
        $recursion = true;
250
251
        $cachefile = new cache_renderer($id, wikiFN($id), 'metadata');
252
253
        $do_render = false;
254
        if ($render & METADATA_RENDER_UNLIMITED || $render_count < P_GET_METADATA_RENDER_LIMIT) {
255
            if ($render & METADATA_RENDER_USING_SIMPLE_CACHE) {
256
                $pagefn = wikiFN($id);
257
                $metafn = metaFN($id, '.meta');
258
                if (!file_exists($metafn) || @filemtime($pagefn) > @filemtime($cachefile->cache)) {
259
                    $do_render = true;
260
                }
261
            } elseif (!$cachefile->useCache()){
262
                $do_render = true;
263
            }
264
        }
265
        if ($do_render) {
266
            if (!defined('DOKU_UNITTEST')) {
267
                ++$render_count;
268
                $rendered_pages[$id] = true;
269
            }
270
            $old_meta = $meta;
271
            $meta = p_render_metadata($id, $meta);
272
            // only update the file when the metadata has been changed
273
            if ($meta == $old_meta || p_save_metadata($id, $meta)) {
274
                // store a timestamp in order to make sure that the cachefile is touched
275
                // this timestamp is also stored when the meta data is still the same
276
                $cachefile->storeCache(time());
277
            } else {
278
                msg('Unable to save metadata file. Hint: disk full; file permissions; safe_mode setting.',-1);
279
            }
280
        }
281
282
        $recursion = false;
283
    }
284
285
    $val = $meta['current'];
286
287
    // filter by $key
288
    foreach(preg_split('/\s+/', $key, 2, PREG_SPLIT_NO_EMPTY) as $cur_key) {
289
        if (!isset($val[$cur_key])) {
290
            return null;
291
        }
292
        $val = $val[$cur_key];
293
    }
294
    return $val;
295
}
296
297
/**
298
 * sets metadata elements of a page
299
 *
300
 * @see http://www.dokuwiki.org/devel:metadata#functions_to_get_and_set_metadata
301
 *
302
 * @param String  $id         is the ID of a wiki page
303
 * @param Array   $data       is an array with key ⇒ value pairs to be set in the metadata
304
 * @param Boolean $render     whether or not the page metadata should be generated with the renderer
305
 * @param Boolean $persistent indicates whether or not the particular metadata value will persist through
306
 *                            the next metadata rendering.
307
 * @return boolean true on success
308
 *
309
 * @author Esther Brunner <[email protected]>
310
 * @author Michael Hamann <[email protected]>
311
 */
312
function p_set_metadata($id, $data, $render=false, $persistent=true){
313
    if (!is_array($data)) return false;
314
315
    global $ID, $METADATA_RENDERERS;
316
317
    // if there is currently a renderer change the data in the renderer instead
318
    if (isset($METADATA_RENDERERS[$id])) {
319
        $orig =& $METADATA_RENDERERS[$id];
320
        $meta = $orig;
321
    } else {
322
        // cache the current page
323
        $cache = ($ID == $id);
324
        $orig = p_read_metadata($id, $cache);
325
326
        // render metadata first?
327
        $meta = $render ? p_render_metadata($id, $orig) : $orig;
328
    }
329
330
    // now add the passed metadata
331
    $protected = array('description', 'date', 'contributor');
332
    foreach ($data as $key => $value){
333
334
        // be careful with sub-arrays of $meta['relation']
335
        if ($key == 'relation'){
336
337
            foreach ($value as $subkey => $subvalue){
338
                if(isset($meta['current'][$key][$subkey]) && is_array($meta['current'][$key][$subkey])) {
339
                    $meta['current'][$key][$subkey] = array_replace($meta['current'][$key][$subkey], (array)$subvalue);
340
                } else {
341
                    $meta['current'][$key][$subkey] = $subvalue;
342
                }
343
                if($persistent) {
344
                    if(isset($meta['persistent'][$key][$subkey]) && is_array($meta['persistent'][$key][$subkey])) {
345
                        $meta['persistent'][$key][$subkey] = array_replace(
346
                            $meta['persistent'][$key][$subkey],
347
                            (array) $subvalue
348
                        );
349
                    } else {
350
                        $meta['persistent'][$key][$subkey] = $subvalue;
351
                    }
352
                }
353
            }
354
355
            // be careful with some senisitive arrays of $meta
356
        } elseif (in_array($key, $protected)){
357
358
            // these keys, must have subkeys - a legitimate value must be an array
359
            if (is_array($value)) {
360
                $meta['current'][$key] = !empty($meta['current'][$key]) ?
361
                    array_replace((array)$meta['current'][$key],$value) :
362
                    $value;
363
364
                if ($persistent) {
365
                    $meta['persistent'][$key] = !empty($meta['persistent'][$key]) ?
366
                        array_replace((array)$meta['persistent'][$key],$value) :
367
                        $value;
368
                }
369
            }
370
371
            // no special treatment for the rest
372
        } else {
373
            $meta['current'][$key] = $value;
374
            if ($persistent) $meta['persistent'][$key] = $value;
375
        }
376
    }
377
378
    // save only if metadata changed
379
    if ($meta == $orig) return true;
380
381
    if (isset($METADATA_RENDERERS[$id])) {
382
        // set both keys individually as the renderer has references to the individual keys
383
        $METADATA_RENDERERS[$id]['current']    = $meta['current'];
384
        $METADATA_RENDERERS[$id]['persistent'] = $meta['persistent'];
385
        return true;
386
    } else {
387
        return p_save_metadata($id, $meta);
388
    }
389
}
390
391
/**
392
 * Purges the non-persistant part of the meta data
393
 * used on page deletion
394
 *
395
 * @author Michael Klier <[email protected]>
396
 *
397
 * @param string $id page id
398
 * @return bool  success / fail
399
 */
400
function p_purge_metadata($id) {
401
    $meta = p_read_metadata($id);
402
    foreach($meta['current'] as $key => $value) {
403
        if(is_array($meta[$key])) {
404
            $meta['current'][$key] = array();
405
        } else {
406
            $meta['current'][$key] = '';
407
        }
408
409
    }
410
    return p_save_metadata($id, $meta);
411
}
412
413
/**
414
 * read the metadata from source/cache for $id
415
 * (internal use only - called by p_get_metadata & p_set_metadata)
416
 *
417
 * @author   Christopher Smith <[email protected]>
418
 *
419
 * @param    string   $id      absolute wiki page id
420
 * @param    bool     $cache   whether or not to cache metadata in memory
421
 *                             (only use for metadata likely to be accessed several times)
422
 *
423
 * @return   array             metadata
424
 */
425
function p_read_metadata($id,$cache=false) {
426
    global $cache_metadata;
427
428
    if (isset($cache_metadata[(string)$id])) return $cache_metadata[(string)$id];
429
430
    $file = metaFN($id, '.meta');
431
    $meta = file_exists($file) ?
432
        unserialize(io_readFile($file, false)) :
433
        array('current'=>array(),'persistent'=>array());
434
435
    if ($cache) {
436
        $cache_metadata[(string)$id] = $meta;
437
    }
438
439
    return $meta;
440
}
441
442
/**
443
 * This is the backend function to save a metadata array to a file
444
 *
445
 * @param    string   $id      absolute wiki page id
446
 * @param    array    $meta    metadata
447
 *
448
 * @return   bool              success / fail
449
 */
450
function p_save_metadata($id, $meta) {
451
    // sync cached copies, including $INFO metadata
452
    global $cache_metadata, $INFO;
453
454
    if (isset($cache_metadata[$id])) $cache_metadata[$id] = $meta;
455
    if (!empty($INFO) && ($id == $INFO['id'])) { $INFO['meta'] = $meta['current']; }
456
457
    return io_saveFile(metaFN($id, '.meta'), serialize($meta));
458
}
459
460
/**
461
 * renders the metadata of a page
462
 *
463
 * @author Esther Brunner <[email protected]>
464
 *
465
 * @param string $id    page id
466
 * @param array  $orig  the original metadata
467
 * @return array|null array('current'=> array,'persistent'=> array);
468
 */
469
function p_render_metadata($id, $orig){
470
    // make sure the correct ID is in global ID
471
    global $ID, $METADATA_RENDERERS;
472
473
    // avoid recursive rendering processes for the same id
474
    if (isset($METADATA_RENDERERS[$id])) {
475
        return $orig;
476
    }
477
478
    // store the original metadata in the global $METADATA_RENDERERS so p_set_metadata can use it
479
    $METADATA_RENDERERS[$id] =& $orig;
480
481
    $keep = $ID;
482
    $ID   = $id;
483
484
    // add an extra key for the event - to tell event handlers the page whose metadata this is
485
    $orig['page'] = $id;
486
    $evt = new Doku_Event('PARSER_METADATA_RENDER', $orig);
487
    if ($evt->advise_before()) {
488
489
        // get instructions
490
        $instructions = p_cached_instructions(wikiFN($id),false,$id);
491
        if(is_null($instructions)){
492
            $ID = $keep;
493
            unset($METADATA_RENDERERS[$id]);
494
            return null; // something went wrong with the instructions
495
        }
496
497
        // set up the renderer
498
        $renderer = new Doku_Renderer_metadata();
499
        $renderer->meta =& $orig['current'];
500
        $renderer->persistent =& $orig['persistent'];
501
502
        // loop through the instructions
503
        foreach ($instructions as $instruction){
504
            // execute the callback against the renderer
505
            call_user_func_array(array(&$renderer, $instruction[0]), (array) $instruction[1]);
506
        }
507
508
        $evt->result = array('current'=>&$renderer->meta,'persistent'=>&$renderer->persistent);
509
    }
510
    $evt->advise_after();
511
512
    // clean up
513
    $ID = $keep;
514
    unset($METADATA_RENDERERS[$id]);
515
    return $evt->result;
516
}
517
518
/**
519
 * returns all available parser syntax modes in correct order
520
 *
521
 * @author Andreas Gohr <[email protected]>
522
 *
523
 * @return array[] with for each plugin the array('sort' => sortnumber, 'mode' => mode string, 'obj'  => plugin object)
524
 */
525
function p_get_parsermodes(){
526
    global $conf;
527
528
    //reuse old data
529
    static $modes = null;
530
    if($modes != null && !defined('DOKU_UNITTEST')){
531
        return $modes;
532
    }
533
534
    //import parser classes and mode definitions
535
    require_once DOKU_INC . 'inc/parser/parser.php';
536
537
    // we now collect all syntax modes and their objects, then they will
538
    // be sorted and added to the parser in correct order
539
    $modes = array();
540
541
    // add syntax plugins
542
    $pluginlist = plugin_list('syntax');
543
    if(count($pluginlist)){
544
        global $PARSER_MODES;
545
        $obj = null;
546
        foreach($pluginlist as $p){
547
            /** @var DokuWiki_Syntax_Plugin $obj */
548
            if(!$obj = plugin_load('syntax',$p)) continue; //attempt to load plugin into $obj
549
            $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...
550
            //add to modes
551
            $modes[] = array(
552
                    '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...
553
                    'mode' => "plugin_$p",
554
                    'obj'  => $obj,
555
                    );
556
            unset($obj); //remove the reference
557
        }
558
    }
559
560
    // add default modes
561
    $std_modes = array('listblock','preformatted','notoc','nocache',
562
            'header','table','linebreak','footnote','hr',
563
            'unformatted','php','html','code','file','quote',
564
            'internallink','rss','media','externallink',
565
            'emaillink','windowssharelink','eol');
566
    if($conf['typography']){
567
        $std_modes[] = 'quotes';
568
        $std_modes[] = 'multiplyentity';
569
    }
570
    foreach($std_modes as $m){
571
        $class = 'dokuwiki\\Parsing\\ParserMode\\'.ucfirst($m);
572
        $obj   = new $class();
573
        $modes[] = array(
574
                'sort' => $obj->getSort(),
575
                'mode' => $m,
576
                'obj'  => $obj
577
                );
578
    }
579
580
    // add formatting modes
581
    $fmt_modes = array('strong','emphasis','underline','monospace',
582
            'subscript','superscript','deleted');
583
    foreach($fmt_modes as $m){
584
        $obj   = new \dokuwiki\Parsing\ParserMode\Formatting($m);
585
        $modes[] = array(
586
                'sort' => $obj->getSort(),
587
                'mode' => $m,
588
                'obj'  => $obj
589
                );
590
    }
591
592
    // add modes which need files
593
    $obj     = new \dokuwiki\Parsing\ParserMode\Smiley(array_keys(getSmileys()));
594
    $modes[] = array('sort' => $obj->getSort(), 'mode' => 'smiley','obj'  => $obj );
595
    $obj     = new \dokuwiki\Parsing\ParserMode\Acronym(array_keys(getAcronyms()));
596
    $modes[] = array('sort' => $obj->getSort(), 'mode' => 'acronym','obj'  => $obj );
597
    $obj     = new \dokuwiki\Parsing\ParserMode\Entity(array_keys(getEntities()));
598
    $modes[] = array('sort' => $obj->getSort(), 'mode' => 'entity','obj'  => $obj );
599
600
    // add optional camelcase mode
601
    if($conf['camelcase']){
602
        $obj     = new \dokuwiki\Parsing\ParserMode\Camelcaselink();
603
        $modes[] = array('sort' => $obj->getSort(), 'mode' => 'camelcaselink','obj'  => $obj );
604
    }
605
606
    //sort modes
607
    usort($modes,'p_sort_modes');
608
609
    return $modes;
610
}
611
612
/**
613
 * Callback function for usort
614
 *
615
 * @author Andreas Gohr <[email protected]>
616
 *
617
 * @param array $a
618
 * @param array $b
619
 * @return int $a is lower/equal/higher than $b
620
 */
621
function p_sort_modes($a, $b){
622
    if($a['sort'] == $b['sort']) return 0;
623
    return ($a['sort'] < $b['sort']) ? -1 : 1;
624
}
625
626
/**
627
 * Renders a list of instruction to the specified output mode
628
 *
629
 * In the $info array is information from the renderer returned
630
 *
631
 * @author Harry Fuecks <[email protected]>
632
 * @author Andreas Gohr <[email protected]>
633
 *
634
 * @param string $mode
635
 * @param array|null|false $instructions
636
 * @param array $info returns render info like enabled toc and cache
637
 * @param string $date_at
638
 * @return null|string rendered output
639
 */
640
function p_render($mode,$instructions,&$info,$date_at=''){
641
    if(is_null($instructions)) return '';
642
    if($instructions === false) return '';
643
644
    $Renderer = p_get_renderer($mode);
645
    if (is_null($Renderer)) return null;
646
647
    $Renderer->reset();
648
649
    if($date_at) {
650
        $Renderer->date_at = $date_at;
651
    }
652
653
    $Renderer->smileys = getSmileys();
654
    $Renderer->entities = getEntities();
655
    $Renderer->acronyms = getAcronyms();
656
    $Renderer->interwiki = getInterwiki();
657
658
    // Loop through the instructions
659
    foreach ( $instructions as $instruction ) {
660
        // Execute the callback against the Renderer
661
        if(method_exists($Renderer, $instruction[0])){
662
            call_user_func_array(array(&$Renderer, $instruction[0]), $instruction[1] ? $instruction[1] : array());
663
        }
664
    }
665
666
    //set info array
667
    $info = $Renderer->info;
668
669
    // Post process and return the output
670
    $data = array($mode,& $Renderer->doc);
671
    trigger_event('RENDERER_CONTENT_POSTPROCESS',$data);
672
    return $Renderer->doc;
673
}
674
675
/**
676
 * Figure out the correct renderer class to use for $mode,
677
 * instantiate and return it
678
 *
679
 * @param string $mode Mode of the renderer to get
680
 * @return null|Doku_Renderer The renderer
681
 *
682
 * @author Christopher Smith <[email protected]>
683
 */
684
function p_get_renderer($mode) {
685
    /** @var Doku_Plugin_Controller $plugin_controller */
686
    global $conf, $plugin_controller;
687
688
    $rname = !empty($conf['renderer_'.$mode]) ? $conf['renderer_'.$mode] : $mode;
689
    $rclass = "Doku_Renderer_$rname";
690
691
    // if requested earlier or a bundled renderer
692
    if( class_exists($rclass) ) {
693
        $Renderer = new $rclass();
694
        return $Renderer;
695
    }
696
697
    // not bundled, see if its an enabled renderer plugin & when $mode is 'xhtml', the renderer can supply that format.
698
    /** @var Doku_Renderer $Renderer */
699
    $Renderer = $plugin_controller->load('renderer',$rname);
700
    if ($Renderer && is_a($Renderer, 'Doku_Renderer')  && ($mode != 'xhtml' || $mode == $Renderer->getFormat())) {
701
        return $Renderer;
702
    }
703
704
    // there is a configuration error!
705
    // not bundled, not a valid enabled plugin, use $mode to try to fallback to a bundled renderer
706
    $rclass = "Doku_Renderer_$mode";
707
    if ( class_exists($rclass) ) {
708
        // viewers should see renderered output, so restrict the warning to admins only
709
        $msg = "No renderer '$rname' found for mode '$mode', check your plugins";
710
        if ($mode == 'xhtml') {
711
            $msg .= " and the 'renderer_xhtml' config setting";
712
        }
713
        $msg .= ".<br/>Attempting to fallback to the bundled renderer.";
714
        msg($msg,-1,'','',MSG_ADMINS_ONLY);
715
716
        $Renderer = new $rclass;
717
        $Renderer->nocache();     // fallback only (and may include admin alerts), don't cache
718
        return $Renderer;
719
    }
720
721
    // fallback failed, alert the world
722
    msg("No renderer '$rname' found for mode '$mode'",-1);
723
    return null;
724
}
725
726
/**
727
 * Gets the first heading from a file
728
 *
729
 * @param   string   $id       dokuwiki page id
730
 * @param   int      $render   rerender if first heading not known
731
 *                             default: METADATA_RENDER_USING_SIMPLE_CACHE
732
 *                             Possible values: METADATA_DONT_RENDER,
733
 *                                              METADATA_RENDER_USING_SIMPLE_CACHE,
734
 *                                              METADATA_RENDER_USING_CACHE,
735
 *                                              METADATA_RENDER_UNLIMITED
736
 * @return string|null The first heading
737
 *
738
 * @author Andreas Gohr <[email protected]>
739
 * @author Michael Hamann <[email protected]>
740
 */
741
function p_get_first_heading($id, $render=METADATA_RENDER_USING_SIMPLE_CACHE){
742
    return p_get_metadata(cleanID($id),'title',$render);
743
}
744
745
/**
746
 * Wrapper for GeSHi Code Highlighter, provides caching of its output
747
 *
748
 * @param  string   $code       source code to be highlighted
749
 * @param  string   $language   language to provide highlighting
750
 * @param  string   $wrapper    html element to wrap the returned highlighted text
751
 * @return string xhtml code
752
 *
753
 * @author Christopher Smith <[email protected]>
754
 * @author Andreas Gohr <[email protected]>
755
 */
756
function p_xhtml_cached_geshi($code, $language, $wrapper='pre', array $options=null) {
757
    global $conf, $config_cascade, $INPUT;
758
    $language = strtolower($language);
759
760
    // remove any leading or trailing blank lines
761
    $code = preg_replace('/^\s*?\n|\s*?\n$/','',$code);
762
763
    $optionsmd5 = md5(serialize($options));
764
    $cache = getCacheName($language.$code.$optionsmd5,".code");
765
    $ctime = @filemtime($cache);
766
    if($ctime && !$INPUT->bool('purge') &&
767
            $ctime > filemtime(DOKU_INC.'vendor/composer/installed.json') &&  // libraries changed
768
            $ctime > filemtime(reset($config_cascade['main']['default']))){ // dokuwiki changed
769
        $highlighted_code = io_readFile($cache, false);
770
    } else {
771
772
        $geshi = new GeSHi($code, $language);
773
        $geshi->set_encoding('utf-8');
774
        $geshi->enable_classes();
775
        $geshi->set_header_type(GESHI_HEADER_PRE);
776
        $geshi->set_link_target($conf['target']['extern']);
777
        if($options !== null) {
778
            foreach ($options as $function => $params) {
779
                if(is_callable(array($geshi, $function))) {
780
                    $geshi->$function($params);
781
                }
782
            }
783
        }
784
785
        // remove GeSHi's wrapper element (we'll replace it with our own later)
786
        // we need to use a GeSHi wrapper to avoid <BR> throughout the highlighted text
787
        $highlighted_code = trim(preg_replace('!^<pre[^>]*>|</pre>$!','',$geshi->parse_code()),"\n\r");
788
        io_saveFile($cache,$highlighted_code);
789
    }
790
791
    // add a wrapper element if required
792
    if ($wrapper) {
793
        return "<$wrapper class=\"code $language\">$highlighted_code</$wrapper>";
794
    } else {
795
        return $highlighted_code;
796
    }
797
}
798
799