Completed
Push — betterJSINFO ( 7bf319...0c39d4 )
by Michael
23:24 queued 16:55
created

template.php ➔ _tpl_ensureJSINFO()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 0
dl 0
loc 13
rs 9.4285
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
    $script = "var NS='".$INFO['namespace']."';";
328
    if($conf['useacl'] && $INPUT->server->str('REMOTE_USER')) {
329
        $script .= "var SIG='".toolbar_signature()."';";
330
    }
331
    jsinfo();
332
    $script .= 'var JSINFO = ' . json_encode($JSINFO).';';
333
    $head['script'][] = array('type'=> 'text/javascript', '_data'=> $script);
334
335
    // load jquery
336
    $jquery = getCdnUrls();
337
    foreach($jquery as $src) {
338
        $head['script'][] = array(
339
            'type' => 'text/javascript', 'charset' => 'utf-8', '_data' => '', 'src' => $src
340
        );
341
    }
342
343
    // load our javascript dispatcher
344
    $head['script'][] = array(
345
        'type'=> 'text/javascript', 'charset'=> 'utf-8', '_data'=> '',
346
        'src' => DOKU_BASE.'lib/exe/js.php'.'?t='.rawurlencode($conf['template']).'&tseed='.$tseed
347
    );
348
349
    // trigger event here
350
    trigger_event('TPL_METAHEADER_OUTPUT', $head, '_tpl_metaheaders_action', true);
351
    return true;
352
}
353
354
/**
355
 * prints the array build by tpl_metaheaders
356
 *
357
 * $data is an array of different header tags. Each tag can have multiple
358
 * instances. Attributes are given as key value pairs. Values will be HTML
359
 * encoded automatically so they should be provided as is in the $data array.
360
 *
361
 * For tags having a body attribute specify the body data in the special
362
 * attribute '_data'. This field will NOT BE ESCAPED automatically.
363
 *
364
 * @author Andreas Gohr <[email protected]>
365
 *
366
 * @param array $data
367
 */
368
function _tpl_metaheaders_action($data) {
369
    foreach($data as $tag => $inst) {
370
        if($tag == 'script') {
371
            echo "<!--[if gte IE 9]><!-->\n"; // no scripts for old IE
372
        }
373
        foreach($inst as $attr) {
374
            if ( empty($attr) ) { continue; }
375
            echo '<', $tag, ' ', buildAttributes($attr);
376
            if(isset($attr['_data']) || $tag == 'script') {
377
                if($tag == 'script' && $attr['_data'])
378
                    $attr['_data'] = "/*<![CDATA[*/".
379
                        $attr['_data'].
380
                        "\n/*!]]>*/";
381
382
                echo '>', $attr['_data'], '</', $tag, '>';
383
            } else {
384
                echo '/>';
385
            }
386
            echo "\n";
387
        }
388
        if($tag == 'script') {
389
            echo "<!--<![endif]-->\n";
390
        }
391
    }
392
}
393
394
/**
395
 * Print a link
396
 *
397
 * Just builds a link.
398
 *
399
 * @author Andreas Gohr <[email protected]>
400
 *
401
 * @param string $url
402
 * @param string $name
403
 * @param string $more
404
 * @param bool $return if true return the link html, otherwise print
405
 * @return bool|string html of the link, or true if printed
406
 */
407
function tpl_link($url, $name, $more = '', $return = false) {
408
    $out = '<a href="'.$url.'" ';
409
    if($more) $out .= ' '.$more;
410
    $out .= ">$name</a>";
411
    if($return) return $out;
412
    print $out;
413
    return true;
414
}
415
416
/**
417
 * Prints a link to a WikiPage
418
 *
419
 * Wrapper around html_wikilink
420
 *
421
 * @author Andreas Gohr <[email protected]>
422
 *
423
 * @param string      $id   page id
424
 * @param string|null $name the name of the link
425
 * @return bool true
426
 */
427
function tpl_pagelink($id, $name = null) {
428
    print '<bdi>'.html_wikilink($id, $name).'</bdi>';
429
    return true;
430
}
431
432
/**
433
 * get the parent page
434
 *
435
 * Tries to find out which page is parent.
436
 * returns false if none is available
437
 *
438
 * @author Andreas Gohr <[email protected]>
439
 *
440
 * @param string $id page id
441
 * @return false|string
442
 */
443
function tpl_getparent($id) {
444
    $parent = getNS($id).':';
445
    resolve_pageid('', $parent, $exists);
446
    if($parent == $id) {
447
        $pos    = strrpos(getNS($id), ':');
448
        $parent = substr($parent, 0, $pos).':';
449
        resolve_pageid('', $parent, $exists);
450
        if($parent == $id) return false;
451
    }
452
    return $parent;
453
}
454
455
/**
456
 * Print one of the buttons
457
 *
458
 * @author Adrian Lang <[email protected]>
459
 * @see    tpl_get_action
460
 *
461
 * @param string $type
462
 * @param bool $return
463
 * @return bool|string html, or false if no data, true if printed
464
 * @deprecated 2017-09-01 see devel:menus
465
 */
466
function tpl_button($type, $return = false) {
467
    dbg_deprecated('see devel:menus');
468
    $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...
469
    if($data === false) {
470
        return false;
471
    } elseif(!is_array($data)) {
472
        $out = sprintf($data, 'button');
473
    } else {
474
        /**
475
         * @var string $accesskey
476
         * @var string $id
477
         * @var string $method
478
         * @var array  $params
479
         */
480
        extract($data);
481
        if($id === '#dokuwiki__top') {
482
            $out = html_topbtn();
483
        } else {
484
            $out = html_btn($type, $id, $accesskey, $params, $method);
485
        }
486
    }
487
    if($return) return $out;
488
    echo $out;
489
    return true;
490
}
491
492
/**
493
 * Like the action buttons but links
494
 *
495
 * @author Adrian Lang <[email protected]>
496
 * @see    tpl_get_action
497
 *
498
 * @param string $type    action command
499
 * @param string $pre     prefix of link
500
 * @param string $suf     suffix of link
501
 * @param string $inner   innerHML of link
502
 * @param bool   $return  if true it returns html, otherwise prints
503
 * @return bool|string html or false if no data, true if printed
504
 * @deprecated 2017-09-01 see devel:menus
505
 */
506
function tpl_actionlink($type, $pre = '', $suf = '', $inner = '', $return = false) {
507
    dbg_deprecated('see devel:menus');
508
    global $lang;
509
    $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...
510
    if($data === false) {
511
        return false;
512
    } elseif(!is_array($data)) {
513
        $out = sprintf($data, 'link');
514
    } else {
515
        /**
516
         * @var string $accesskey
517
         * @var string $id
518
         * @var string $method
519
         * @var bool   $nofollow
520
         * @var array  $params
521
         * @var string $replacement
522
         */
523
        extract($data);
524
        if(strpos($id, '#') === 0) {
525
            $linktarget = $id;
526
        } else {
527
            $linktarget = wl($id, $params);
528
        }
529
        $caption = $lang['btn_'.$type];
530
        if(strpos($caption, '%s')){
531
            $caption = sprintf($caption, $replacement);
532
        }
533
        $akey    = $addTitle = '';
534
        if($accesskey) {
535
            $akey     = 'accesskey="'.$accesskey.'" ';
536
            $addTitle = ' ['.strtoupper($accesskey).']';
537
        }
538
        $rel = $nofollow ? 'rel="nofollow" ' : '';
539
        $out = tpl_link(
540
            $linktarget, $pre.(($inner) ? $inner : $caption).$suf,
541
            'class="action '.$type.'" '.
542
                $akey.$rel.
543
                'title="'.hsc($caption).$addTitle.'"', true
544
        );
545
    }
546
    if($return) return $out;
547
    echo $out;
548
    return true;
549
}
550
551
/**
552
 * Check the actions and get data for buttons and links
553
 *
554
 * @author Andreas Gohr <[email protected]>
555
 * @author Matthias Grimm <[email protected]>
556
 * @author Adrian Lang <[email protected]>
557
 *
558
 * @param string $type
559
 * @return array|bool|string
560
 * @deprecated 2017-09-01 see devel:menus
561
 */
562
function tpl_get_action($type) {
563
    dbg_deprecated('see devel:menus');
564
    if($type == 'history') $type = 'revisions';
565
    if($type == 'subscription') $type = 'subscribe';
566
    if($type == 'img_backto') $type = 'imgBackto';
567
568
    $class = '\\dokuwiki\\Menu\\Item\\' . ucfirst($type);
569
    if(class_exists($class)) {
570
        try {
571
            /** @var \dokuwiki\Menu\Item\AbstractItem $item */
572
            $item = new $class;
573
            $data = $item->getLegacyData();
574
            $unknown = false;
575
        } catch(\RuntimeException $ignored) {
576
            return false;
577
        }
578
    } else {
579
        global $ID;
580
        $data = array(
581
            'accesskey' => null,
582
            'type' => $type,
583
            'id' => $ID,
584
            'method' => 'get',
585
            'params' => array('do' => $type),
586
            'nofollow' => true,
587
            'replacement' => '',
588
        );
589
        $unknown = true;
590
    }
591
592
    $evt = new Doku_Event('TPL_ACTION_GET', $data);
593
    if($evt->advise_before()) {
594
        //handle unknown types
595
        if($unknown) {
596
            $data = '[unknown %s type]';
597
        }
598
    }
599
    $evt->advise_after();
600
    unset($evt);
601
602
    return $data;
603
}
604
605
/**
606
 * Wrapper around tpl_button() and tpl_actionlink()
607
 *
608
 * @author Anika Henke <[email protected]>
609
 *
610
 * @param string        $type action command
611
 * @param bool          $link link or form button?
612
 * @param string|bool   $wrapper HTML element wrapper
613
 * @param bool          $return return or print
614
 * @param string        $pre prefix for links
615
 * @param string        $suf suffix for links
616
 * @param string        $inner inner HTML for links
617
 * @return bool|string
618
 * @deprecated 2017-09-01 see devel:menus
619
 */
620
function tpl_action($type, $link = false, $wrapper = false, $return = false, $pre = '', $suf = '', $inner = '') {
621
    dbg_deprecated('see devel:menus');
622
    $out = '';
623
    if($link) {
624
        $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...
625
    } else {
626
        $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...
627
    }
628
    if($out && $wrapper) $out = "<$wrapper>$out</$wrapper>";
629
630
    if($return) return $out;
631
    print $out;
632
    return $out ? true : false;
633
}
634
635
/**
636
 * Print the search form
637
 *
638
 * If the first parameter is given a div with the ID 'qsearch_out' will
639
 * be added which instructs the ajax pagequicksearch to kick in and place
640
 * its output into this div. The second parameter controls the propritary
641
 * attribute autocomplete. If set to false this attribute will be set with an
642
 * value of "off" to instruct the browser to disable it's own built in
643
 * autocompletion feature (MSIE and Firefox)
644
 *
645
 * @author Andreas Gohr <[email protected]>
646
 *
647
 * @param bool $ajax
648
 * @param bool $autocomplete
649
 * @return bool
650
 */
651
function tpl_searchform($ajax = true, $autocomplete = true) {
652
    global $lang;
653
    global $ACT;
654
    global $QUERY;
655
656
    // don't print the search form if search action has been disabled
657
    if(!actionOK('search')) return false;
658
659
    print '<form action="'.wl().'" accept-charset="utf-8" class="search" id="dw__search" method="get" role="search"><div class="no">';
660
    print '<input type="hidden" name="do" value="search" />';
661
    print '<input type="text" ';
662
    if($ACT == 'search') print 'value="'.htmlspecialchars($QUERY).'" ';
663
    print 'placeholder="'.$lang['btn_search'].'" ';
664
    if(!$autocomplete) print 'autocomplete="off" ';
665
    print 'id="qsearch__in" accesskey="f" name="id" class="edit" title="[F]" />';
666
    print '<button type="submit" title="'.$lang['btn_search'].'">'.$lang['btn_search'].'</button>';
667
    if($ajax) print '<div id="qsearch__out" class="ajax_qsearch JSpopup"></div>';
668
    print '</div></form>';
669
    return true;
670
}
671
672
/**
673
 * Print the breadcrumbs trace
674
 *
675
 * @author Andreas Gohr <[email protected]>
676
 *
677
 * @param string $sep Separator between entries
678
 * @return bool
679
 */
680
function tpl_breadcrumbs($sep = '•') {
681
    global $lang;
682
    global $conf;
683
684
    //check if enabled
685
    if(!$conf['breadcrumbs']) return false;
686
687
    $crumbs = breadcrumbs(); //setup crumb trace
688
689
    $crumbs_sep = ' <span class="bcsep">'.$sep.'</span> ';
690
691
    //render crumbs, highlight the last one
692
    print '<span class="bchead">'.$lang['breadcrumb'].'</span>';
693
    $last = count($crumbs);
694
    $i    = 0;
695
    foreach($crumbs as $id => $name) {
696
        $i++;
697
        echo $crumbs_sep;
698
        if($i == $last) print '<span class="curid">';
699
        print '<bdi>';
700
        tpl_link(wl($id), hsc($name), 'class="breadcrumbs" title="'.$id.'"');
701
        print '</bdi>';
702
        if($i == $last) print '</span>';
703
    }
704
    return true;
705
}
706
707
/**
708
 * Hierarchical breadcrumbs
709
 *
710
 * This code was suggested as replacement for the usual breadcrumbs.
711
 * It only makes sense with a deep site structure.
712
 *
713
 * @author Andreas Gohr <[email protected]>
714
 * @author Nigel McNie <[email protected]>
715
 * @author Sean Coates <[email protected]>
716
 * @author <[email protected]>
717
 * @todo   May behave strangely in RTL languages
718
 *
719
 * @param string $sep Separator between entries
720
 * @return bool
721
 */
722
function tpl_youarehere($sep = ' » ') {
723
    global $conf;
724
    global $ID;
725
    global $lang;
726
727
    // check if enabled
728
    if(!$conf['youarehere']) return false;
729
730
    $parts = explode(':', $ID);
731
    $count = count($parts);
732
733
    echo '<span class="bchead">'.$lang['youarehere'].' </span>';
734
735
    // always print the startpage
736
    echo '<span class="home">';
737
    tpl_pagelink(':'.$conf['start']);
738
    echo '</span>';
739
740
    // print intermediate namespace links
741
    $part = '';
742
    for($i = 0; $i < $count - 1; $i++) {
743
        $part .= $parts[$i].':';
744
        $page = $part;
745
        if($page == $conf['start']) continue; // Skip startpage
746
747
        // output
748
        echo $sep;
749
        tpl_pagelink($page);
750
    }
751
752
    // print current page, skipping start page, skipping for namespace index
753
    resolve_pageid('', $page, $exists);
754
    if(isset($page) && $page == $part.$parts[$i]) return true;
755
    $page = $part.$parts[$i];
756
    if($page == $conf['start']) return true;
757
    echo $sep;
758
    tpl_pagelink($page);
759
    return true;
760
}
761
762
/**
763
 * Print info if the user is logged in
764
 * and show full name in that case
765
 *
766
 * Could be enhanced with a profile link in future?
767
 *
768
 * @author Andreas Gohr <[email protected]>
769
 *
770
 * @return bool
771
 */
772
function tpl_userinfo() {
773
    global $lang;
774
    /** @var Input $INPUT */
775
    global $INPUT;
776
777
    if($INPUT->server->str('REMOTE_USER')) {
778
        print $lang['loggedinas'].' '.userlink();
779
        return true;
780
    }
781
    return false;
782
}
783
784
/**
785
 * Print some info about the current page
786
 *
787
 * @author Andreas Gohr <[email protected]>
788
 *
789
 * @param bool $ret return content instead of printing it
790
 * @return bool|string
791
 */
792
function tpl_pageinfo($ret = false) {
793
    global $conf;
794
    global $lang;
795
    global $INFO;
796
    global $ID;
797
798
    // return if we are not allowed to view the page
799
    if(!auth_quickaclcheck($ID)) {
800
        return false;
801
    }
802
803
    // prepare date and path
804
    $fn = $INFO['filepath'];
805
    if(!$conf['fullpath']) {
806
        if($INFO['rev']) {
807
            $fn = str_replace($conf['olddir'].'/', '', $fn);
808
        } else {
809
            $fn = str_replace($conf['datadir'].'/', '', $fn);
810
        }
811
    }
812
    $fn   = utf8_decodeFN($fn);
813
    $date = dformat($INFO['lastmod']);
814
815
    // print it
816
    if($INFO['exists']) {
817
        $out = '';
818
        $out .= '<bdi>'.$fn.'</bdi>';
819
        $out .= ' · ';
820
        $out .= $lang['lastmod'];
821
        $out .= ' ';
822
        $out .= $date;
823
        if($INFO['editor']) {
824
            $out .= ' '.$lang['by'].' ';
825
            $out .= '<bdi>'.editorinfo($INFO['editor']).'</bdi>';
826
        } else {
827
            $out .= ' ('.$lang['external_edit'].')';
828
        }
829
        if($INFO['locked']) {
830
            $out .= ' · ';
831
            $out .= $lang['lockedby'];
832
            $out .= ' ';
833
            $out .= '<bdi>'.editorinfo($INFO['locked']).'</bdi>';
834
        }
835
        if($ret) {
836
            return $out;
837
        } else {
838
            echo $out;
839
            return true;
840
        }
841
    }
842
    return false;
843
}
844
845
/**
846
 * Prints or returns the name of the given page (current one if none given).
847
 *
848
 * If useheading is enabled this will use the first headline else
849
 * the given ID is used.
850
 *
851
 * @author Andreas Gohr <[email protected]>
852
 *
853
 * @param string $id page id
854
 * @param bool   $ret return content instead of printing
855
 * @return bool|string
856
 */
857
function tpl_pagetitle($id = null, $ret = false) {
858
    global $ACT, $INPUT, $conf, $lang;
859
860
    if(is_null($id)) {
861
        global $ID;
862
        $id = $ID;
863
    }
864
865
    $name = $id;
866
    if(useHeading('navigation')) {
867
        $first_heading = p_get_first_heading($id);
868
        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...
869
    }
870
871
    // default page title is the page name, modify with the current action
872
    switch ($ACT) {
873
        // admin functions
874
        case 'admin' :
875
            $page_title = $lang['btn_admin'];
876
            // try to get the plugin name
877
            /** @var $plugin DokuWiki_Admin_Plugin */
878
            if ($plugin = plugin_getRequestAdminPlugin()){
879
                $plugin_title = $plugin->getMenuText($conf['lang']);
880
                $page_title = $plugin_title ? $plugin_title : $plugin->getPluginName();
881
            }
882
            break;
883
884
        // user functions
885
        case 'login' :
886
        case 'profile' :
887
        case 'register' :
888
        case 'resendpwd' :
889
            $page_title = $lang['btn_'.$ACT];
890
            break;
891
892
         // wiki functions
893
        case 'search' :
894
        case 'index' :
895
            $page_title = $lang['btn_'.$ACT];
896
            break;
897
898
        // page functions
899
        case 'edit' :
900
            $page_title = "✎ ".$name;
901
            break;
902
903
        case 'revisions' :
904
            $page_title = $name . ' - ' . $lang['btn_revs'];
905
            break;
906
907
        case 'backlink' :
908
        case 'recent' :
909
        case 'subscribe' :
910
            $page_title = $name . ' - ' . $lang['btn_'.$ACT];
911
            break;
912
913
        default : // SHOW and anything else not included
914
            $page_title = $name;
915
    }
916
917
    if($ret) {
918
        return hsc($page_title);
919
    } else {
920
        print hsc($page_title);
921
        return true;
922
    }
923
}
924
925
/**
926
 * Returns the requested EXIF/IPTC tag from the current image
927
 *
928
 * If $tags is an array all given tags are tried until a
929
 * value is found. If no value is found $alt is returned.
930
 *
931
 * Which texts are known is defined in the functions _exifTagNames
932
 * and _iptcTagNames() in inc/jpeg.php (You need to prepend IPTC
933
 * to the names of the latter one)
934
 *
935
 * Only allowed in: detail.php
936
 *
937
 * @author Andreas Gohr <[email protected]>
938
 *
939
 * @param array|string $tags tag or array of tags to try
940
 * @param string       $alt  alternative output if no data was found
941
 * @param null|string  $src  the image src, uses global $SRC if not given
942
 * @return string
943
 */
944
function tpl_img_getTag($tags, $alt = '', $src = null) {
945
    // Init Exif Reader
946
    global $SRC;
947
948
    if(is_null($src)) $src = $SRC;
949
950
    static $meta = null;
951
    if(is_null($meta)) $meta = new JpegMeta($src);
952
    if($meta === false) return $alt;
953
    $info = cleanText($meta->getField($tags));
954
    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...
955
    return $info;
956
}
957
958
/**
959
 * Returns a description list of the metatags of the current image
960
 *
961
 * @return string html of description list
962
 */
963
function tpl_img_meta() {
964
    global $lang;
965
966
    $tags = tpl_get_img_meta();
967
968
    echo '<dl>';
969
    foreach($tags as $tag) {
970
        $label = $lang[$tag['langkey']];
971
        if(!$label) $label = $tag['langkey'] . ':';
972
973
        echo '<dt>'.$label.'</dt><dd>';
974
        if ($tag['type'] == 'date') {
975
            echo dformat($tag['value']);
976
        } else {
977
            echo hsc($tag['value']);
978
        }
979
        echo '</dd>';
980
    }
981
    echo '</dl>';
982
}
983
984
/**
985
 * Returns metadata as configured in mediameta config file, ready for creating html
986
 *
987
 * @return array with arrays containing the entries:
988
 *   - string langkey  key to lookup in the $lang var, if not found printed as is
989
 *   - string type     type of value
990
 *   - string value    tag value (unescaped)
991
 */
992
function tpl_get_img_meta() {
993
994
    $config_files = getConfigFiles('mediameta');
995
    foreach ($config_files as $config_file) {
996
        if(file_exists($config_file)) {
997
            include($config_file);
998
        }
999
    }
1000
    /** @var array $fields the included array with metadata */
1001
1002
    $tags = array();
1003
    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...
1004
        $t = array();
1005
        if (!empty($tag[0])) {
1006
            $t = array($tag[0]);
1007
        }
1008
        if(is_array($tag[3])) {
1009
            $t = array_merge($t,$tag[3]);
1010
        }
1011
        $value = tpl_img_getTag($t);
1012
        if ($value) {
1013
            $tags[] = array('langkey' => $tag[1], 'type' => $tag[2], 'value' => $value);
1014
        }
1015
    }
1016
    return $tags;
1017
}
1018
1019
/**
1020
 * Prints the image with a link to the full sized version
1021
 *
1022
 * Only allowed in: detail.php
1023
 *
1024
 * @triggers TPL_IMG_DISPLAY
1025
 * @param $maxwidth  int - maximal width of the image
1026
 * @param $maxheight int - maximal height of the image
1027
 * @param $link bool     - link to the orginal size?
1028
 * @param $params array  - additional image attributes
1029
 * @return bool Result of TPL_IMG_DISPLAY
1030
 */
1031
function tpl_img($maxwidth = 0, $maxheight = 0, $link = true, $params = null) {
1032
    global $IMG;
1033
    /** @var Input $INPUT */
1034
    global $INPUT;
1035
    global $REV;
1036
    $w = (int) tpl_img_getTag('File.Width');
1037
    $h = (int) tpl_img_getTag('File.Height');
1038
1039
    //resize to given max values
1040
    $ratio = 1;
1041
    if($w >= $h) {
1042
        if($maxwidth && $w >= $maxwidth) {
1043
            $ratio = $maxwidth / $w;
1044
        } elseif($maxheight && $h > $maxheight) {
1045
            $ratio = $maxheight / $h;
1046
        }
1047
    } else {
1048
        if($maxheight && $h >= $maxheight) {
1049
            $ratio = $maxheight / $h;
1050
        } elseif($maxwidth && $w > $maxwidth) {
1051
            $ratio = $maxwidth / $w;
1052
        }
1053
    }
1054
    if($ratio) {
1055
        $w = floor($ratio * $w);
1056
        $h = floor($ratio * $h);
1057
    }
1058
1059
    //prepare URLs
1060
    $url = ml($IMG, array('cache'=> $INPUT->str('cache'),'rev'=>$REV), true, '&');
1061
    $src = ml($IMG, array('cache'=> $INPUT->str('cache'),'rev'=>$REV, 'w'=> $w, 'h'=> $h), true, '&');
1062
1063
    //prepare attributes
1064
    $alt = tpl_img_getTag('Simple.Title');
1065
    if(is_null($params)) {
1066
        $p = array();
1067
    } else {
1068
        $p = $params;
1069
    }
1070
    if($w) $p['width'] = $w;
1071
    if($h) $p['height'] = $h;
1072
    $p['class'] = 'img_detail';
1073
    if($alt) {
1074
        $p['alt']   = $alt;
1075
        $p['title'] = $alt;
1076
    } else {
1077
        $p['alt'] = '';
1078
    }
1079
    $p['src'] = $src;
1080
1081
    $data = array('url'=> ($link ? $url : null), 'params'=> $p);
1082
    return trigger_event('TPL_IMG_DISPLAY', $data, '_tpl_img_action', true);
1083
}
1084
1085
/**
1086
 * Default action for TPL_IMG_DISPLAY
1087
 *
1088
 * @param array $data
1089
 * @return bool
1090
 */
1091
function _tpl_img_action($data) {
1092
    global $lang;
1093
    $p = buildAttributes($data['params']);
1094
1095
    if($data['url']) print '<a href="'.hsc($data['url']).'" title="'.$lang['mediaview'].'">';
1096
    print '<img '.$p.'/>';
1097
    if($data['url']) print '</a>';
1098
    return true;
1099
}
1100
1101
/**
1102
 * This function inserts a small gif which in reality is the indexer function.
1103
 *
1104
 * Should be called somewhere at the very end of the main.php
1105
 * template
1106
 *
1107
 * @return bool
1108
 */
1109
function tpl_indexerWebBug() {
1110
    global $ID;
1111
1112
    $p           = array();
1113
    $p['src']    = DOKU_BASE.'lib/exe/indexer.php?id='.rawurlencode($ID).
1114
        '&'.time();
1115
    $p['width']  = 2; //no more 1x1 px image because we live in times of ad blockers...
1116
    $p['height'] = 1;
1117
    $p['alt']    = '';
1118
    $att         = buildAttributes($p);
1119
    print "<img $att />";
1120
    return true;
1121
}
1122
1123
/**
1124
 * tpl_getConf($id)
1125
 *
1126
 * use this function to access template configuration variables
1127
 *
1128
 * @param string $id      name of the value to access
1129
 * @param mixed  $notset  what to return if the setting is not available
1130
 * @return mixed
1131
 */
1132
function tpl_getConf($id, $notset=false) {
1133
    global $conf;
1134
    static $tpl_configloaded = false;
1135
1136
    $tpl = $conf['template'];
1137
1138
    if(!$tpl_configloaded) {
1139
        $tconf = tpl_loadConfig();
1140
        if($tconf !== false) {
1141
            foreach($tconf as $key => $value) {
1142
                if(isset($conf['tpl'][$tpl][$key])) continue;
1143
                $conf['tpl'][$tpl][$key] = $value;
1144
            }
1145
            $tpl_configloaded = true;
1146
        }
1147
    }
1148
1149
    if(isset($conf['tpl'][$tpl][$id])){
1150
        return $conf['tpl'][$tpl][$id];
1151
    }
1152
1153
    return $notset;
1154
}
1155
1156
/**
1157
 * tpl_loadConfig()
1158
 *
1159
 * reads all template configuration variables
1160
 * this function is automatically called by tpl_getConf()
1161
 *
1162
 * @return array
1163
 */
1164
function tpl_loadConfig() {
1165
1166
    $file = tpl_incdir().'/conf/default.php';
1167
    $conf = array();
1168
1169
    if(!file_exists($file)) return false;
1170
1171
    // load default config file
1172
    include($file);
1173
1174
    return $conf;
1175
}
1176
1177
// language methods
1178
/**
1179
 * tpl_getLang($id)
1180
 *
1181
 * use this function to access template language variables
1182
 *
1183
 * @param string $id key of language string
1184
 * @return string
1185
 */
1186
function tpl_getLang($id) {
1187
    static $lang = array();
1188
1189
    if(count($lang) === 0) {
1190
        global $conf, $config_cascade; // definitely don't invoke "global $lang"
1191
1192
        $path = tpl_incdir() . 'lang/';
1193
1194
        $lang = array();
1195
1196
        // don't include once
1197
        @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...
1198
        foreach($config_cascade['lang']['template'] as $config_file) {
1199
            if(file_exists($config_file . $conf['template'] . '/en/lang.php')) {
1200
                include($config_file . $conf['template'] . '/en/lang.php');
1201
            }
1202
        }
1203
1204
        if($conf['lang'] != 'en') {
1205
            @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...
1206
            foreach($config_cascade['lang']['template'] as $config_file) {
1207
                if(file_exists($config_file . $conf['template'] . '/' . $conf['lang'] . '/lang.php')) {
1208
                    include($config_file . $conf['template'] . '/' . $conf['lang'] . '/lang.php');
1209
                }
1210
            }
1211
        }
1212
    }
1213
    return $lang[$id];
1214
}
1215
1216
/**
1217
 * Retrieve a language dependent file and pass to xhtml renderer for display
1218
 * template equivalent of p_locale_xhtml()
1219
 *
1220
 * @param   string $id id of language dependent wiki page
1221
 * @return  string     parsed contents of the wiki page in xhtml format
1222
 */
1223
function tpl_locale_xhtml($id) {
1224
    return p_cached_output(tpl_localeFN($id));
1225
}
1226
1227
/**
1228
 * Prepends appropriate path for a language dependent filename
1229
 *
1230
 * @param string $id id of localized text
1231
 * @return string wiki text
1232
 */
1233
function tpl_localeFN($id) {
1234
    $path = tpl_incdir().'lang/';
1235
    global $conf;
1236
    $file = DOKU_CONF.'template_lang/'.$conf['template'].'/'.$conf['lang'].'/'.$id.'.txt';
1237
    if (!file_exists($file)){
1238
        $file = $path.$conf['lang'].'/'.$id.'.txt';
1239
        if(!file_exists($file)){
1240
            //fall back to english
1241
            $file = $path.'en/'.$id.'.txt';
1242
        }
1243
    }
1244
    return $file;
1245
}
1246
1247
/**
1248
 * prints the "main content" in the mediamanager popup
1249
 *
1250
 * Depending on the user's actions this may be a list of
1251
 * files in a namespace, the meta editing dialog or
1252
 * a message of referencing pages
1253
 *
1254
 * Only allowed in mediamanager.php
1255
 *
1256
 * @triggers MEDIAMANAGER_CONTENT_OUTPUT
1257
 * @param bool $fromajax - set true when calling this function via ajax
1258
 * @param string $sort
1259
 *
1260
 * @author Andreas Gohr <[email protected]>
1261
 */
1262
function tpl_mediaContent($fromajax = false, $sort='natural') {
1263
    global $IMG;
1264
    global $AUTH;
1265
    global $INUSE;
1266
    global $NS;
1267
    global $JUMPTO;
1268
    /** @var Input $INPUT */
1269
    global $INPUT;
1270
1271
    $do = $INPUT->extract('do')->str('do');
1272
    if(in_array($do, array('save', 'cancel'))) $do = '';
1273
1274
    if(!$do) {
1275
        if($INPUT->bool('edit')) {
1276
            $do = 'metaform';
1277
        } elseif(is_array($INUSE)) {
1278
            $do = 'filesinuse';
1279
        } else {
1280
            $do = 'filelist';
1281
        }
1282
    }
1283
1284
    // output the content pane, wrapped in an event.
1285
    if(!$fromajax) ptln('<div id="media__content">');
1286
    $data = array('do' => $do);
1287
    $evt  = new Doku_Event('MEDIAMANAGER_CONTENT_OUTPUT', $data);
1288
    if($evt->advise_before()) {
1289
        $do = $data['do'];
1290
        if($do == 'filesinuse') {
1291
            media_filesinuse($INUSE, $IMG);
1292
        } elseif($do == 'filelist') {
1293
            media_filelist($NS, $AUTH, $JUMPTO,false,$sort);
1294
        } elseif($do == 'searchlist') {
1295
            media_searchlist($INPUT->str('q'), $NS, $AUTH);
1296
        } else {
1297
            msg('Unknown action '.hsc($do), -1);
1298
        }
1299
    }
1300
    $evt->advise_after();
1301
    unset($evt);
1302
    if(!$fromajax) ptln('</div>');
1303
1304
}
1305
1306
/**
1307
 * Prints the central column in full-screen media manager
1308
 * Depending on the opened tab this may be a list of
1309
 * files in a namespace, upload form or search form
1310
 *
1311
 * @author Kate Arzamastseva <[email protected]>
1312
 */
1313
function tpl_mediaFileList() {
1314
    global $AUTH;
1315
    global $NS;
1316
    global $JUMPTO;
1317
    global $lang;
1318
    /** @var Input $INPUT */
1319
    global $INPUT;
1320
1321
    $opened_tab = $INPUT->str('tab_files');
1322
    if(!$opened_tab || !in_array($opened_tab, array('files', 'upload', 'search'))) $opened_tab = 'files';
1323
    if($INPUT->str('mediado') == 'update') $opened_tab = 'upload';
1324
1325
    echo '<h2 class="a11y">'.$lang['mediaselect'].'</h2>'.NL;
1326
1327
    media_tabs_files($opened_tab);
1328
1329
    echo '<div class="panelHeader">'.NL;
1330
    echo '<h3>';
1331
    $tabTitle = ($NS) ? $NS : '['.$lang['mediaroot'].']';
1332
    printf($lang['media_'.$opened_tab], '<strong>'.hsc($tabTitle).'</strong>');
1333
    echo '</h3>'.NL;
1334
    if($opened_tab === 'search' || $opened_tab === 'files') {
1335
        media_tab_files_options();
1336
    }
1337
    echo '</div>'.NL;
1338
1339
    echo '<div class="panelContent">'.NL;
1340
    if($opened_tab == 'files') {
1341
        media_tab_files($NS, $AUTH, $JUMPTO);
1342
    } elseif($opened_tab == 'upload') {
1343
        media_tab_upload($NS, $AUTH, $JUMPTO);
1344
    } elseif($opened_tab == 'search') {
1345
        media_tab_search($NS, $AUTH);
1346
    }
1347
    echo '</div>'.NL;
1348
}
1349
1350
/**
1351
 * Prints the third column in full-screen media manager
1352
 * Depending on the opened tab this may be details of the
1353
 * selected file, the meta editing dialog or
1354
 * list of file revisions
1355
 *
1356
 * @author Kate Arzamastseva <[email protected]>
1357
 *
1358
 * @param string $image
1359
 * @param boolean $rev
1360
 */
1361
function tpl_mediaFileDetails($image, $rev) {
1362
    global $conf, $DEL, $lang;
1363
    /** @var Input $INPUT */
1364
    global $INPUT;
1365
1366
    $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')) && $conf['mediarevisions']);
1367
    if(!$image || (!file_exists(mediaFN($image)) && !$removed) || $DEL) return;
1368
    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...
1369
    $ns = getNS($image);
1370
    $do = $INPUT->str('mediado');
1371
1372
    $opened_tab = $INPUT->str('tab_details');
1373
1374
    $tab_array = array('view');
1375
    list(, $mime) = mimetype($image);
1376
    if($mime == 'image/jpeg') {
1377
        $tab_array[] = 'edit';
1378
    }
1379
    if($conf['mediarevisions']) {
1380
        $tab_array[] = 'history';
1381
    }
1382
1383
    if(!$opened_tab || !in_array($opened_tab, $tab_array)) $opened_tab = 'view';
1384
    if($INPUT->bool('edit')) $opened_tab = 'edit';
1385
    if($do == 'restore') $opened_tab = 'view';
1386
1387
    media_tabs_details($image, $opened_tab);
1388
1389
    echo '<div class="panelHeader"><h3>';
1390
    list($ext) = mimetype($image, false);
1391
    $class    = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext);
1392
    $class    = 'select mediafile mf_'.$class;
1393
    $attributes = $rev ? ['rev' => $rev] : [];
1394
    $tabTitle = '<strong><a href="'.ml($image, $attributes).'" class="'.$class.'" title="'.$lang['mediaview'].'">'.$image.'</a>'.'</strong>';
1395
    if($opened_tab === 'view' && $rev) {
1396
        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...
1397
    } else {
1398
        printf($lang['media_'.$opened_tab], $tabTitle);
1399
    }
1400
1401
    echo '</h3></div>'.NL;
1402
1403
    echo '<div class="panelContent">'.NL;
1404
1405
    if($opened_tab == 'view') {
1406
        media_tab_view($image, $ns, null, $rev);
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...
Security Bug introduced by
It seems like $ns defined by getNS($image) on line 1369 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...
1407
1408
    } elseif($opened_tab == 'edit' && !$removed) {
1409
        media_tab_edit($image, $ns);
0 ignored issues
show
Security Bug introduced by
It seems like $ns defined by getNS($image) on line 1369 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...
1410
1411
    } elseif($opened_tab == 'history' && $conf['mediarevisions']) {
1412
        media_tab_history($image, $ns);
0 ignored issues
show
Security Bug introduced by
It seems like $ns defined by getNS($image) on line 1369 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...
1413
    }
1414
1415
    echo '</div>'.NL;
1416
}
1417
1418
/**
1419
 * prints the namespace tree in the mediamanager popup
1420
 *
1421
 * Only allowed in mediamanager.php
1422
 *
1423
 * @author Andreas Gohr <[email protected]>
1424
 */
1425
function tpl_mediaTree() {
1426
    global $NS;
1427
    ptln('<div id="media__tree">');
1428
    media_nstree($NS);
1429
    ptln('</div>');
1430
}
1431
1432
/**
1433
 * Print a dropdown menu with all DokuWiki actions
1434
 *
1435
 * Note: this will not use any pretty URLs
1436
 *
1437
 * @author Andreas Gohr <[email protected]>
1438
 *
1439
 * @param string $empty empty option label
1440
 * @param string $button submit button label
1441
 * @deprecated 2017-09-01 see devel:menus
1442
 */
1443
function tpl_actiondropdown($empty = '', $button = '&gt;') {
1444
    dbg_deprecated('see devel:menus');
1445
    $menu = new \dokuwiki\Menu\MobileMenu();
1446
    echo $menu->getDropdown($empty, $button);
1447
}
1448
1449
/**
1450
 * Print a informational line about the used license
1451
 *
1452
 * @author Andreas Gohr <[email protected]>
1453
 * @param  string $img     print image? (|button|badge)
1454
 * @param  bool   $imgonly skip the textual description?
1455
 * @param  bool   $return  when true don't print, but return HTML
1456
 * @param  bool   $wrap    wrap in div with class="license"?
1457
 * @return string
1458
 */
1459
function tpl_license($img = 'badge', $imgonly = false, $return = false, $wrap = true) {
1460
    global $license;
1461
    global $conf;
1462
    global $lang;
1463
    if(!$conf['license']) return '';
1464
    if(!is_array($license[$conf['license']])) return '';
1465
    $lic    = $license[$conf['license']];
1466
    $target = ($conf['target']['extern']) ? ' target="'.$conf['target']['extern'].'"' : '';
1467
1468
    $out = '';
1469
    if($wrap) $out .= '<div class="license">';
1470
    if($img) {
1471
        $src = license_img($img);
1472
        if($src) {
1473
            $out .= '<a href="'.$lic['url'].'" rel="license"'.$target;
1474
            $out .= '><img src="'.DOKU_BASE.$src.'" alt="'.$lic['name'].'" /></a>';
1475
            if(!$imgonly) $out .= ' ';
1476
        }
1477
    }
1478
    if(!$imgonly) {
1479
        $out .= $lang['license'].' ';
1480
        $out .= '<bdi><a href="'.$lic['url'].'" rel="license" class="urlextern"'.$target;
1481
        $out .= '>'.$lic['name'].'</a></bdi>';
1482
    }
1483
    if($wrap) $out .= '</div>';
1484
1485
    if($return) return $out;
1486
    echo $out;
1487
    return '';
1488
}
1489
1490
/**
1491
 * Includes the rendered HTML of a given page
1492
 *
1493
 * This function is useful to populate sidebars or similar features in a
1494
 * template
1495
 *
1496
 * @param string $pageid The page name you want to include
1497
 * @param bool $print Should the content be printed or returned only
1498
 * @param bool $propagate Search higher namespaces, too?
1499
 * @param bool $useacl Include the page only if the ACLs check out?
1500
 * @return bool|null|string
1501
 */
1502
function tpl_include_page($pageid, $print = true, $propagate = false, $useacl = true) {
1503
    if($propagate) {
1504
        $pageid = page_findnearest($pageid, $useacl);
1505
    } elseif($useacl && auth_quickaclcheck($pageid) == AUTH_NONE) {
1506
        return false;
1507
    }
1508
    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...
1509
1510
    global $TOC;
1511
    $oldtoc = $TOC;
1512
    $html   = p_wiki_xhtml($pageid, '', false);
1513
    $TOC    = $oldtoc;
1514
1515
    if($print) echo $html;
1516
    return $html;
1517
}
1518
1519
/**
1520
 * Display the subscribe form
1521
 *
1522
 * @author Adrian Lang <[email protected]>
1523
 */
1524
function tpl_subscribe() {
1525
    global $INFO;
1526
    global $ID;
1527
    global $lang;
1528
    global $conf;
1529
    $stime_days = $conf['subscribe_time'] / 60 / 60 / 24;
1530
1531
    echo p_locale_xhtml('subscr_form');
1532
    echo '<h2>'.$lang['subscr_m_current_header'].'</h2>';
1533
    echo '<div class="level2">';
1534
    if($INFO['subscribed'] === false) {
1535
        echo '<p>'.$lang['subscr_m_not_subscribed'].'</p>';
1536
    } else {
1537
        echo '<ul>';
1538
        foreach($INFO['subscribed'] as $sub) {
1539
            echo '<li><div class="li">';
1540
            if($sub['target'] !== $ID) {
1541
                echo '<code class="ns">'.hsc(prettyprint_id($sub['target'])).'</code>';
1542
            } else {
1543
                echo '<code class="page">'.hsc(prettyprint_id($sub['target'])).'</code>';
1544
            }
1545
            $sstl = sprintf($lang['subscr_style_'.$sub['style']], $stime_days);
1546
            if(!$sstl) $sstl = hsc($sub['style']);
1547
            echo ' ('.$sstl.') ';
1548
1549
            echo '<a href="'.wl(
1550
                $ID,
1551
                array(
1552
                     'do'        => 'subscribe',
1553
                     'sub_target'=> $sub['target'],
1554
                     'sub_style' => $sub['style'],
1555
                     'sub_action'=> 'unsubscribe',
1556
                     'sectok'    => getSecurityToken()
1557
                )
1558
            ).
1559
                '" class="unsubscribe">'.$lang['subscr_m_unsubscribe'].
1560
                '</a></div></li>';
1561
        }
1562
        echo '</ul>';
1563
    }
1564
    echo '</div>';
1565
1566
    // Add new subscription form
1567
    echo '<h2>'.$lang['subscr_m_new_header'].'</h2>';
1568
    echo '<div class="level2">';
1569
    $ns      = getNS($ID).':';
1570
    $targets = array(
1571
        $ID => '<code class="page">'.prettyprint_id($ID).'</code>',
1572
        $ns => '<code class="ns">'.prettyprint_id($ns).'</code>',
1573
    );
1574
    $styles  = array(
1575
        'every'  => $lang['subscr_style_every'],
1576
        'digest' => sprintf($lang['subscr_style_digest'], $stime_days),
1577
        'list'   => sprintf($lang['subscr_style_list'], $stime_days),
1578
    );
1579
1580
    $form = new Doku_Form(array('id' => 'subscribe__form'));
1581
    $form->startFieldset($lang['subscr_m_subscribe']);
1582
    $form->addRadioSet('sub_target', $targets);
1583
    $form->startFieldset($lang['subscr_m_receive']);
1584
    $form->addRadioSet('sub_style', $styles);
1585
    $form->addHidden('sub_action', 'subscribe');
1586
    $form->addHidden('do', 'subscribe');
1587
    $form->addHidden('id', $ID);
1588
    $form->endFieldset();
1589
    $form->addElement(form_makeButton('submit', 'subscribe', $lang['subscr_m_subscribe']));
1590
    html_form('SUBSCRIBE', $form);
1591
    echo '</div>';
1592
}
1593
1594
/**
1595
 * Tries to send already created content right to the browser
1596
 *
1597
 * Wraps around ob_flush() and flush()
1598
 *
1599
 * @author Andreas Gohr <[email protected]>
1600
 */
1601
function tpl_flush() {
1602
    ob_flush();
1603
    flush();
1604
}
1605
1606
/**
1607
 * Tries to find a ressource file in the given locations.
1608
 *
1609
 * If a given location starts with a colon it is assumed to be a media
1610
 * file, otherwise it is assumed to be relative to the current template
1611
 *
1612
 * @param  string[] $search       locations to look at
1613
 * @param  bool     $abs           if to use absolute URL
1614
 * @param  array   &$imginfo   filled with getimagesize()
1615
 * @return string
1616
 *
1617
 * @author Andreas  Gohr <[email protected]>
1618
 */
1619
function tpl_getMediaFile($search, $abs = false, &$imginfo = null) {
1620
    $img     = '';
1621
    $file    = '';
1622
    $ismedia = false;
1623
    // loop through candidates until a match was found:
1624
    foreach($search as $img) {
1625
        if(substr($img, 0, 1) == ':') {
1626
            $file    = mediaFN($img);
1627
            $ismedia = true;
1628
        } else {
1629
            $file    = tpl_incdir().$img;
1630
            $ismedia = false;
1631
        }
1632
1633
        if(file_exists($file)) break;
1634
    }
1635
1636
    // fetch image data if requested
1637
    if(!is_null($imginfo)) {
1638
        $imginfo = getimagesize($file);
1639
    }
1640
1641
    // build URL
1642
    if($ismedia) {
1643
        $url = ml($img, '', true, '', $abs);
1644
    } else {
1645
        $url = tpl_basedir().$img;
1646
        if($abs) $url = DOKU_URL.substr($url, strlen(DOKU_REL));
1647
    }
1648
1649
    return $url;
1650
}
1651
1652
/**
1653
 * PHP include a file
1654
 *
1655
 * either from the conf directory if it exists, otherwise use
1656
 * file in the template's root directory.
1657
 *
1658
 * The function honours config cascade settings and looks for the given
1659
 * file next to the ´main´ config files, in the order protected, local,
1660
 * default.
1661
 *
1662
 * Note: no escaping or sanity checking is done here. Never pass user input
1663
 * to this function!
1664
 *
1665
 * @author Anika Henke <[email protected]>
1666
 * @author Andreas Gohr <[email protected]>
1667
 *
1668
 * @param string $file
1669
 */
1670
function tpl_includeFile($file) {
1671
    global $config_cascade;
1672
    foreach(array('protected', 'local', 'default') as $config_group) {
1673
        if(empty($config_cascade['main'][$config_group])) continue;
1674
        foreach($config_cascade['main'][$config_group] as $conf_file) {
1675
            $dir = dirname($conf_file);
1676
            if(file_exists("$dir/$file")) {
1677
                include("$dir/$file");
1678
                return;
1679
            }
1680
        }
1681
    }
1682
1683
    // still here? try the template dir
1684
    $file = tpl_incdir().$file;
1685
    if(file_exists($file)) {
1686
        include($file);
1687
    }
1688
}
1689
1690
/**
1691
 * Returns <link> tag for various icon types (favicon|mobile|generic)
1692
 *
1693
 * @author Anika Henke <[email protected]>
1694
 *
1695
 * @param  array $types - list of icon types to display (favicon|mobile|generic)
1696
 * @return string
1697
 */
1698
function tpl_favicon($types = array('favicon')) {
1699
1700
    $return = '';
1701
1702
    foreach($types as $type) {
1703
        switch($type) {
1704
            case 'favicon':
1705
                $look = array(':wiki:favicon.ico', ':favicon.ico', 'images/favicon.ico');
1706
                $return .= '<link rel="shortcut icon" href="'.tpl_getMediaFile($look).'" />'.NL;
1707
                break;
1708
            case 'mobile':
1709
                $look = array(':wiki:apple-touch-icon.png', ':apple-touch-icon.png', 'images/apple-touch-icon.png');
1710
                $return .= '<link rel="apple-touch-icon" href="'.tpl_getMediaFile($look).'" />'.NL;
1711
                break;
1712
            case 'generic':
1713
                // ideal world solution, which doesn't work in any browser yet
1714
                $look = array(':wiki:favicon.svg', ':favicon.svg', 'images/favicon.svg');
1715
                $return .= '<link rel="icon" href="'.tpl_getMediaFile($look).'" type="image/svg+xml" />'.NL;
1716
                break;
1717
        }
1718
    }
1719
1720
    return $return;
1721
}
1722
1723
/**
1724
 * Prints full-screen media manager
1725
 *
1726
 * @author Kate Arzamastseva <[email protected]>
1727
 */
1728
function tpl_media() {
1729
    global $NS, $IMG, $JUMPTO, $REV, $lang, $fullscreen, $INPUT;
1730
    $fullscreen = true;
1731
    require_once DOKU_INC.'lib/exe/mediamanager.php';
1732
1733
    $rev   = '';
1734
    $image = cleanID($INPUT->str('image'));
1735
    if(isset($IMG)) $image = $IMG;
1736
    if(isset($JUMPTO)) $image = $JUMPTO;
1737
    if(isset($REV) && !$JUMPTO) $rev = $REV;
1738
1739
    echo '<div id="mediamanager__page">'.NL;
1740
    echo '<h1>'.$lang['btn_media'].'</h1>'.NL;
1741
    html_msgarea();
1742
1743
    echo '<div class="panel namespaces">'.NL;
1744
    echo '<h2>'.$lang['namespaces'].'</h2>'.NL;
1745
    echo '<div class="panelHeader">';
1746
    echo $lang['media_namespaces'];
1747
    echo '</div>'.NL;
1748
1749
    echo '<div class="panelContent" id="media__tree">'.NL;
1750
    media_nstree($NS);
1751
    echo '</div>'.NL;
1752
    echo '</div>'.NL;
1753
1754
    echo '<div class="panel filelist">'.NL;
1755
    tpl_mediaFileList();
1756
    echo '</div>'.NL;
1757
1758
    echo '<div class="panel file">'.NL;
1759
    echo '<h2 class="a11y">'.$lang['media_file'].'</h2>'.NL;
1760
    tpl_mediaFileDetails($image, $rev);
1761
    echo '</div>'.NL;
1762
1763
    echo '</div>'.NL;
1764
}
1765
1766
/**
1767
 * Return useful layout classes
1768
 *
1769
 * @author Anika Henke <[email protected]>
1770
 *
1771
 * @return string
1772
 */
1773
function tpl_classes() {
1774
    global $ACT, $conf, $ID, $INFO;
1775
    /** @var Input $INPUT */
1776
    global $INPUT;
1777
1778
    $classes = array(
1779
        'dokuwiki',
1780
        'mode_'.$ACT,
1781
        'tpl_'.$conf['template'],
1782
        $INPUT->server->bool('REMOTE_USER') ? 'loggedIn' : '',
1783
        $INFO['exists'] ? '' : 'notFound',
1784
        ($ID == $conf['start']) ? 'home' : '',
1785
    );
1786
    return join(' ', $classes);
1787
}
1788
1789
/**
1790
 * Create event for tools menues
1791
 *
1792
 * @author Anika Henke <[email protected]>
1793
 * @param string $toolsname name of menu
1794
 * @param array $items
1795
 * @param string $view e.g. 'main', 'detail', ...
1796
 * @deprecated 2017-09-01 see devel:menus
1797
 */
1798
function tpl_toolsevent($toolsname, $items, $view = 'main') {
1799
    dbg_deprecated('see devel:menus');
1800
    $data = array(
1801
        'view' => $view,
1802
        'items' => $items
1803
    );
1804
1805
    $hook = 'TEMPLATE_' . strtoupper($toolsname) . '_DISPLAY';
1806
    $evt = new Doku_Event($hook, $data);
1807
    if($evt->advise_before()) {
1808
        foreach($evt->data['items'] as $k => $html) echo $html;
1809
    }
1810
    $evt->advise_after();
1811
}
1812
1813
//Setup VIM: ex: et ts=4 :
1814
1815