Completed
Push — svgpagetools ( 49b6e2...1e875d )
by Andreas
03:44
created

template.php ➔ tpl_actiondropdown()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 2
dl 0
loc 4
rs 10
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
    global $ACT;
97
    global $TEXT;
98
    global $PRE;
99
    global $SUF;
100
    global $SUM;
101
    global $IDX;
102
    global $INPUT;
103
104
    switch($ACT) {
105
        case 'show':
106
            html_show();
107
            break;
108
        /** @noinspection PhpMissingBreakStatementInspection */
109
        case 'locked':
110
            html_locked();
111
        case 'edit':
112
        case 'recover':
113
            html_edit();
114
            break;
115
        case 'preview':
116
            html_edit();
117
            html_show($TEXT);
118
            break;
119
        case 'draft':
120
            html_draft();
121
            break;
122
        case 'search':
123
            html_search();
124
            break;
125
        case 'revisions':
126
            html_revisions($INPUT->int('first'));
127
            break;
128
        case 'diff':
129
            html_diff();
130
            break;
131
        case 'recent':
132
            $show_changes = $INPUT->str('show_changes');
133
            if (empty($show_changes)) {
134
                $show_changes = get_doku_pref('show_changes', $show_changes);
135
            }
136
            html_recent($INPUT->extract('first')->int('first'), $show_changes);
137
            break;
138
        case 'index':
139
            html_index($IDX); #FIXME can this be pulled from globals? is it sanitized correctly?
140
            break;
141
        case 'backlink':
142
            html_backlinks();
143
            break;
144
        case 'conflict':
145
            html_conflict(con($PRE, $TEXT, $SUF), $SUM);
146
            html_diff(con($PRE, $TEXT, $SUF), false);
147
            break;
148
        case 'login':
149
            html_login();
150
            break;
151
        case 'register':
152
            html_register();
153
            break;
154
        case 'resendpwd':
155
            html_resendpwd();
156
            break;
157
        case 'denied':
158
            html_denied();
159
            break;
160
        case 'profile' :
161
            html_updateprofile();
162
            break;
163
        case 'admin':
164
            tpl_admin();
165
            break;
166
        case 'subscribe':
167
            tpl_subscribe();
168
            break;
169
        case 'media':
170
            tpl_media();
171
            break;
172
        default:
173
            $evt = new Doku_Event('TPL_ACT_UNKNOWN', $ACT);
174
            if($evt->advise_before()) {
175
                msg("Failed to handle command: ".hsc($ACT), -1);
176
            }
177
            $evt->advise_after();
178
            unset($evt);
179
            return false;
180
    }
181
    return true;
182
}
183
184
/**
185
 * Places the TOC where the function is called
186
 *
187
 * If you use this you most probably want to call tpl_content with
188
 * a false argument
189
 *
190
 * @author Andreas Gohr <[email protected]>
191
 *
192
 * @param bool $return Should the TOC be returned instead to be printed?
193
 * @return string
194
 */
195
function tpl_toc($return = false) {
196
    global $TOC;
197
    global $ACT;
198
    global $ID;
199
    global $REV;
200
    global $INFO;
201
    global $conf;
202
    global $INPUT;
203
    $toc = array();
204
205
    if(is_array($TOC)) {
206
        // if a TOC was prepared in global scope, always use it
207
        $toc = $TOC;
208
    } elseif(($ACT == 'show' || substr($ACT, 0, 6) == 'export') && !$REV && $INFO['exists']) {
209
        // get TOC from metadata, render if neccessary
210
        $meta = p_get_metadata($ID, '', METADATA_RENDER_USING_CACHE);
211
        if(isset($meta['internal']['toc'])) {
212
            $tocok = $meta['internal']['toc'];
213
        } else {
214
            $tocok = true;
215
        }
216
        $toc = isset($meta['description']['tableofcontents']) ? $meta['description']['tableofcontents'] : null;
217
        if(!$tocok || !is_array($toc) || !$conf['tocminheads'] || count($toc) < $conf['tocminheads']) {
218
            $toc = array();
219
        }
220
    } elseif($ACT == 'admin') {
221
        // try to load admin plugin TOC
222
        /** @var $plugin DokuWiki_Admin_Plugin */
223
        if ($plugin = plugin_getRequestAdminPlugin()) {
224
            $toc = $plugin->getTOC();
225
            $TOC = $toc; // avoid later rebuild
226
        }
227
    }
228
229
    trigger_event('TPL_TOC_RENDER', $toc, null, false);
230
    $html = html_TOC($toc);
231
    if($return) return $html;
232
    echo $html;
233
    return '';
234
}
235
236
/**
237
 * Handle the admin page contents
238
 *
239
 * @author Andreas Gohr <[email protected]>
240
 *
241
 * @return bool
242
 */
243
function tpl_admin() {
244
    global $INFO;
245
    global $TOC;
246
    global $INPUT;
247
248
    $plugin = null;
249
    $class  = $INPUT->str('page');
250
    if(!empty($class)) {
251
        $pluginlist = plugin_list('admin');
252
253
        if(in_array($class, $pluginlist)) {
254
            // attempt to load the plugin
255
            /** @var $plugin DokuWiki_Admin_Plugin */
256
            $plugin = plugin_load('admin', $class);
257
        }
258
    }
259
260
    if($plugin !== null) {
261
        if(!is_array($TOC)) $TOC = $plugin->getTOC(); //if TOC wasn't requested yet
262
        if($INFO['prependTOC']) tpl_toc();
263
        $plugin->html();
264
    } else {
265
        $admin = new dokuwiki\Ui\Admin();
266
        $admin->show();
267
    }
268
    return true;
269
}
270
271
/**
272
 * Print the correct HTML meta headers
273
 *
274
 * This has to go into the head section of your template.
275
 *
276
 * @author Andreas Gohr <[email protected]>
277
 *
278
 * @triggers TPL_METAHEADER_OUTPUT
279
 * @param  bool $alt Should feeds and alternative format links be added?
280
 * @return bool
281
 */
282
function tpl_metaheaders($alt = true) {
283
    global $ID;
284
    global $REV;
285
    global $INFO;
286
    global $JSINFO;
287
    global $ACT;
288
    global $QUERY;
289
    global $lang;
290
    global $conf;
291
    global $updateVersion;
292
    /** @var Input $INPUT */
293
    global $INPUT;
294
295
    // prepare the head array
296
    $head = array();
297
298
    // prepare seed for js and css
299
    $tseed   = $updateVersion;
300
    $depends = getConfigFiles('main');
301
    $depends[] = DOKU_CONF."tpl/".$conf['template']."/style.ini";
302
    foreach($depends as $f) $tseed .= @filemtime($f);
303
    $tseed   = md5($tseed);
304
305
    // the usual stuff
306
    $head['meta'][] = array('name'=> 'generator', 'content'=> 'DokuWiki');
307
    if(actionOK('search')) {
308
        $head['link'][] = array(
309
            'rel' => 'search', 'type'=> 'application/opensearchdescription+xml',
310
            'href'=> DOKU_BASE.'lib/exe/opensearch.php', 'title'=> $conf['title']
311
        );
312
    }
313
314
    $head['link'][] = array('rel'=> 'start', 'href'=> DOKU_BASE);
315
    if(actionOK('index')) {
316
        $head['link'][] = array(
317
            'rel'  => 'contents', 'href'=> wl($ID, 'do=index', false, '&'),
318
            'title'=> $lang['btn_index']
319
        );
320
    }
321
322
    if($alt) {
323
        if(actionOK('rss')) {
324
            $head['link'][] = array(
325
                'rel'  => 'alternate', 'type'=> 'application/rss+xml',
326
                'title'=> $lang['btn_recent'], 'href'=> DOKU_BASE.'feed.php'
327
            );
328
            $head['link'][] = array(
329
                'rel'  => 'alternate', 'type'=> 'application/rss+xml',
330
                'title'=> $lang['currentns'],
331
                'href' => DOKU_BASE.'feed.php?mode=list&ns='.$INFO['namespace']
332
            );
333
        }
334
        if(($ACT == 'show' || $ACT == 'search') && $INFO['writable']) {
335
            $head['link'][] = array(
336
                'rel'  => 'edit',
337
                'title'=> $lang['btn_edit'],
338
                'href' => wl($ID, 'do=edit', false, '&')
339
            );
340
        }
341
342
        if(actionOK('rss') && $ACT == 'search') {
343
            $head['link'][] = array(
344
                'rel'  => 'alternate', 'type'=> 'application/rss+xml',
345
                'title'=> $lang['searchresult'],
346
                'href' => DOKU_BASE.'feed.php?mode=search&q='.$QUERY
347
            );
348
        }
349
350
        if(actionOK('export_xhtml')) {
351
            $head['link'][] = array(
352
                'rel' => 'alternate', 'type'=> 'text/html', 'title'=> $lang['plainhtml'],
353
                'href'=> exportlink($ID, 'xhtml', '', false, '&')
354
            );
355
        }
356
357
        if(actionOK('export_raw')) {
358
            $head['link'][] = array(
359
                'rel' => 'alternate', 'type'=> 'text/plain', 'title'=> $lang['wikimarkup'],
360
                'href'=> exportlink($ID, 'raw', '', false, '&')
361
            );
362
        }
363
    }
364
365
    // setup robot tags apropriate for different modes
366
    if(($ACT == 'show' || $ACT == 'export_xhtml') && !$REV) {
367
        if($INFO['exists']) {
368
            //delay indexing:
369
            if((time() - $INFO['lastmod']) >= $conf['indexdelay']) {
370
                $head['meta'][] = array('name'=> 'robots', 'content'=> 'index,follow');
371
            } else {
372
                $head['meta'][] = array('name'=> 'robots', 'content'=> 'noindex,nofollow');
373
            }
374
            $canonicalUrl = wl($ID, '', true, '&');
375
            if ($ID == $conf['start']) {
376
                $canonicalUrl = DOKU_URL;
377
            }
378
            $head['link'][] = array('rel'=> 'canonical', 'href'=> $canonicalUrl);
379
        } else {
380
            $head['meta'][] = array('name'=> 'robots', 'content'=> 'noindex,follow');
381
        }
382
    } elseif(defined('DOKU_MEDIADETAIL')) {
383
        $head['meta'][] = array('name'=> 'robots', 'content'=> 'index,follow');
384
    } else {
385
        $head['meta'][] = array('name'=> 'robots', 'content'=> 'noindex,nofollow');
386
    }
387
388
    // set metadata
389
    if($ACT == 'show' || $ACT == 'export_xhtml') {
390
        // keywords (explicit or implicit)
391
        if(!empty($INFO['meta']['subject'])) {
392
            $head['meta'][] = array('name'=> 'keywords', 'content'=> join(',', $INFO['meta']['subject']));
393
        } else {
394
            $head['meta'][] = array('name'=> 'keywords', 'content'=> str_replace(':', ',', $ID));
395
        }
396
    }
397
398
    // load stylesheets
399
    $head['link'][] = array(
400
        'rel' => 'stylesheet', 'type'=> 'text/css',
401
        'href'=> DOKU_BASE.'lib/exe/css.php?t='.rawurlencode($conf['template']).'&tseed='.$tseed
402
    );
403
404
    // make $INFO and other vars available to JavaScripts
405
    $json   = new JSON();
406
    $script = "var NS='".$INFO['namespace']."';";
407
    if($conf['useacl'] && $INPUT->server->str('REMOTE_USER')) {
408
        $script .= "var SIG='".toolbar_signature()."';";
409
    }
410
    $script .= 'var JSINFO = '.$json->encode($JSINFO).';';
411
    $head['script'][] = array('type'=> 'text/javascript', '_data'=> $script);
412
413
    // load jquery
414
    $jquery = getCdnUrls();
415
    foreach($jquery as $src) {
416
        $head['script'][] = array(
417
            'type' => 'text/javascript', 'charset' => 'utf-8', '_data' => '', 'src' => $src
418
        );
419
    }
420
421
    // load our javascript dispatcher
422
    $head['script'][] = array(
423
        'type'=> 'text/javascript', 'charset'=> 'utf-8', '_data'=> '',
424
        'src' => DOKU_BASE.'lib/exe/js.php'.'?t='.rawurlencode($conf['template']).'&tseed='.$tseed
425
    );
426
427
    // trigger event here
428
    trigger_event('TPL_METAHEADER_OUTPUT', $head, '_tpl_metaheaders_action', true);
429
    return true;
430
}
431
432
/**
433
 * prints the array build by tpl_metaheaders
434
 *
435
 * $data is an array of different header tags. Each tag can have multiple
436
 * instances. Attributes are given as key value pairs. Values will be HTML
437
 * encoded automatically so they should be provided as is in the $data array.
438
 *
439
 * For tags having a body attribute specify the body data in the special
440
 * attribute '_data'. This field will NOT BE ESCAPED automatically.
441
 *
442
 * @author Andreas Gohr <[email protected]>
443
 *
444
 * @param array $data
445
 */
446
function _tpl_metaheaders_action($data) {
447
    foreach($data as $tag => $inst) {
448
        if($tag == 'script') {
449
            echo "<!--[if gte IE 9]><!-->\n"; // no scripts for old IE
450
        }
451
        foreach($inst as $attr) {
452
            if ( empty($attr) ) { continue; }
453
            echo '<', $tag, ' ', buildAttributes($attr);
454
            if(isset($attr['_data']) || $tag == 'script') {
455
                if($tag == 'script' && $attr['_data'])
456
                    $attr['_data'] = "/*<![CDATA[*/".
457
                        $attr['_data'].
458
                        "\n/*!]]>*/";
459
460
                echo '>', $attr['_data'], '</', $tag, '>';
461
            } else {
462
                echo '/>';
463
            }
464
            echo "\n";
465
        }
466
        if($tag == 'script') {
467
            echo "<!--<![endif]-->\n";
468
        }
469
    }
470
}
471
472
/**
473
 * Print a link
474
 *
475
 * Just builds a link.
476
 *
477
 * @author Andreas Gohr <[email protected]>
478
 *
479
 * @param string $url
480
 * @param string $name
481
 * @param string $more
482
 * @param bool $return if true return the link html, otherwise print
483
 * @return bool|string html of the link, or true if printed
484
 */
485
function tpl_link($url, $name, $more = '', $return = false) {
486
    $out = '<a href="'.$url.'" ';
487
    if($more) $out .= ' '.$more;
488
    $out .= ">$name</a>";
489
    if($return) return $out;
490
    print $out;
491
    return true;
492
}
493
494
/**
495
 * Prints a link to a WikiPage
496
 *
497
 * Wrapper around html_wikilink
498
 *
499
 * @author Andreas Gohr <[email protected]>
500
 *
501
 * @param string      $id   page id
502
 * @param string|null $name the name of the link
503
 * @return bool true
504
 */
505
function tpl_pagelink($id, $name = null) {
506
    print '<bdi>'.html_wikilink($id, $name).'</bdi>';
507
    return true;
508
}
509
510
/**
511
 * get the parent page
512
 *
513
 * Tries to find out which page is parent.
514
 * returns false if none is available
515
 *
516
 * @author Andreas Gohr <[email protected]>
517
 *
518
 * @param string $id page id
519
 * @return false|string
520
 */
521
function tpl_getparent($id) {
522
    $parent = getNS($id).':';
523
    resolve_pageid('', $parent, $exists);
524
    if($parent == $id) {
525
        $pos    = strrpos(getNS($id), ':');
526
        $parent = substr($parent, 0, $pos).':';
527
        resolve_pageid('', $parent, $exists);
528
        if($parent == $id) return false;
529
    }
530
    return $parent;
531
}
532
533
/**
534
 * Print one of the buttons
535
 *
536
 * @author Adrian Lang <[email protected]>
537
 * @see    tpl_get_action
538
 *
539
 * @param string $type
540
 * @param bool $return
541
 * @return bool|string html, or false if no data, true if printed
542
 */
543
function tpl_button($type, $return = false) {
544
    $data = tpl_get_action($type);
545
    if($data === false) {
546
        return false;
547
    } elseif(!is_array($data)) {
548
        $out = sprintf($data, 'button');
549
    } else {
550
        /**
551
         * @var string $accesskey
552
         * @var string $id
553
         * @var string $method
554
         * @var array  $params
555
         */
556
        extract($data);
557
        if($id === '#dokuwiki__top') {
558
            $out = html_topbtn();
559
        } else {
560
            $out = html_btn($type, $id, $accesskey, $params, $method);
561
        }
562
    }
563
    if($return) return $out;
564
    echo $out;
565
    return true;
566
}
567
568
/**
569
 * Like the action buttons but links
570
 *
571
 * @author Adrian Lang <[email protected]>
572
 * @see    tpl_get_action
573
 *
574
 * @param string $type    action command
575
 * @param string $pre     prefix of link
576
 * @param string $suf     suffix of link
577
 * @param string $inner   innerHML of link
578
 * @param bool   $return  if true it returns html, otherwise prints
579
 * @return bool|string html or false if no data, true if printed
580
 */
581
function tpl_actionlink($type, $pre = '', $suf = '', $inner = '', $return = false) {
582
    global $lang;
583
    $data = tpl_get_action($type);
584
    if($data === false) {
585
        return false;
586
    } elseif(!is_array($data)) {
587
        $out = sprintf($data, 'link');
588
    } else {
589
        /**
590
         * @var string $accesskey
591
         * @var string $id
592
         * @var string $method
593
         * @var bool   $nofollow
594
         * @var array  $params
595
         * @var string $replacement
596
         */
597
        extract($data);
598
        if(strpos($id, '#') === 0) {
599
            $linktarget = $id;
600
        } else {
601
            $linktarget = wl($id, $params);
602
        }
603
        $caption = $lang['btn_'.$type];
604
        if(strpos($caption, '%s')){
605
            $caption = sprintf($caption, $replacement);
606
        }
607
        $akey    = $addTitle = '';
608
        if($accesskey) {
609
            $akey     = 'accesskey="'.$accesskey.'" ';
610
            $addTitle = ' ['.strtoupper($accesskey).']';
611
        }
612
        $rel = $nofollow ? 'rel="nofollow" ' : '';
613
        $out = tpl_link(
614
            $linktarget, $pre.(($inner) ? $inner : $caption).$suf,
615
            'class="action '.$type.'" '.
616
                $akey.$rel.
617
                'title="'.hsc($caption).$addTitle.'"', true
618
        );
619
    }
620
    if($return) return $out;
621
    echo $out;
622
    return true;
623
}
624
625
/**
626
 * Check the actions and get data for buttons and links
627
 *
628
 * Available actions are
629
 *
630
 *  edit        - edit/create/show/draft
631
 *  history     - old revisions
632
 *  recent      - recent changes
633
 *  login       - login/logout - if ACL enabled
634
 *  profile     - user profile (if logged in)
635
 *  index       - The index
636
 *  admin       - admin page - if enough rights
637
 *  top         - back to top
638
 *  back        - back to parent - if available
639
 *  backlink    - links to the list of backlinks
640
 *  subscribe/subscription- subscribe/unsubscribe
641
 *
642
 * @author Andreas Gohr <[email protected]>
643
 * @author Matthias Grimm <[email protected]>
644
 * @author Adrian Lang <[email protected]>
645
 *
646
 * @param string $type
647
 * @return array|bool|string
648
 */
649
function tpl_get_action($type) {
650
    global $ID;
651
    global $INFO;
652
    global $REV;
653
    global $ACT;
654
    global $conf;
655
    /** @var Input $INPUT */
656
    global $INPUT;
657
658
    // check disabled actions and fix the badly named ones
659
    if($type == 'history') $type = 'revisions';
660
    if ($type == 'subscription') $type = 'subscribe';
661
    if(!actionOK($type)) return false;
662
663
    $accesskey   = null;
664
    $id          = $ID;
665
    $method      = 'get';
666
    $params      = array('do' => $type);
667
    $nofollow    = true;
668
    $replacement = '';
669
670
    $unknown = false;
671
    switch($type) {
672
        case 'edit':
673
            // most complicated type - we need to decide on current action
674
            if($ACT == 'show' || $ACT == 'search') {
675
                $method = 'post';
676
                if($INFO['writable']) {
677
                    $accesskey = 'e';
678
                    if(!empty($INFO['draft'])) {
679
                        $type         = 'draft';
680
                        $params['do'] = 'draft';
681
                    } else {
682
                        $params['rev'] = $REV;
683
                        if(!$INFO['exists']) {
684
                            $type = 'create';
685
                        }
686
                    }
687
                } else {
688
                    if(!actionOK('source')) return false; //pseudo action
689
                    $params['rev'] = $REV;
690
                    $type          = 'source';
691
                    $accesskey     = 'v';
692
                }
693
            } else {
694
                $params    = array('do' => '');
695
                $type      = 'show';
696
                $accesskey = 'v';
697
            }
698
            break;
699
        case 'revisions':
700
            $type      = 'revs';
701
            $accesskey = 'o';
702
            break;
703
        case 'recent':
704
            $accesskey = 'r';
705
            break;
706
        case 'index':
707
            $accesskey = 'x';
708
            // allow searchbots to get to the sitemap from the homepage (when dokuwiki isn't providing a sitemap.xml)
709
            if ($conf['start'] == $ID && !$conf['sitemap']) {
710
                $nofollow = false;
711
            }
712
            break;
713
        case 'top':
714
            $accesskey = 't';
715
            $params    = array('do' => '');
716
            $id        = '#dokuwiki__top';
717
            break;
718
        case 'back':
719
            $parent = tpl_getparent($ID);
720
            if(!$parent) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parent 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...
721
                return false;
722
            }
723
            $id        = $parent;
724
            $params    = array('do' => '');
725
            $accesskey = 'b';
726
            break;
727
        case 'img_backto':
728
            $params = array();
729
            $accesskey = 'b';
730
            $replacement = $ID;
731
            break;
732
        case 'login':
733
            $params['sectok'] = getSecurityToken();
734
            if($INPUT->server->has('REMOTE_USER')) {
735
                if(!actionOK('logout')) {
736
                    return false;
737
                }
738
                $params['do'] = 'logout';
739
                $type         = 'logout';
740
            }
741
            break;
742
        case 'register':
743
            if($INPUT->server->str('REMOTE_USER')) {
744
                return false;
745
            }
746
            break;
747
        case 'resendpwd':
748
            if($INPUT->server->str('REMOTE_USER')) {
749
                return false;
750
            }
751
            break;
752
        case 'admin':
753
            if(!$INFO['ismanager']) {
754
                return false;
755
            }
756
            break;
757
        case 'revert':
758
            if(!$INFO['ismanager'] || !$REV || !$INFO['writable']) {
759
                return false;
760
            }
761
            $params['rev']    = $REV;
762
            $params['sectok'] = getSecurityToken();
763
            break;
764
        case 'subscribe':
765
            if(!$INPUT->server->str('REMOTE_USER')) {
766
                return false;
767
            }
768
            break;
769
        case 'backlink':
770
            break;
771
        case 'profile':
772
            if(!$INPUT->server->has('REMOTE_USER')) {
773
                return false;
774
            }
775
            break;
776
        case 'media':
777
            $params['ns'] = getNS($ID);
778
            break;
779
        case 'mediaManager':
780
            // View image in media manager
781
            global $IMG;
782
            $imgNS = getNS($IMG);
783
            $authNS = auth_quickaclcheck("$imgNS:*");
784
            if ($authNS < AUTH_UPLOAD) {
785
                return false;
786
            }
787
            $params = array(
788
                'ns' => $imgNS,
789
                'image' => $IMG,
790
                'do' => 'media'
791
            );
792
            //$type = 'media';
793
            break;
794
        default:
795
            //unknown type
796
            $unknown = true;
797
    }
798
799
    $data = compact('accesskey', 'type', 'id', 'method', 'params', 'nofollow', 'replacement');
800
801
    $evt = new Doku_Event('TPL_ACTION_GET', $data);
802
    if($evt->advise_before()) {
803
        //handle unknown types
804
        if($unknown) {
805
            $data = '[unknown %s type]';
806
        }
807
    }
808
    $evt->advise_after();
809
    unset($evt);
810
811
    return $data;
812
}
813
814
/**
815
 * Wrapper around tpl_button() and tpl_actionlink()
816
 *
817
 * @author Anika Henke <[email protected]>
818
 *
819
 * @param string        $type action command
820
 * @param bool          $link link or form button?
821
 * @param string|bool   $wrapper HTML element wrapper
822
 * @param bool          $return return or print
823
 * @param string        $pre prefix for links
824
 * @param string        $suf suffix for links
825
 * @param string        $inner inner HTML for links
826
 * @return bool|string
827
 */
828
function tpl_action($type, $link = false, $wrapper = false, $return = false, $pre = '', $suf = '', $inner = '') {
829
    $out = '';
830
    if($link) {
831
        $out .= tpl_actionlink($type, $pre, $suf, $inner, true);
832
    } else {
833
        $out .= tpl_button($type, true);
834
    }
835
    if($out && $wrapper) $out = "<$wrapper>$out</$wrapper>";
836
837
    if($return) return $out;
838
    print $out;
839
    return $out ? true : false;
840
}
841
842
/**
843
 * Print the search form
844
 *
845
 * If the first parameter is given a div with the ID 'qsearch_out' will
846
 * be added which instructs the ajax pagequicksearch to kick in and place
847
 * its output into this div. The second parameter controls the propritary
848
 * attribute autocomplete. If set to false this attribute will be set with an
849
 * value of "off" to instruct the browser to disable it's own built in
850
 * autocompletion feature (MSIE and Firefox)
851
 *
852
 * @author Andreas Gohr <[email protected]>
853
 *
854
 * @param bool $ajax
855
 * @param bool $autocomplete
856
 * @return bool
857
 */
858
function tpl_searchform($ajax = true, $autocomplete = true) {
859
    global $lang;
860
    global $ACT;
861
    global $QUERY;
862
863
    // don't print the search form if search action has been disabled
864
    if(!actionOK('search')) return false;
865
866
    print '<form action="'.wl().'" accept-charset="utf-8" class="search" id="dw__search" method="get" role="search"><div class="no">';
867
    print '<input type="hidden" name="do" value="search" />';
868
    print '<input type="text" ';
869
    if($ACT == 'search') print 'value="'.htmlspecialchars($QUERY).'" ';
870
    print 'placeholder="'.$lang['btn_search'].'" ';
871
    if(!$autocomplete) print 'autocomplete="off" ';
872
    print 'id="qsearch__in" accesskey="f" name="id" class="edit" title="[F]" />';
873
    print '<button type="submit" title="'.$lang['btn_search'].'">'.$lang['btn_search'].'</button>';
874
    if($ajax) print '<div id="qsearch__out" class="ajax_qsearch JSpopup"></div>';
875
    print '</div></form>';
876
    return true;
877
}
878
879
/**
880
 * Print the breadcrumbs trace
881
 *
882
 * @author Andreas Gohr <[email protected]>
883
 *
884
 * @param string $sep Separator between entries
885
 * @return bool
886
 */
887
function tpl_breadcrumbs($sep = '•') {
888
    global $lang;
889
    global $conf;
890
891
    //check if enabled
892
    if(!$conf['breadcrumbs']) return false;
893
894
    $crumbs = breadcrumbs(); //setup crumb trace
895
896
    $crumbs_sep = ' <span class="bcsep">'.$sep.'</span> ';
897
898
    //render crumbs, highlight the last one
899
    print '<span class="bchead">'.$lang['breadcrumb'].'</span>';
900
    $last = count($crumbs);
901
    $i    = 0;
902
    foreach($crumbs as $id => $name) {
903
        $i++;
904
        echo $crumbs_sep;
905
        if($i == $last) print '<span class="curid">';
906
        print '<bdi>';
907
        tpl_link(wl($id), hsc($name), 'class="breadcrumbs" title="'.$id.'"');
908
        print '</bdi>';
909
        if($i == $last) print '</span>';
910
    }
911
    return true;
912
}
913
914
/**
915
 * Hierarchical breadcrumbs
916
 *
917
 * This code was suggested as replacement for the usual breadcrumbs.
918
 * It only makes sense with a deep site structure.
919
 *
920
 * @author Andreas Gohr <[email protected]>
921
 * @author Nigel McNie <[email protected]>
922
 * @author Sean Coates <[email protected]>
923
 * @author <[email protected]>
924
 * @todo   May behave strangely in RTL languages
925
 *
926
 * @param string $sep Separator between entries
927
 * @return bool
928
 */
929
function tpl_youarehere($sep = ' » ') {
930
    global $conf;
931
    global $ID;
932
    global $lang;
933
934
    // check if enabled
935
    if(!$conf['youarehere']) return false;
936
937
    $parts = explode(':', $ID);
938
    $count = count($parts);
939
940
    echo '<span class="bchead">'.$lang['youarehere'].' </span>';
941
942
    // always print the startpage
943
    echo '<span class="home">';
944
    tpl_pagelink(':'.$conf['start']);
945
    echo '</span>';
946
947
    // print intermediate namespace links
948
    $part = '';
949
    for($i = 0; $i < $count - 1; $i++) {
950
        $part .= $parts[$i].':';
951
        $page = $part;
952
        if($page == $conf['start']) continue; // Skip startpage
953
954
        // output
955
        echo $sep;
956
        tpl_pagelink($page);
957
    }
958
959
    // print current page, skipping start page, skipping for namespace index
960
    resolve_pageid('', $page, $exists);
961
    if(isset($page) && $page == $part.$parts[$i]) return true;
962
    $page = $part.$parts[$i];
963
    if($page == $conf['start']) return true;
964
    echo $sep;
965
    tpl_pagelink($page);
966
    return true;
967
}
968
969
/**
970
 * Print info if the user is logged in
971
 * and show full name in that case
972
 *
973
 * Could be enhanced with a profile link in future?
974
 *
975
 * @author Andreas Gohr <[email protected]>
976
 *
977
 * @return bool
978
 */
979
function tpl_userinfo() {
980
    global $lang;
981
    /** @var Input $INPUT */
982
    global $INPUT;
983
984
    if($INPUT->server->str('REMOTE_USER')) {
985
        print $lang['loggedinas'].' '.userlink();
986
        return true;
987
    }
988
    return false;
989
}
990
991
/**
992
 * Print some info about the current page
993
 *
994
 * @author Andreas Gohr <[email protected]>
995
 *
996
 * @param bool $ret return content instead of printing it
997
 * @return bool|string
998
 */
999
function tpl_pageinfo($ret = false) {
1000
    global $conf;
1001
    global $lang;
1002
    global $INFO;
1003
    global $ID;
1004
1005
    // return if we are not allowed to view the page
1006
    if(!auth_quickaclcheck($ID)) {
1007
        return false;
1008
    }
1009
1010
    // prepare date and path
1011
    $fn = $INFO['filepath'];
1012
    if(!$conf['fullpath']) {
1013
        if($INFO['rev']) {
1014
            $fn = str_replace($conf['olddir'].'/', '', $fn);
1015
        } else {
1016
            $fn = str_replace($conf['datadir'].'/', '', $fn);
1017
        }
1018
    }
1019
    $fn   = utf8_decodeFN($fn);
1020
    $date = dformat($INFO['lastmod']);
1021
1022
    // print it
1023
    if($INFO['exists']) {
1024
        $out = '';
1025
        $out .= '<bdi>'.$fn.'</bdi>';
1026
        $out .= ' · ';
1027
        $out .= $lang['lastmod'];
1028
        $out .= ' ';
1029
        $out .= $date;
1030
        if($INFO['editor']) {
1031
            $out .= ' '.$lang['by'].' ';
1032
            $out .= '<bdi>'.editorinfo($INFO['editor']).'</bdi>';
1033
        } else {
1034
            $out .= ' ('.$lang['external_edit'].')';
1035
        }
1036
        if($INFO['locked']) {
1037
            $out .= ' · ';
1038
            $out .= $lang['lockedby'];
1039
            $out .= ' ';
1040
            $out .= '<bdi>'.editorinfo($INFO['locked']).'</bdi>';
1041
        }
1042
        if($ret) {
1043
            return $out;
1044
        } else {
1045
            echo $out;
1046
            return true;
1047
        }
1048
    }
1049
    return false;
1050
}
1051
1052
/**
1053
 * Prints or returns the name of the given page (current one if none given).
1054
 *
1055
 * If useheading is enabled this will use the first headline else
1056
 * the given ID is used.
1057
 *
1058
 * @author Andreas Gohr <[email protected]>
1059
 *
1060
 * @param string $id page id
1061
 * @param bool   $ret return content instead of printing
1062
 * @return bool|string
1063
 */
1064
function tpl_pagetitle($id = null, $ret = false) {
1065
    global $ACT, $INPUT, $conf, $lang;
1066
1067
    if(is_null($id)) {
1068
        global $ID;
1069
        $id = $ID;
1070
    }
1071
1072
    $name = $id;
1073
    if(useHeading('navigation')) {
1074
        $first_heading = p_get_first_heading($id);
1075
        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...
1076
    }
1077
1078
    // default page title is the page name, modify with the current action
1079
    switch ($ACT) {
1080
        // admin functions
1081
        case 'admin' :
1082
            $page_title = $lang['btn_admin'];
1083
            // try to get the plugin name
1084
            /** @var $plugin DokuWiki_Admin_Plugin */
1085
            if ($plugin = plugin_getRequestAdminPlugin()){
1086
                $plugin_title = $plugin->getMenuText($conf['lang']);
1087
                $page_title = $plugin_title ? $plugin_title : $plugin->getPluginName();
1088
            }
1089
            break;
1090
1091
        // user functions
1092
        case 'login' :
1093
        case 'profile' :
1094
        case 'register' :
1095
        case 'resendpwd' :
1096
            $page_title = $lang['btn_'.$ACT];
1097
            break;
1098
1099
         // wiki functions
1100
        case 'search' :
1101
        case 'index' :
1102
            $page_title = $lang['btn_'.$ACT];
1103
            break;
1104
1105
        // page functions
1106
        case 'edit' :
1107
            $page_title = "✎ ".$name;
1108
            break;
1109
1110
        case 'revisions' :
1111
            $page_title = $name . ' - ' . $lang['btn_revs'];
1112
            break;
1113
1114
        case 'backlink' :
1115
        case 'recent' :
1116
        case 'subscribe' :
1117
            $page_title = $name . ' - ' . $lang['btn_'.$ACT];
1118
            break;
1119
1120
        default : // SHOW and anything else not included
1121
            $page_title = $name;
1122
    }
1123
1124
    if($ret) {
1125
        return hsc($page_title);
1126
    } else {
1127
        print hsc($page_title);
1128
        return true;
1129
    }
1130
}
1131
1132
/**
1133
 * Returns the requested EXIF/IPTC tag from the current image
1134
 *
1135
 * If $tags is an array all given tags are tried until a
1136
 * value is found. If no value is found $alt is returned.
1137
 *
1138
 * Which texts are known is defined in the functions _exifTagNames
1139
 * and _iptcTagNames() in inc/jpeg.php (You need to prepend IPTC
1140
 * to the names of the latter one)
1141
 *
1142
 * Only allowed in: detail.php
1143
 *
1144
 * @author Andreas Gohr <[email protected]>
1145
 *
1146
 * @param array|string $tags tag or array of tags to try
1147
 * @param string       $alt  alternative output if no data was found
1148
 * @param null|string  $src  the image src, uses global $SRC if not given
1149
 * @return string
1150
 */
1151
function tpl_img_getTag($tags, $alt = '', $src = null) {
1152
    // Init Exif Reader
1153
    global $SRC;
1154
1155
    if(is_null($src)) $src = $SRC;
1156
1157
    static $meta = null;
1158
    if(is_null($meta)) $meta = new JpegMeta($src);
1159
    if($meta === false) return $alt;
1160
    $info = cleanText($meta->getField($tags));
1161
    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...
1162
    return $info;
1163
}
1164
1165
/**
1166
 * Returns a description list of the metatags of the current image
1167
 *
1168
 * @return string html of description list
1169
 */
1170
function tpl_img_meta() {
1171
    global $lang;
1172
1173
    $tags = tpl_get_img_meta();
1174
1175
    echo '<dl>';
1176
    foreach($tags as $tag) {
1177
        $label = $lang[$tag['langkey']];
1178
        if(!$label) $label = $tag['langkey'] . ':';
1179
1180
        echo '<dt>'.$label.'</dt><dd>';
1181
        if ($tag['type'] == 'date') {
1182
            echo dformat($tag['value']);
1183
        } else {
1184
            echo hsc($tag['value']);
1185
        }
1186
        echo '</dd>';
1187
    }
1188
    echo '</dl>';
1189
}
1190
1191
/**
1192
 * Returns metadata as configured in mediameta config file, ready for creating html
1193
 *
1194
 * @return array with arrays containing the entries:
1195
 *   - string langkey  key to lookup in the $lang var, if not found printed as is
1196
 *   - string type     type of value
1197
 *   - string value    tag value (unescaped)
1198
 */
1199
function tpl_get_img_meta() {
1200
1201
    $config_files = getConfigFiles('mediameta');
1202
    foreach ($config_files as $config_file) {
1203
        if(file_exists($config_file)) {
1204
            include($config_file);
1205
        }
1206
    }
1207
    /** @var array $fields the included array with metadata */
1208
1209
    $tags = array();
1210
    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...
1211
        $t = array();
1212
        if (!empty($tag[0])) {
1213
            $t = array($tag[0]);
1214
        }
1215
        if(is_array($tag[3])) {
1216
            $t = array_merge($t,$tag[3]);
1217
        }
1218
        $value = tpl_img_getTag($t);
1219
        if ($value) {
1220
            $tags[] = array('langkey' => $tag[1], 'type' => $tag[2], 'value' => $value);
1221
        }
1222
    }
1223
    return $tags;
1224
}
1225
1226
/**
1227
 * Prints the image with a link to the full sized version
1228
 *
1229
 * Only allowed in: detail.php
1230
 *
1231
 * @triggers TPL_IMG_DISPLAY
1232
 * @param $maxwidth  int - maximal width of the image
1233
 * @param $maxheight int - maximal height of the image
1234
 * @param $link bool     - link to the orginal size?
1235
 * @param $params array  - additional image attributes
1236
 * @return bool Result of TPL_IMG_DISPLAY
1237
 */
1238
function tpl_img($maxwidth = 0, $maxheight = 0, $link = true, $params = null) {
1239
    global $IMG;
1240
    /** @var Input $INPUT */
1241
    global $INPUT;
1242
    global $REV;
1243
    $w = (int) tpl_img_getTag('File.Width');
1244
    $h = (int) tpl_img_getTag('File.Height');
1245
1246
    //resize to given max values
1247
    $ratio = 1;
1248
    if($w >= $h) {
1249
        if($maxwidth && $w >= $maxwidth) {
1250
            $ratio = $maxwidth / $w;
1251
        } elseif($maxheight && $h > $maxheight) {
1252
            $ratio = $maxheight / $h;
1253
        }
1254
    } else {
1255
        if($maxheight && $h >= $maxheight) {
1256
            $ratio = $maxheight / $h;
1257
        } elseif($maxwidth && $w > $maxwidth) {
1258
            $ratio = $maxwidth / $w;
1259
        }
1260
    }
1261
    if($ratio) {
1262
        $w = floor($ratio * $w);
1263
        $h = floor($ratio * $h);
1264
    }
1265
1266
    //prepare URLs
1267
    $url = ml($IMG, array('cache'=> $INPUT->str('cache'),'rev'=>$REV), true, '&');
1268
    $src = ml($IMG, array('cache'=> $INPUT->str('cache'),'rev'=>$REV, 'w'=> $w, 'h'=> $h), true, '&');
1269
1270
    //prepare attributes
1271
    $alt = tpl_img_getTag('Simple.Title');
1272
    if(is_null($params)) {
1273
        $p = array();
1274
    } else {
1275
        $p = $params;
1276
    }
1277
    if($w) $p['width'] = $w;
1278
    if($h) $p['height'] = $h;
1279
    $p['class'] = 'img_detail';
1280
    if($alt) {
1281
        $p['alt']   = $alt;
1282
        $p['title'] = $alt;
1283
    } else {
1284
        $p['alt'] = '';
1285
    }
1286
    $p['src'] = $src;
1287
1288
    $data = array('url'=> ($link ? $url : null), 'params'=> $p);
1289
    return trigger_event('TPL_IMG_DISPLAY', $data, '_tpl_img_action', true);
1290
}
1291
1292
/**
1293
 * Default action for TPL_IMG_DISPLAY
1294
 *
1295
 * @param array $data
1296
 * @return bool
1297
 */
1298
function _tpl_img_action($data) {
1299
    global $lang;
1300
    $p = buildAttributes($data['params']);
1301
1302
    if($data['url']) print '<a href="'.hsc($data['url']).'" title="'.$lang['mediaview'].'">';
1303
    print '<img '.$p.'/>';
1304
    if($data['url']) print '</a>';
1305
    return true;
1306
}
1307
1308
/**
1309
 * This function inserts a small gif which in reality is the indexer function.
1310
 *
1311
 * Should be called somewhere at the very end of the main.php
1312
 * template
1313
 *
1314
 * @return bool
1315
 */
1316
function tpl_indexerWebBug() {
1317
    global $ID;
1318
1319
    $p           = array();
1320
    $p['src']    = DOKU_BASE.'lib/exe/indexer.php?id='.rawurlencode($ID).
1321
        '&'.time();
1322
    $p['width']  = 2; //no more 1x1 px image because we live in times of ad blockers...
1323
    $p['height'] = 1;
1324
    $p['alt']    = '';
1325
    $att         = buildAttributes($p);
1326
    print "<img $att />";
1327
    return true;
1328
}
1329
1330
/**
1331
 * tpl_getConf($id)
1332
 *
1333
 * use this function to access template configuration variables
1334
 *
1335
 * @param string $id      name of the value to access
1336
 * @param mixed  $notset  what to return if the setting is not available
1337
 * @return mixed
1338
 */
1339
function tpl_getConf($id, $notset=false) {
1340
    global $conf;
1341
    static $tpl_configloaded = false;
1342
1343
    $tpl = $conf['template'];
1344
1345
    if(!$tpl_configloaded) {
1346
        $tconf = tpl_loadConfig();
1347
        if($tconf !== false) {
1348
            foreach($tconf as $key => $value) {
1349
                if(isset($conf['tpl'][$tpl][$key])) continue;
1350
                $conf['tpl'][$tpl][$key] = $value;
1351
            }
1352
            $tpl_configloaded = true;
1353
        }
1354
    }
1355
1356
    if(isset($conf['tpl'][$tpl][$id])){
1357
        return $conf['tpl'][$tpl][$id];
1358
    }
1359
1360
    return $notset;
1361
}
1362
1363
/**
1364
 * tpl_loadConfig()
1365
 *
1366
 * reads all template configuration variables
1367
 * this function is automatically called by tpl_getConf()
1368
 *
1369
 * @return array
1370
 */
1371
function tpl_loadConfig() {
1372
1373
    $file = tpl_incdir().'/conf/default.php';
1374
    $conf = array();
1375
1376
    if(!file_exists($file)) return false;
1377
1378
    // load default config file
1379
    include($file);
1380
1381
    return $conf;
1382
}
1383
1384
// language methods
1385
/**
1386
 * tpl_getLang($id)
1387
 *
1388
 * use this function to access template language variables
1389
 *
1390
 * @param string $id key of language string
1391
 * @return string
1392
 */
1393
function tpl_getLang($id) {
1394
    static $lang = array();
1395
1396
    if(count($lang) === 0) {
1397
        global $conf, $config_cascade; // definitely don't invoke "global $lang"
1398
1399
        $path = tpl_incdir() . 'lang/';
1400
1401
        $lang = array();
1402
1403
        // don't include once
1404
        @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...
1405
        foreach($config_cascade['lang']['template'] as $config_file) {
1406
            if(file_exists($config_file . $conf['template'] . '/en/lang.php')) {
1407
                include($config_file . $conf['template'] . '/en/lang.php');
1408
            }
1409
        }
1410
1411
        if($conf['lang'] != 'en') {
1412
            @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...
1413
            foreach($config_cascade['lang']['template'] as $config_file) {
1414
                if(file_exists($config_file . $conf['template'] . '/' . $conf['lang'] . '/lang.php')) {
1415
                    include($config_file . $conf['template'] . '/' . $conf['lang'] . '/lang.php');
1416
                }
1417
            }
1418
        }
1419
    }
1420
    return $lang[$id];
1421
}
1422
1423
/**
1424
 * Retrieve a language dependent file and pass to xhtml renderer for display
1425
 * template equivalent of p_locale_xhtml()
1426
 *
1427
 * @param   string $id id of language dependent wiki page
1428
 * @return  string     parsed contents of the wiki page in xhtml format
1429
 */
1430
function tpl_locale_xhtml($id) {
1431
    return p_cached_output(tpl_localeFN($id));
1432
}
1433
1434
/**
1435
 * Prepends appropriate path for a language dependent filename
1436
 *
1437
 * @param string $id id of localized text
1438
 * @return string wiki text
1439
 */
1440
function tpl_localeFN($id) {
1441
    $path = tpl_incdir().'lang/';
1442
    global $conf;
1443
    $file = DOKU_CONF.'template_lang/'.$conf['template'].'/'.$conf['lang'].'/'.$id.'.txt';
1444
    if (!file_exists($file)){
1445
        $file = $path.$conf['lang'].'/'.$id.'.txt';
1446
        if(!file_exists($file)){
1447
            //fall back to english
1448
            $file = $path.'en/'.$id.'.txt';
1449
        }
1450
    }
1451
    return $file;
1452
}
1453
1454
/**
1455
 * prints the "main content" in the mediamanager popup
1456
 *
1457
 * Depending on the user's actions this may be a list of
1458
 * files in a namespace, the meta editing dialog or
1459
 * a message of referencing pages
1460
 *
1461
 * Only allowed in mediamanager.php
1462
 *
1463
 * @triggers MEDIAMANAGER_CONTENT_OUTPUT
1464
 * @param bool $fromajax - set true when calling this function via ajax
1465
 * @param string $sort
1466
 *
1467
 * @author Andreas Gohr <[email protected]>
1468
 */
1469
function tpl_mediaContent($fromajax = false, $sort='natural') {
1470
    global $IMG;
1471
    global $AUTH;
1472
    global $INUSE;
1473
    global $NS;
1474
    global $JUMPTO;
1475
    /** @var Input $INPUT */
1476
    global $INPUT;
1477
1478
    $do = $INPUT->extract('do')->str('do');
1479
    if(in_array($do, array('save', 'cancel'))) $do = '';
1480
1481
    if(!$do) {
1482
        if($INPUT->bool('edit')) {
1483
            $do = 'metaform';
1484
        } elseif(is_array($INUSE)) {
1485
            $do = 'filesinuse';
1486
        } else {
1487
            $do = 'filelist';
1488
        }
1489
    }
1490
1491
    // output the content pane, wrapped in an event.
1492
    if(!$fromajax) ptln('<div id="media__content">');
1493
    $data = array('do' => $do);
1494
    $evt  = new Doku_Event('MEDIAMANAGER_CONTENT_OUTPUT', $data);
1495
    if($evt->advise_before()) {
1496
        $do = $data['do'];
1497
        if($do == 'filesinuse') {
1498
            media_filesinuse($INUSE, $IMG);
1499
        } elseif($do == 'filelist') {
1500
            media_filelist($NS, $AUTH, $JUMPTO,false,$sort);
1501
        } elseif($do == 'searchlist') {
1502
            media_searchlist($INPUT->str('q'), $NS, $AUTH);
1503
        } else {
1504
            msg('Unknown action '.hsc($do), -1);
1505
        }
1506
    }
1507
    $evt->advise_after();
1508
    unset($evt);
1509
    if(!$fromajax) ptln('</div>');
1510
1511
}
1512
1513
/**
1514
 * Prints the central column in full-screen media manager
1515
 * Depending on the opened tab this may be a list of
1516
 * files in a namespace, upload form or search form
1517
 *
1518
 * @author Kate Arzamastseva <[email protected]>
1519
 */
1520
function tpl_mediaFileList() {
1521
    global $AUTH;
1522
    global $NS;
1523
    global $JUMPTO;
1524
    global $lang;
1525
    /** @var Input $INPUT */
1526
    global $INPUT;
1527
1528
    $opened_tab = $INPUT->str('tab_files');
1529
    if(!$opened_tab || !in_array($opened_tab, array('files', 'upload', 'search'))) $opened_tab = 'files';
1530
    if($INPUT->str('mediado') == 'update') $opened_tab = 'upload';
1531
1532
    echo '<h2 class="a11y">'.$lang['mediaselect'].'</h2>'.NL;
1533
1534
    media_tabs_files($opened_tab);
1535
1536
    echo '<div class="panelHeader">'.NL;
1537
    echo '<h3>';
1538
    $tabTitle = ($NS) ? $NS : '['.$lang['mediaroot'].']';
1539
    printf($lang['media_'.$opened_tab], '<strong>'.hsc($tabTitle).'</strong>');
1540
    echo '</h3>'.NL;
1541
    if($opened_tab === 'search' || $opened_tab === 'files') {
1542
        media_tab_files_options();
1543
    }
1544
    echo '</div>'.NL;
1545
1546
    echo '<div class="panelContent">'.NL;
1547
    if($opened_tab == 'files') {
1548
        media_tab_files($NS, $AUTH, $JUMPTO);
1549
    } elseif($opened_tab == 'upload') {
1550
        media_tab_upload($NS, $AUTH, $JUMPTO);
1551
    } elseif($opened_tab == 'search') {
1552
        media_tab_search($NS, $AUTH);
1553
    }
1554
    echo '</div>'.NL;
1555
}
1556
1557
/**
1558
 * Prints the third column in full-screen media manager
1559
 * Depending on the opened tab this may be details of the
1560
 * selected file, the meta editing dialog or
1561
 * list of file revisions
1562
 *
1563
 * @author Kate Arzamastseva <[email protected]>
1564
 *
1565
 * @param string $image
1566
 * @param boolean $rev
1567
 */
1568
function tpl_mediaFileDetails($image, $rev) {
1569
    global $conf, $DEL, $lang;
1570
    /** @var Input $INPUT */
1571
    global $INPUT;
1572
1573
    $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')) && $conf['mediarevisions']);
1574
    if(!$image || (!file_exists(mediaFN($image)) && !$removed) || $DEL) return;
1575
    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...
1576
    $ns = getNS($image);
1577
    $do = $INPUT->str('mediado');
1578
1579
    $opened_tab = $INPUT->str('tab_details');
1580
1581
    $tab_array = array('view');
1582
    list(, $mime) = mimetype($image);
1583
    if($mime == 'image/jpeg') {
1584
        $tab_array[] = 'edit';
1585
    }
1586
    if($conf['mediarevisions']) {
1587
        $tab_array[] = 'history';
1588
    }
1589
1590
    if(!$opened_tab || !in_array($opened_tab, $tab_array)) $opened_tab = 'view';
1591
    if($INPUT->bool('edit')) $opened_tab = 'edit';
1592
    if($do == 'restore') $opened_tab = 'view';
1593
1594
    media_tabs_details($image, $opened_tab);
1595
1596
    echo '<div class="panelHeader"><h3>';
1597
    list($ext) = mimetype($image, false);
1598
    $class    = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext);
1599
    $class    = 'select mediafile mf_'.$class;
1600
    $tabTitle = '<strong><a href="'.ml($image).'" class="'.$class.'" title="'.$lang['mediaview'].'">'.$image.'</a>'.'</strong>';
1601
    if($opened_tab === 'view' && $rev) {
1602
        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...
1603
    } else {
1604
        printf($lang['media_'.$opened_tab], $tabTitle);
1605
    }
1606
1607
    echo '</h3></div>'.NL;
1608
1609
    echo '<div class="panelContent">'.NL;
1610
1611
    if($opened_tab == 'view') {
1612
        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 1576 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...
1613
1614
    } elseif($opened_tab == 'edit' && !$removed) {
1615
        media_tab_edit($image, $ns);
0 ignored issues
show
Security Bug introduced by
It seems like $ns defined by getNS($image) on line 1576 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...
1616
1617
    } elseif($opened_tab == 'history' && $conf['mediarevisions']) {
1618
        media_tab_history($image, $ns);
0 ignored issues
show
Security Bug introduced by
It seems like $ns defined by getNS($image) on line 1576 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...
1619
    }
1620
1621
    echo '</div>'.NL;
1622
}
1623
1624
/**
1625
 * prints the namespace tree in the mediamanager popup
1626
 *
1627
 * Only allowed in mediamanager.php
1628
 *
1629
 * @author Andreas Gohr <[email protected]>
1630
 */
1631
function tpl_mediaTree() {
1632
    global $NS;
1633
    ptln('<div id="media__tree">');
1634
    media_nstree($NS);
1635
    ptln('</div>');
1636
}
1637
1638
/**
1639
 * Print a dropdown menu with all DokuWiki actions
1640
 *
1641
 * Note: this will not use any pretty URLs
1642
 *
1643
 * @author Andreas Gohr <[email protected]>
1644
 *
1645
 * @param string $empty empty option label
1646
 * @param string $button submit button label
1647
 */
1648
function tpl_actiondropdown($empty = '', $button = '&gt;') {
1649
    $menu = new \dokuwiki\Menu\MobileMenu();
1650
    echo $menu->getDropdown($empty, $button);
1651
}
1652
1653
/**
1654
 * Print a informational line about the used license
1655
 *
1656
 * @author Andreas Gohr <[email protected]>
1657
 * @param  string $img     print image? (|button|badge)
1658
 * @param  bool   $imgonly skip the textual description?
1659
 * @param  bool   $return  when true don't print, but return HTML
1660
 * @param  bool   $wrap    wrap in div with class="license"?
1661
 * @return string
1662
 */
1663
function tpl_license($img = 'badge', $imgonly = false, $return = false, $wrap = true) {
1664
    global $license;
1665
    global $conf;
1666
    global $lang;
1667
    if(!$conf['license']) return '';
1668
    if(!is_array($license[$conf['license']])) return '';
1669
    $lic    = $license[$conf['license']];
1670
    $target = ($conf['target']['extern']) ? ' target="'.$conf['target']['extern'].'"' : '';
1671
1672
    $out = '';
1673
    if($wrap) $out .= '<div class="license">';
1674
    if($img) {
1675
        $src = license_img($img);
1676
        if($src) {
1677
            $out .= '<a href="'.$lic['url'].'" rel="license"'.$target;
1678
            $out .= '><img src="'.DOKU_BASE.$src.'" alt="'.$lic['name'].'" /></a>';
1679
            if(!$imgonly) $out .= ' ';
1680
        }
1681
    }
1682
    if(!$imgonly) {
1683
        $out .= $lang['license'].' ';
1684
        $out .= '<bdi><a href="'.$lic['url'].'" rel="license" class="urlextern"'.$target;
1685
        $out .= '>'.$lic['name'].'</a></bdi>';
1686
    }
1687
    if($wrap) $out .= '</div>';
1688
1689
    if($return) return $out;
1690
    echo $out;
1691
    return '';
1692
}
1693
1694
/**
1695
 * Includes the rendered HTML of a given page
1696
 *
1697
 * This function is useful to populate sidebars or similar features in a
1698
 * template
1699
 *
1700
 * @param string $pageid The page name you want to include
1701
 * @param bool $print Should the content be printed or returned only
1702
 * @param bool $propagate Search higher namespaces, too?
1703
 * @param bool $useacl Include the page only if the ACLs check out?
1704
 * @return bool|null|string
1705
 */
1706
function tpl_include_page($pageid, $print = true, $propagate = false, $useacl = true) {
1707
    if($propagate) {
1708
        $pageid = page_findnearest($pageid, $useacl);
1709
    } elseif($useacl && auth_quickaclcheck($pageid) == AUTH_NONE) {
1710
        return false;
1711
    }
1712
    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...
1713
1714
    global $TOC;
1715
    $oldtoc = $TOC;
1716
    $html   = p_wiki_xhtml($pageid, '', false);
1717
    $TOC    = $oldtoc;
1718
1719
    if($print) echo $html;
1720
    return $html;
1721
}
1722
1723
/**
1724
 * Display the subscribe form
1725
 *
1726
 * @author Adrian Lang <[email protected]>
1727
 */
1728
function tpl_subscribe() {
1729
    global $INFO;
1730
    global $ID;
1731
    global $lang;
1732
    global $conf;
1733
    $stime_days = $conf['subscribe_time'] / 60 / 60 / 24;
1734
1735
    echo p_locale_xhtml('subscr_form');
1736
    echo '<h2>'.$lang['subscr_m_current_header'].'</h2>';
1737
    echo '<div class="level2">';
1738
    if($INFO['subscribed'] === false) {
1739
        echo '<p>'.$lang['subscr_m_not_subscribed'].'</p>';
1740
    } else {
1741
        echo '<ul>';
1742
        foreach($INFO['subscribed'] as $sub) {
1743
            echo '<li><div class="li">';
1744
            if($sub['target'] !== $ID) {
1745
                echo '<code class="ns">'.hsc(prettyprint_id($sub['target'])).'</code>';
1746
            } else {
1747
                echo '<code class="page">'.hsc(prettyprint_id($sub['target'])).'</code>';
1748
            }
1749
            $sstl = sprintf($lang['subscr_style_'.$sub['style']], $stime_days);
1750
            if(!$sstl) $sstl = hsc($sub['style']);
1751
            echo ' ('.$sstl.') ';
1752
1753
            echo '<a href="'.wl(
1754
                $ID,
1755
                array(
1756
                     'do'        => 'subscribe',
1757
                     'sub_target'=> $sub['target'],
1758
                     'sub_style' => $sub['style'],
1759
                     'sub_action'=> 'unsubscribe',
1760
                     'sectok'    => getSecurityToken()
1761
                )
1762
            ).
1763
                '" class="unsubscribe">'.$lang['subscr_m_unsubscribe'].
1764
                '</a></div></li>';
1765
        }
1766
        echo '</ul>';
1767
    }
1768
    echo '</div>';
1769
1770
    // Add new subscription form
1771
    echo '<h2>'.$lang['subscr_m_new_header'].'</h2>';
1772
    echo '<div class="level2">';
1773
    $ns      = getNS($ID).':';
1774
    $targets = array(
1775
        $ID => '<code class="page">'.prettyprint_id($ID).'</code>',
1776
        $ns => '<code class="ns">'.prettyprint_id($ns).'</code>',
1777
    );
1778
    $styles  = array(
1779
        'every'  => $lang['subscr_style_every'],
1780
        'digest' => sprintf($lang['subscr_style_digest'], $stime_days),
1781
        'list'   => sprintf($lang['subscr_style_list'], $stime_days),
1782
    );
1783
1784
    $form = new Doku_Form(array('id' => 'subscribe__form'));
1785
    $form->startFieldset($lang['subscr_m_subscribe']);
1786
    $form->addRadioSet('sub_target', $targets);
1787
    $form->startFieldset($lang['subscr_m_receive']);
1788
    $form->addRadioSet('sub_style', $styles);
1789
    $form->addHidden('sub_action', 'subscribe');
1790
    $form->addHidden('do', 'subscribe');
1791
    $form->addHidden('id', $ID);
1792
    $form->endFieldset();
1793
    $form->addElement(form_makeButton('submit', 'subscribe', $lang['subscr_m_subscribe']));
1794
    html_form('SUBSCRIBE', $form);
1795
    echo '</div>';
1796
}
1797
1798
/**
1799
 * Tries to send already created content right to the browser
1800
 *
1801
 * Wraps around ob_flush() and flush()
1802
 *
1803
 * @author Andreas Gohr <[email protected]>
1804
 */
1805
function tpl_flush() {
1806
    ob_flush();
1807
    flush();
1808
}
1809
1810
/**
1811
 * Tries to find a ressource file in the given locations.
1812
 *
1813
 * If a given location starts with a colon it is assumed to be a media
1814
 * file, otherwise it is assumed to be relative to the current template
1815
 *
1816
 * @param  string[] $search       locations to look at
1817
 * @param  bool     $abs           if to use absolute URL
1818
 * @param  array   &$imginfo   filled with getimagesize()
1819
 * @return string
1820
 *
1821
 * @author Andreas  Gohr <[email protected]>
1822
 */
1823
function tpl_getMediaFile($search, $abs = false, &$imginfo = null) {
1824
    $img     = '';
1825
    $file    = '';
1826
    $ismedia = false;
1827
    // loop through candidates until a match was found:
1828
    foreach($search as $img) {
1829
        if(substr($img, 0, 1) == ':') {
1830
            $file    = mediaFN($img);
1831
            $ismedia = true;
1832
        } else {
1833
            $file    = tpl_incdir().$img;
1834
            $ismedia = false;
1835
        }
1836
1837
        if(file_exists($file)) break;
1838
    }
1839
1840
    // fetch image data if requested
1841
    if(!is_null($imginfo)) {
1842
        $imginfo = getimagesize($file);
1843
    }
1844
1845
    // build URL
1846
    if($ismedia) {
1847
        $url = ml($img, '', true, '', $abs);
1848
    } else {
1849
        $url = tpl_basedir().$img;
1850
        if($abs) $url = DOKU_URL.substr($url, strlen(DOKU_REL));
1851
    }
1852
1853
    return $url;
1854
}
1855
1856
/**
1857
 * PHP include a file
1858
 *
1859
 * either from the conf directory if it exists, otherwise use
1860
 * file in the template's root directory.
1861
 *
1862
 * The function honours config cascade settings and looks for the given
1863
 * file next to the ´main´ config files, in the order protected, local,
1864
 * default.
1865
 *
1866
 * Note: no escaping or sanity checking is done here. Never pass user input
1867
 * to this function!
1868
 *
1869
 * @author Anika Henke <[email protected]>
1870
 * @author Andreas Gohr <[email protected]>
1871
 *
1872
 * @param string $file
1873
 */
1874
function tpl_includeFile($file) {
1875
    global $config_cascade;
1876
    foreach(array('protected', 'local', 'default') as $config_group) {
1877
        if(empty($config_cascade['main'][$config_group])) continue;
1878
        foreach($config_cascade['main'][$config_group] as $conf_file) {
1879
            $dir = dirname($conf_file);
1880
            if(file_exists("$dir/$file")) {
1881
                include("$dir/$file");
1882
                return;
1883
            }
1884
        }
1885
    }
1886
1887
    // still here? try the template dir
1888
    $file = tpl_incdir().$file;
1889
    if(file_exists($file)) {
1890
        include($file);
1891
    }
1892
}
1893
1894
/**
1895
 * Returns <link> tag for various icon types (favicon|mobile|generic)
1896
 *
1897
 * @author Anika Henke <[email protected]>
1898
 *
1899
 * @param  array $types - list of icon types to display (favicon|mobile|generic)
1900
 * @return string
1901
 */
1902
function tpl_favicon($types = array('favicon')) {
1903
1904
    $return = '';
1905
1906
    foreach($types as $type) {
1907
        switch($type) {
1908
            case 'favicon':
1909
                $look = array(':wiki:favicon.ico', ':favicon.ico', 'images/favicon.ico');
1910
                $return .= '<link rel="shortcut icon" href="'.tpl_getMediaFile($look).'" />'.NL;
1911
                break;
1912
            case 'mobile':
1913
                $look = array(':wiki:apple-touch-icon.png', ':apple-touch-icon.png', 'images/apple-touch-icon.png');
1914
                $return .= '<link rel="apple-touch-icon" href="'.tpl_getMediaFile($look).'" />'.NL;
1915
                break;
1916
            case 'generic':
1917
                // ideal world solution, which doesn't work in any browser yet
1918
                $look = array(':wiki:favicon.svg', ':favicon.svg', 'images/favicon.svg');
1919
                $return .= '<link rel="icon" href="'.tpl_getMediaFile($look).'" type="image/svg+xml" />'.NL;
1920
                break;
1921
        }
1922
    }
1923
1924
    return $return;
1925
}
1926
1927
/**
1928
 * Prints full-screen media manager
1929
 *
1930
 * @author Kate Arzamastseva <[email protected]>
1931
 */
1932
function tpl_media() {
1933
    global $NS, $IMG, $JUMPTO, $REV, $lang, $fullscreen, $INPUT;
1934
    $fullscreen = true;
1935
    require_once DOKU_INC.'lib/exe/mediamanager.php';
1936
1937
    $rev   = '';
1938
    $image = cleanID($INPUT->str('image'));
1939
    if(isset($IMG)) $image = $IMG;
1940
    if(isset($JUMPTO)) $image = $JUMPTO;
1941
    if(isset($REV) && !$JUMPTO) $rev = $REV;
1942
1943
    echo '<div id="mediamanager__page">'.NL;
1944
    echo '<h1>'.$lang['btn_media'].'</h1>'.NL;
1945
    html_msgarea();
1946
1947
    echo '<div class="panel namespaces">'.NL;
1948
    echo '<h2>'.$lang['namespaces'].'</h2>'.NL;
1949
    echo '<div class="panelHeader">';
1950
    echo $lang['media_namespaces'];
1951
    echo '</div>'.NL;
1952
1953
    echo '<div class="panelContent" id="media__tree">'.NL;
1954
    media_nstree($NS);
1955
    echo '</div>'.NL;
1956
    echo '</div>'.NL;
1957
1958
    echo '<div class="panel filelist">'.NL;
1959
    tpl_mediaFileList();
1960
    echo '</div>'.NL;
1961
1962
    echo '<div class="panel file">'.NL;
1963
    echo '<h2 class="a11y">'.$lang['media_file'].'</h2>'.NL;
1964
    tpl_mediaFileDetails($image, $rev);
1965
    echo '</div>'.NL;
1966
1967
    echo '</div>'.NL;
1968
}
1969
1970
/**
1971
 * Return useful layout classes
1972
 *
1973
 * @author Anika Henke <[email protected]>
1974
 *
1975
 * @return string
1976
 */
1977
function tpl_classes() {
1978
    global $ACT, $conf, $ID, $INFO;
1979
    /** @var Input $INPUT */
1980
    global $INPUT;
1981
1982
    $classes = array(
1983
        'dokuwiki',
1984
        'mode_'.$ACT,
1985
        'tpl_'.$conf['template'],
1986
        $INPUT->server->bool('REMOTE_USER') ? 'loggedIn' : '',
1987
        $INFO['exists'] ? '' : 'notFound',
1988
        ($ID == $conf['start']) ? 'home' : '',
1989
    );
1990
    return join(' ', $classes);
1991
}
1992
1993
/**
1994
 * Create event for tools menues
1995
 *
1996
 * @author Anika Henke <[email protected]>
1997
 * @param string $toolsname name of menu
1998
 * @param array $items
1999
 * @param string $view e.g. 'main', 'detail', ...
2000
 */
2001
function tpl_toolsevent($toolsname, $items, $view = 'main') {
2002
    $data = array(
2003
        'view' => $view,
2004
        'items' => $items
2005
    );
2006
2007
    $hook = 'TEMPLATE_' . strtoupper($toolsname) . '_DISPLAY';
2008
    $evt = new Doku_Event($hook, $data);
2009
    if($evt->advise_before()) {
2010
        foreach($evt->data['items'] as $k => $html) echo $html;
2011
    }
2012
    $evt->advise_after();
2013
}
2014
2015
//Setup VIM: ex: et ts=4 :
2016
2017