Failed Conditions
Push — oldmediaonrev ( 996e87...cfefec )
by Andreas
06:59 queued 03:56
created

Doku_Renderer_xhtml::getLastlevel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Renderer for XHTML output
4
 *
5
 * @author Harry Fuecks <[email protected]>
6
 * @author Andreas Gohr <[email protected]>
7
 */
8
if(!defined('DOKU_INC')) die('meh.');
9
10
if(!defined('DOKU_LF')) {
11
    // Some whitespace to help View > Source
12
    define ('DOKU_LF', "\n");
13
}
14
15
if(!defined('DOKU_TAB')) {
16
    // Some whitespace to help View > Source
17
    define ('DOKU_TAB', "\t");
18
}
19
20
/**
21
 * The XHTML Renderer
22
 *
23
 * This is DokuWiki's main renderer used to display page content in the wiki
24
 */
25
class Doku_Renderer_xhtml extends Doku_Renderer {
26
    /** @var array store the table of contents */
27
    public $toc = array();
28
29
    /** @var array A stack of section edit data */
30
    protected $sectionedits = array();
31
    var $date_at = '';    // link pages and media against this revision
32
33
    /** @var int last section edit id, used by startSectionEdit */
34
    protected $lastsecid = 0;
35
36
    /** @var array the list of headers used to create unique link ids */
37
    protected $headers = array();
38
39
    /** @var array a list of footnotes, list starts at 1! */
40
    protected $footnotes = array();
41
42
    /** @var int current section level */
43
    protected $lastlevel = 0;
44
    /** @var array section node tracker */
45
    protected $node = array(0, 0, 0, 0, 0);
46
47
    /** @var string temporary $doc store */
48
    protected $store = '';
49
50
    /** @var array global counter, for table classes etc. */
51
    protected $_counter = array(); //
52
53
    /** @var int counts the code and file blocks, used to provide download links */
54
    protected $_codeblock = 0;
55
56
    /** @var array list of allowed URL schemes */
57
    protected $schemes = null;
58
59
    /**
60
     * Register a new edit section range
61
     *
62
     * @param int    $start  The byte position for the edit start
63
     * @param array  $data   Associative array with section data:
64
     *                       Key 'name': the section name/title
65
     *                       Key 'target': the target for the section edit,
66
     *                                     e.g. 'section' or 'table'
67
     *                       Key 'hid': header id
68
     *                       Key 'codeblockOffset': actual code block index
69
     *                       Key 'start': set in startSectionEdit(),
70
     *                                    do not set yourself
71
     *                       Key 'range': calculated from 'start' and
72
     *                                    $key in finishSectionEdit(),
73
     *                                    do not set yourself
74
     * @return string  A marker class for the starting HTML element
75
     *
76
     * @author Adrian Lang <[email protected]>
77
     */
78
    public function startSectionEdit($start, $data) {
79
        if (!is_array($data)) {
80
            msg(
81
                sprintf(
82
                    'startSectionEdit: $data "%s" is NOT an array! One of your plugins needs an update.',
83
                    hsc((string) $data)
84
                ), -1
85
            );
86
87
            // @deprecated 2018-04-14, backward compatibility
88
            $args = func_get_args();
89
            $data = array();
90
            if(isset($args[1])) $data['target'] = $args[1];
91
            if(isset($args[2])) $data['name'] = $args[2];
92
            if(isset($args[3])) $data['hid'] = $args[3];
93
        }
94
        $data['secid'] = ++$this->lastsecid;
95
        $data['start'] = $start;
96
        $this->sectionedits[] = $data;
97
        return 'sectionedit'.$data['secid'];
98
    }
99
100
    /**
101
     * Finish an edit section range
102
     *
103
     * @param int  $end     The byte position for the edit end; null for the rest of the page
104
     *
105
     * @author Adrian Lang <[email protected]>
106
     */
107
    public function finishSectionEdit($end = null, $hid = null) {
108
        $data = array_pop($this->sectionedits);
109
        if(!is_null($end) && $end <= $data['start']) {
110
            return;
111
        }
112
        if(!is_null($hid)) {
113
            $data['hid'] .= $hid;
114
        }
115
        $data['range'] = $data['start'].'-'.(is_null($end) ? '' : $end);
116
        unset($data['start']);
117
        $this->doc .= '<!-- EDIT'.hsc(json_encode ($data)).' -->';
118
    }
119
120
    /**
121
     * Returns the format produced by this renderer.
122
     *
123
     * @return string always 'xhtml'
124
     */
125
    function getFormat() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
126
        return 'xhtml';
127
    }
128
129
    /**
130
     * Initialize the document
131
     */
132
    function document_start() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
133
        //reset some internals
134
        $this->toc     = array();
135
        $this->headers = array();
136
    }
137
138
    /**
139
     * Finalize the document
140
     */
141
    function document_end() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
142
        // Finish open section edits.
143
        while(count($this->sectionedits) > 0) {
144
            if($this->sectionedits[count($this->sectionedits) - 1]['start'] <= 1) {
145
                // If there is only one section, do not write a section edit
146
                // marker.
147
                array_pop($this->sectionedits);
148
            } else {
149
                $this->finishSectionEdit();
150
            }
151
        }
152
153
        if(count($this->footnotes) > 0) {
154
            $this->doc .= '<div class="footnotes">'.DOKU_LF;
155
156
            foreach($this->footnotes as $id => $footnote) {
157
                // check its not a placeholder that indicates actual footnote text is elsewhere
158
                if(substr($footnote, 0, 5) != "@@FNT") {
159
160
                    // open the footnote and set the anchor and backlink
161
                    $this->doc .= '<div class="fn">';
162
                    $this->doc .= '<sup><a href="#fnt__'.$id.'" id="fn__'.$id.'" class="fn_bot">';
163
                    $this->doc .= $id.')</a></sup> '.DOKU_LF;
164
165
                    // get any other footnotes that use the same markup
166
                    $alt = array_keys($this->footnotes, "@@FNT$id");
167
168
                    if(count($alt)) {
169
                        foreach($alt as $ref) {
170
                            // set anchor and backlink for the other footnotes
171
                            $this->doc .= ', <sup><a href="#fnt__'.($ref).'" id="fn__'.($ref).'" class="fn_bot">';
172
                            $this->doc .= ($ref).')</a></sup> '.DOKU_LF;
173
                        }
174
                    }
175
176
                    // add footnote markup and close this footnote
177
                    $this->doc .= '<div class="content">'.$footnote.'</div>';
178
                    $this->doc .= '</div>'.DOKU_LF;
179
                }
180
            }
181
            $this->doc .= '</div>'.DOKU_LF;
182
        }
183
184
        // Prepare the TOC
185
        global $conf;
186
        if($this->info['toc'] && is_array($this->toc) && $conf['tocminheads'] && count($this->toc) >= $conf['tocminheads']) {
187
            global $TOC;
188
            $TOC = $this->toc;
189
        }
190
191
        // make sure there are no empty paragraphs
192
        $this->doc = preg_replace('#<p>\s*</p>#', '', $this->doc);
193
    }
194
195
    /**
196
     * Add an item to the TOC
197
     *
198
     * @param string $id       the hash link
199
     * @param string $text     the text to display
200
     * @param int    $level    the nesting level
201
     */
202
    function toc_additem($id, $text, $level) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
203
        global $conf;
204
205
        //handle TOC
206
        if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']) {
207
            $this->toc[] = html_mktocitem($id, $text, $level - $conf['toptoclevel'] + 1);
208
        }
209
    }
210
211
    /**
212
     * Render a heading
213
     *
214
     * @param string $text  the text to display
215
     * @param int    $level header level
216
     * @param int    $pos   byte position in the original source
217
     */
218
    function header($text, $level, $pos) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
219
        global $conf;
220
221
        if(blank($text)) return; //skip empty headlines
222
223
        $hid = $this->_headerToLink($text, true);
224
225
        //only add items within configured levels
226
        $this->toc_additem($hid, $text, $level);
227
228
        // adjust $node to reflect hierarchy of levels
229
        $this->node[$level - 1]++;
230
        if($level < $this->lastlevel) {
231
            for($i = 0; $i < $this->lastlevel - $level; $i++) {
232
                $this->node[$this->lastlevel - $i - 1] = 0;
233
            }
234
        }
235
        $this->lastlevel = $level;
236
237
        if($level <= $conf['maxseclevel'] &&
238
            count($this->sectionedits) > 0 &&
239
            $this->sectionedits[count($this->sectionedits) - 1]['target'] === 'section'
240
        ) {
241
            $this->finishSectionEdit($pos - 1);
242
        }
243
244
        // write the header
245
        $this->doc .= DOKU_LF.'<h'.$level;
246
        if($level <= $conf['maxseclevel']) {
247
            $data = array();
248
            $data['target'] = 'section';
249
            $data['name'] = $text;
250
            $data['hid'] = $hid;
251
            $data['codeblockOffset'] = $this->_codeblock;
252
            $this->doc .= ' class="'.$this->startSectionEdit($pos, $data).'"';
253
        }
254
        $this->doc .= ' id="'.$hid.'">';
255
        $this->doc .= $this->_xmlEntities($text);
256
        $this->doc .= "</h$level>".DOKU_LF;
257
    }
258
259
    /**
260
     * Open a new section
261
     *
262
     * @param int $level section level (as determined by the previous header)
263
     */
264
    function section_open($level) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
265
        $this->doc .= '<div class="level'.$level.'">'.DOKU_LF;
266
    }
267
268
    /**
269
     * Close the current section
270
     */
271
    function section_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
272
        $this->doc .= DOKU_LF.'</div>'.DOKU_LF;
273
    }
274
275
    /**
276
     * Render plain text data
277
     *
278
     * @param $text
279
     */
280
    function cdata($text) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
281
        $this->doc .= $this->_xmlEntities($text);
282
    }
283
284
    /**
285
     * Open a paragraph
286
     */
287
    function p_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
288
        $this->doc .= DOKU_LF.'<p>'.DOKU_LF;
289
    }
290
291
    /**
292
     * Close a paragraph
293
     */
294
    function p_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
295
        $this->doc .= DOKU_LF.'</p>'.DOKU_LF;
296
    }
297
298
    /**
299
     * Create a line break
300
     */
301
    function linebreak() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
302
        $this->doc .= '<br/>'.DOKU_LF;
303
    }
304
305
    /**
306
     * Create a horizontal line
307
     */
308
    function hr() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
309
        $this->doc .= '<hr />'.DOKU_LF;
310
    }
311
312
    /**
313
     * Start strong (bold) formatting
314
     */
315
    function strong_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
316
        $this->doc .= '<strong>';
317
    }
318
319
    /**
320
     * Stop strong (bold) formatting
321
     */
322
    function strong_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
323
        $this->doc .= '</strong>';
324
    }
325
326
    /**
327
     * Start emphasis (italics) formatting
328
     */
329
    function emphasis_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
330
        $this->doc .= '<em>';
331
    }
332
333
    /**
334
     * Stop emphasis (italics) formatting
335
     */
336
    function emphasis_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
337
        $this->doc .= '</em>';
338
    }
339
340
    /**
341
     * Start underline formatting
342
     */
343
    function underline_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
344
        $this->doc .= '<em class="u">';
345
    }
346
347
    /**
348
     * Stop underline formatting
349
     */
350
    function underline_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
351
        $this->doc .= '</em>';
352
    }
353
354
    /**
355
     * Start monospace formatting
356
     */
357
    function monospace_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
358
        $this->doc .= '<code>';
359
    }
360
361
    /**
362
     * Stop monospace formatting
363
     */
364
    function monospace_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
365
        $this->doc .= '</code>';
366
    }
367
368
    /**
369
     * Start a subscript
370
     */
371
    function subscript_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
372
        $this->doc .= '<sub>';
373
    }
374
375
    /**
376
     * Stop a subscript
377
     */
378
    function subscript_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
379
        $this->doc .= '</sub>';
380
    }
381
382
    /**
383
     * Start a superscript
384
     */
385
    function superscript_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
386
        $this->doc .= '<sup>';
387
    }
388
389
    /**
390
     * Stop a superscript
391
     */
392
    function superscript_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
393
        $this->doc .= '</sup>';
394
    }
395
396
    /**
397
     * Start deleted (strike-through) formatting
398
     */
399
    function deleted_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
400
        $this->doc .= '<del>';
401
    }
402
403
    /**
404
     * Stop deleted (strike-through) formatting
405
     */
406
    function deleted_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
407
        $this->doc .= '</del>';
408
    }
409
410
    /**
411
     * Callback for footnote start syntax
412
     *
413
     * All following content will go to the footnote instead of
414
     * the document. To achieve this the previous rendered content
415
     * is moved to $store and $doc is cleared
416
     *
417
     * @author Andreas Gohr <[email protected]>
418
     */
419
    function footnote_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
420
421
        // move current content to store and record footnote
422
        $this->store = $this->doc;
423
        $this->doc   = '';
424
    }
425
426
    /**
427
     * Callback for footnote end syntax
428
     *
429
     * All rendered content is moved to the $footnotes array and the old
430
     * content is restored from $store again
431
     *
432
     * @author Andreas Gohr
433
     */
434
    function footnote_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
435
        /** @var $fnid int takes track of seen footnotes, assures they are unique even across multiple docs FS#2841 */
436
        static $fnid = 0;
437
        // assign new footnote id (we start at 1)
438
        $fnid++;
439
440
        // recover footnote into the stack and restore old content
441
        $footnote    = $this->doc;
442
        $this->doc   = $this->store;
443
        $this->store = '';
444
445
        // check to see if this footnote has been seen before
446
        $i = array_search($footnote, $this->footnotes);
447
448
        if($i === false) {
449
            // its a new footnote, add it to the $footnotes array
450
            $this->footnotes[$fnid] = $footnote;
451
        } else {
452
            // seen this one before, save a placeholder
453
            $this->footnotes[$fnid] = "@@FNT".($i);
454
        }
455
456
        // output the footnote reference and link
457
        $this->doc .= '<sup><a href="#fn__'.$fnid.'" id="fnt__'.$fnid.'" class="fn_top">'.$fnid.')</a></sup>';
458
    }
459
460
    /**
461
     * Open an unordered list
462
     *
463
     * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input
464
     */
465
    function listu_open($classes = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
466
        $class = '';
467
        if($classes !== null) {
468
            if(is_array($classes)) $classes = join(' ', $classes);
469
            $class = " class=\"$classes\"";
470
        }
471
        $this->doc .= "<ul$class>".DOKU_LF;
472
    }
473
474
    /**
475
     * Close an unordered list
476
     */
477
    function listu_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
478
        $this->doc .= '</ul>'.DOKU_LF;
479
    }
480
481
    /**
482
     * Open an ordered list
483
     *
484
     * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input
485
     */
486
    function listo_open($classes = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
487
        $class = '';
488
        if($classes !== null) {
489
            if(is_array($classes)) $classes = join(' ', $classes);
490
            $class = " class=\"$classes\"";
491
        }
492
        $this->doc .= "<ol$class>".DOKU_LF;
493
    }
494
495
    /**
496
     * Close an ordered list
497
     */
498
    function listo_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
499
        $this->doc .= '</ol>'.DOKU_LF;
500
    }
501
502
    /**
503
     * Open a list item
504
     *
505
     * @param int $level the nesting level
506
     * @param bool $node true when a node; false when a leaf
507
     */
508
    function listitem_open($level, $node=false) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
509
        $branching = $node ? ' node' : '';
510
        $this->doc .= '<li class="level'.$level.$branching.'">';
511
    }
512
513
    /**
514
     * Close a list item
515
     */
516
    function listitem_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
517
        $this->doc .= '</li>'.DOKU_LF;
518
    }
519
520
    /**
521
     * Start the content of a list item
522
     */
523
    function listcontent_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
524
        $this->doc .= '<div class="li">';
525
    }
526
527
    /**
528
     * Stop the content of a list item
529
     */
530
    function listcontent_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
531
        $this->doc .= '</div>'.DOKU_LF;
532
    }
533
534
    /**
535
     * Output unformatted $text
536
     *
537
     * Defaults to $this->cdata()
538
     *
539
     * @param string $text
540
     */
541
    function unformatted($text) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
542
        $this->doc .= $this->_xmlEntities($text);
543
    }
544
545
    /**
546
     * Execute PHP code if allowed
547
     *
548
     * @param  string $text      PHP code that is either executed or printed
549
     * @param  string $wrapper   html element to wrap result if $conf['phpok'] is okff
550
     *
551
     * @author Andreas Gohr <[email protected]>
552
     */
553
    function php($text, $wrapper = 'code') {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
554
        global $conf;
555
556
        if($conf['phpok']) {
557
            ob_start();
558
            eval($text);
559
            $this->doc .= ob_get_contents();
560
            ob_end_clean();
561
        } else {
562
            $this->doc .= p_xhtml_cached_geshi($text, 'php', $wrapper);
563
        }
564
    }
565
566
    /**
567
     * Output block level PHP code
568
     *
569
     * If $conf['phpok'] is true this should evaluate the given code and append the result
570
     * to $doc
571
     *
572
     * @param string $text The PHP code
573
     */
574
    function phpblock($text) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
575
        $this->php($text, 'pre');
576
    }
577
578
    /**
579
     * Insert HTML if allowed
580
     *
581
     * @param  string $text      html text
582
     * @param  string $wrapper   html element to wrap result if $conf['htmlok'] is okff
583
     *
584
     * @author Andreas Gohr <[email protected]>
585
     */
586
    function html($text, $wrapper = 'code') {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
587
        global $conf;
588
589
        if($conf['htmlok']) {
590
            $this->doc .= $text;
591
        } else {
592
            $this->doc .= p_xhtml_cached_geshi($text, 'html4strict', $wrapper);
593
        }
594
    }
595
596
    /**
597
     * Output raw block-level HTML
598
     *
599
     * If $conf['htmlok'] is true this should add the code as is to $doc
600
     *
601
     * @param string $text The HTML
602
     */
603
    function htmlblock($text) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
604
        $this->html($text, 'pre');
605
    }
606
607
    /**
608
     * Start a block quote
609
     */
610
    function quote_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
611
        $this->doc .= '<blockquote><div class="no">'.DOKU_LF;
612
    }
613
614
    /**
615
     * Stop a block quote
616
     */
617
    function quote_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
618
        $this->doc .= '</div></blockquote>'.DOKU_LF;
619
    }
620
621
    /**
622
     * Output preformatted text
623
     *
624
     * @param string $text
625
     */
626
    function preformatted($text) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
627
        $this->doc .= '<pre class="code">'.trim($this->_xmlEntities($text), "\n\r").'</pre>'.DOKU_LF;
628
    }
629
630
    /**
631
     * Display text as file content, optionally syntax highlighted
632
     *
633
     * @param string $text     text to show
634
     * @param string $language programming language to use for syntax highlighting
635
     * @param string $filename file path label
636
     * @param array  $options  assoziative array with additional geshi options
637
     */
638
    function file($text, $language = null, $filename = null, $options=null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
639
        $this->_highlight('file', $text, $language, $filename, $options);
640
    }
641
642
    /**
643
     * Display text as code content, optionally syntax highlighted
644
     *
645
     * @param string $text     text to show
646
     * @param string $language programming language to use for syntax highlighting
647
     * @param string $filename file path label
648
     * @param array  $options  assoziative array with additional geshi options
649
     */
650
    function code($text, $language = null, $filename = null, $options=null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
651
        $this->_highlight('code', $text, $language, $filename, $options);
652
    }
653
654
    /**
655
     * Use GeSHi to highlight language syntax in code and file blocks
656
     *
657
     * @author Andreas Gohr <[email protected]>
658
     * @param string $type     code|file
659
     * @param string $text     text to show
660
     * @param string $language programming language to use for syntax highlighting
661
     * @param string $filename file path label
662
     * @param array  $options  assoziative array with additional geshi options
663
     */
664
    function _highlight($type, $text, $language = null, $filename = null, $options = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
665
        global $ID;
666
        global $lang;
667
        global $INPUT;
668
669
        $language = preg_replace(PREG_PATTERN_VALID_LANGUAGE, '', $language);
670
671
        if($filename) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filename of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
672
            // add icon
673
            list($ext) = mimetype($filename, false);
674
            $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext);
675
            $class = 'mediafile mf_'.$class;
676
677
            $offset = 0;
678
            if ($INPUT->has('codeblockOffset')) {
679
                $offset = $INPUT->str('codeblockOffset');
680
            }
681
            $this->doc .= '<dl class="'.$type.'">'.DOKU_LF;
682
            $this->doc .= '<dt><a href="'.exportlink($ID, 'code', array('codeblock' => $offset+$this->_codeblock)).'" title="'.$lang['download'].'" class="'.$class.'">';
683
            $this->doc .= hsc($filename);
684
            $this->doc .= '</a></dt>'.DOKU_LF.'<dd>';
685
        }
686
687
        if($text{0} == "\n") {
688
            $text = substr($text, 1);
689
        }
690
        if(substr($text, -1) == "\n") {
691
            $text = substr($text, 0, -1);
692
        }
693
694
        if(empty($language)) { // empty is faster than is_null and can prevent '' string
695
            $this->doc .= '<pre class="'.$type.'">'.$this->_xmlEntities($text).'</pre>'.DOKU_LF;
696
        } else {
697
            $class = 'code'; //we always need the code class to make the syntax highlighting apply
698
            if($type != 'code') $class .= ' '.$type;
699
700
            $this->doc .= "<pre class=\"$class $language\">".p_xhtml_cached_geshi($text, $language, '', $options).'</pre>'.DOKU_LF;
701
        }
702
703
        if($filename) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filename of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
704
            $this->doc .= '</dd></dl>'.DOKU_LF;
705
        }
706
707
        $this->_codeblock++;
708
    }
709
710
    /**
711
     * Format an acronym
712
     *
713
     * Uses $this->acronyms
714
     *
715
     * @param string $acronym
716
     */
717
    function acronym($acronym) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
718
719
        if(array_key_exists($acronym, $this->acronyms)) {
720
721
            $title = $this->_xmlEntities($this->acronyms[$acronym]);
722
723
            $this->doc .= '<abbr title="'.$title
724
                .'">'.$this->_xmlEntities($acronym).'</abbr>';
725
726
        } else {
727
            $this->doc .= $this->_xmlEntities($acronym);
728
        }
729
    }
730
731
    /**
732
     * Format a smiley
733
     *
734
     * Uses $this->smiley
735
     *
736
     * @param string $smiley
737
     */
738
    function smiley($smiley) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
739
        if(array_key_exists($smiley, $this->smileys)) {
740
            $this->doc .= '<img src="'.DOKU_BASE.'lib/images/smileys/'.$this->smileys[$smiley].
741
                '" class="icon" alt="'.
742
                $this->_xmlEntities($smiley).'" />';
743
        } else {
744
            $this->doc .= $this->_xmlEntities($smiley);
745
        }
746
    }
747
748
    /**
749
     * Format an entity
750
     *
751
     * Entities are basically small text replacements
752
     *
753
     * Uses $this->entities
754
     *
755
     * @param string $entity
756
     */
757
    function entity($entity) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
758
        if(array_key_exists($entity, $this->entities)) {
759
            $this->doc .= $this->entities[$entity];
760
        } else {
761
            $this->doc .= $this->_xmlEntities($entity);
762
        }
763
    }
764
765
    /**
766
     * Typographically format a multiply sign
767
     *
768
     * Example: ($x=640, $y=480) should result in "640×480"
769
     *
770
     * @param string|int $x first value
771
     * @param string|int $y second value
772
     */
773
    function multiplyentity($x, $y) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
774
        $this->doc .= "$x&times;$y";
775
    }
776
777
    /**
778
     * Render an opening single quote char (language specific)
779
     */
780
    function singlequoteopening() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
781
        global $lang;
782
        $this->doc .= $lang['singlequoteopening'];
783
    }
784
785
    /**
786
     * Render a closing single quote char (language specific)
787
     */
788
    function singlequoteclosing() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
789
        global $lang;
790
        $this->doc .= $lang['singlequoteclosing'];
791
    }
792
793
    /**
794
     * Render an apostrophe char (language specific)
795
     */
796
    function apostrophe() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
797
        global $lang;
798
        $this->doc .= $lang['apostrophe'];
799
    }
800
801
    /**
802
     * Render an opening double quote char (language specific)
803
     */
804
    function doublequoteopening() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
805
        global $lang;
806
        $this->doc .= $lang['doublequoteopening'];
807
    }
808
809
    /**
810
     * Render an closinging double quote char (language specific)
811
     */
812
    function doublequoteclosing() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
813
        global $lang;
814
        $this->doc .= $lang['doublequoteclosing'];
815
    }
816
817
    /**
818
     * Render a CamelCase link
819
     *
820
     * @param string $link       The link name
821
     * @param bool   $returnonly whether to return html or write to doc attribute
822
     * @return void|string writes to doc attribute or returns html depends on $returnonly
823
     *
824
     * @see http://en.wikipedia.org/wiki/CamelCase
825
     */
826
    function camelcaselink($link, $returnonly = false) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
827
        if($returnonly) {
828
          return $this->internallink($link, $link, null, true);
829
        } else {
830
          $this->internallink($link, $link);
831
        }
832
    }
833
834
    /**
835
     * Render a page local link
836
     *
837
     * @param string $hash       hash link identifier
838
     * @param string $name       name for the link
839
     * @param bool   $returnonly whether to return html or write to doc attribute
840
     * @return void|string writes to doc attribute or returns html depends on $returnonly
841
     */
842
    function locallink($hash, $name = null, $returnonly = false) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
843
        global $ID;
844
        $name  = $this->_getLinkTitle($name, $hash, $isImage);
845
        $hash  = $this->_headerToLink($hash);
846
        $title = $ID.' ↵';
847
848
        $doc = '<a href="#'.$hash.'" title="'.$title.'" class="wikilink1">';
849
        $doc .= $name;
850
        $doc .= '</a>';
851
852
        if($returnonly) {
853
          return $doc;
854
        } else {
855
          $this->doc .= $doc;
856
        }
857
    }
858
859
    /**
860
     * Render an internal Wiki Link
861
     *
862
     * $search,$returnonly & $linktype are not for the renderer but are used
863
     * elsewhere - no need to implement them in other renderers
864
     *
865
     * @author Andreas Gohr <[email protected]>
866
     * @param string      $id         pageid
867
     * @param string|null $name       link name
868
     * @param string|null $search     adds search url param
869
     * @param bool        $returnonly whether to return html or write to doc attribute
870
     * @param string      $linktype   type to set use of headings
871
     * @return void|string writes to doc attribute or returns html depends on $returnonly
872
     */
873
    function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
874
        global $conf;
875
        global $ID;
876
        global $INFO;
877
878
        $params = '';
879
        $parts  = explode('?', $id, 2);
880
        if(count($parts) === 2) {
881
            $id     = $parts[0];
882
            $params = $parts[1];
883
        }
884
885
        // For empty $id we need to know the current $ID
886
        // We need this check because _simpleTitle needs
887
        // correct $id and resolve_pageid() use cleanID($id)
888
        // (some things could be lost)
889
        if($id === '') {
890
            $id = $ID;
891
        }
892
893
        // default name is based on $id as given
894
        $default = $this->_simpleTitle($id);
895
896
        // now first resolve and clean up the $id
897
        if ($conf['rev_handle'] == 'only_media') {
898
            $date_at = '';
899
        } else {
900
            $date_at = $this->date_at;
901
        }
902
        resolve_pageid(getNS($ID), $id, $exists, $date_at, true);
0 ignored issues
show
Security Bug introduced by
It seems like getNS($ID) targeting getNS() can also be of type false; however, resolve_pageid() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
903
904
        $link = array();
905
        $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype);
906
        if(!$isImage) {
907
            if($exists) {
908
                $class = 'wikilink1';
909
            } else {
910
                $class       = 'wikilink2';
911
                $link['rel'] = 'nofollow';
912
            }
913
        } else {
914
            $class = 'media';
915
        }
916
917
        //keep hash anchor
918
        @list($id, $hash) = explode('#', $id, 2);
919
        if(!empty($hash)) $hash = $this->_headerToLink($hash);
920
921
        //prepare for formating
922
        $link['target'] = $conf['target']['wiki'];
923
        $link['style']  = '';
924
        $link['pre']    = '';
925
        $link['suf']    = '';
926
        // highlight link to current page
927
        if($id == $INFO['id']) {
928
            $link['pre'] = '<span class="curid">';
929
            $link['suf'] = '</span>';
930
        }
931
        $link['more']   = '';
932
        $link['class']  = $class;
933
        if($this->date_at && $conf['rev_handle'] != 'only_media') {
934
            $params = $params.'&at='.rawurlencode($this->date_at);
935
        }
936
        $link['url']    = wl($id, $params);
937
        $link['name']   = $name;
938
        $link['title']  = $id;
939
        //add search string
940
        if($search) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $search of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
941
            ($conf['userewrite']) ? $link['url'] .= '?' : $link['url'] .= '&amp;';
942
            if(is_array($search)) {
943
                $search = array_map('rawurlencode', $search);
944
                $link['url'] .= 's[]='.join('&amp;s[]=', $search);
945
            } else {
946
                $link['url'] .= 's='.rawurlencode($search);
947
            }
948
        }
949
950
        //keep hash
951
        if($hash) $link['url'] .= '#'.$hash;
952
953
        //output formatted
954
        if($returnonly) {
955
            return $this->_formatLink($link);
956
        } else {
957
            $this->doc .= $this->_formatLink($link);
958
        }
959
    }
960
961
    /**
962
     * Render an external link
963
     *
964
     * @param string       $url        full URL with scheme
965
     * @param string|array $name       name for the link, array for media file
966
     * @param bool         $returnonly whether to return html or write to doc attribute
967
     * @return void|string writes to doc attribute or returns html depends on $returnonly
968
     */
969
    function externallink($url, $name = null, $returnonly = false) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
970
        global $conf;
971
972
        $name = $this->_getLinkTitle($name, $url, $isImage);
0 ignored issues
show
Bug introduced by
It seems like $name can also be of type null; however, Doku_Renderer_xhtml::_getLinkTitle() does only seem to accept string|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
973
974
        // url might be an attack vector, only allow registered protocols
975
        if(is_null($this->schemes)) $this->schemes = getSchemes();
976
        list($scheme) = explode('://', $url);
977
        $scheme = strtolower($scheme);
978
        if(!in_array($scheme, $this->schemes)) $url = '';
979
980
        // is there still an URL?
981
        if(!$url) {
982
            if($returnonly) {
983
                return $name;
984
            } else {
985
                $this->doc .= $name;
986
            }
987
            return;
988
        }
989
990
        // set class
991
        if(!$isImage) {
992
            $class = 'urlextern';
993
        } else {
994
            $class = 'media';
995
        }
996
997
        //prepare for formating
998
        $link = array();
999
        $link['target'] = $conf['target']['extern'];
1000
        $link['style']  = '';
1001
        $link['pre']    = '';
1002
        $link['suf']    = '';
1003
        $link['more']   = '';
1004
        $link['class']  = $class;
1005
        $link['url']    = $url;
1006
        $link['rel']    = '';
1007
1008
        $link['name']  = $name;
1009
        $link['title'] = $this->_xmlEntities($url);
1010
        if($conf['relnofollow']) $link['rel'] .= ' nofollow';
1011
        if($conf['target']['extern']) $link['rel'] .= ' noopener';
1012
1013
        //output formatted
1014
        if($returnonly) {
1015
            return $this->_formatLink($link);
1016
        } else {
1017
            $this->doc .= $this->_formatLink($link);
1018
        }
1019
    }
1020
1021
    /**
1022
     * Render an interwiki link
1023
     *
1024
     * You may want to use $this->_resolveInterWiki() here
1025
     *
1026
     * @param string       $match      original link - probably not much use
1027
     * @param string|array $name       name for the link, array for media file
1028
     * @param string       $wikiName   indentifier (shortcut) for the remote wiki
1029
     * @param string       $wikiUri    the fragment parsed from the original link
1030
     * @param bool         $returnonly whether to return html or write to doc attribute
1031
     * @return void|string writes to doc attribute or returns html depends on $returnonly
1032
     */
1033
    function interwikilink($match, $name = null, $wikiName, $wikiUri, $returnonly = false) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1034
        global $conf;
1035
1036
        $link           = array();
1037
        $link['target'] = $conf['target']['interwiki'];
1038
        $link['pre']    = '';
1039
        $link['suf']    = '';
1040
        $link['more']   = '';
1041
        $link['name']   = $this->_getLinkTitle($name, $wikiUri, $isImage);
0 ignored issues
show
Bug introduced by
It seems like $name defined by parameter $name on line 1033 can also be of type null; however, Doku_Renderer_xhtml::_getLinkTitle() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1042
        $link['rel']    = '';
1043
1044
        //get interwiki URL
1045
        $exists = null;
1046
        $url    = $this->_resolveInterWiki($wikiName, $wikiUri, $exists);
1047
1048
        if(!$isImage) {
1049
            $class         = preg_replace('/[^_\-a-z0-9]+/i', '_', $wikiName);
1050
            $link['class'] = "interwiki iw_$class";
1051
        } else {
1052
            $link['class'] = 'media';
1053
        }
1054
1055
        //do we stay at the same server? Use local target
1056
        if(strpos($url, DOKU_URL) === 0 OR strpos($url, DOKU_BASE) === 0) {
1057
            $link['target'] = $conf['target']['wiki'];
1058
        }
1059
        if($exists !== null && !$isImage) {
1060
            if($exists) {
1061
                $link['class'] .= ' wikilink1';
1062
            } else {
1063
                $link['class'] .= ' wikilink2';
1064
                $link['rel'] .= ' nofollow';
1065
            }
1066
        }
1067
        if($conf['target']['interwiki']) $link['rel'] .= ' noopener';
1068
1069
        $link['url']   = $url;
1070
        $link['title'] = htmlspecialchars($link['url']);
1071
1072
        //output formatted
1073
        if($returnonly) {
1074
            return $this->_formatLink($link);
1075
        } else {
1076
            $this->doc .= $this->_formatLink($link);
1077
        }
1078
    }
1079
1080
    /**
1081
     * Link to windows share
1082
     *
1083
     * @param string       $url        the link
1084
     * @param string|array $name       name for the link, array for media file
1085
     * @param bool         $returnonly whether to return html or write to doc attribute
1086
     * @return void|string writes to doc attribute or returns html depends on $returnonly
1087
     */
1088
    function windowssharelink($url, $name = null, $returnonly = false) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1089
        global $conf;
1090
1091
        //simple setup
1092
        $link = array();
1093
        $link['target'] = $conf['target']['windows'];
1094
        $link['pre']    = '';
1095
        $link['suf']    = '';
1096
        $link['style']  = '';
1097
1098
        $link['name'] = $this->_getLinkTitle($name, $url, $isImage);
0 ignored issues
show
Bug introduced by
It seems like $name defined by parameter $name on line 1088 can also be of type null; however, Doku_Renderer_xhtml::_getLinkTitle() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1099
        if(!$isImage) {
1100
            $link['class'] = 'windows';
1101
        } else {
1102
            $link['class'] = 'media';
1103
        }
1104
1105
        $link['title'] = $this->_xmlEntities($url);
1106
        $url           = str_replace('\\', '/', $url);
1107
        $url           = 'file:///'.$url;
1108
        $link['url']   = $url;
1109
1110
        //output formatted
1111
        if($returnonly) {
1112
            return $this->_formatLink($link);
1113
        } else {
1114
            $this->doc .= $this->_formatLink($link);
1115
        }
1116
    }
1117
1118
    /**
1119
     * Render a linked E-Mail Address
1120
     *
1121
     * Honors $conf['mailguard'] setting
1122
     *
1123
     * @param string       $address    Email-Address
1124
     * @param string|array $name       name for the link, array for media file
1125
     * @param bool         $returnonly whether to return html or write to doc attribute
1126
     * @return void|string writes to doc attribute or returns html depends on $returnonly
1127
     */
1128
    function emaillink($address, $name = null, $returnonly = false) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1129
        global $conf;
1130
        //simple setup
1131
        $link           = array();
1132
        $link['target'] = '';
1133
        $link['pre']    = '';
1134
        $link['suf']    = '';
1135
        $link['style']  = '';
1136
        $link['more']   = '';
1137
1138
        $name = $this->_getLinkTitle($name, '', $isImage);
0 ignored issues
show
Bug introduced by
It seems like $name can also be of type null; however, Doku_Renderer_xhtml::_getLinkTitle() does only seem to accept string|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1139
        if(!$isImage) {
1140
            $link['class'] = 'mail';
1141
        } else {
1142
            $link['class'] = 'media';
1143
        }
1144
1145
        $address = $this->_xmlEntities($address);
1146
        $address = obfuscate($address);
1147
        $title   = $address;
1148
1149
        if(empty($name)) {
1150
            $name = $address;
1151
        }
1152
1153
        if($conf['mailguard'] == 'visible') $address = rawurlencode($address);
1154
1155
        $link['url']   = 'mailto:'.$address;
1156
        $link['name']  = $name;
1157
        $link['title'] = $title;
1158
1159
        //output formatted
1160
        if($returnonly) {
1161
            return $this->_formatLink($link);
1162
        } else {
1163
            $this->doc .= $this->_formatLink($link);
1164
        }
1165
    }
1166
1167
    /**
1168
     * Render an internal media file
1169
     *
1170
     * @param string $src       media ID
1171
     * @param string $title     descriptive text
1172
     * @param string $align     left|center|right
1173
     * @param int    $width     width of media in pixel
1174
     * @param int    $height    height of media in pixel
1175
     * @param string $cache     cache|recache|nocache
1176
     * @param string $linking   linkonly|detail|nolink
1177
     * @param bool   $return    return HTML instead of adding to $doc
1178
     * @return void|string writes to doc attribute or returns html depends on $return
1179
     */
1180
    function internalmedia($src, $title = null, $align = null, $width = null,
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1181
                           $height = null, $cache = null, $linking = null, $return = false) {
1182
        global $ID;
1183
        if (strpos($src, '#') !== false) {
1184
            list($src, $hash) = explode('#', $src, 2);
1185
        }
1186
        resolve_mediaid(getNS($ID), $src, $exists, $this->date_at, true);
0 ignored issues
show
Security Bug introduced by
It seems like getNS($ID) targeting getNS() can also be of type false; however, resolve_mediaid() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
1187
1188
        $noLink = false;
1189
        $render = ($linking == 'linkonly') ? false : true;
1190
        $link   = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render);
1191
1192
        list($ext, $mime) = mimetype($src, false);
1193
        if(substr($mime, 0, 5) == 'image' && $render) {
1194
            $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src)), ($linking == 'direct'));
1195
        } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) {
1196
            // don't link movies
1197
            $noLink = true;
1198
        } else {
1199
            // add file icons
1200
            $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext);
1201
            $link['class'] .= ' mediafile mf_'.$class;
1202
            $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache , 'rev'=>$this->_getLastMediaRevisionAt($src)), true);
1203
            if($exists) $link['title'] .= ' ('.filesize_h(filesize(mediaFN($src))).')';
1204
        }
1205
1206
        if (!empty($hash)) $link['url'] .= '#'.$hash;
1207
1208
        //markup non existing files
1209
        if(!$exists) {
1210
            $link['class'] .= ' wikilink2';
1211
        }
1212
1213
        //output formatted
1214
        if($return) {
1215
            if($linking == 'nolink' || $noLink) return $link['name'];
1216
            else return $this->_formatLink($link);
1217
        } else {
1218
            if($linking == 'nolink' || $noLink) $this->doc .= $link['name'];
1219
            else $this->doc .= $this->_formatLink($link);
1220
        }
1221
    }
1222
1223
    /**
1224
     * Render an external media file
1225
     *
1226
     * @param string $src     full media URL
1227
     * @param string $title   descriptive text
1228
     * @param string $align   left|center|right
1229
     * @param int    $width   width of media in pixel
1230
     * @param int    $height  height of media in pixel
1231
     * @param string $cache   cache|recache|nocache
1232
     * @param string $linking linkonly|detail|nolink
1233
     * @param bool   $return  return HTML instead of adding to $doc
1234
     * @return void|string writes to doc attribute or returns html depends on $return
1235
     */
1236
    function externalmedia($src, $title = null, $align = null, $width = null,
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1237
                           $height = null, $cache = null, $linking = null, $return = false) {
1238
        if(link_isinterwiki($src)){
1239
            list($shortcut, $reference) = explode('>', $src, 2);
1240
            $exists = null;
1241
            $src = $this->_resolveInterWiki($shortcut, $reference, $exists);
1242
        }
1243
        list($src, $hash) = explode('#', $src, 2);
1244
        $noLink = false;
1245
        $render = ($linking == 'linkonly') ? false : true;
1246
        $link   = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render);
1247
1248
        $link['url'] = ml($src, array('cache' => $cache));
1249
1250
        list($ext, $mime) = mimetype($src, false);
1251
        if(substr($mime, 0, 5) == 'image' && $render) {
1252
            // link only jpeg images
1253
            // if ($ext != 'jpg' && $ext != 'jpeg') $noLink = true;
1254
        } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) {
1255
            // don't link movies
1256
            $noLink = true;
1257
        } else {
1258
            // add file icons
1259
            $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext);
1260
            $link['class'] .= ' mediafile mf_'.$class;
1261
        }
1262
1263
        if($hash) $link['url'] .= '#'.$hash;
1264
1265
        //output formatted
1266
        if($return) {
1267
            if($linking == 'nolink' || $noLink) return $link['name'];
1268
            else return $this->_formatLink($link);
1269
        } else {
1270
            if($linking == 'nolink' || $noLink) $this->doc .= $link['name'];
1271
            else $this->doc .= $this->_formatLink($link);
1272
        }
1273
    }
1274
1275
    /**
1276
     * Renders an RSS feed
1277
     *
1278
     * @param string $url    URL of the feed
1279
     * @param array  $params Finetuning of the output
1280
     *
1281
     * @author Andreas Gohr <[email protected]>
1282
     */
1283
    function rss($url, $params) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1284
        global $lang;
1285
        global $conf;
1286
1287
        require_once(DOKU_INC.'inc/FeedParser.php');
1288
        $feed = new FeedParser();
1289
        $feed->set_feed_url($url);
1290
1291
        //disable warning while fetching
1292
        if(!defined('DOKU_E_LEVEL')) {
1293
            $elvl = error_reporting(E_ERROR);
1294
        }
1295
        $rc = $feed->init();
1296
        if(isset($elvl)) {
1297
            error_reporting($elvl);
1298
        }
1299
1300
        if($params['nosort']) $feed->enable_order_by_date(false);
1301
1302
        //decide on start and end
1303
        if($params['reverse']) {
1304
            $mod   = -1;
1305
            $start = $feed->get_item_quantity() - 1;
1306
            $end   = $start - ($params['max']);
1307
            $end   = ($end < -1) ? -1 : $end;
1308
        } else {
1309
            $mod   = 1;
1310
            $start = 0;
1311
            $end   = $feed->get_item_quantity();
1312
            $end   = ($end > $params['max']) ? $params['max'] : $end;
1313
        }
1314
1315
        $this->doc .= '<ul class="rss">';
1316
        if($rc) {
1317
            for($x = $start; $x != $end; $x += $mod) {
1318
                $item = $feed->get_item($x);
1319
                $this->doc .= '<li><div class="li">';
1320
                // support feeds without links
1321
                $lnkurl = $item->get_permalink();
1322
                if($lnkurl) {
1323
                    // title is escaped by SimplePie, we unescape here because it
1324
                    // is escaped again in externallink() FS#1705
1325
                    $this->externallink(
1326
                        $item->get_permalink(),
1327
                        html_entity_decode($item->get_title(), ENT_QUOTES, 'UTF-8')
1328
                    );
1329
                } else {
1330
                    $this->doc .= ' '.$item->get_title();
1331
                }
1332
                if($params['author']) {
1333
                    $author = $item->get_author(0);
1334
                    if($author) {
1335
                        $name = $author->get_name();
1336
                        if(!$name) $name = $author->get_email();
1337
                        if($name) $this->doc .= ' '.$lang['by'].' '.hsc($name);
1338
                    }
1339
                }
1340
                if($params['date']) {
1341
                    $this->doc .= ' ('.$item->get_local_date($conf['dformat']).')';
1342
                }
1343
                if($params['details']) {
1344
                    $this->doc .= '<div class="detail">';
1345
                    if($conf['htmlok']) {
1346
                        $this->doc .= $item->get_description();
1347
                    } else {
1348
                        $this->doc .= strip_tags($item->get_description());
1349
                    }
1350
                    $this->doc .= '</div>';
1351
                }
1352
1353
                $this->doc .= '</div></li>';
1354
            }
1355
        } else {
1356
            $this->doc .= '<li><div class="li">';
1357
            $this->doc .= '<em>'.$lang['rssfailed'].'</em>';
1358
            $this->externallink($url);
1359
            if($conf['allowdebug']) {
1360
                $this->doc .= '<!--'.hsc($feed->error).'-->';
1361
            }
1362
            $this->doc .= '</div></li>';
1363
        }
1364
        $this->doc .= '</ul>';
1365
    }
1366
1367
    /**
1368
     * Start a table
1369
     *
1370
     * @param int $maxcols maximum number of columns
1371
     * @param int $numrows NOT IMPLEMENTED
1372
     * @param int $pos byte position in the original source
1373
     * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input
1374
     */
1375
    function table_open($maxcols = null, $numrows = null, $pos = null, $classes = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1376
        // initialize the row counter used for classes
1377
        $this->_counter['row_counter'] = 0;
1378
        $class                         = 'table';
1379
        if($classes !== null) {
1380
            if(is_array($classes)) $classes = join(' ', $classes);
1381
            $class .= ' ' . $classes;
1382
        }
1383
        if($pos !== null) {
1384
            $hid = $this->_headerToLink($class, true);
1385
            $data = array();
1386
            $data['target'] = 'table';
1387
            $data['name'] = '';
1388
            $data['hid'] = $hid;
1389
            $class .= ' '.$this->startSectionEdit($pos, $data);
1390
        }
1391
        $this->doc .= '<div class="'.$class.'"><table class="inline">'.
1392
            DOKU_LF;
1393
    }
1394
1395
    /**
1396
     * Close a table
1397
     *
1398
     * @param int $pos byte position in the original source
1399
     */
1400
    function table_close($pos = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1401
        $this->doc .= '</table></div>'.DOKU_LF;
1402
        if($pos !== null) {
1403
            $this->finishSectionEdit($pos);
1404
        }
1405
    }
1406
1407
    /**
1408
     * Open a table header
1409
     */
1410
    function tablethead_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1411
        $this->doc .= DOKU_TAB.'<thead>'.DOKU_LF;
1412
    }
1413
1414
    /**
1415
     * Close a table header
1416
     */
1417
    function tablethead_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1418
        $this->doc .= DOKU_TAB.'</thead>'.DOKU_LF;
1419
    }
1420
1421
    /**
1422
     * Open a table body
1423
     */
1424
    function tabletbody_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1425
        $this->doc .= DOKU_TAB.'<tbody>'.DOKU_LF;
1426
    }
1427
1428
    /**
1429
     * Close a table body
1430
     */
1431
    function tabletbody_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1432
        $this->doc .= DOKU_TAB.'</tbody>'.DOKU_LF;
1433
    }
1434
1435
    /**
1436
     * Open a table footer
1437
     */
1438
    function tabletfoot_open() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1439
        $this->doc .= DOKU_TAB.'<tfoot>'.DOKU_LF;
1440
    }
1441
1442
    /**
1443
     * Close a table footer
1444
     */
1445
    function tabletfoot_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1446
        $this->doc .= DOKU_TAB.'</tfoot>'.DOKU_LF;
1447
    }
1448
1449
    /**
1450
     * Open a table row
1451
     *
1452
     * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input
1453
     */
1454
    function tablerow_open($classes = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1455
        // initialize the cell counter used for classes
1456
        $this->_counter['cell_counter'] = 0;
1457
        $class                          = 'row'.$this->_counter['row_counter']++;
1458
        if($classes !== null) {
1459
            if(is_array($classes)) $classes = join(' ', $classes);
1460
            $class .= ' ' . $classes;
1461
        }
1462
        $this->doc .= DOKU_TAB.'<tr class="'.$class.'">'.DOKU_LF.DOKU_TAB.DOKU_TAB;
1463
    }
1464
1465
    /**
1466
     * Close a table row
1467
     */
1468
    function tablerow_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1469
        $this->doc .= DOKU_LF.DOKU_TAB.'</tr>'.DOKU_LF;
1470
    }
1471
1472
    /**
1473
     * Open a table header cell
1474
     *
1475
     * @param int    $colspan
1476
     * @param string $align left|center|right
1477
     * @param int    $rowspan
1478
     * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input
1479
     */
1480
    function tableheader_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1481
        $class = 'class="col'.$this->_counter['cell_counter']++;
1482
        if(!is_null($align)) {
1483
            $class .= ' '.$align.'align';
1484
        }
1485
        if($classes !== null) {
1486
            if(is_array($classes)) $classes = join(' ', $classes);
1487
            $class .= ' ' . $classes;
1488
        }
1489
        $class .= '"';
1490
        $this->doc .= '<th '.$class;
1491
        if($colspan > 1) {
1492
            $this->_counter['cell_counter'] += $colspan - 1;
1493
            $this->doc .= ' colspan="'.$colspan.'"';
1494
        }
1495
        if($rowspan > 1) {
1496
            $this->doc .= ' rowspan="'.$rowspan.'"';
1497
        }
1498
        $this->doc .= '>';
1499
    }
1500
1501
    /**
1502
     * Close a table header cell
1503
     */
1504
    function tableheader_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1505
        $this->doc .= '</th>';
1506
    }
1507
1508
    /**
1509
     * Open a table cell
1510
     *
1511
     * @param int       $colspan
1512
     * @param string    $align left|center|right
1513
     * @param int       $rowspan
1514
     * @param string|string[]    $classes css classes - have to be valid, do not pass unfiltered user input
1515
     */
1516
    function tablecell_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1517
        $class = 'class="col'.$this->_counter['cell_counter']++;
1518
        if(!is_null($align)) {
1519
            $class .= ' '.$align.'align';
1520
        }
1521
        if($classes !== null) {
1522
            if(is_array($classes)) $classes = join(' ', $classes);
1523
            $class .= ' ' . $classes;
1524
        }
1525
        $class .= '"';
1526
        $this->doc .= '<td '.$class;
1527
        if($colspan > 1) {
1528
            $this->_counter['cell_counter'] += $colspan - 1;
1529
            $this->doc .= ' colspan="'.$colspan.'"';
1530
        }
1531
        if($rowspan > 1) {
1532
            $this->doc .= ' rowspan="'.$rowspan.'"';
1533
        }
1534
        $this->doc .= '>';
1535
    }
1536
1537
    /**
1538
     * Close a table cell
1539
     */
1540
    function tablecell_close() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1541
        $this->doc .= '</td>';
1542
    }
1543
1544
    /**
1545
     * Returns the current header level.
1546
     * (required e.g. by the filelist plugin)
1547
     *
1548
     * @return int The current header level
1549
     */
1550
    function getLastlevel() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1551
        return $this->lastlevel;
1552
    }
1553
1554
    #region Utility functions
1555
1556
    /**
1557
     * Build a link
1558
     *
1559
     * Assembles all parts defined in $link returns HTML for the link
1560
     *
1561
     * @param array $link attributes of a link
1562
     * @return string
1563
     *
1564
     * @author Andreas Gohr <[email protected]>
1565
     */
1566
    function _formatLink($link) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1567
        //make sure the url is XHTML compliant (skip mailto)
1568
        if(substr($link['url'], 0, 7) != 'mailto:') {
1569
            $link['url'] = str_replace('&', '&amp;', $link['url']);
1570
            $link['url'] = str_replace('&amp;amp;', '&amp;', $link['url']);
1571
        }
1572
        //remove double encodings in titles
1573
        $link['title'] = str_replace('&amp;amp;', '&amp;', $link['title']);
1574
1575
        // be sure there are no bad chars in url or title
1576
        // (we can't do this for name because it can contain an img tag)
1577
        $link['url']   = strtr($link['url'], array('>' => '%3E', '<' => '%3C', '"' => '%22'));
1578
        $link['title'] = strtr($link['title'], array('>' => '&gt;', '<' => '&lt;', '"' => '&quot;'));
1579
1580
        $ret = '';
1581
        $ret .= $link['pre'];
1582
        $ret .= '<a href="'.$link['url'].'"';
1583
        if(!empty($link['class'])) $ret .= ' class="'.$link['class'].'"';
1584
        if(!empty($link['target'])) $ret .= ' target="'.$link['target'].'"';
1585
        if(!empty($link['title'])) $ret .= ' title="'.$link['title'].'"';
1586
        if(!empty($link['style'])) $ret .= ' style="'.$link['style'].'"';
1587
        if(!empty($link['rel'])) $ret .= ' rel="'.trim($link['rel']).'"';
1588
        if(!empty($link['more'])) $ret .= ' '.$link['more'];
1589
        $ret .= '>';
1590
        $ret .= $link['name'];
1591
        $ret .= '</a>';
1592
        $ret .= $link['suf'];
1593
        return $ret;
1594
    }
1595
1596
    /**
1597
     * Renders internal and external media
1598
     *
1599
     * @author Andreas Gohr <[email protected]>
1600
     * @param string $src       media ID
1601
     * @param string $title     descriptive text
1602
     * @param string $align     left|center|right
1603
     * @param int    $width     width of media in pixel
1604
     * @param int    $height    height of media in pixel
1605
     * @param string $cache     cache|recache|nocache
1606
     * @param bool   $render    should the media be embedded inline or just linked
1607
     * @return string
1608
     */
1609
    function _media($src, $title = null, $align = null, $width = null,
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1610
                    $height = null, $cache = null, $render = true) {
1611
1612
        $ret = '';
1613
1614
        list($ext, $mime) = mimetype($src);
1615
        if(substr($mime, 0, 5) == 'image') {
1616
            // first get the $title
1617
            if(!is_null($title)) {
1618
                $title = $this->_xmlEntities($title);
1619
            } elseif($ext == 'jpg' || $ext == 'jpeg') {
1620
                //try to use the caption from IPTC/EXIF
1621
                require_once(DOKU_INC.'inc/JpegMeta.php');
1622
                $jpeg = new JpegMeta(mediaFN($src));
1623
                if($jpeg !== false) $cap = $jpeg->getTitle();
1624
                if(!empty($cap)) {
1625
                    $title = $this->_xmlEntities($cap);
1626
                }
1627
            }
1628
            if(!$render) {
1629
                // if the picture is not supposed to be rendered
1630
                // return the title of the picture
1631
                if(!$title) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $title of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1632
                    // just show the sourcename
1633
                    $title = $this->_xmlEntities(utf8_basename(noNS($src)));
1634
                }
1635
                return $title;
1636
            }
1637
            //add image tag
1638
            $ret .= '<img src="'.ml($src, array('w' => $width, 'h' => $height, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src))).'"';
1639
            $ret .= ' class="media'.$align.'"';
1640
1641
            if($title) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $title of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1642
                $ret .= ' title="'.$title.'"';
1643
                $ret .= ' alt="'.$title.'"';
1644
            } else {
1645
                $ret .= ' alt=""';
1646
            }
1647
1648
            if(!is_null($width))
1649
                $ret .= ' width="'.$this->_xmlEntities($width).'"';
1650
1651
            if(!is_null($height))
1652
                $ret .= ' height="'.$this->_xmlEntities($height).'"';
1653
1654
            $ret .= ' />';
1655
1656
        } elseif(media_supportedav($mime, 'video') || media_supportedav($mime, 'audio')) {
1657
            // first get the $title
1658
            $title = !is_null($title) ? $this->_xmlEntities($title) : false;
1659
            if(!$render) {
1660
                // if the file is not supposed to be rendered
1661
                // return the title of the file (just the sourcename if there is no title)
1662
                return $title ? $title : $this->_xmlEntities(utf8_basename(noNS($src)));
1663
            }
1664
1665
            $att          = array();
1666
            $att['class'] = "media$align";
1667
            if($title) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $title of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1668
                $att['title'] = $title;
1669
            }
1670
1671
            if(media_supportedav($mime, 'video')) {
1672
                //add video
1673
                $ret .= $this->_video($src, $width, $height, $att);
1674
            }
1675
            if(media_supportedav($mime, 'audio')) {
1676
                //add audio
1677
                $ret .= $this->_audio($src, $att);
1678
            }
1679
1680
        } elseif($mime == 'application/x-shockwave-flash') {
1681
            if(!$render) {
1682
                // if the flash is not supposed to be rendered
1683
                // return the title of the flash
1684
                if(!$title) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $title of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1685
                    // just show the sourcename
1686
                    $title = utf8_basename(noNS($src));
1687
                }
1688
                return $this->_xmlEntities($title);
1689
            }
1690
1691
            $att          = array();
1692
            $att['class'] = "media$align";
1693
            if($align == 'right') $att['align'] = 'right';
1694
            if($align == 'left') $att['align'] = 'left';
1695
            $ret .= html_flashobject(
1696
                ml($src, array('cache' => $cache), true, '&'), $width, $height,
1697
                array('quality' => 'high'),
1698
                null,
1699
                $att,
1700
                $this->_xmlEntities($title)
1701
            );
1702
        } elseif($title) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $title of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1703
            // well at least we have a title to display
1704
            $ret .= $this->_xmlEntities($title);
1705
        } else {
1706
            // just show the sourcename
1707
            $ret .= $this->_xmlEntities(utf8_basename(noNS($src)));
1708
        }
1709
1710
        return $ret;
1711
    }
1712
1713
    /**
1714
     * Escape string for output
1715
     *
1716
     * @param $string
1717
     * @return string
1718
     */
1719
    function _xmlEntities($string) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1720
        return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
1721
    }
1722
1723
    /**
1724
     * Creates a linkid from a headline
1725
     *
1726
     * @author Andreas Gohr <[email protected]>
1727
     * @param string  $title   The headline title
1728
     * @param boolean $create  Create a new unique ID?
1729
     * @return string
1730
     */
1731
    function _headerToLink($title, $create = false) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1732
        if($create) {
1733
            return sectionID($title, $this->headers);
1734
        } else {
1735
            $check = false;
1736
            return sectionID($title, $check);
1737
        }
1738
    }
1739
1740
    /**
1741
     * Construct a title and handle images in titles
1742
     *
1743
     * @author Harry Fuecks <[email protected]>
1744
     * @param string|array $title    either string title or media array
1745
     * @param string       $default  default title if nothing else is found
1746
     * @param bool         $isImage  will be set to true if it's a media file
1747
     * @param null|string  $id       linked page id (used to extract title from first heading)
1748
     * @param string       $linktype content|navigation
1749
     * @return string      HTML of the title, might be full image tag or just escaped text
1750
     */
1751
    function _getLinkTitle($title, $default, &$isImage, $id = null, $linktype = 'content') {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1752
        $isImage = false;
1753
        if(is_array($title)) {
1754
            $isImage = true;
1755
            return $this->_imageTitle($title);
1756
        } elseif(is_null($title) || trim($title) == '') {
1757
            if(useHeading($linktype) && $id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1758
                $heading = p_get_first_heading($id);
1759
                if(!blank($heading)) {
1760
                    return $this->_xmlEntities($heading);
1761
                }
1762
            }
1763
            return $this->_xmlEntities($default);
1764
        } else {
1765
            return $this->_xmlEntities($title);
1766
        }
1767
    }
1768
1769
    /**
1770
     * Returns HTML code for images used in link titles
1771
     *
1772
     * @author Andreas Gohr <[email protected]>
1773
     * @param array $img
1774
     * @return string HTML img tag or similar
1775
     */
1776
    function _imageTitle($img) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1777
        global $ID;
1778
1779
        // some fixes on $img['src']
1780
        // see internalmedia() and externalmedia()
1781
        list($img['src']) = explode('#', $img['src'], 2);
1782
        if($img['type'] == 'internalmedia') {
1783
            resolve_mediaid(getNS($ID), $img['src'], $exists ,$this->date_at, true);
0 ignored issues
show
Security Bug introduced by
It seems like getNS($ID) targeting getNS() can also be of type false; however, resolve_mediaid() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
1784
        }
1785
1786
        return $this->_media(
1787
            $img['src'],
1788
            $img['title'],
1789
            $img['align'],
1790
            $img['width'],
1791
            $img['height'],
1792
            $img['cache']
1793
        );
1794
    }
1795
1796
    /**
1797
     * helperfunction to return a basic link to a media
1798
     *
1799
     * used in internalmedia() and externalmedia()
1800
     *
1801
     * @author   Pierre Spring <[email protected]>
1802
     * @param string $src       media ID
1803
     * @param string $title     descriptive text
1804
     * @param string $align     left|center|right
1805
     * @param int    $width     width of media in pixel
1806
     * @param int    $height    height of media in pixel
1807
     * @param string $cache     cache|recache|nocache
1808
     * @param bool   $render    should the media be embedded inline or just linked
1809
     * @return array associative array with link config
1810
     */
1811
    function _getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1812
        global $conf;
1813
1814
        $link           = array();
1815
        $link['class']  = 'media';
1816
        $link['style']  = '';
1817
        $link['pre']    = '';
1818
        $link['suf']    = '';
1819
        $link['more']   = '';
1820
        $link['target'] = $conf['target']['media'];
1821
        if($conf['target']['media']) $link['rel'] = 'noopener';
1822
        $link['title']  = $this->_xmlEntities($src);
1823
        $link['name']   = $this->_media($src, $title, $align, $width, $height, $cache, $render);
1824
1825
        return $link;
1826
    }
1827
1828
    /**
1829
     * Embed video(s) in HTML
1830
     *
1831
     * @author Anika Henke <[email protected]>
1832
     * @author Schplurtz le Déboulonné <[email protected]>
1833
     *
1834
     * @param string $src         - ID of video to embed
1835
     * @param int    $width       - width of the video in pixels
1836
     * @param int    $height      - height of the video in pixels
1837
     * @param array  $atts        - additional attributes for the <video> tag
1838
     * @return string
1839
     */
1840
    function _video($src, $width, $height, $atts = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1841
        // prepare width and height
1842
        if(is_null($atts)) $atts = array();
1843
        $atts['width']  = (int) $width;
1844
        $atts['height'] = (int) $height;
1845
        if(!$atts['width']) $atts['width'] = 320;
1846
        if(!$atts['height']) $atts['height'] = 240;
1847
1848
        $posterUrl = '';
1849
        $files = array();
1850
        $tracks = array();
1851
        $isExternal = media_isexternal($src);
1852
1853
        if ($isExternal) {
1854
            // take direct source for external files
1855
            list(/*ext*/, $srcMime) = mimetype($src);
1856
            $files[$srcMime] = $src;
1857
        } else {
1858
            // prepare alternative formats
1859
            $extensions   = array('webm', 'ogv', 'mp4');
1860
            $files        = media_alternativefiles($src, $extensions);
1861
            $poster       = media_alternativefiles($src, array('jpg', 'png'));
1862
            $tracks       = media_trackfiles($src);
1863
            if(!empty($poster)) {
1864
                $posterUrl = ml(reset($poster), '', true, '&');
1865
            }
1866
        }
1867
1868
        $out = '';
1869
        // open video tag
1870
        $out .= '<video '.buildAttributes($atts).' controls="controls"';
1871
        if($posterUrl) $out .= ' poster="'.hsc($posterUrl).'"';
1872
        $out .= '>'.NL;
1873
        $fallback = '';
1874
1875
        // output source for each alternative video format
1876
        foreach($files as $mime => $file) {
1877
            if ($isExternal) {
1878
                $url = $file;
1879
                $linkType = 'externalmedia';
1880
            } else {
1881
                $url = ml($file, '', true, '&');
1882
                $linkType = 'internalmedia';
1883
            }
1884
            $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file)));
1885
1886
            $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL;
1887
            // alternative content (just a link to the file)
1888
            $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true);
1889
        }
1890
1891
        // output each track if any
1892
        foreach( $tracks as $trackid => $info ) {
1893
            list( $kind, $srclang ) = array_map( 'hsc', $info );
1894
            $out .= "<track kind=\"$kind\" srclang=\"$srclang\" ";
1895
            $out .= "label=\"$srclang\" ";
1896
            $out .= 'src="'.ml($trackid, '', true).'">'.NL;
1897
        }
1898
1899
        // finish
1900
        $out .= $fallback;
1901
        $out .= '</video>'.NL;
1902
        return $out;
1903
    }
1904
1905
    /**
1906
     * Embed audio in HTML
1907
     *
1908
     * @author Anika Henke <[email protected]>
1909
     *
1910
     * @param string $src       - ID of audio to embed
1911
     * @param array  $atts      - additional attributes for the <audio> tag
1912
     * @return string
1913
     */
1914
    function _audio($src, $atts = array()) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1915
        $files = array();
1916
        $isExternal = media_isexternal($src);
1917
1918
        if ($isExternal) {
1919
            // take direct source for external files
1920
            list(/*ext*/, $srcMime) = mimetype($src);
1921
            $files[$srcMime] = $src;
1922
        } else {
1923
            // prepare alternative formats
1924
            $extensions   = array('ogg', 'mp3', 'wav');
1925
            $files        = media_alternativefiles($src, $extensions);
1926
        }
1927
1928
        $out = '';
1929
        // open audio tag
1930
        $out .= '<audio '.buildAttributes($atts).' controls="controls">'.NL;
1931
        $fallback = '';
1932
1933
        // output source for each alternative audio format
1934
        foreach($files as $mime => $file) {
1935
            if ($isExternal) {
1936
                $url = $file;
1937
                $linkType = 'externalmedia';
1938
            } else {
1939
                $url = ml($file, '', true, '&');
1940
                $linkType = 'internalmedia';
1941
            }
1942
            $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file)));
1943
1944
            $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL;
1945
            // alternative content (just a link to the file)
1946
            $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true);
1947
        }
1948
1949
        // finish
1950
        $out .= $fallback;
1951
        $out .= '</audio>'.NL;
1952
        return $out;
1953
    }
1954
1955
    /**
1956
     * _getLastMediaRevisionAt is a helperfunction to internalmedia() and _media()
1957
     * which returns an existing media revision less or equal to rev or date_at
1958
     *
1959
     * @author lisps
1960
     * @param string $media_id
1961
     * @access protected
1962
     * @return string revision ('' for current)
1963
     */
1964
    function _getLastMediaRevisionAt($media_id){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1965
        if(!$this->date_at || media_isexternal($media_id)) return '';
1966
        $pagelog = new MediaChangeLog($media_id);
1967
        return $pagelog->getLastRevisionAt($this->date_at);
1968
    }
1969
1970
    #endregion
1971
}
1972
1973
//Setup VIM: ex: et ts=4 :
1974