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