Completed
Push — master ( 0ae3e6...4d2583 )
by Andreas
05:17
created

template.php ➔ tpl_breadcrumbs()   C

Complexity

Conditions 8
Paths 31

Size

Total Lines 31
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 20
nc 31
nop 2
dl 0
loc 31
rs 5.3846
c 0
b 0
f 0
1
<?php
2
/**
3
 * DokuWiki template functions
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Andreas Gohr <[email protected]>
7
 */
8
9
if(!defined('DOKU_INC')) die('meh.');
10
11
/**
12
 * Access a template file
13
 *
14
 * Returns the path to the given file inside the current template, uses
15
 * default template if the custom version doesn't exist.
16
 *
17
 * @author Andreas Gohr <[email protected]>
18
 * @param string $file
19
 * @return string
20
 */
21
function template($file) {
22
    global $conf;
23
24
    if(@is_readable(DOKU_INC.'lib/tpl/'.$conf['template'].'/'.$file))
25
        return DOKU_INC.'lib/tpl/'.$conf['template'].'/'.$file;
26
27
    return DOKU_INC.'lib/tpl/dokuwiki/'.$file;
28
}
29
30
/**
31
 * Convenience function to access template dir from local FS
32
 *
33
 * This replaces the deprecated DOKU_TPLINC constant
34
 *
35
 * @author Andreas Gohr <[email protected]>
36
 * @param string $tpl The template to use, default to current one
37
 * @return string
38
 */
39
function tpl_incdir($tpl='') {
40
    global $conf;
41
    if(!$tpl) $tpl = $conf['template'];
42
    return DOKU_INC.'lib/tpl/'.$tpl.'/';
43
}
44
45
/**
46
 * Convenience function to access template dir from web
47
 *
48
 * This replaces the deprecated DOKU_TPL constant
49
 *
50
 * @author Andreas Gohr <[email protected]>
51
 * @param string $tpl The template to use, default to current one
52
 * @return string
53
 */
54
function tpl_basedir($tpl='') {
55
    global $conf;
56
    if(!$tpl) $tpl = $conf['template'];
57
    return DOKU_BASE.'lib/tpl/'.$tpl.'/';
58
}
59
60
/**
61
 * Print the content
62
 *
63
 * This function is used for printing all the usual content
64
 * (defined by the global $ACT var) by calling the appropriate
65
 * outputfunction(s) from html.php
66
 *
67
 * Everything that doesn't use the main template file isn't
68
 * handled by this function. ACL stuff is not done here either.
69
 *
70
 * @author Andreas Gohr <[email protected]>
71
 *
72
 * @triggers TPL_ACT_RENDER
73
 * @triggers TPL_CONTENT_DISPLAY
74
 * @param bool $prependTOC should the TOC be displayed here?
75
 * @return bool true if any output
76
 */
77
function tpl_content($prependTOC = true) {
78
    global $ACT;
79
    global $INFO;
80
    $INFO['prependTOC'] = $prependTOC;
81
82
    ob_start();
83
    trigger_event('TPL_ACT_RENDER', $ACT, 'tpl_content_core');
84
    $html_output = ob_get_clean();
85
    trigger_event('TPL_CONTENT_DISPLAY', $html_output, 'ptln');
86
87
    return !empty($html_output);
88
}
89
90
/**
91
 * Default Action of TPL_ACT_RENDER
92
 *
93
 * @return bool
94
 */
95
function tpl_content_core() {
96
    $router = \dokuwiki\ActionRouter::getInstance();
97
    try {
98
        $router->getAction()->tplContent();
99
    } catch(\dokuwiki\Action\Exception\FatalException $e) {
100
        // there was no content for the action
101
        msg(hsc($e->getMessage()), -1);
102
        return false;
103
    }
104
    return true;
105
}
106
107
/**
108
 * Places the TOC where the function is called
109
 *
110
 * If you use this you most probably want to call tpl_content with
111
 * a false argument
112
 *
113
 * @author Andreas Gohr <[email protected]>
114
 *
115
 * @param bool $return Should the TOC be returned instead to be printed?
116
 * @return string
117
 */
118
function tpl_toc($return = false) {
119
    global $TOC;
120
    global $ACT;
121
    global $ID;
122
    global $REV;
123
    global $INFO;
124
    global $conf;
125
    global $INPUT;
126
    $toc = array();
127
128
    if(is_array($TOC)) {
129
        // if a TOC was prepared in global scope, always use it
130
        $toc = $TOC;
131
    } elseif(($ACT == 'show' || substr($ACT, 0, 6) == 'export') && !$REV && $INFO['exists']) {
132
        // get TOC from metadata, render if neccessary
133
        $meta = p_get_metadata($ID, '', METADATA_RENDER_USING_CACHE);
134
        if(isset($meta['internal']['toc'])) {
135
            $tocok = $meta['internal']['toc'];
136
        } else {
137
            $tocok = true;
138
        }
139
        $toc = isset($meta['description']['tableofcontents']) ? $meta['description']['tableofcontents'] : null;
140
        if(!$tocok || !is_array($toc) || !$conf['tocminheads'] || count($toc) < $conf['tocminheads']) {
141
            $toc = array();
142
        }
143
    } elseif($ACT == 'admin') {
144
        // try to load admin plugin TOC
145
        /** @var $plugin DokuWiki_Admin_Plugin */
146
        if ($plugin = plugin_getRequestAdminPlugin()) {
147
            $toc = $plugin->getTOC();
148
            $TOC = $toc; // avoid later rebuild
149
        }
150
    }
151
152
    trigger_event('TPL_TOC_RENDER', $toc, null, false);
153
    $html = html_TOC($toc);
154
    if($return) return $html;
155
    echo $html;
156
    return '';
157
}
158
159
/**
160
 * Handle the admin page contents
161
 *
162
 * @author Andreas Gohr <[email protected]>
163
 *
164
 * @return bool
165
 */
166
function tpl_admin() {
167
    global $INFO;
168
    global $TOC;
169
    global $INPUT;
170
171
    $plugin = null;
172
    $class  = $INPUT->str('page');
173
    if(!empty($class)) {
174
        $pluginlist = plugin_list('admin');
175
176
        if(in_array($class, $pluginlist)) {
177
            // attempt to load the plugin
178
            /** @var $plugin DokuWiki_Admin_Plugin */
179
            $plugin = plugin_load('admin', $class);
180
        }
181
    }
182
183
    if($plugin !== null) {
184
        if(!is_array($TOC)) $TOC = $plugin->getTOC(); //if TOC wasn't requested yet
185
        if($INFO['prependTOC']) tpl_toc();
186
        $plugin->html();
187
    } else {
188
        $admin = new dokuwiki\Ui\Admin();
189
        $admin->show();
190
    }
191
    return true;
192
}
193
194
/**
195
 * Print the correct HTML meta headers
196
 *
197
 * This has to go into the head section of your template.
198
 *
199
 * @author Andreas Gohr <[email protected]>
200
 *
201
 * @triggers TPL_METAHEADER_OUTPUT
202
 * @param  bool $alt Should feeds and alternative format links be added?
203
 * @return bool
204
 */
205
function tpl_metaheaders($alt = true) {
206
    global $ID;
207
    global $REV;
208
    global $INFO;
209
    global $JSINFO;
210
    global $ACT;
211
    global $QUERY;
212
    global $lang;
213
    global $conf;
214
    global $updateVersion;
215
    /** @var Input $INPUT */
216
    global $INPUT;
217
218
    // prepare the head array
219
    $head = array();
220
221
    // prepare seed for js and css
222
    $tseed   = $updateVersion;
223
    $depends = getConfigFiles('main');
224
    $depends[] = DOKU_CONF."tpl/".$conf['template']."/style.ini";
225
    foreach($depends as $f) $tseed .= @filemtime($f);
226
    $tseed   = md5($tseed);
227
228
    // the usual stuff
229
    $head['meta'][] = array('name'=> 'generator', 'content'=> 'DokuWiki');
230
    if(actionOK('search')) {
231
        $head['link'][] = array(
232
            'rel' => 'search', 'type'=> 'application/opensearchdescription+xml',
233
            'href'=> DOKU_BASE.'lib/exe/opensearch.php', 'title'=> $conf['title']
234
        );
235
    }
236
237
    $head['link'][] = array('rel'=> 'start', 'href'=> DOKU_BASE);
238
    if(actionOK('index')) {
239
        $head['link'][] = array(
240
            'rel'  => 'contents', 'href'=> wl($ID, 'do=index', false, '&'),
241
            'title'=> $lang['btn_index']
242
        );
243
    }
244
245
    if($alt) {
246
        if(actionOK('rss')) {
247
            $head['link'][] = array(
248
                'rel'  => 'alternate', 'type'=> 'application/rss+xml',
249
                'title'=> $lang['btn_recent'], 'href'=> DOKU_BASE.'feed.php'
250
            );
251
            $head['link'][] = array(
252
                'rel'  => 'alternate', 'type'=> 'application/rss+xml',
253
                'title'=> $lang['currentns'],
254
                'href' => DOKU_BASE.'feed.php?mode=list&ns='.$INFO['namespace']
255
            );
256
        }
257
        if(($ACT == 'show' || $ACT == 'search') && $INFO['writable']) {
258
            $head['link'][] = array(
259
                'rel'  => 'edit',
260
                'title'=> $lang['btn_edit'],
261
                'href' => wl($ID, 'do=edit', false, '&')
262
            );
263
        }
264
265
        if(actionOK('rss') && $ACT == 'search') {
266
            $head['link'][] = array(
267
                'rel'  => 'alternate', 'type'=> 'application/rss+xml',
268
                'title'=> $lang['searchresult'],
269
                'href' => DOKU_BASE.'feed.php?mode=search&q='.$QUERY
270
            );
271
        }
272
273
        if(actionOK('export_xhtml')) {
274
            $head['link'][] = array(
275
                'rel' => 'alternate', 'type'=> 'text/html', 'title'=> $lang['plainhtml'],
276
                'href'=> exportlink($ID, 'xhtml', '', false, '&')
277
            );
278
        }
279
280
        if(actionOK('export_raw')) {
281
            $head['link'][] = array(
282
                'rel' => 'alternate', 'type'=> 'text/plain', 'title'=> $lang['wikimarkup'],
283
                'href'=> exportlink($ID, 'raw', '', false, '&')
284
            );
285
        }
286
    }
287
288
    // setup robot tags apropriate for different modes
289
    if(($ACT == 'show' || $ACT == 'export_xhtml') && !$REV) {
290
        if($INFO['exists']) {
291
            //delay indexing:
292
            if((time() - $INFO['lastmod']) >= $conf['indexdelay'] && !isHiddenPage($ID) ) {
293
                $head['meta'][] = array('name'=> 'robots', 'content'=> 'index,follow');
294
            } else {
295
                $head['meta'][] = array('name'=> 'robots', 'content'=> 'noindex,nofollow');
296
            }
297
            $canonicalUrl = wl($ID, '', true, '&');
298
            if ($ID == $conf['start']) {
299
                $canonicalUrl = DOKU_URL;
300
            }
301
            $head['link'][] = array('rel'=> 'canonical', 'href'=> $canonicalUrl);
302
        } else {
303
            $head['meta'][] = array('name'=> 'robots', 'content'=> 'noindex,follow');
304
        }
305
    } elseif(defined('DOKU_MEDIADETAIL')) {
306
        $head['meta'][] = array('name'=> 'robots', 'content'=> 'index,follow');
307
    } else {
308
        $head['meta'][] = array('name'=> 'robots', 'content'=> 'noindex,nofollow');
309
    }
310
311
    // set metadata
312
    if($ACT == 'show' || $ACT == 'export_xhtml') {
313
        // keywords (explicit or implicit)
314
        if(!empty($INFO['meta']['subject'])) {
315
            $head['meta'][] = array('name'=> 'keywords', 'content'=> join(',', $INFO['meta']['subject']));
316
        } else {
317
            $head['meta'][] = array('name'=> 'keywords', 'content'=> str_replace(':', ',', $ID));
318
        }
319
    }
320
321
    // load stylesheets
322
    $head['link'][] = array(
323
        'rel' => 'stylesheet', 'type'=> 'text/css',
324
        'href'=> DOKU_BASE.'lib/exe/css.php?t='.rawurlencode($conf['template']).'&tseed='.$tseed
325
    );
326
327
    // make $INFO and other vars available to JavaScripts
328
    $json   = new JSON();
329
    $script = "var NS='".$INFO['namespace']."';";
330
    if($conf['useacl'] && $INPUT->server->str('REMOTE_USER')) {
331
        $script .= "var SIG='".toolbar_signature()."';";
332
    }
333
    $script .= 'var JSINFO = '.$json->encode($JSINFO).';';
334
    $head['script'][] = array('type'=> 'text/javascript', '_data'=> $script);
335
336
    // load jquery
337
    $jquery = getCdnUrls();
338
    foreach($jquery as $src) {
339
        $head['script'][] = array(
340
            'type' => 'text/javascript', 'charset' => 'utf-8', '_data' => '', 'src' => $src
341
        );
342
    }
343
344
    // load our javascript dispatcher
345
    $head['script'][] = array(
346
        'type'=> 'text/javascript', 'charset'=> 'utf-8', '_data'=> '',
347
        'src' => DOKU_BASE.'lib/exe/js.php'.'?t='.rawurlencode($conf['template']).'&tseed='.$tseed
348
    );
349
350
    // trigger event here
351
    trigger_event('TPL_METAHEADER_OUTPUT', $head, '_tpl_metaheaders_action', true);
352
    return true;
353
}
354
355
/**
356
 * prints the array build by tpl_metaheaders
357
 *
358
 * $data is an array of different header tags. Each tag can have multiple
359
 * instances. Attributes are given as key value pairs. Values will be HTML
360
 * encoded automatically so they should be provided as is in the $data array.
361
 *
362
 * For tags having a body attribute specify the body data in the special
363
 * attribute '_data'. This field will NOT BE ESCAPED automatically.
364
 *
365
 * @author Andreas Gohr <[email protected]>
366
 *
367
 * @param array $data
368
 */
369
function _tpl_metaheaders_action($data) {
370
    foreach($data as $tag => $inst) {
371
        if($tag == 'script') {
372
            echo "<!--[if gte IE 9]><!-->\n"; // no scripts for old IE
373
        }
374
        foreach($inst as $attr) {
375
            if ( empty($attr) ) { continue; }
376
            echo '<', $tag, ' ', buildAttributes($attr);
377
            if(isset($attr['_data']) || $tag == 'script') {
378
                if($tag == 'script' && $attr['_data'])
379
                    $attr['_data'] = "/*<![CDATA[*/".
380
                        $attr['_data'].
381
                        "\n/*!]]>*/";
382
383
                echo '>', $attr['_data'], '</', $tag, '>';
384
            } else {
385
                echo '/>';
386
            }
387
            echo "\n";
388
        }
389
        if($tag == 'script') {
390
            echo "<!--<![endif]-->\n";
391
        }
392
    }
393
}
394
395
/**
396
 * Print a link
397
 *
398
 * Just builds a link.
399
 *
400
 * @author Andreas Gohr <[email protected]>
401
 *
402
 * @param string $url
403
 * @param string $name
404
 * @param string $more
405
 * @param bool $return if true return the link html, otherwise print
406
 * @return bool|string html of the link, or true if printed
407
 */
408
function tpl_link($url, $name, $more = '', $return = false) {
409
    $out = '<a href="'.$url.'" ';
410
    if($more) $out .= ' '.$more;
411
    $out .= ">$name</a>";
412
    if($return) return $out;
413
    print $out;
414
    return true;
415
}
416
417
/**
418
 * Prints a link to a WikiPage
419
 *
420
 * Wrapper around html_wikilink
421
 *
422
 * @author Andreas Gohr <[email protected]>
423
 *
424
 * @param string      $id   page id
425
 * @param string|null $name the name of the link
426
 * @param bool        $return
427
 * @return true|string
428
 */
429
function tpl_pagelink($id, $name = null, $return = false) {
430
    $out = '<bdi>'.html_wikilink($id, $name).'</bdi>';
431
    if($return) return $out;
432
    print $out;
433
    return true;
434
}
435
436
/**
437
 * get the parent page
438
 *
439
 * Tries to find out which page is parent.
440
 * returns false if none is available
441
 *
442
 * @author Andreas Gohr <[email protected]>
443
 *
444
 * @param string $id page id
445
 * @return false|string
446
 */
447
function tpl_getparent($id) {
448
    $parent = getNS($id).':';
449
    resolve_pageid('', $parent, $exists);
450
    if($parent == $id) {
451
        $pos    = strrpos(getNS($id), ':');
452
        $parent = substr($parent, 0, $pos).':';
453
        resolve_pageid('', $parent, $exists);
454
        if($parent == $id) return false;
455
    }
456
    return $parent;
457
}
458
459
/**
460
 * Print one of the buttons
461
 *
462
 * @author Adrian Lang <[email protected]>
463
 * @see    tpl_get_action
464
 *
465
 * @param string $type
466
 * @param bool $return
467
 * @return bool|string html, or false if no data, true if printed
468
 * @deprecated 2017-09-01 see devel:menus
469
 */
470
function tpl_button($type, $return = false) {
471
    dbg_deprecated('see devel:menus');
472
    $data = tpl_get_action($type);
0 ignored issues
show
Deprecated Code introduced by
The function tpl_get_action() has been deprecated with message: 2017-09-01 see devel:menus

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
473
    if($data === false) {
474
        return false;
475
    } elseif(!is_array($data)) {
476
        $out = sprintf($data, 'button');
477
    } else {
478
        /**
479
         * @var string $accesskey
480
         * @var string $id
481
         * @var string $method
482
         * @var array  $params
483
         */
484
        extract($data);
485
        if($id === '#dokuwiki__top') {
486
            $out = html_topbtn();
487
        } else {
488
            $out = html_btn($type, $id, $accesskey, $params, $method);
489
        }
490
    }
491
    if($return) return $out;
492
    echo $out;
493
    return true;
494
}
495
496
/**
497
 * Like the action buttons but links
498
 *
499
 * @author Adrian Lang <[email protected]>
500
 * @see    tpl_get_action
501
 *
502
 * @param string $type    action command
503
 * @param string $pre     prefix of link
504
 * @param string $suf     suffix of link
505
 * @param string $inner   innerHML of link
506
 * @param bool   $return  if true it returns html, otherwise prints
507
 * @return bool|string html or false if no data, true if printed
508
 * @deprecated 2017-09-01 see devel:menus
509
 */
510
function tpl_actionlink($type, $pre = '', $suf = '', $inner = '', $return = false) {
511
    dbg_deprecated('see devel:menus');
512
    global $lang;
513
    $data = tpl_get_action($type);
0 ignored issues
show
Deprecated Code introduced by
The function tpl_get_action() has been deprecated with message: 2017-09-01 see devel:menus

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
514
    if($data === false) {
515
        return false;
516
    } elseif(!is_array($data)) {
517
        $out = sprintf($data, 'link');
518
    } else {
519
        /**
520
         * @var string $accesskey
521
         * @var string $id
522
         * @var string $method
523
         * @var bool   $nofollow
524
         * @var array  $params
525
         * @var string $replacement
526
         */
527
        extract($data);
528
        if(strpos($id, '#') === 0) {
529
            $linktarget = $id;
530
        } else {
531
            $linktarget = wl($id, $params);
532
        }
533
        $caption = $lang['btn_'.$type];
534
        if(strpos($caption, '%s')){
535
            $caption = sprintf($caption, $replacement);
536
        }
537
        $akey    = $addTitle = '';
538
        if($accesskey) {
539
            $akey     = 'accesskey="'.$accesskey.'" ';
540
            $addTitle = ' ['.strtoupper($accesskey).']';
541
        }
542
        $rel = $nofollow ? 'rel="nofollow" ' : '';
543
        $out = tpl_link(
544
            $linktarget, $pre.(($inner) ? $inner : $caption).$suf,
545
            'class="action '.$type.'" '.
546
                $akey.$rel.
547
                'title="'.hsc($caption).$addTitle.'"', true
548
        );
549
    }
550
    if($return) return $out;
551
    echo $out;
552
    return true;
553
}
554
555
/**
556
 * Check the actions and get data for buttons and links
557
 *
558
 * @author Andreas Gohr <[email protected]>
559
 * @author Matthias Grimm <[email protected]>
560
 * @author Adrian Lang <[email protected]>
561
 *
562
 * @param string $type
563
 * @return array|bool|string
564
 * @deprecated 2017-09-01 see devel:menus
565
 */
566
function tpl_get_action($type) {
567
    dbg_deprecated('see devel:menus');
568
    if($type == 'history') $type = 'revisions';
569
    if($type == 'subscription') $type = 'subscribe';
570
    if($type == 'img_backto') $type = 'imgBackto';
571
572
    $class = '\\dokuwiki\\Menu\\Item\\' . ucfirst($type);
573
    if(class_exists($class)) {
574
        try {
575
            /** @var \dokuwiki\Menu\Item\AbstractItem $item */
576
            $item = new $class;
577
            $data = $item->getLegacyData();
578
            $unknown = false;
579
        } catch(\RuntimeException $ignored) {
580
            return false;
581
        }
582
    } else {
583
        global $ID;
584
        $data = array(
585
            'accesskey' => null,
586
            'type' => $type,
587
            'id' => $ID,
588
            'method' => 'get',
589
            'params' => array('do' => $type),
590
            'nofollow' => true,
591
            'replacement' => '',
592
        );
593
        $unknown = true;
594
    }
595
596
    $evt = new Doku_Event('TPL_ACTION_GET', $data);
597
    if($evt->advise_before()) {
598
        //handle unknown types
599
        if($unknown) {
600
            $data = '[unknown %s type]';
601
        }
602
    }
603
    $evt->advise_after();
604
    unset($evt);
605
606
    return $data;
607
}
608
609
/**
610
 * Wrapper around tpl_button() and tpl_actionlink()
611
 *
612
 * @author Anika Henke <[email protected]>
613
 *
614
 * @param string        $type action command
615
 * @param bool          $link link or form button?
616
 * @param string|bool   $wrapper HTML element wrapper
617
 * @param bool          $return return or print
618
 * @param string        $pre prefix for links
619
 * @param string        $suf suffix for links
620
 * @param string        $inner inner HTML for links
621
 * @return bool|string
622
 * @deprecated 2017-09-01 see devel:menus
623
 */
624
function tpl_action($type, $link = false, $wrapper = false, $return = false, $pre = '', $suf = '', $inner = '') {
625
    dbg_deprecated('see devel:menus');
626
    $out = '';
627
    if($link) {
628
        $out .= tpl_actionlink($type, $pre, $suf, $inner, true);
0 ignored issues
show
Deprecated Code introduced by
The function tpl_actionlink() has been deprecated with message: 2017-09-01 see devel:menus

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
629
    } else {
630
        $out .= tpl_button($type, true);
0 ignored issues
show
Deprecated Code introduced by
The function tpl_button() has been deprecated with message: 2017-09-01 see devel:menus

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
631
    }
632
    if($out && $wrapper) $out = "<$wrapper>$out</$wrapper>";
633
634
    if($return) return $out;
635
    print $out;
636
    return $out ? true : false;
637
}
638
639
/**
640
 * Print the search form
641
 *
642
 * If the first parameter is given a div with the ID 'qsearch_out' will
643
 * be added which instructs the ajax pagequicksearch to kick in and place
644
 * its output into this div. The second parameter controls the propritary
645
 * attribute autocomplete. If set to false this attribute will be set with an
646
 * value of "off" to instruct the browser to disable it's own built in
647
 * autocompletion feature (MSIE and Firefox)
648
 *
649
 * @author Andreas Gohr <[email protected]>
650
 *
651
 * @param bool $ajax
652
 * @param bool $autocomplete
653
 * @return bool
654
 */
655
function tpl_searchform($ajax = true, $autocomplete = true) {
656
    global $lang;
657
    global $ACT;
658
    global $QUERY;
659
660
    // don't print the search form if search action has been disabled
661
    if(!actionOK('search')) return false;
662
663
    print '<form action="'.wl().'" accept-charset="utf-8" class="search" id="dw__search" method="get" role="search"><div class="no">';
664
    print '<input type="hidden" name="do" value="search" />';
665
    print '<input type="text" ';
666
    if($ACT == 'search') print 'value="'.htmlspecialchars($QUERY).'" ';
667
    print 'placeholder="'.$lang['btn_search'].'" ';
668
    if(!$autocomplete) print 'autocomplete="off" ';
669
    print 'id="qsearch__in" accesskey="f" name="id" class="edit" title="[F]" />';
670
    print '<button type="submit" title="'.$lang['btn_search'].'">'.$lang['btn_search'].'</button>';
671
    if($ajax) print '<div id="qsearch__out" class="ajax_qsearch JSpopup"></div>';
672
    print '</div></form>';
673
    return true;
674
}
675
676
/**
677
 * Print the breadcrumbs trace
678
 *
679
 * @author Andreas Gohr <[email protected]>
680
 *
681
 * @param string $sep Separator between entries
682
 * @param bool   $return return or print
683
 * @return bool|string
684
 */
685
function tpl_breadcrumbs($sep = null, $return = false) {
686
    global $lang;
687
    global $conf;
688
689
    //check if enabled
690
    if(!$conf['breadcrumbs']) return false;
691
692
    //set default
693
    if(is_null($sep)) $sep = '•';
694
695
    $out='';
696
697
    $crumbs = breadcrumbs(); //setup crumb trace
698
699
    $crumbs_sep = ' <span class="bcsep">'.$sep.'</span> ';
700
701
    //render crumbs, highlight the last one
702
    $out .= '<span class="bchead">'.$lang['breadcrumb'].'</span>';
703
    $last = count($crumbs);
704
    $i    = 0;
705
    foreach($crumbs as $id => $name) {
706
        $i++;
707
        $out .= $crumbs_sep;
708
        if($i == $last) $out .= '<span class="curid">';
709
        $out .= '<bdi>' . tpl_link(wl($id), hsc($name), 'class="breadcrumbs" title="'.$id.'"', true) .  '</bdi>';
710
        if($i == $last) $out .= '</span>';
711
    }
712
    if($return) return $out;
713
    print $out;
714
    return $out ? true : false;
715
}
716
717
/**
718
 * Hierarchical breadcrumbs
719
 *
720
 * This code was suggested as replacement for the usual breadcrumbs.
721
 * It only makes sense with a deep site structure.
722
 *
723
 * @author Andreas Gohr <[email protected]>
724
 * @author Nigel McNie <[email protected]>
725
 * @author Sean Coates <[email protected]>
726
 * @author <[email protected]>
727
 * @todo   May behave strangely in RTL languages
728
 *
729
 * @param string $sep Separator between entries
730
 * @param bool   $return return or print
731
 * @return bool|string
732
 */
733
function tpl_youarehere($sep = null, $return = false) {
734
    global $conf;
735
    global $ID;
736
    global $lang;
737
738
    // check if enabled
739
    if(!$conf['youarehere']) return false;
740
741
    //set default
742
    if(is_null($sep)) $sep = ' » ';
743
744
    $out = '';
745
746
    $parts = explode(':', $ID);
747
    $count = count($parts);
748
749
    $out .= '<span class="bchead">'.$lang['youarehere'].' </span>';
750
751
    // always print the startpage
752
    $out .= '<span class="home">' . tpl_pagelink(':'.$conf['start'], null, true) . '</span>';
753
754
    // print intermediate namespace links
755
    $part = '';
756
    for($i = 0; $i < $count - 1; $i++) {
757
        $part .= $parts[$i].':';
758
        $page = $part;
759
        if($page == $conf['start']) continue; // Skip startpage
760
761
        // output
762
        $out .= $sep . tpl_pagelink($page, null, true);
763
    }
764
765
    // print current page, skipping start page, skipping for namespace index
766
    resolve_pageid('', $page, $exists);
767
    if(isset($page) && $page == $part.$parts[$i]) return true;
768
    $page = $part.$parts[$i];
769
    if($page == $conf['start']) return true;
770
    $out .= $sep;
771
    $out .= tpl_pagelink($page, null, true);
772
    if($return) return $out;
773
    print $out;
774
    return $out ? true : false;
775
}
776
777
/**
778
 * Print info if the user is logged in
779
 * and show full name in that case
780
 *
781
 * Could be enhanced with a profile link in future?
782
 *
783
 * @author Andreas Gohr <[email protected]>
784
 *
785
 * @return bool
786
 */
787
function tpl_userinfo() {
788
    global $lang;
789
    /** @var Input $INPUT */
790
    global $INPUT;
791
792
    if($INPUT->server->str('REMOTE_USER')) {
793
        print $lang['loggedinas'].' '.userlink();
794
        return true;
795
    }
796
    return false;
797
}
798
799
/**
800
 * Print some info about the current page
801
 *
802
 * @author Andreas Gohr <[email protected]>
803
 *
804
 * @param bool $ret return content instead of printing it
805
 * @return bool|string
806
 */
807
function tpl_pageinfo($ret = false) {
808
    global $conf;
809
    global $lang;
810
    global $INFO;
811
    global $ID;
812
813
    // return if we are not allowed to view the page
814
    if(!auth_quickaclcheck($ID)) {
815
        return false;
816
    }
817
818
    // prepare date and path
819
    $fn = $INFO['filepath'];
820
    if(!$conf['fullpath']) {
821
        if($INFO['rev']) {
822
            $fn = str_replace($conf['olddir'].'/', '', $fn);
823
        } else {
824
            $fn = str_replace($conf['datadir'].'/', '', $fn);
825
        }
826
    }
827
    $fn   = utf8_decodeFN($fn);
828
    $date = dformat($INFO['lastmod']);
829
830
    // print it
831
    if($INFO['exists']) {
832
        $out = '';
833
        $out .= '<bdi>'.$fn.'</bdi>';
834
        $out .= ' · ';
835
        $out .= $lang['lastmod'];
836
        $out .= ' ';
837
        $out .= $date;
838
        if($INFO['editor']) {
839
            $out .= ' '.$lang['by'].' ';
840
            $out .= '<bdi>'.editorinfo($INFO['editor']).'</bdi>';
841
        } else {
842
            $out .= ' ('.$lang['external_edit'].')';
843
        }
844
        if($INFO['locked']) {
845
            $out .= ' · ';
846
            $out .= $lang['lockedby'];
847
            $out .= ' ';
848
            $out .= '<bdi>'.editorinfo($INFO['locked']).'</bdi>';
849
        }
850
        if($ret) {
851
            return $out;
852
        } else {
853
            echo $out;
854
            return true;
855
        }
856
    }
857
    return false;
858
}
859
860
/**
861
 * Prints or returns the name of the given page (current one if none given).
862
 *
863
 * If useheading is enabled this will use the first headline else
864
 * the given ID is used.
865
 *
866
 * @author Andreas Gohr <[email protected]>
867
 *
868
 * @param string $id page id
869
 * @param bool   $ret return content instead of printing
870
 * @return bool|string
871
 */
872
function tpl_pagetitle($id = null, $ret = false) {
873
    global $ACT, $INPUT, $conf, $lang;
874
875
    if(is_null($id)) {
876
        global $ID;
877
        $id = $ID;
878
    }
879
880
    $name = $id;
881
    if(useHeading('navigation')) {
882
        $first_heading = p_get_first_heading($id);
883
        if($first_heading) $name = $first_heading;
0 ignored issues
show
Bug Best Practice introduced by
The expression $first_heading 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...
884
    }
885
886
    // default page title is the page name, modify with the current action
887
    switch ($ACT) {
888
        // admin functions
889
        case 'admin' :
890
            $page_title = $lang['btn_admin'];
891
            // try to get the plugin name
892
            /** @var $plugin DokuWiki_Admin_Plugin */
893
            if ($plugin = plugin_getRequestAdminPlugin()){
894
                $plugin_title = $plugin->getMenuText($conf['lang']);
895
                $page_title = $plugin_title ? $plugin_title : $plugin->getPluginName();
896
            }
897
            break;
898
899
        // user functions
900
        case 'login' :
901
        case 'profile' :
902
        case 'register' :
903
        case 'resendpwd' :
904
            $page_title = $lang['btn_'.$ACT];
905
            break;
906
907
         // wiki functions
908
        case 'search' :
909
        case 'index' :
910
            $page_title = $lang['btn_'.$ACT];
911
            break;
912
913
        // page functions
914
        case 'edit' :
915
            $page_title = "✎ ".$name;
916
            break;
917
918
        case 'revisions' :
919
            $page_title = $name . ' - ' . $lang['btn_revs'];
920
            break;
921
922
        case 'backlink' :
923
        case 'recent' :
924
        case 'subscribe' :
925
            $page_title = $name . ' - ' . $lang['btn_'.$ACT];
926
            break;
927
928
        default : // SHOW and anything else not included
929
            $page_title = $name;
930
    }
931
932
    if($ret) {
933
        return hsc($page_title);
934
    } else {
935
        print hsc($page_title);
936
        return true;
937
    }
938
}
939
940
/**
941
 * Returns the requested EXIF/IPTC tag from the current image
942
 *
943
 * If $tags is an array all given tags are tried until a
944
 * value is found. If no value is found $alt is returned.
945
 *
946
 * Which texts are known is defined in the functions _exifTagNames
947
 * and _iptcTagNames() in inc/jpeg.php (You need to prepend IPTC
948
 * to the names of the latter one)
949
 *
950
 * Only allowed in: detail.php
951
 *
952
 * @author Andreas Gohr <[email protected]>
953
 *
954
 * @param array|string $tags tag or array of tags to try
955
 * @param string       $alt  alternative output if no data was found
956
 * @param null|string  $src  the image src, uses global $SRC if not given
957
 * @return string
958
 */
959
function tpl_img_getTag($tags, $alt = '', $src = null) {
960
    // Init Exif Reader
961
    global $SRC;
962
963
    if(is_null($src)) $src = $SRC;
964
965
    static $meta = null;
966
    if(is_null($meta)) $meta = new JpegMeta($src);
967
    if($meta === false) return $alt;
968
    $info = cleanText($meta->getField($tags));
969
    if($info == false) return $alt;
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $info of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
970
    return $info;
971
}
972
973
/**
974
 * Returns a description list of the metatags of the current image
975
 *
976
 * @return string html of description list
977
 */
978
function tpl_img_meta() {
979
    global $lang;
980
981
    $tags = tpl_get_img_meta();
982
983
    echo '<dl>';
984
    foreach($tags as $tag) {
985
        $label = $lang[$tag['langkey']];
986
        if(!$label) $label = $tag['langkey'] . ':';
987
988
        echo '<dt>'.$label.'</dt><dd>';
989
        if ($tag['type'] == 'date') {
990
            echo dformat($tag['value']);
991
        } else {
992
            echo hsc($tag['value']);
993
        }
994
        echo '</dd>';
995
    }
996
    echo '</dl>';
997
}
998
999
/**
1000
 * Returns metadata as configured in mediameta config file, ready for creating html
1001
 *
1002
 * @return array with arrays containing the entries:
1003
 *   - string langkey  key to lookup in the $lang var, if not found printed as is
1004
 *   - string type     type of value
1005
 *   - string value    tag value (unescaped)
1006
 */
1007
function tpl_get_img_meta() {
1008
1009
    $config_files = getConfigFiles('mediameta');
1010
    foreach ($config_files as $config_file) {
1011
        if(file_exists($config_file)) {
1012
            include($config_file);
1013
        }
1014
    }
1015
    /** @var array $fields the included array with metadata */
1016
1017
    $tags = array();
1018
    foreach($fields as $tag){
0 ignored issues
show
Bug introduced by
The variable $fields does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1019
        $t = array();
1020
        if (!empty($tag[0])) {
1021
            $t = array($tag[0]);
1022
        }
1023
        if(is_array($tag[3])) {
1024
            $t = array_merge($t,$tag[3]);
1025
        }
1026
        $value = tpl_img_getTag($t);
1027
        if ($value) {
1028
            $tags[] = array('langkey' => $tag[1], 'type' => $tag[2], 'value' => $value);
1029
        }
1030
    }
1031
    return $tags;
1032
}
1033
1034
/**
1035
 * Prints the image with a link to the full sized version
1036
 *
1037
 * Only allowed in: detail.php
1038
 *
1039
 * @triggers TPL_IMG_DISPLAY
1040
 * @param $maxwidth  int - maximal width of the image
1041
 * @param $maxheight int - maximal height of the image
1042
 * @param $link bool     - link to the orginal size?
1043
 * @param $params array  - additional image attributes
1044
 * @return bool Result of TPL_IMG_DISPLAY
1045
 */
1046
function tpl_img($maxwidth = 0, $maxheight = 0, $link = true, $params = null) {
1047
    global $IMG;
1048
    /** @var Input $INPUT */
1049
    global $INPUT;
1050
    global $REV;
1051
    $w = (int) tpl_img_getTag('File.Width');
1052
    $h = (int) tpl_img_getTag('File.Height');
1053
1054
    //resize to given max values
1055
    $ratio = 1;
1056
    if($w >= $h) {
1057
        if($maxwidth && $w >= $maxwidth) {
1058
            $ratio = $maxwidth / $w;
1059
        } elseif($maxheight && $h > $maxheight) {
1060
            $ratio = $maxheight / $h;
1061
        }
1062
    } else {
1063
        if($maxheight && $h >= $maxheight) {
1064
            $ratio = $maxheight / $h;
1065
        } elseif($maxwidth && $w > $maxwidth) {
1066
            $ratio = $maxwidth / $w;
1067
        }
1068
    }
1069
    if($ratio) {
1070
        $w = floor($ratio * $w);
1071
        $h = floor($ratio * $h);
1072
    }
1073
1074
    //prepare URLs
1075
    $url = ml($IMG, array('cache'=> $INPUT->str('cache'),'rev'=>$REV), true, '&');
1076
    $src = ml($IMG, array('cache'=> $INPUT->str('cache'),'rev'=>$REV, 'w'=> $w, 'h'=> $h), true, '&');
1077
1078
    //prepare attributes
1079
    $alt = tpl_img_getTag('Simple.Title');
1080
    if(is_null($params)) {
1081
        $p = array();
1082
    } else {
1083
        $p = $params;
1084
    }
1085
    if($w) $p['width'] = $w;
1086
    if($h) $p['height'] = $h;
1087
    $p['class'] = 'img_detail';
1088
    if($alt) {
1089
        $p['alt']   = $alt;
1090
        $p['title'] = $alt;
1091
    } else {
1092
        $p['alt'] = '';
1093
    }
1094
    $p['src'] = $src;
1095
1096
    $data = array('url'=> ($link ? $url : null), 'params'=> $p);
1097
    return trigger_event('TPL_IMG_DISPLAY', $data, '_tpl_img_action', true);
1098
}
1099
1100
/**
1101
 * Default action for TPL_IMG_DISPLAY
1102
 *
1103
 * @param array $data
1104
 * @return bool
1105
 */
1106
function _tpl_img_action($data) {
1107
    global $lang;
1108
    $p = buildAttributes($data['params']);
1109
1110
    if($data['url']) print '<a href="'.hsc($data['url']).'" title="'.$lang['mediaview'].'">';
1111
    print '<img '.$p.'/>';
1112
    if($data['url']) print '</a>';
1113
    return true;
1114
}
1115
1116
/**
1117
 * This function inserts a small gif which in reality is the indexer function.
1118
 *
1119
 * Should be called somewhere at the very end of the main.php
1120
 * template
1121
 *
1122
 * @return bool
1123
 */
1124
function tpl_indexerWebBug() {
1125
    global $ID;
1126
1127
    $p           = array();
1128
    $p['src']    = DOKU_BASE.'lib/exe/indexer.php?id='.rawurlencode($ID).
1129
        '&'.time();
1130
    $p['width']  = 2; //no more 1x1 px image because we live in times of ad blockers...
1131
    $p['height'] = 1;
1132
    $p['alt']    = '';
1133
    $att         = buildAttributes($p);
1134
    print "<img $att />";
1135
    return true;
1136
}
1137
1138
/**
1139
 * tpl_getConf($id)
1140
 *
1141
 * use this function to access template configuration variables
1142
 *
1143
 * @param string $id      name of the value to access
1144
 * @param mixed  $notset  what to return if the setting is not available
1145
 * @return mixed
1146
 */
1147
function tpl_getConf($id, $notset=false) {
1148
    global $conf;
1149
    static $tpl_configloaded = false;
1150
1151
    $tpl = $conf['template'];
1152
1153
    if(!$tpl_configloaded) {
1154
        $tconf = tpl_loadConfig();
1155
        if($tconf !== false) {
1156
            foreach($tconf as $key => $value) {
1157
                if(isset($conf['tpl'][$tpl][$key])) continue;
1158
                $conf['tpl'][$tpl][$key] = $value;
1159
            }
1160
            $tpl_configloaded = true;
1161
        }
1162
    }
1163
1164
    if(isset($conf['tpl'][$tpl][$id])){
1165
        return $conf['tpl'][$tpl][$id];
1166
    }
1167
1168
    return $notset;
1169
}
1170
1171
/**
1172
 * tpl_loadConfig()
1173
 *
1174
 * reads all template configuration variables
1175
 * this function is automatically called by tpl_getConf()
1176
 *
1177
 * @return array
1178
 */
1179
function tpl_loadConfig() {
1180
1181
    $file = tpl_incdir().'/conf/default.php';
1182
    $conf = array();
1183
1184
    if(!file_exists($file)) return false;
1185
1186
    // load default config file
1187
    include($file);
1188
1189
    return $conf;
1190
}
1191
1192
// language methods
1193
/**
1194
 * tpl_getLang($id)
1195
 *
1196
 * use this function to access template language variables
1197
 *
1198
 * @param string $id key of language string
1199
 * @return string
1200
 */
1201
function tpl_getLang($id) {
1202
    static $lang = array();
1203
1204
    if(count($lang) === 0) {
1205
        global $conf, $config_cascade; // definitely don't invoke "global $lang"
1206
1207
        $path = tpl_incdir() . 'lang/';
1208
1209
        $lang = array();
1210
1211
        // don't include once
1212
        @include($path . 'en/lang.php');
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1213
        foreach($config_cascade['lang']['template'] as $config_file) {
1214
            if(file_exists($config_file . $conf['template'] . '/en/lang.php')) {
1215
                include($config_file . $conf['template'] . '/en/lang.php');
1216
            }
1217
        }
1218
1219
        if($conf['lang'] != 'en') {
1220
            @include($path . $conf['lang'] . '/lang.php');
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1221
            foreach($config_cascade['lang']['template'] as $config_file) {
1222
                if(file_exists($config_file . $conf['template'] . '/' . $conf['lang'] . '/lang.php')) {
1223
                    include($config_file . $conf['template'] . '/' . $conf['lang'] . '/lang.php');
1224
                }
1225
            }
1226
        }
1227
    }
1228
    return $lang[$id];
1229
}
1230
1231
/**
1232
 * Retrieve a language dependent file and pass to xhtml renderer for display
1233
 * template equivalent of p_locale_xhtml()
1234
 *
1235
 * @param   string $id id of language dependent wiki page
1236
 * @return  string     parsed contents of the wiki page in xhtml format
1237
 */
1238
function tpl_locale_xhtml($id) {
1239
    return p_cached_output(tpl_localeFN($id));
1240
}
1241
1242
/**
1243
 * Prepends appropriate path for a language dependent filename
1244
 *
1245
 * @param string $id id of localized text
1246
 * @return string wiki text
1247
 */
1248
function tpl_localeFN($id) {
1249
    $path = tpl_incdir().'lang/';
1250
    global $conf;
1251
    $file = DOKU_CONF.'template_lang/'.$conf['template'].'/'.$conf['lang'].'/'.$id.'.txt';
1252
    if (!file_exists($file)){
1253
        $file = $path.$conf['lang'].'/'.$id.'.txt';
1254
        if(!file_exists($file)){
1255
            //fall back to english
1256
            $file = $path.'en/'.$id.'.txt';
1257
        }
1258
    }
1259
    return $file;
1260
}
1261
1262
/**
1263
 * prints the "main content" in the mediamanager popup
1264
 *
1265
 * Depending on the user's actions this may be a list of
1266
 * files in a namespace, the meta editing dialog or
1267
 * a message of referencing pages
1268
 *
1269
 * Only allowed in mediamanager.php
1270
 *
1271
 * @triggers MEDIAMANAGER_CONTENT_OUTPUT
1272
 * @param bool $fromajax - set true when calling this function via ajax
1273
 * @param string $sort
1274
 *
1275
 * @author Andreas Gohr <[email protected]>
1276
 */
1277
function tpl_mediaContent($fromajax = false, $sort='natural') {
1278
    global $IMG;
1279
    global $AUTH;
1280
    global $INUSE;
1281
    global $NS;
1282
    global $JUMPTO;
1283
    /** @var Input $INPUT */
1284
    global $INPUT;
1285
1286
    $do = $INPUT->extract('do')->str('do');
1287
    if(in_array($do, array('save', 'cancel'))) $do = '';
1288
1289
    if(!$do) {
1290
        if($INPUT->bool('edit')) {
1291
            $do = 'metaform';
1292
        } elseif(is_array($INUSE)) {
1293
            $do = 'filesinuse';
1294
        } else {
1295
            $do = 'filelist';
1296
        }
1297
    }
1298
1299
    // output the content pane, wrapped in an event.
1300
    if(!$fromajax) ptln('<div id="media__content">');
1301
    $data = array('do' => $do);
1302
    $evt  = new Doku_Event('MEDIAMANAGER_CONTENT_OUTPUT', $data);
1303
    if($evt->advise_before()) {
1304
        $do = $data['do'];
1305
        if($do == 'filesinuse') {
1306
            media_filesinuse($INUSE, $IMG);
1307
        } elseif($do == 'filelist') {
1308
            media_filelist($NS, $AUTH, $JUMPTO,false,$sort);
1309
        } elseif($do == 'searchlist') {
1310
            media_searchlist($INPUT->str('q'), $NS, $AUTH);
1311
        } else {
1312
            msg('Unknown action '.hsc($do), -1);
1313
        }
1314
    }
1315
    $evt->advise_after();
1316
    unset($evt);
1317
    if(!$fromajax) ptln('</div>');
1318
1319
}
1320
1321
/**
1322
 * Prints the central column in full-screen media manager
1323
 * Depending on the opened tab this may be a list of
1324
 * files in a namespace, upload form or search form
1325
 *
1326
 * @author Kate Arzamastseva <[email protected]>
1327
 */
1328
function tpl_mediaFileList() {
1329
    global $AUTH;
1330
    global $NS;
1331
    global $JUMPTO;
1332
    global $lang;
1333
    /** @var Input $INPUT */
1334
    global $INPUT;
1335
1336
    $opened_tab = $INPUT->str('tab_files');
1337
    if(!$opened_tab || !in_array($opened_tab, array('files', 'upload', 'search'))) $opened_tab = 'files';
1338
    if($INPUT->str('mediado') == 'update') $opened_tab = 'upload';
1339
1340
    echo '<h2 class="a11y">'.$lang['mediaselect'].'</h2>'.NL;
1341
1342
    media_tabs_files($opened_tab);
1343
1344
    echo '<div class="panelHeader">'.NL;
1345
    echo '<h3>';
1346
    $tabTitle = ($NS) ? $NS : '['.$lang['mediaroot'].']';
1347
    printf($lang['media_'.$opened_tab], '<strong>'.hsc($tabTitle).'</strong>');
1348
    echo '</h3>'.NL;
1349
    if($opened_tab === 'search' || $opened_tab === 'files') {
1350
        media_tab_files_options();
1351
    }
1352
    echo '</div>'.NL;
1353
1354
    echo '<div class="panelContent">'.NL;
1355
    if($opened_tab == 'files') {
1356
        media_tab_files($NS, $AUTH, $JUMPTO);
1357
    } elseif($opened_tab == 'upload') {
1358
        media_tab_upload($NS, $AUTH, $JUMPTO);
1359
    } elseif($opened_tab == 'search') {
1360
        media_tab_search($NS, $AUTH);
1361
    }
1362
    echo '</div>'.NL;
1363
}
1364
1365
/**
1366
 * Prints the third column in full-screen media manager
1367
 * Depending on the opened tab this may be details of the
1368
 * selected file, the meta editing dialog or
1369
 * list of file revisions
1370
 *
1371
 * @author Kate Arzamastseva <[email protected]>
1372
 *
1373
 * @param string $image
1374
 * @param boolean $rev
1375
 */
1376
function tpl_mediaFileDetails($image, $rev) {
1377
    global $conf, $DEL, $lang;
1378
    /** @var Input $INPUT */
1379
    global $INPUT;
1380
1381
    $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')) && $conf['mediarevisions']);
1382
    if(!$image || (!file_exists(mediaFN($image)) && !$removed) || $DEL) return;
1383
    if($rev && !file_exists(mediaFN($image, $rev))) $rev = false;
0 ignored issues
show
Documentation introduced by
$rev is of type boolean, but the function expects a string|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1384
    $ns = getNS($image);
1385
    $do = $INPUT->str('mediado');
1386
1387
    $opened_tab = $INPUT->str('tab_details');
1388
1389
    $tab_array = array('view');
1390
    list(, $mime) = mimetype($image);
1391
    if($mime == 'image/jpeg') {
1392
        $tab_array[] = 'edit';
1393
    }
1394
    if($conf['mediarevisions']) {
1395
        $tab_array[] = 'history';
1396
    }
1397
1398
    if(!$opened_tab || !in_array($opened_tab, $tab_array)) $opened_tab = 'view';
1399
    if($INPUT->bool('edit')) $opened_tab = 'edit';
1400
    if($do == 'restore') $opened_tab = 'view';
1401
1402
    media_tabs_details($image, $opened_tab);
1403
1404
    echo '<div class="panelHeader"><h3>';
1405
    list($ext) = mimetype($image, false);
1406
    $class    = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext);
1407
    $class    = 'select mediafile mf_'.$class;
1408
    $attributes = $rev ? ['rev' => $rev] : [];
1409
    $tabTitle = '<strong><a href="'.ml($image, $attributes).'" class="'.$class.'" title="'.$lang['mediaview'].'">'.$image.'</a>'.'</strong>';
1410
    if($opened_tab === 'view' && $rev) {
1411
        printf($lang['media_viewold'], $tabTitle, dformat($rev));
0 ignored issues
show
Documentation introduced by
$rev is of type boolean, but the function expects a integer|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1412
    } else {
1413
        printf($lang['media_'.$opened_tab], $tabTitle);
1414
    }
1415
1416
    echo '</h3></div>'.NL;
1417
1418
    echo '<div class="panelContent">'.NL;
1419
1420
    if($opened_tab == 'view') {
1421
        media_tab_view($image, $ns, null, $rev);
0 ignored issues
show
Security Bug introduced by
It seems like $ns defined by getNS($image) on line 1384 can also be of type false; however, media_tab_view() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
Documentation introduced by
$rev is of type boolean, but the function expects a string|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1422
1423
    } elseif($opened_tab == 'edit' && !$removed) {
1424
        media_tab_edit($image, $ns);
0 ignored issues
show
Security Bug introduced by
It seems like $ns defined by getNS($image) on line 1384 can also be of type false; however, media_tab_edit() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
1425
1426
    } elseif($opened_tab == 'history' && $conf['mediarevisions']) {
1427
        media_tab_history($image, $ns);
0 ignored issues
show
Security Bug introduced by
It seems like $ns defined by getNS($image) on line 1384 can also be of type false; however, media_tab_history() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
1428
    }
1429
1430
    echo '</div>'.NL;
1431
}
1432
1433
/**
1434
 * prints the namespace tree in the mediamanager popup
1435
 *
1436
 * Only allowed in mediamanager.php
1437
 *
1438
 * @author Andreas Gohr <[email protected]>
1439
 */
1440
function tpl_mediaTree() {
1441
    global $NS;
1442
    ptln('<div id="media__tree">');
1443
    media_nstree($NS);
1444
    ptln('</div>');
1445
}
1446
1447
/**
1448
 * Print a dropdown menu with all DokuWiki actions
1449
 *
1450
 * Note: this will not use any pretty URLs
1451
 *
1452
 * @author Andreas Gohr <[email protected]>
1453
 *
1454
 * @param string $empty empty option label
1455
 * @param string $button submit button label
1456
 * @deprecated 2017-09-01 see devel:menus
1457
 */
1458
function tpl_actiondropdown($empty = '', $button = '&gt;') {
1459
    dbg_deprecated('see devel:menus');
1460
    $menu = new \dokuwiki\Menu\MobileMenu();
1461
    echo $menu->getDropdown($empty, $button);
1462
}
1463
1464
/**
1465
 * Print a informational line about the used license
1466
 *
1467
 * @author Andreas Gohr <[email protected]>
1468
 * @param  string $img     print image? (|button|badge)
1469
 * @param  bool   $imgonly skip the textual description?
1470
 * @param  bool   $return  when true don't print, but return HTML
1471
 * @param  bool   $wrap    wrap in div with class="license"?
1472
 * @return string
1473
 */
1474
function tpl_license($img = 'badge', $imgonly = false, $return = false, $wrap = true) {
1475
    global $license;
1476
    global $conf;
1477
    global $lang;
1478
    if(!$conf['license']) return '';
1479
    if(!is_array($license[$conf['license']])) return '';
1480
    $lic    = $license[$conf['license']];
1481
    $target = ($conf['target']['extern']) ? ' target="'.$conf['target']['extern'].'"' : '';
1482
1483
    $out = '';
1484
    if($wrap) $out .= '<div class="license">';
1485
    if($img) {
1486
        $src = license_img($img);
1487
        if($src) {
1488
            $out .= '<a href="'.$lic['url'].'" rel="license"'.$target;
1489
            $out .= '><img src="'.DOKU_BASE.$src.'" alt="'.$lic['name'].'" /></a>';
1490
            if(!$imgonly) $out .= ' ';
1491
        }
1492
    }
1493
    if(!$imgonly) {
1494
        $out .= $lang['license'].' ';
1495
        $out .= '<bdi><a href="'.$lic['url'].'" rel="license" class="urlextern"'.$target;
1496
        $out .= '>'.$lic['name'].'</a></bdi>';
1497
    }
1498
    if($wrap) $out .= '</div>';
1499
1500
    if($return) return $out;
1501
    echo $out;
1502
    return '';
1503
}
1504
1505
/**
1506
 * Includes the rendered HTML of a given page
1507
 *
1508
 * This function is useful to populate sidebars or similar features in a
1509
 * template
1510
 *
1511
 * @param string $pageid The page name you want to include
1512
 * @param bool $print Should the content be printed or returned only
1513
 * @param bool $propagate Search higher namespaces, too?
1514
 * @param bool $useacl Include the page only if the ACLs check out?
1515
 * @return bool|null|string
1516
 */
1517
function tpl_include_page($pageid, $print = true, $propagate = false, $useacl = true) {
1518
    if($propagate) {
1519
        $pageid = page_findnearest($pageid, $useacl);
1520
    } elseif($useacl && auth_quickaclcheck($pageid) == AUTH_NONE) {
1521
        return false;
1522
    }
1523
    if(!$pageid) return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression $pageid of type false|string is loosely compared to false; 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...
1524
1525
    global $TOC;
1526
    $oldtoc = $TOC;
1527
    $html   = p_wiki_xhtml($pageid, '', false);
1528
    $TOC    = $oldtoc;
1529
1530
    if($print) echo $html;
1531
    return $html;
1532
}
1533
1534
/**
1535
 * Display the subscribe form
1536
 *
1537
 * @author Adrian Lang <[email protected]>
1538
 */
1539
function tpl_subscribe() {
1540
    global $INFO;
1541
    global $ID;
1542
    global $lang;
1543
    global $conf;
1544
    $stime_days = $conf['subscribe_time'] / 60 / 60 / 24;
1545
1546
    echo p_locale_xhtml('subscr_form');
1547
    echo '<h2>'.$lang['subscr_m_current_header'].'</h2>';
1548
    echo '<div class="level2">';
1549
    if($INFO['subscribed'] === false) {
1550
        echo '<p>'.$lang['subscr_m_not_subscribed'].'</p>';
1551
    } else {
1552
        echo '<ul>';
1553
        foreach($INFO['subscribed'] as $sub) {
1554
            echo '<li><div class="li">';
1555
            if($sub['target'] !== $ID) {
1556
                echo '<code class="ns">'.hsc(prettyprint_id($sub['target'])).'</code>';
1557
            } else {
1558
                echo '<code class="page">'.hsc(prettyprint_id($sub['target'])).'</code>';
1559
            }
1560
            $sstl = sprintf($lang['subscr_style_'.$sub['style']], $stime_days);
1561
            if(!$sstl) $sstl = hsc($sub['style']);
1562
            echo ' ('.$sstl.') ';
1563
1564
            echo '<a href="'.wl(
1565
                $ID,
1566
                array(
1567
                     'do'        => 'subscribe',
1568
                     'sub_target'=> $sub['target'],
1569
                     'sub_style' => $sub['style'],
1570
                     'sub_action'=> 'unsubscribe',
1571
                     'sectok'    => getSecurityToken()
1572
                )
1573
            ).
1574
                '" class="unsubscribe">'.$lang['subscr_m_unsubscribe'].
1575
                '</a></div></li>';
1576
        }
1577
        echo '</ul>';
1578
    }
1579
    echo '</div>';
1580
1581
    // Add new subscription form
1582
    echo '<h2>'.$lang['subscr_m_new_header'].'</h2>';
1583
    echo '<div class="level2">';
1584
    $ns      = getNS($ID).':';
1585
    $targets = array(
1586
        $ID => '<code class="page">'.prettyprint_id($ID).'</code>',
1587
        $ns => '<code class="ns">'.prettyprint_id($ns).'</code>',
1588
    );
1589
    $styles  = array(
1590
        'every'  => $lang['subscr_style_every'],
1591
        'digest' => sprintf($lang['subscr_style_digest'], $stime_days),
1592
        'list'   => sprintf($lang['subscr_style_list'], $stime_days),
1593
    );
1594
1595
    $form = new Doku_Form(array('id' => 'subscribe__form'));
1596
    $form->startFieldset($lang['subscr_m_subscribe']);
1597
    $form->addRadioSet('sub_target', $targets);
1598
    $form->startFieldset($lang['subscr_m_receive']);
1599
    $form->addRadioSet('sub_style', $styles);
1600
    $form->addHidden('sub_action', 'subscribe');
1601
    $form->addHidden('do', 'subscribe');
1602
    $form->addHidden('id', $ID);
1603
    $form->endFieldset();
1604
    $form->addElement(form_makeButton('submit', 'subscribe', $lang['subscr_m_subscribe']));
1605
    html_form('SUBSCRIBE', $form);
1606
    echo '</div>';
1607
}
1608
1609
/**
1610
 * Tries to send already created content right to the browser
1611
 *
1612
 * Wraps around ob_flush() and flush()
1613
 *
1614
 * @author Andreas Gohr <[email protected]>
1615
 */
1616
function tpl_flush() {
1617
    ob_flush();
1618
    flush();
1619
}
1620
1621
/**
1622
 * Tries to find a ressource file in the given locations.
1623
 *
1624
 * If a given location starts with a colon it is assumed to be a media
1625
 * file, otherwise it is assumed to be relative to the current template
1626
 *
1627
 * @param  string[] $search       locations to look at
1628
 * @param  bool     $abs           if to use absolute URL
1629
 * @param  array   &$imginfo   filled with getimagesize()
1630
 * @return string
1631
 *
1632
 * @author Andreas  Gohr <[email protected]>
1633
 */
1634
function tpl_getMediaFile($search, $abs = false, &$imginfo = null) {
1635
    $img     = '';
1636
    $file    = '';
1637
    $ismedia = false;
1638
    // loop through candidates until a match was found:
1639
    foreach($search as $img) {
1640
        if(substr($img, 0, 1) == ':') {
1641
            $file    = mediaFN($img);
1642
            $ismedia = true;
1643
        } else {
1644
            $file    = tpl_incdir().$img;
1645
            $ismedia = false;
1646
        }
1647
1648
        if(file_exists($file)) break;
1649
    }
1650
1651
    // fetch image data if requested
1652
    if(!is_null($imginfo)) {
1653
        $imginfo = getimagesize($file);
1654
    }
1655
1656
    // build URL
1657
    if($ismedia) {
1658
        $url = ml($img, '', true, '', $abs);
1659
    } else {
1660
        $url = tpl_basedir().$img;
1661
        if($abs) $url = DOKU_URL.substr($url, strlen(DOKU_REL));
1662
    }
1663
1664
    return $url;
1665
}
1666
1667
/**
1668
 * PHP include a file
1669
 *
1670
 * either from the conf directory if it exists, otherwise use
1671
 * file in the template's root directory.
1672
 *
1673
 * The function honours config cascade settings and looks for the given
1674
 * file next to the ´main´ config files, in the order protected, local,
1675
 * default.
1676
 *
1677
 * Note: no escaping or sanity checking is done here. Never pass user input
1678
 * to this function!
1679
 *
1680
 * @author Anika Henke <[email protected]>
1681
 * @author Andreas Gohr <[email protected]>
1682
 *
1683
 * @param string $file
1684
 */
1685
function tpl_includeFile($file) {
1686
    global $config_cascade;
1687
    foreach(array('protected', 'local', 'default') as $config_group) {
1688
        if(empty($config_cascade['main'][$config_group])) continue;
1689
        foreach($config_cascade['main'][$config_group] as $conf_file) {
1690
            $dir = dirname($conf_file);
1691
            if(file_exists("$dir/$file")) {
1692
                include("$dir/$file");
1693
                return;
1694
            }
1695
        }
1696
    }
1697
1698
    // still here? try the template dir
1699
    $file = tpl_incdir().$file;
1700
    if(file_exists($file)) {
1701
        include($file);
1702
    }
1703
}
1704
1705
/**
1706
 * Returns <link> tag for various icon types (favicon|mobile|generic)
1707
 *
1708
 * @author Anika Henke <[email protected]>
1709
 *
1710
 * @param  array $types - list of icon types to display (favicon|mobile|generic)
1711
 * @return string
1712
 */
1713
function tpl_favicon($types = array('favicon')) {
1714
1715
    $return = '';
1716
1717
    foreach($types as $type) {
1718
        switch($type) {
1719
            case 'favicon':
1720
                $look = array(':wiki:favicon.ico', ':favicon.ico', 'images/favicon.ico');
1721
                $return .= '<link rel="shortcut icon" href="'.tpl_getMediaFile($look).'" />'.NL;
1722
                break;
1723
            case 'mobile':
1724
                $look = array(':wiki:apple-touch-icon.png', ':apple-touch-icon.png', 'images/apple-touch-icon.png');
1725
                $return .= '<link rel="apple-touch-icon" href="'.tpl_getMediaFile($look).'" />'.NL;
1726
                break;
1727
            case 'generic':
1728
                // ideal world solution, which doesn't work in any browser yet
1729
                $look = array(':wiki:favicon.svg', ':favicon.svg', 'images/favicon.svg');
1730
                $return .= '<link rel="icon" href="'.tpl_getMediaFile($look).'" type="image/svg+xml" />'.NL;
1731
                break;
1732
        }
1733
    }
1734
1735
    return $return;
1736
}
1737
1738
/**
1739
 * Prints full-screen media manager
1740
 *
1741
 * @author Kate Arzamastseva <[email protected]>
1742
 */
1743
function tpl_media() {
1744
    global $NS, $IMG, $JUMPTO, $REV, $lang, $fullscreen, $INPUT;
1745
    $fullscreen = true;
1746
    require_once DOKU_INC.'lib/exe/mediamanager.php';
1747
1748
    $rev   = '';
1749
    $image = cleanID($INPUT->str('image'));
1750
    if(isset($IMG)) $image = $IMG;
1751
    if(isset($JUMPTO)) $image = $JUMPTO;
1752
    if(isset($REV) && !$JUMPTO) $rev = $REV;
1753
1754
    echo '<div id="mediamanager__page">'.NL;
1755
    echo '<h1>'.$lang['btn_media'].'</h1>'.NL;
1756
    html_msgarea();
1757
1758
    echo '<div class="panel namespaces">'.NL;
1759
    echo '<h2>'.$lang['namespaces'].'</h2>'.NL;
1760
    echo '<div class="panelHeader">';
1761
    echo $lang['media_namespaces'];
1762
    echo '</div>'.NL;
1763
1764
    echo '<div class="panelContent" id="media__tree">'.NL;
1765
    media_nstree($NS);
1766
    echo '</div>'.NL;
1767
    echo '</div>'.NL;
1768
1769
    echo '<div class="panel filelist">'.NL;
1770
    tpl_mediaFileList();
1771
    echo '</div>'.NL;
1772
1773
    echo '<div class="panel file">'.NL;
1774
    echo '<h2 class="a11y">'.$lang['media_file'].'</h2>'.NL;
1775
    tpl_mediaFileDetails($image, $rev);
1776
    echo '</div>'.NL;
1777
1778
    echo '</div>'.NL;
1779
}
1780
1781
/**
1782
 * Return useful layout classes
1783
 *
1784
 * @author Anika Henke <[email protected]>
1785
 *
1786
 * @return string
1787
 */
1788
function tpl_classes() {
1789
    global $ACT, $conf, $ID, $INFO;
1790
    /** @var Input $INPUT */
1791
    global $INPUT;
1792
1793
    $classes = array(
1794
        'dokuwiki',
1795
        'mode_'.$ACT,
1796
        'tpl_'.$conf['template'],
1797
        $INPUT->server->bool('REMOTE_USER') ? 'loggedIn' : '',
1798
        $INFO['exists'] ? '' : 'notFound',
1799
        ($ID == $conf['start']) ? 'home' : '',
1800
    );
1801
    return join(' ', $classes);
1802
}
1803
1804
/**
1805
 * Create event for tools menues
1806
 *
1807
 * @author Anika Henke <[email protected]>
1808
 * @param string $toolsname name of menu
1809
 * @param array $items
1810
 * @param string $view e.g. 'main', 'detail', ...
1811
 * @deprecated 2017-09-01 see devel:menus
1812
 */
1813
function tpl_toolsevent($toolsname, $items, $view = 'main') {
1814
    dbg_deprecated('see devel:menus');
1815
    $data = array(
1816
        'view' => $view,
1817
        'items' => $items
1818
    );
1819
1820
    $hook = 'TEMPLATE_' . strtoupper($toolsname) . '_DISPLAY';
1821
    $evt = new Doku_Event($hook, $data);
1822
    if($evt->advise_before()) {
1823
        foreach($evt->data['items'] as $k => $html) echo $html;
1824
    }
1825
    $evt->advise_after();
1826
}
1827
1828
//Setup VIM: ex: et ts=4 :
1829
1830