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