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