xref: /dokuwiki/inc/template.php (revision bdc67fe7b11d95a896a62142b9cf675bd5e51d94)
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 * Returns the path to the given template, uses
13 * default one if the custom version doesn't exist.
14 *
15 * @author Andreas Gohr <andi@splitbrain.org>
16 */
17function template($tpl){
18    global $conf;
19
20    if(@is_readable(DOKU_INC.'lib/tpl/'.$conf['template'].'/'.$tpl))
21        return DOKU_INC.'lib/tpl/'.$conf['template'].'/'.$tpl;
22
23    return DOKU_INC.'lib/tpl/default/'.$tpl;
24}
25
26
27/**
28 * Convenience function to access template dir from local FS
29 *
30 * This replaces the deprecated DOKU_TPLINC constant
31 *
32 * @author Andreas Gohr <andi@splitbrain.org>
33 */
34function tpl_incdir(){
35    global $conf;
36    return DOKU_INC.'lib/tpl/'.$conf['template'].'/';
37}
38
39/**
40 * Convenience function to access template dir from web
41 *
42 * This replaces the deprecated DOKU_TPL constant
43 *
44 * @author Andreas Gohr <andi@splitbrain.org>
45 */
46function tpl_basedir(){
47    global $conf;
48    return DOKU_BASE.'lib/tpl/'.$conf['template'].'/';
49}
50
51/**
52 * Print the content
53 *
54 * This function is used for printing all the usual content
55 * (defined by the global $ACT var) by calling the appropriate
56 * outputfunction(s) from html.php
57 *
58 * Everything that doesn't use the main template file isn't
59 * handled by this function. ACL stuff is not done here either.
60 *
61 * @author Andreas Gohr <andi@splitbrain.org>
62 */
63function tpl_content($prependTOC=true) {
64    global $ACT;
65    global $INFO;
66    $INFO['prependTOC'] = $prependTOC;
67
68    ob_start();
69    trigger_event('TPL_ACT_RENDER',$ACT,'tpl_content_core');
70    $html_output = ob_get_clean();
71    trigger_event('TPL_CONTENT_DISPLAY',$html_output,'ptln');
72
73    return !empty($html_output);
74}
75
76function tpl_content_core(){
77    global $ACT;
78    global $TEXT;
79    global $PRE;
80    global $SUF;
81    global $SUM;
82    global $IDX;
83
84    switch($ACT){
85        case 'show':
86            html_show();
87            break;
88        case 'locked':
89            html_locked();
90        case 'edit':
91        case 'recover':
92            html_edit();
93            break;
94        case 'preview':
95            html_edit();
96            html_show($TEXT);
97            break;
98        case 'draft':
99            html_draft();
100            break;
101        case 'search':
102            html_search();
103            break;
104        case 'revisions':
105            $first = isset($_REQUEST['first']) ? intval($_REQUEST['first']) : 0;
106            html_revisions($first);
107            break;
108        case 'diff':
109            html_diff();
110            break;
111        case 'recent':
112            if (is_array($_REQUEST['first'])) {
113                $_REQUEST['first'] = array_keys($_REQUEST['first']);
114                $_REQUEST['first'] = $_REQUEST['first'][0];
115            }
116            $first = is_numeric($_REQUEST['first']) ? intval($_REQUEST['first']) : 0;
117            $show_changes = $_REQUEST['show_changes'];
118            html_recent($first, $show_changes);
119            break;
120        case 'index':
121            html_index($IDX); #FIXME can this be pulled from globals? is it sanitized correctly?
122            break;
123        case 'backlink':
124            html_backlinks();
125            break;
126        case 'conflict':
127            html_conflict(con($PRE,$TEXT,$SUF),$SUM);
128            html_diff(con($PRE,$TEXT,$SUF),false);
129            break;
130        case 'login':
131            html_login();
132            break;
133        case 'register':
134            html_register();
135            break;
136        case 'resendpwd':
137            html_resendpwd();
138            break;
139        case 'denied':
140            print p_locale_xhtml('denied');
141            break;
142        case 'profile' :
143            html_updateprofile();
144            break;
145        case 'admin':
146            tpl_admin();
147            break;
148        case 'subscribe':
149            tpl_subscribe();
150            break;
151        case 'media':
152            tpl_media();
153            break;
154        default:
155            $evt = new Doku_Event('TPL_ACT_UNKNOWN',$ACT);
156            if ($evt->advise_before())
157                msg("Failed to handle command: ".hsc($ACT),-1);
158            $evt->advise_after();
159            unset($evt);
160            return false;
161    }
162    return true;
163}
164
165/**
166 * Places the TOC where the function is called
167 *
168 * If you use this you most probably want to call tpl_content with
169 * a false argument
170 *
171 * @author Andreas Gohr <andi@splitbrain.org>
172 */
173function tpl_toc($return=false){
174    global $TOC;
175    global $ACT;
176    global $ID;
177    global $REV;
178    global $INFO;
179    global $conf;
180    $toc = array();
181
182    if(is_array($TOC)){
183        // if a TOC was prepared in global scope, always use it
184        $toc = $TOC;
185    }elseif(($ACT == 'show' || substr($ACT,0,6) == 'export') && !$REV && $INFO['exists']){
186        // get TOC from metadata, render if neccessary
187        $meta = p_get_metadata($ID, false, METADATA_RENDER_USING_CACHE);
188        if(isset($meta['internal']['toc'])){
189            $tocok = $meta['internal']['toc'];
190        }else{
191            $tocok = true;
192        }
193        $toc   = $meta['description']['tableofcontents'];
194        if(!$tocok || !is_array($toc) || !$conf['tocminheads'] || count($toc) < $conf['tocminheads']){
195            $toc = array();
196        }
197    }elseif($ACT == 'admin'){
198        // try to load admin plugin TOC FIXME: duplicates code from tpl_admin
199        $plugin = null;
200        if (!empty($_REQUEST['page'])) {
201            $pluginlist = plugin_list('admin');
202            if (in_array($_REQUEST['page'], $pluginlist)) {
203                // attempt to load the plugin
204                $plugin =& plugin_load('admin',$_REQUEST['page']);
205            }
206        }
207        if ( ($plugin !== null) &&
208                (!$plugin->forAdminOnly() || $INFO['isadmin']) ){
209            $toc = $plugin->getTOC();
210            $TOC = $toc; // avoid later rebuild
211        }
212    }
213
214    trigger_event('TPL_TOC_RENDER', $toc, null, false);
215    $html = html_TOC($toc);
216    if($return) return $html;
217    echo $html;
218}
219
220/**
221 * Handle the admin page contents
222 *
223 * @author Andreas Gohr <andi@splitbrain.org>
224 */
225function tpl_admin(){
226    global $INFO;
227    global $TOC;
228
229    $plugin = null;
230    if (!empty($_REQUEST['page'])) {
231        $pluginlist = plugin_list('admin');
232
233        if (in_array($_REQUEST['page'], $pluginlist)) {
234
235            // attempt to load the plugin
236            $plugin =& plugin_load('admin',$_REQUEST['page']);
237        }
238    }
239
240    if ($plugin !== null){
241        if(!is_array($TOC)) $TOC = $plugin->getTOC(); //if TOC wasn't requested yet
242        if($INFO['prependTOC']) tpl_toc();
243        $plugin->html();
244    }else{
245        html_admin();
246    }
247    return true;
248}
249
250/**
251 * Print the correct HTML meta headers
252 *
253 * This has to go into the head section of your template.
254 *
255 * @triggers TPL_METAHEADER_OUTPUT
256 * @param  boolean $alt Should feeds and alternative format links be added?
257 * @author Andreas Gohr <andi@splitbrain.org>
258 */
259function tpl_metaheaders($alt=true){
260    global $ID;
261    global $REV;
262    global $INFO;
263    global $JSINFO;
264    global $ACT;
265    global $QUERY;
266    global $lang;
267    global $conf;
268    $it=2;
269
270    // prepare the head array
271    $head = array();
272
273    // prepare seed for js and css
274    $tseed = 0;
275    $depends = getConfigFiles('main');
276    foreach($depends as $f) {
277        $time = @filemtime($f);
278        if($time > $tseed) $tseed = $time;
279    }
280
281    // the usual stuff
282    $head['meta'][] = array( 'name'=>'generator', 'content'=>'DokuWiki');
283    $head['link'][] = array( 'rel'=>'search', 'type'=>'application/opensearchdescription+xml',
284            'href'=>DOKU_BASE.'lib/exe/opensearch.php', 'title'=>$conf['title'] );
285    $head['link'][] = array( 'rel'=>'start', 'href'=>DOKU_BASE );
286    if(actionOK('index')){
287        $head['link'][] = array( 'rel'=>'contents', 'href'=> wl($ID,'do=index',false,'&'),
288                'title'=>$lang['btn_index'] );
289    }
290
291    if($alt){
292        $head['link'][] = array( 'rel'=>'alternate', 'type'=>'application/rss+xml',
293                'title'=>'Recent Changes', 'href'=>DOKU_BASE.'feed.php');
294        $head['link'][] = array( 'rel'=>'alternate', 'type'=>'application/rss+xml',
295                'title'=>'Current Namespace',
296                'href'=>DOKU_BASE.'feed.php?mode=list&ns='.$INFO['namespace']);
297        if(($ACT == 'show' || $ACT == 'search') && $INFO['writable']){
298            $head['link'][] = array( 'rel'=>'edit',
299                    'title'=>$lang['btn_edit'],
300                    'href'=> wl($ID,'do=edit',false,'&'));
301        }
302
303        if($ACT == 'search'){
304            $head['link'][] = array( 'rel'=>'alternate', 'type'=>'application/rss+xml',
305                    'title'=>'Search Result',
306                    'href'=>DOKU_BASE.'feed.php?mode=search&q='.$QUERY);
307        }
308
309        if(actionOK('export_xhtml')){
310            $head['link'][] = array( 'rel'=>'alternate', 'type'=>'text/html', 'title'=>'Plain HTML',
311                    'href'=>exportlink($ID, 'xhtml', '', false, '&'));
312        }
313
314        if(actionOK('export_raw')){
315            $head['link'][] = array( 'rel'=>'alternate', 'type'=>'text/plain', 'title'=>'Wiki Markup',
316                    'href'=>exportlink($ID, 'raw', '', false, '&'));
317        }
318    }
319
320    // setup robot tags apropriate for different modes
321    if( ($ACT=='show' || $ACT=='export_xhtml') && !$REV){
322        if($INFO['exists']){
323            //delay indexing:
324            if((time() - $INFO['lastmod']) >= $conf['indexdelay']){
325                $head['meta'][] = array( 'name'=>'robots', 'content'=>'index,follow');
326            }else{
327                $head['meta'][] = array( 'name'=>'robots', 'content'=>'noindex,nofollow');
328            }
329            $head['link'][] = array( 'rel'=>'canonical', 'href'=>wl($ID,'',true,'&') );
330        }else{
331            $head['meta'][] = array( 'name'=>'robots', 'content'=>'noindex,follow');
332        }
333    }elseif(defined('DOKU_MEDIADETAIL')){
334        $head['meta'][] = array( 'name'=>'robots', 'content'=>'index,follow');
335    }else{
336        $head['meta'][] = array( 'name'=>'robots', 'content'=>'noindex,nofollow');
337    }
338
339    // set metadata
340    if($ACT == 'show' || $ACT=='export_xhtml'){
341        // date of modification
342        if($REV){
343            $head['meta'][] = array( 'name'=>'date', 'content'=>date('Y-m-d\TH:i:sO',$REV));
344        }else{
345            $head['meta'][] = array( 'name'=>'date', 'content'=>date('Y-m-d\TH:i:sO',$INFO['lastmod']));
346        }
347
348        // keywords (explicit or implicit)
349        if(!empty($INFO['meta']['subject'])){
350            $head['meta'][] = array( 'name'=>'keywords', 'content'=>join(',',$INFO['meta']['subject']));
351        }else{
352            $head['meta'][] = array( 'name'=>'keywords', 'content'=>str_replace(':',',',$ID));
353        }
354    }
355
356    // load stylesheets
357    $head['link'][] = array('rel'=>'stylesheet', 'media'=>'screen', 'type'=>'text/css',
358            'href'=>DOKU_BASE.'lib/exe/css.php?t='.$conf['template'].'&tseed='.$tseed);
359    $head['link'][] = array('rel'=>'stylesheet', 'media'=>'all', 'type'=>'text/css',
360            'href'=>DOKU_BASE.'lib/exe/css.php?s=all&t='.$conf['template'].'&tseed='.$tseed);
361    $head['link'][] = array('rel'=>'stylesheet', 'media'=>'print', 'type'=>'text/css',
362            'href'=>DOKU_BASE.'lib/exe/css.php?s=print&t='.$conf['template'].'&tseed='.$tseed);
363
364    // make $INFO and other vars available to JavaScripts
365    $json = new JSON();
366    $script = "var NS='".$INFO['namespace']."';";
367    if($conf['useacl'] && $_SERVER['REMOTE_USER']){
368        $script .= "var SIG='".toolbar_signature()."';";
369    }
370    $script .= 'var JSINFO = '.$json->encode($JSINFO).';';
371    $head['script'][] = array( 'type'=>'text/javascript', '_data'=> $script);
372
373    // load external javascript
374    $head['script'][] = array( 'type'=>'text/javascript', 'charset'=>'utf-8', '_data'=>'',
375            'src'=>DOKU_BASE.'lib/exe/js.php'.'?tseed='.$tseed);
376
377    // trigger event here
378    trigger_event('TPL_METAHEADER_OUTPUT',$head,'_tpl_metaheaders_action',true);
379    return true;
380}
381
382/**
383 * prints the array build by tpl_metaheaders
384 *
385 * $data is an array of different header tags. Each tag can have multiple
386 * instances. Attributes are given as key value pairs. Values will be HTML
387 * encoded automatically so they should be provided as is in the $data array.
388 *
389 * For tags having a body attribute specify the the body data in the special
390 * attribute '_data'. This field will NOT BE ESCAPED automatically.
391 *
392 * @author Andreas Gohr <andi@splitbrain.org>
393 */
394function _tpl_metaheaders_action($data){
395    foreach($data as $tag => $inst){
396        foreach($inst as $attr){
397            echo '<',$tag,' ',buildAttributes($attr);
398            if(isset($attr['_data']) || $tag == 'script'){
399                if($tag == 'script' && $attr['_data'])
400                    $attr['_data'] = "<!--//--><![CDATA[//><!--\n".
401                        $attr['_data'].
402                        "\n//--><!]]>";
403
404                echo '>',$attr['_data'],'</',$tag,'>';
405            }else{
406                echo '/>';
407            }
408            echo "\n";
409        }
410    }
411}
412
413/**
414 * Print a link
415 *
416 * Just builds a link.
417 *
418 * @author Andreas Gohr <andi@splitbrain.org>
419 */
420function tpl_link($url,$name,$more='',$return=false){
421    $out = '<a href="'.$url.'" ';
422    if ($more) $out .= ' '.$more;
423    $out .= ">$name</a>";
424    if ($return) return $out;
425    print $out;
426    return true;
427}
428
429/**
430 * Prints a link to a WikiPage
431 *
432 * Wrapper around html_wikilink
433 *
434 * @author Andreas Gohr <andi@splitbrain.org>
435 */
436function tpl_pagelink($id,$name=null){
437    print html_wikilink($id,$name);
438    return true;
439}
440
441/**
442 * get the parent page
443 *
444 * Tries to find out which page is parent.
445 * returns false if none is available
446 *
447 * @author Andreas Gohr <andi@splitbrain.org>
448 */
449function tpl_getparent($id){
450    global $conf;
451    $parent = getNS($id).':';
452    resolve_pageid('',$parent,$exists);
453    if($parent == $id) {
454        $pos = strrpos (getNS($id),':');
455        $parent = substr($parent,0,$pos).':';
456        resolve_pageid('',$parent,$exists);
457        if($parent == $id) return false;
458    }
459    return $parent;
460}
461
462/**
463 * Print one of the buttons
464 *
465 * @author Adrian Lang <mail@adrianlang.de>
466 * @see    tpl_get_action
467 */
468function tpl_button($type,$return=false){
469    $data = tpl_get_action($type);
470    if ($data === false) {
471        return false;
472    } elseif (!is_array($data)) {
473        $out = sprintf($data, 'button');
474    } else {
475        extract($data);
476        if ($id === '#dokuwiki__top') {
477            $out = html_topbtn();
478        } else {
479            $out = html_btn($type, $id, $accesskey, $params, $method);
480        }
481    }
482    if ($return) return $out;
483    echo $out;
484    return true;
485}
486
487/**
488 * Like the action buttons but links
489 *
490 * @author Adrian Lang <mail@adrianlang.de>
491 * @see    tpl_get_action
492 */
493function tpl_actionlink($type,$pre='',$suf='',$inner='',$return=false){
494    global $lang;
495    $data = tpl_get_action($type);
496    if ($data === false) {
497        return false;
498    } elseif (!is_array($data)) {
499        $out = sprintf($data, 'link');
500    } else {
501        extract($data);
502        if (strpos($id, '#') === 0) {
503            $linktarget = $id;
504        } else {
505            $linktarget = wl($id, $params);
506        }
507        $caption = $lang['btn_' . $type];
508        $akey = $addTitle = '';
509        if($accesskey){
510            $akey = 'accesskey="'.$accesskey.'" ';
511            $addTitle = ' ['.strtoupper($accesskey).']';
512        }
513        $out = tpl_link($linktarget, $pre.(($inner)?$inner:$caption).$suf,
514                        'class="action ' . $type . '" ' .
515                        $akey . 'rel="nofollow" ' .
516                        'title="' . hsc($caption).$addTitle . '"', 1);
517    }
518    if ($return) return $out;
519    echo $out;
520    return true;
521}
522
523/**
524 * Check the actions and get data for buttons and links
525 *
526 * Available actions are
527 *
528 *  edit        - edit/create/show/draft
529 *  history     - old revisions
530 *  recent      - recent changes
531 *  login       - login/logout - if ACL enabled
532 *  profile     - user profile (if logged in)
533 *  index       - The index
534 *  admin       - admin page - if enough rights
535 *  top         - back to top
536 *  back        - back to parent - if available
537 *  backlink    - links to the list of backlinks
538 *  subscribe/subscription- subscribe/unsubscribe
539 *
540 * @author Andreas Gohr <andi@splitbrain.org>
541 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
542 * @author Adrian Lang <mail@adrianlang.de>
543 */
544function tpl_get_action($type) {
545    global $ID;
546    global $INFO;
547    global $REV;
548    global $ACT;
549    global $conf;
550    global $auth;
551
552    // check disabled actions and fix the badly named ones
553    if($type == 'history') $type='revisions';
554    if(!actionOK($type)) return false;
555
556    $accesskey = null;
557    $id        = $ID;
558    $method    = 'get';
559    $params    = array('do' => $type);
560    switch($type){
561        case 'edit':
562            // most complicated type - we need to decide on current action
563            if($ACT == 'show' || $ACT == 'search'){
564                $method = 'post';
565                if($INFO['writable']){
566                    $accesskey = 'e';
567                    if(!empty($INFO['draft'])) {
568                        $type = 'draft';
569                        $params['do'] = 'draft';
570                    } else {
571                        $params['rev'] = $REV;
572                        if(!$INFO['exists']){
573                            $type   = 'create';
574                        }
575                    }
576                }else{
577                    if(!actionOK('source')) return false; //pseudo action
578                    $params['rev'] = $REV;
579                    $type = 'source';
580                    $accesskey = 'v';
581                }
582            }else{
583                $params = '';
584                $type = 'show';
585                $accesskey = 'v';
586            }
587            break;
588        case 'revisions':
589            $type = 'revs';
590            $accesskey = 'o';
591            break;
592        case 'recent':
593            $accesskey = 'r';
594            break;
595        case 'index':
596            $accesskey = 'x';
597            break;
598        case 'top':
599            $accesskey = 'x';
600            $params = '';
601            $id = '#dokuwiki__top';
602            break;
603        case 'back':
604            $parent = tpl_getparent($ID);
605            if (!$parent) {
606                return false;
607            }
608            $id = $parent;
609            $params = '';
610            $accesskey = 'b';
611            break;
612        case 'login':
613            $params['sectok'] = getSecurityToken();
614            if(isset($_SERVER['REMOTE_USER'])){
615                if (!actionOK('logout')) {
616                    return false;
617                }
618                $params['do'] = 'logout';
619                $type = 'logout';
620            }
621            break;
622        case 'register':
623            if($_SERVER['REMOTE_USER']){
624                return false;
625            }
626            break;
627        case 'resendpwd':
628            if($_SERVER['REMOTE_USER']){
629                return false;
630            }
631            break;
632        case 'admin':
633            if(!$INFO['ismanager']){
634                return false;
635            }
636            break;
637        case 'revert':
638            if(!$INFO['ismanager'] || !$REV || !$INFO['writable']) {
639                return false;
640            }
641            $params['rev'] = $REV;
642            $params['sectok'] = getSecurityToken();
643            break;
644        case 'subscription':
645            $type = 'subscribe';
646            $params['do'] = 'subscribe';
647        case 'subscribe':
648            if(!$_SERVER['REMOTE_USER']){
649                return false;
650            }
651            break;
652        case 'backlink':
653            break;
654        case 'profile':
655            if(!isset($_SERVER['REMOTE_USER'])){
656                return false;
657            }
658            break;
659        case 'media':
660            break;
661        default:
662            return '[unknown %s type]';
663            break;
664    }
665    return compact('accesskey', 'type', 'id', 'method', 'params');
666}
667
668/**
669 * Wrapper around tpl_button() and tpl_actionlink()
670 *
671 * @author Anika Henke <anika@selfthinker.org>
672 */
673function tpl_action($type,$link=0,$wrapper=false,$return=false,$pre='',$suf='',$inner='') {
674    $out = '';
675    if ($link) $out .= tpl_actionlink($type,$pre,$suf,$inner,1);
676    else $out .= tpl_button($type,1);
677    if ($out && $wrapper) $out = "<$wrapper>$out</$wrapper>";
678
679    if ($return) return $out;
680    print $out;
681    return $out ? true : false;
682}
683
684/**
685 * Print the search form
686 *
687 * If the first parameter is given a div with the ID 'qsearch_out' will
688 * be added which instructs the ajax pagequicksearch to kick in and place
689 * its output into this div. The second parameter controls the propritary
690 * attribute autocomplete. If set to false this attribute will be set with an
691 * value of "off" to instruct the browser to disable it's own built in
692 * autocompletion feature (MSIE and Firefox)
693 *
694 * @author Andreas Gohr <andi@splitbrain.org>
695 */
696function tpl_searchform($ajax=true,$autocomplete=true){
697    global $lang;
698    global $ACT;
699    global $QUERY;
700
701    // don't print the search form if search action has been disabled
702    if (!actionOk('search')) return false;
703
704    print '<form action="'.wl().'" accept-charset="utf-8" class="search" id="dw__search" method="get"><div class="no">';
705    print '<input type="hidden" name="do" value="search" />';
706    print '<input type="text" ';
707    if($ACT == 'search') print 'value="'.htmlspecialchars($QUERY).'" ';
708    if(!$autocomplete) print 'autocomplete="off" ';
709    print 'id="qsearch__in" accesskey="f" name="id" class="edit" title="[F]" />';
710    print '<input type="submit" value="'.$lang['btn_search'].'" class="button" title="'.$lang['btn_search'].'" />';
711    if($ajax) print '<div id="qsearch__out" class="ajax_qsearch JSpopup"></div>';
712    print '</div></form>';
713    return true;
714}
715
716/**
717 * Print the breadcrumbs trace
718 *
719 * @author Andreas Gohr <andi@splitbrain.org>
720 */
721function tpl_breadcrumbs($sep='&bull;'){
722    global $lang;
723    global $conf;
724
725    //check if enabled
726    if(!$conf['breadcrumbs']) return false;
727
728    $crumbs = breadcrumbs(); //setup crumb trace
729
730    //reverse crumborder in right-to-left mode, add RLM character to fix heb/eng display mixups
731    if($lang['direction'] == 'rtl') {
732        $crumbs = array_reverse($crumbs,true);
733        $crumbs_sep = ' &#8207;<span class="bcsep">'.$sep.'</span>&#8207; ';
734    } else {
735        $crumbs_sep = ' <span class="bcsep">'.$sep.'</span> ';
736    }
737
738    //render crumbs, highlight the last one
739    print '<span class="bchead">'.$lang['breadcrumb'].':</span>';
740    $last = count($crumbs);
741    $i = 0;
742    foreach ($crumbs as $id => $name){
743        $i++;
744        echo $crumbs_sep;
745        if ($i == $last) print '<span class="curid">';
746        tpl_link(wl($id),hsc($name),'class="breadcrumbs" title="'.$id.'"');
747        if ($i == $last) print '</span>';
748    }
749    return true;
750}
751
752/**
753 * Hierarchical breadcrumbs
754 *
755 * This code was suggested as replacement for the usual breadcrumbs.
756 * It only makes sense with a deep site structure.
757 *
758 * @author Andreas Gohr <andi@splitbrain.org>
759 * @author Nigel McNie <oracle.shinoda@gmail.com>
760 * @author Sean Coates <sean@caedmon.net>
761 * @author <fredrik@averpil.com>
762 * @todo   May behave strangely in RTL languages
763 */
764function tpl_youarehere($sep=' &raquo; '){
765    global $conf;
766    global $ID;
767    global $lang;
768
769    // check if enabled
770    if(!$conf['youarehere']) return false;
771
772    $parts = explode(':', $ID);
773    $count = count($parts);
774
775    echo '<span class="bchead">'.$lang['youarehere'].': </span>';
776
777    // always print the startpage
778    tpl_pagelink(':'.$conf['start']);
779
780    // print intermediate namespace links
781    $part = '';
782    for($i=0; $i<$count - 1; $i++){
783        $part .= $parts[$i].':';
784        $page = $part;
785        if ($page == $conf['start']) continue; // Skip startpage
786
787        // output
788        echo $sep;
789        tpl_pagelink($page);
790    }
791
792    // print current page, skipping start page, skipping for namespace index
793    resolve_pageid('',$page,$exists);
794    if(isset($page) && $page==$part.$parts[$i]) return;
795    $page = $part.$parts[$i];
796    if($page == $conf['start']) return;
797    echo $sep;
798    tpl_pagelink($page);
799    return true;
800}
801
802/**
803 * Print info if the user is logged in
804 * and show full name in that case
805 *
806 * Could be enhanced with a profile link in future?
807 *
808 * @author Andreas Gohr <andi@splitbrain.org>
809 */
810function tpl_userinfo(){
811    global $lang;
812    global $INFO;
813    if(isset($_SERVER['REMOTE_USER'])){
814        print $lang['loggedinas'].': '.hsc($INFO['userinfo']['name']).' ('.hsc($_SERVER['REMOTE_USER']).')';
815        return true;
816    }
817    return false;
818}
819
820/**
821 * Print some info about the current page
822 *
823 * @author Andreas Gohr <andi@splitbrain.org>
824 */
825function tpl_pageinfo($ret=false){
826    global $conf;
827    global $lang;
828    global $INFO;
829    global $ID;
830
831    // return if we are not allowed to view the page
832    if (!auth_quickaclcheck($ID)) { return false; }
833
834    // prepare date and path
835    $fn = $INFO['filepath'];
836    if(!$conf['fullpath']){
837        if($INFO['rev']){
838            $fn = str_replace(fullpath($conf['olddir']).'/','',$fn);
839        }else{
840            $fn = str_replace(fullpath($conf['datadir']).'/','',$fn);
841        }
842    }
843    $fn = utf8_decodeFN($fn);
844    $date = dformat($INFO['lastmod']);
845
846    // print it
847    if($INFO['exists']){
848        $out = '';
849        $out .= $fn;
850        $out .= ' &middot; ';
851        $out .= $lang['lastmod'];
852        $out .= ': ';
853        $out .= $date;
854        if($INFO['editor']){
855            $out .= ' '.$lang['by'].' ';
856            $out .= editorinfo($INFO['editor']);
857        }else{
858            $out .= ' ('.$lang['external_edit'].')';
859        }
860        if($INFO['locked']){
861            $out .= ' &middot; ';
862            $out .= $lang['lockedby'];
863            $out .= ': ';
864            $out .= editorinfo($INFO['locked']);
865        }
866        if($ret){
867            return $out;
868        }else{
869            echo $out;
870            return true;
871        }
872    }
873    return false;
874}
875
876/**
877 * Prints or returns the name of the given page (current one if none given).
878 *
879 * If useheading is enabled this will use the first headline else
880 * the given ID is used.
881 *
882 * @author Andreas Gohr <andi@splitbrain.org>
883 */
884function tpl_pagetitle($id=null, $ret=false){
885    global $conf;
886    if(is_null($id)){
887        global $ID;
888        $id = $ID;
889    }
890
891    $name = $id;
892    if (useHeading('navigation')) {
893        $title = p_get_first_heading($id);
894        if ($title) $name = $title;
895    }
896
897    if ($ret) {
898        return hsc($name);
899    } else {
900        print hsc($name);
901        return true;
902    }
903}
904
905/**
906 * Returns the requested EXIF/IPTC tag from the current image
907 *
908 * If $tags is an array all given tags are tried until a
909 * value is found. If no value is found $alt is returned.
910 *
911 * Which texts are known is defined in the functions _exifTagNames
912 * and _iptcTagNames() in inc/jpeg.php (You need to prepend IPTC
913 * to the names of the latter one)
914 *
915 * Only allowed in: detail.php
916 *
917 * @author Andreas Gohr <andi@splitbrain.org>
918 */
919function tpl_img_getTag($tags,$alt='',$src=null){
920    // Init Exif Reader
921    global $SRC;
922
923    if(is_null($src)) $src = $SRC;
924
925    static $meta = null;
926    if(is_null($meta)) $meta = new JpegMeta($src);
927    if($meta === false) return $alt;
928    $info = $meta->getField($tags);
929    if($info == false) return $alt;
930    return $info;
931}
932
933/**
934 * Prints the image with a link to the full sized version
935 *
936 * Only allowed in: detail.php
937 *
938 * @param $maxwidth  int - maximal width of the image
939 * @param $maxheight int - maximal height of the image
940 * @param $link bool     - link to the orginal size?
941 * @param $params array  - additional image attributes
942 */
943function tpl_img($maxwidth=0,$maxheight=0,$link=true,$params=null){
944    global $IMG;
945    $w = tpl_img_getTag('File.Width');
946    $h = tpl_img_getTag('File.Height');
947
948    //resize to given max values
949    $ratio = 1;
950    if($w >= $h){
951        if($maxwidth && $w >= $maxwidth){
952            $ratio = $maxwidth/$w;
953        }elseif($maxheight && $h > $maxheight){
954            $ratio = $maxheight/$h;
955        }
956    }else{
957        if($maxheight && $h >= $maxheight){
958            $ratio = $maxheight/$h;
959        }elseif($maxwidth && $w > $maxwidth){
960            $ratio = $maxwidth/$w;
961        }
962    }
963    if($ratio){
964        $w = floor($ratio*$w);
965        $h = floor($ratio*$h);
966    }
967
968    //prepare URLs
969    $url=ml($IMG,array('cache'=>$_REQUEST['cache']),true,'&');
970    $src=ml($IMG,array('cache'=>$_REQUEST['cache'],'w'=>$w,'h'=>$h),true,'&');
971
972    //prepare attributes
973    $alt=tpl_img_getTag('Simple.Title');
974    if(is_null($params)){
975        $p = array();
976    }else{
977        $p = $params;
978    }
979    if($w) $p['width']  = $w;
980    if($h) $p['height'] = $h;
981    $p['class']  = 'img_detail';
982    if($alt){
983        $p['alt']   = $alt;
984        $p['title'] = $alt;
985    }else{
986        $p['alt'] = '';
987    }
988    $p['src'] = $src;
989
990    $data = array('url'=>($link?$url:null), 'params'=>$p);
991    return trigger_event('TPL_IMG_DISPLAY',$data,'_tpl_img_action',true);
992}
993
994/**
995 * Default action for TPL_IMG_DISPLAY
996 */
997function _tpl_img_action($data, $param=null) {
998    global $lang;
999    $p = buildAttributes($data['params']);
1000
1001    if($data['url']) print '<a href="'.hsc($data['url']).'" title="'.$lang['mediaview'].'">';
1002    print '<img '.$p.'/>';
1003    if($data['url']) print '</a>';
1004    return true;
1005}
1006
1007/**
1008 * This function inserts a small gif which in reality is the indexer function.
1009 *
1010 * Should be called somewhere at the very end of the main.php
1011 * template
1012 */
1013function tpl_indexerWebBug(){
1014    global $ID;
1015
1016    $p = array();
1017    $p['src']    = DOKU_BASE.'lib/exe/indexer.php?id='.rawurlencode($ID).
1018        '&'.time();
1019    $p['width']  = 2; //no more 1x1 px image because we live in times of ad blockers...
1020    $p['height'] = 1;
1021    $p['alt']    = '';
1022    $att = buildAttributes($p);
1023    print "<img $att />";
1024    return true;
1025}
1026
1027// configuration methods
1028/**
1029 * tpl_getConf($id)
1030 *
1031 * use this function to access template configuration variables
1032 */
1033function tpl_getConf($id){
1034    global $conf;
1035    static $tpl_configloaded = false;
1036
1037    $tpl = $conf['template'];
1038
1039    if (!$tpl_configloaded){
1040        $tconf = tpl_loadConfig();
1041        if ($tconf !== false){
1042            foreach ($tconf as $key => $value){
1043                if (isset($conf['tpl'][$tpl][$key])) continue;
1044                $conf['tpl'][$tpl][$key] = $value;
1045            }
1046            $tpl_configloaded = true;
1047        }
1048    }
1049
1050    return $conf['tpl'][$tpl][$id];
1051}
1052
1053/**
1054 * tpl_loadConfig()
1055 * reads all template configuration variables
1056 * this function is automatically called by tpl_getConf()
1057 */
1058function tpl_loadConfig(){
1059
1060    $file = tpl_incdir().'/conf/default.php';
1061    $conf = array();
1062
1063    if (!@file_exists($file)) return false;
1064
1065    // load default config file
1066    include($file);
1067
1068    return $conf;
1069}
1070
1071// language methods
1072/**
1073 * tpl_getLang($id)
1074 *
1075 * use this function to access template language variables
1076 */
1077function tpl_getLang($id){
1078    static $lang = array();
1079
1080    if (count($lang) === 0){
1081        $path = tpl_incdir().'lang/';
1082
1083        $lang = array();
1084
1085        global $conf;            // definitely don't invoke "global $lang"
1086        // don't include once
1087        @include($path.'en/lang.php');
1088        if ($conf['lang'] != 'en') @include($path.$conf['lang'].'/lang.php');
1089    }
1090
1091    return $lang[$id];
1092}
1093
1094/**
1095 * prints the "main content" in the mediamanger popup
1096 *
1097 * Depending on the user's actions this may be a list of
1098 * files in a namespace, the meta editing dialog or
1099 * a message of referencing pages
1100 *
1101 * Only allowed in mediamanager.php
1102 *
1103 * @triggers MEDIAMANAGER_CONTENT_OUTPUT
1104 * @param bool $fromajax - set true when calling this function via ajax
1105 * @author Andreas Gohr <andi@splitbrain.org>
1106 */
1107function tpl_mediaContent($fromajax=false){
1108    global $IMG;
1109    global $AUTH;
1110    global $INUSE;
1111    global $NS;
1112    global $JUMPTO;
1113
1114    if(is_array($_REQUEST['do'])){
1115        $do = array_shift(array_keys($_REQUEST['do']));
1116    }else{
1117        $do = $_REQUEST['do'];
1118    }
1119    if(in_array($do,array('save','cancel'))) $do = '';
1120
1121    if(!$do){
1122        if($_REQUEST['edit']){
1123            $do = 'metaform';
1124        }elseif(is_array($INUSE)){
1125            $do = 'filesinuse';
1126        }else{
1127            $do = 'filelist';
1128        }
1129    }
1130
1131    // output the content pane, wrapped in an event.
1132    if(!$fromajax) ptln('<div id="media__content">');
1133    $data = array( 'do' => $do);
1134    $evt = new Doku_Event('MEDIAMANAGER_CONTENT_OUTPUT', $data);
1135    if ($evt->advise_before()) {
1136        $do = $data['do'];
1137        if($do == 'filesinuse'){
1138            media_filesinuse($INUSE,$IMG);
1139        }elseif($do == 'filelist'){
1140            media_filelist($NS,$AUTH,$JUMPTO);
1141        }elseif($do == 'searchlist'){
1142            media_searchlist($_REQUEST['q'],$NS,$AUTH);
1143        }else{
1144            msg('Unknown action '.hsc($do),-1);
1145        }
1146    }
1147    $evt->advise_after();
1148    unset($evt);
1149    if(!$fromajax) ptln('</div>');
1150
1151}
1152
1153/**
1154 * Prints the central column in full-screen media manager
1155 * Depending on the opened tab this may be a list of
1156 * files in a namespace, upload form or search form
1157 *
1158 * @author Kate Arzamastseva <pshns@ukr.net>
1159 */
1160function tpl_mediaFileList(){
1161    global $AUTH;
1162    global $NS;
1163    global $JUMPTO;
1164    global $lang;
1165
1166    $opened_tab = $_REQUEST['tab_files'];
1167    if (!$opened_tab || !in_array($opened_tab, array('files', 'upload', 'search'))) $opened_tab = 'files';
1168    if ($_REQUEST['mediado'] == 'update') $opened_tab = 'upload';
1169
1170    echo '<h2 class="a11y">' . $lang['mediaselect'] . '</h2>'.NL;
1171
1172    media_tabs_files($opened_tab);
1173
1174    echo '<div class="panelHeader">'.NL;
1175    echo '<h3>';
1176    $tabTitle = ($NS) ? $NS : '['.$lang['mediaroot'].']';
1177    printf($lang['media_' . $opened_tab], '<strong>'.$tabTitle.'</strong>');
1178    echo '</h3>'.NL;
1179    if ($opened_tab === 'search' || $opened_tab === 'files') {
1180        media_tab_files_options();
1181    }
1182    echo '</div>'.NL;
1183
1184    echo '<div class="panelContent">'.NL;
1185    if ($opened_tab == 'files') {
1186        media_tab_files($NS,$AUTH,$JUMPTO);
1187    } elseif ($opened_tab == 'upload') {
1188        media_tab_upload($NS,$AUTH,$JUMPTO);
1189    } elseif ($opened_tab == 'search') {
1190        media_tab_search($NS,$AUTH);
1191    }
1192    echo '</div>'.NL;
1193}
1194
1195/**
1196 * Prints the third column in full-screen media manager
1197 * Depending on the opened tab this may be details of the
1198 * selected file, the meta editing dialog or
1199 * list of file revisions
1200 *
1201 * @author Kate Arzamastseva <pshns@ukr.net>
1202 */
1203function tpl_mediaFileDetails($image, $rev){
1204    global $AUTH, $NS, $conf, $DEL, $lang;
1205
1206    $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')) && $conf['mediarevisions']);
1207    if (!$image || (!file_exists(mediaFN($image)) && !$removed) || $DEL) return '';
1208    if ($rev && !file_exists(mediaFN($image, $rev))) $rev = false;
1209    if (isset($NS) && getNS($image) != $NS) return '';
1210    $do = $_REQUEST['mediado'];
1211
1212    $opened_tab = $_REQUEST['tab_details'];
1213
1214    $tab_array = array('view');
1215    list($ext, $mime) = mimetype($image);
1216    if ($mime == 'image/jpeg') {
1217        $tab_array[] = 'edit';
1218    }
1219    if ($conf['mediarevisions']) {
1220        $tab_array[] = 'history';
1221    }
1222
1223    if (!$opened_tab || !in_array($opened_tab, $tab_array)) $opened_tab = 'view';
1224    if ($_REQUEST['edit']) $opened_tab = 'edit';
1225    if ($do == 'restore') $opened_tab = 'view';
1226
1227    media_tabs_details($image, $opened_tab);
1228
1229    echo '<div class="panelHeader"><h3>';
1230    list($ext,$mime,$dl) = mimetype($image,false);
1231    $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
1232    $class = 'select mediafile mf_'.$class;
1233    $tabTitle = '<strong class="'.$class.'"><a href="'.ml($image).'" title="'.$lang['mediaview'].'">'.$image.'</a>'.'</strong>';
1234    if ($opened_tab === 'view' && $rev) {
1235        printf($lang['media_viewold'], $tabTitle, dformat($rev));
1236    } else {
1237        printf($lang['media_' . $opened_tab], $tabTitle);
1238    }
1239
1240    echo '</h3></div>'.NL;
1241
1242    echo '<div class="panelContent">'.NL;
1243
1244    if ($opened_tab == 'view') {
1245        media_tab_view($image, $NS, $AUTH, $rev);
1246
1247    } elseif ($opened_tab == 'edit' && !$removed) {
1248        media_tab_edit($image, $NS, $AUTH);
1249
1250    } elseif ($opened_tab == 'history' && $conf['mediarevisions']) {
1251        media_tab_history($image,$NS,$AUTH);
1252    }
1253
1254    echo '</div>'.NL;
1255}
1256
1257/**
1258 * prints the namespace tree in the mediamanger popup
1259 *
1260 * Only allowed in mediamanager.php
1261 *
1262 * @author Andreas Gohr <andi@splitbrain.org>
1263 */
1264function tpl_mediaTree(){
1265    global $NS;
1266    ptln('<div id="media__tree">');
1267    media_nstree($NS);
1268    ptln('</div>');
1269}
1270
1271
1272/**
1273 * Print a dropdown menu with all DokuWiki actions
1274 *
1275 * Note: this will not use any pretty URLs
1276 *
1277 * @author Andreas Gohr <andi@splitbrain.org>
1278 */
1279function tpl_actiondropdown($empty='',$button='&gt;'){
1280    global $ID;
1281    global $INFO;
1282    global $REV;
1283    global $ACT;
1284    global $conf;
1285    global $lang;
1286    global $auth;
1287
1288    echo '<form action="' . DOKU_SCRIPT . '" method="post" accept-charset="utf-8">';
1289    echo '<div class="no">';
1290    echo '<input type="hidden" name="id" value="'.$ID.'" />';
1291    if($REV) echo '<input type="hidden" name="rev" value="'.$REV.'" />';
1292    echo '<input type="hidden" name="sectok" value="'.getSecurityToken().'" />';
1293
1294    echo '<select name="do" class="edit quickselect">';
1295    echo '<option value="">'.$empty.'</option>';
1296
1297    echo '<optgroup label="'.$lang['page_tools'].'">';
1298        $act = tpl_get_action('edit');
1299        if($act) echo '<option value="'.$act['params']['do'].'">'.$lang['btn_'.$act['type']].'</option>';
1300
1301        $act = tpl_get_action('revert');
1302        if($act) echo '<option value="'.$act['params']['do'].'">'.$lang['btn_'.$act['type']].'</option>';
1303
1304        $act = tpl_get_action('revisions');
1305        if($act) echo '<option value="'.$act['params']['do'].'">'.$lang['btn_'.$act['type']].'</option>';
1306
1307        $act = tpl_get_action('backlink');
1308        if($act) echo '<option value="'.$act['params']['do'].'">'.$lang['btn_'.$act['type']].'</option>';
1309
1310        $act = tpl_get_action('subscribe');
1311        if($act) echo '<option value="'.$act['params']['do'].'">'.$lang['btn_'.$act['type']].'</option>';
1312        echo '</optgroup>';
1313
1314    echo '<optgroup label="'.$lang['site_tools'].'">';
1315        $act = tpl_get_action('recent');
1316        if($act) echo '<option value="'.$act['params']['do'].'">'.$lang['btn_'.$act['type']].'</option>';
1317
1318        $act = tpl_get_action('media');
1319        if($act) echo '<option value="'.$act['params']['do'].'">'.$lang['btn_'.$act['type']].'</option>';
1320
1321        $act = tpl_get_action('index');
1322        if($act) echo '<option value="'.$act['params']['do'].'">'.$lang['btn_'.$act['type']].'</option>';
1323    echo '</optgroup>';
1324
1325    echo '<optgroup label="'.$lang['user_tools'].'">';
1326        $act = tpl_get_action('login');
1327        if($act) echo '<option value="'.$act['params']['do'].'">'.$lang['btn_'.$act['type']].'</option>';
1328
1329        $act = tpl_get_action('register');
1330        if($act) echo '<option value="'.$act['params']['do'].'">'.$lang['btn_'.$act['type']].'</option>';
1331
1332        $act = tpl_get_action('profile');
1333        if($act) echo '<option value="'.$act['params']['do'].'">'.$lang['btn_'.$act['type']].'</option>';
1334
1335        $act = tpl_get_action('admin');
1336        if($act) echo '<option value="'.$act['params']['do'].'">'.$lang['btn_'.$act['type']].'</option>';
1337    echo '</optgroup>';
1338
1339    echo '</select>';
1340    echo '<input type="submit" value="'.$button.'" />';
1341    echo '</div>';
1342    echo '</form>';
1343}
1344
1345/**
1346 * Print a informational line about the used license
1347 *
1348 * @author Andreas Gohr <andi@splitbrain.org>
1349 * @param  string $img    - print image? (|button|badge)
1350 * @param  bool   $return - when true don't print, but return HTML
1351 * @param  bool   $wrap   - wrap in div with class="license"?
1352 */
1353function tpl_license($img='badge',$imgonly=false,$return=false,$wrap=true){
1354    global $license;
1355    global $conf;
1356    global $lang;
1357    if(!$conf['license']) return '';
1358    if(!is_array($license[$conf['license']])) return '';
1359    $lic = $license[$conf['license']];
1360    $target = ($conf['target']['extern']) ? ' target="'.$conf['target']['extern'].'"' : '';
1361
1362    $out = '';
1363    if($wrap) $out  .= '<div class="license">';
1364    if($img){
1365        $src = license_img($img);
1366        if($src){
1367            $out .= '<a href="'.$lic['url'].'" rel="license"'.$target;
1368            $out .= '><img src="'.DOKU_BASE.$src.'" alt="'.$lic['name'].'" /></a>';
1369            if(!$imgonly) $out .= ' ';
1370        }
1371    }
1372    if(!$imgonly) {
1373        $out .= $lang['license'].' ';
1374        $out .= '<a href="'.$lic['url'].'" rel="license" class="urlextern"'.$target;
1375        $out .= '>'.$lic['name'].'</a>';
1376    }
1377    if($wrap) $out .= '</div>';
1378
1379    if($return) return $out;
1380    echo $out;
1381}
1382
1383
1384/**
1385 * Includes the rendered XHTML of a given page
1386 *
1387 * This function is useful to populate sidebars or similar features in a
1388 * template
1389 */
1390function tpl_include_page($pageid,$print=true){
1391    global $ID;
1392    global $TOC;
1393    $oldid  = $ID;
1394    $oldtoc = $TOC;
1395    $html = p_wiki_xhtml($pageid,'',false);
1396    $ID  = $oldid;
1397    $TOC = $oldtoc;
1398
1399    if(!$print) return $html;
1400    echo $html;
1401}
1402
1403/**
1404 * Display the subscribe form
1405 *
1406 * @author Adrian Lang <lang@cosmocode.de>
1407 */
1408function tpl_subscribe() {
1409    global $INFO;
1410    global $ID;
1411    global $lang;
1412    global $conf;
1413    $stime_days = $conf['subscribe_time']/60/60/24;
1414
1415    echo p_locale_xhtml('subscr_form');
1416    echo '<h2>' . $lang['subscr_m_current_header'] . '</h2>';
1417    echo '<div class="level2">';
1418    if ($INFO['subscribed'] === false) {
1419        echo '<p>' . $lang['subscr_m_not_subscribed'] . '</p>';
1420    } else {
1421        echo '<ul>';
1422        foreach($INFO['subscribed'] as $sub) {
1423            echo '<li><div class="li">';
1424            if ($sub['target'] !== $ID) {
1425                echo '<code class="ns">'.hsc(prettyprint_id($sub['target'])).'</code>';
1426            } else {
1427                echo '<code class="page">'.hsc(prettyprint_id($sub['target'])).'</code>';
1428            }
1429            $sstl = sprintf($lang['subscr_style_'.$sub['style']], $stime_days);
1430            if(!$sstl) $sstl = hsc($sub['style']);
1431            echo ' ('.$sstl.') ';
1432
1433            echo '<a href="' . wl($ID,
1434                                  array('do'=>'subscribe',
1435                                        'sub_target'=>$sub['target'],
1436                                        'sub_style'=>$sub['style'],
1437                                        'sub_action'=>'unsubscribe',
1438                                        'sectok' => getSecurityToken())) .
1439                 '" class="unsubscribe">'.$lang['subscr_m_unsubscribe'] .
1440                 '</a></div></li>';
1441        }
1442        echo '</ul>';
1443    }
1444    echo '</div>';
1445
1446    // Add new subscription form
1447    echo '<h2>' . $lang['subscr_m_new_header'] . '</h2>';
1448    echo '<div class="level2">';
1449    $ns = getNS($ID).':';
1450    $targets = array(
1451            $ID => '<code class="page">'.prettyprint_id($ID).'</code>',
1452            $ns => '<code class="ns">'.prettyprint_id($ns).'</code>',
1453            );
1454    $styles = array(
1455            'every'  => $lang['subscr_style_every'],
1456            'digest' => sprintf($lang['subscr_style_digest'], $stime_days),
1457            'list' => sprintf($lang['subscr_style_list'], $stime_days),
1458            );
1459
1460    $form = new Doku_Form(array('id' => 'subscribe__form'));
1461    $form->startFieldset($lang['subscr_m_subscribe']);
1462    $form->addRadioSet('sub_target', $targets);
1463    $form->startFieldset($lang['subscr_m_receive']);
1464    $form->addRadioSet('sub_style', $styles);
1465    $form->addHidden('sub_action', 'subscribe');
1466    $form->addHidden('do', 'subscribe');
1467    $form->addHidden('id', $ID);
1468    $form->endFieldset();
1469    $form->addElement(form_makeButton('submit', 'subscribe', $lang['subscr_m_subscribe']));
1470    html_form('SUBSCRIBE', $form);
1471    echo '</div>';
1472}
1473
1474/**
1475 * Tries to send already created content right to the browser
1476 *
1477 * Wraps around ob_flush() and flush()
1478 *
1479 * @author Andreas Gohr <andi@splitbrain.org>
1480 */
1481function tpl_flush(){
1482    ob_flush();
1483    flush();
1484}
1485
1486/**
1487 * Tries to find a ressource file in the given locations.
1488 *
1489 * If a given location starts with a colon it is assumed to be a media
1490 * file, otherwise it is assumed to be relative to the current template
1491 *
1492 * @param  array $search       locations to look at
1493 * @param  bool $abs           if to use absolute URL
1494 * @param  arrayref $imginfo   filled with getimagesize()
1495 * @author Andreas  Gohr <andi@splitbrain.org>
1496 */
1497function tpl_getMediaFile($search, $abs=false, &$imginfo=null){
1498    // loop through candidates until a match was found:
1499    foreach($search as $img){
1500        if(substr($img,0,1) == ':'){
1501            $file    = mediaFN($img);
1502            $ismedia = true;
1503        }else{
1504            $file = tpl_incdir().$img;
1505            $ismedia = false;
1506        }
1507
1508        if(file_exists($file)) break;
1509    }
1510
1511    // fetch image data if requested
1512    if(!is_null($imginfo)){
1513        $imginfo = getimagesize($file);
1514    }
1515
1516    // build URL
1517    if($ismedia){
1518        $url = ml($img, '', true, '', $abs);
1519    }else{
1520        $url = tpl_basedir().$img;
1521        if($abs) $url = DOKU_URL.substr($url, strlen(DOKU_REL));
1522    }
1523
1524    return $url;
1525}
1526
1527/**
1528 * PHP include a file
1529 *
1530 * either from the conf directory if it exists, otherwise use
1531 * file in the template's root directory.
1532 *
1533 * The function honours config cascade settings and looks for the given
1534 * file next to the ´main´ config files, in the order protected, local,
1535 * default.
1536 *
1537 * Note: no escaping or sanity checking is done here. Never pass user input
1538 * to this function!
1539 *
1540 * @author Anika Henke <anika@selfthinker.org>
1541 * @author Andreas Gohr <andi@splitbrain.org>
1542 */
1543function tpl_includeFile($file){
1544    global $config_cascade;
1545    foreach (array('protected','local','default') as $config_group) {
1546        if (empty($config_cascade['main'][$config_group])) continue;
1547        foreach ($config_cascade['main'][$config_group] as $conf_file) {
1548            $dir = dirname($conf_file);
1549            if(file_exists("$dir/$file")){
1550                include("$dir/$file");
1551                return;
1552            }
1553        }
1554    }
1555
1556    // still here? try the template dir
1557    $file = tpl_incdir().$file;
1558    if(file_exists($file)){
1559        include($file);
1560    }
1561}
1562
1563/**
1564 * Returns icon from data/media root directory if it exists, otherwise
1565 * the one in the template's image directory.
1566 *
1567 * @deprecated Use tpl_getMediaFile() instead
1568 * @author Anika Henke <anika@selfthinker.org>
1569 */
1570function tpl_getFavicon($abs=false, $fileName='favicon.ico') {
1571    $look = array(":wiki:$fileName", ":$fileName", "images/$fileName");
1572    return tpl_getMediaFile($look, $abs);
1573}
1574
1575/**
1576 * Returns <link> tag for various icon types (favicon|mobile|generic)
1577 *
1578 * @param  array $types - list of icon types to display (favicon|mobile|generic)
1579 * @author Anika Henke <anika@selfthinker.org>
1580 */
1581function tpl_favicon($types=array('favicon')) {
1582
1583    $return = '';
1584
1585    foreach ($types as $type) {
1586        switch($type) {
1587            case 'favicon':
1588                $look = array(':wiki:favicon.ico', ':favicon.ico', 'images/favicon.ico');
1589                $return .= '<link rel="shortcut icon" href="'.tpl_getMediaFile($look).'" />'.NL;
1590                break;
1591            case 'mobile':
1592                $look = array(':wiki:apple-touch-icon.png', ':apple-touch-icon.png', 'images/apple-touch-icon.ico');
1593                $return .= '<link rel="apple-touch-icon" href="'.tpl_getMediaFile($look).'" />'.NL;
1594                break;
1595            case 'generic':
1596                // ideal world solution, which doesn't work in any browser yet
1597                $look = array(':wiki:favicon.svg', ':favicon.svg', 'images/favicon.svg');
1598                $return .= '<link rel="icon" href="'.tpl_getMediaFile($look).'" type="image/svg+xml" />'.NL;
1599                break;
1600        }
1601    }
1602
1603    return $return;
1604}
1605
1606/**
1607 * Prints full-screen media manager
1608 *
1609 * @author Kate Arzamastseva <pshns@ukr.net>
1610 */
1611function tpl_media() {
1612    global $DEL, $NS, $IMG, $AUTH, $JUMPTO, $REV, $lang, $fullscreen, $conf;
1613    $fullscreen = true;
1614    require_once DOKU_INC.'lib/exe/mediamanager.php';
1615
1616    if ($_REQUEST['image']) $image = cleanID($_REQUEST['image']);
1617    if (isset($IMG)) $image = $IMG;
1618    if (isset($JUMPTO)) $image = $JUMPTO;
1619    if (isset($REV) && !$JUMPTO) $rev = $REV;
1620
1621    echo '<div id="mediamanager__page">'.NL;
1622    echo '<h1>'.$lang['btn_media'].'</h1>'.NL;
1623    html_msgarea();
1624
1625    echo '<div class="panel namespaces">'.NL;
1626    echo '<h2>'.$lang['namespaces'].'</h2>'.NL;
1627    echo '<div class="panelHeader">';
1628    echo $lang['media_namespaces'];
1629    echo '</div>'.NL;
1630
1631    echo '<div class="panelContent" id="media__tree">'.NL;
1632    media_nstree($NS);
1633    echo '</div>'.NL;
1634    echo '</div>'.NL;
1635
1636    echo '<div class="panel filelist">'.NL;
1637    tpl_mediaFileList();
1638    echo '</div>'.NL;
1639
1640    echo '<div class="panel file">'.NL;
1641    echo '<h2 class="a11y">'.$lang['media_file'].'</h2>'.NL;
1642    tpl_mediaFileDetails($image, $rev);
1643    echo '</div>'.NL;
1644
1645    echo '</div>'.NL;
1646}
1647
1648//Setup VIM: ex: et ts=4 :
1649
1650