xref: /dokuwiki/inc/template.php (revision c2a6d81662045023bdf1617b6b49f71c274d55ca)
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    $json = new JSON();
349    $script = "var NS='".$INFO['namespace']."';";
350    if($conf['useacl'] && $_SERVER['REMOTE_USER']){
351        $script .= "var SIG='".toolbar_signature()."';";
352    }
353    $script .= 'var JSINFO = '.$json->encode($JSINFO).';';
354    $head['script'][] = array( 'type'=>'text/javascript', 'charset'=>'utf-8',
355            '_data'=> $script);
356
357    // load external javascript
358    $head['script'][] = array( 'type'=>'text/javascript', 'charset'=>'utf-8', '_data'=>'',
359            'src'=>DOKU_BASE.'lib/exe/js.php'.'?tseed='.$tseed);
360
361    // trigger event here
362    trigger_event('TPL_METAHEADER_OUTPUT',$head,'_tpl_metaheaders_action',true);
363    return true;
364}
365
366/**
367 * prints the array build by tpl_metaheaders
368 *
369 * $data is an array of different header tags. Each tag can have multiple
370 * instances. Attributes are given as key value pairs. Values will be HTML
371 * encoded automatically so they should be provided as is in the $data array.
372 *
373 * For tags having a body attribute specify the the body data in the special
374 * attribute '_data'. This field will NOT BE ESCAPED automatically.
375 *
376 * @author Andreas Gohr <andi@splitbrain.org>
377 */
378function _tpl_metaheaders_action($data){
379    foreach($data as $tag => $inst){
380        foreach($inst as $attr){
381            echo '<',$tag,' ',buildAttributes($attr);
382            if(isset($attr['_data']) || $tag == 'script'){
383                if($tag == 'script' && $attr['_data'])
384                    $attr['_data'] = "<!--//--><![CDATA[//><!--\n".
385                        $attr['_data'].
386                        "\n//--><!]]>";
387
388                echo '>',$attr['_data'],'</',$tag,'>';
389            }else{
390                echo '/>';
391            }
392            echo "\n";
393        }
394    }
395}
396
397/**
398 * Print a link
399 *
400 * Just builds a link.
401 *
402 * @author Andreas Gohr <andi@splitbrain.org>
403 */
404function tpl_link($url,$name,$more='',$return=false){
405    $out = '<a href="'.$url.'" ';
406    if ($more) $out .= ' '.$more;
407    $out .= ">$name</a>";
408    if ($return) return $out;
409    print $out;
410    return true;
411}
412
413/**
414 * Prints a link to a WikiPage
415 *
416 * Wrapper around html_wikilink
417 *
418 * @author Andreas Gohr <andi@splitbrain.org>
419 */
420function tpl_pagelink($id,$name=null){
421    print html_wikilink($id,$name);
422    return true;
423}
424
425/**
426 * get the parent page
427 *
428 * Tries to find out which page is parent.
429 * returns false if none is available
430 *
431 * @author Andreas Gohr <andi@splitbrain.org>
432 */
433function tpl_getparent($id){
434    global $conf;
435    $parent = getNS($id).':';
436    resolve_pageid('',$parent,$exists);
437    if($parent == $id) {
438        $pos = strrpos (getNS($id),':');
439        $parent = substr($parent,0,$pos).':';
440        resolve_pageid('',$parent,$exists);
441        if($parent == $id) return false;
442    }
443    return $parent;
444}
445
446/**
447 * Print one of the buttons
448 *
449 * Available Buttons are
450 *
451 *  edit        - edit/create/show/draft button
452 *  history     - old revisions
453 *  recent      - recent changes
454 *  login       - login/logout button - if ACL enabled
455 *  profile     - user profile button (if logged in)
456 *  index       - The index
457 *  admin       - admin page - if enough rights
458 *  top         - a back to top button
459 *  back        - a back to parent button - if available
460 *  backlink    - links to the list of backlinks
461 *  subscription- subscribe/unsubscribe button
462 *
463 * @author Andreas Gohr <andi@splitbrain.org>
464 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
465 */
466function tpl_button($type,$return=false){
467    global $ACT;
468    global $ID;
469    global $REV;
470    global $NS;
471    global $INFO;
472    global $conf;
473    global $auth;
474
475    // check disabled actions and fix the badly named ones
476    $ctype = $type;
477    if($type == 'history') $ctype='revisions';
478    if(!actionOK($ctype)) return false;
479
480    $out = '';
481    switch($type){
482        case 'edit':
483            // most complicated type - we need to decide on current action
484            if($ACT == 'show' || $ACT == 'search'){
485                if($INFO['writable']){
486                    if(!empty($INFO['draft'])){
487                        $out .= html_btn('draft',$ID,'e',array('do' => 'draft'),'post');
488                    }else{
489                        if($INFO['exists']){
490                            $out .= html_btn('edit',$ID,'e',array('do' => 'edit','rev' => $REV),'post');
491                        }else{
492                            $out .= html_btn('create',$ID,'e',array('do' => 'edit','rev' => $REV),'post');
493                        }
494                    }
495                }else{
496                    if(!actionOK('source')) return false; //pseudo action
497                    $out .= html_btn('source',$ID,'v',array('do' => 'edit','rev' => $REV),'post');
498                }
499            }else{
500                $out .= html_btn('show',$ID,'v',array('do' => 'show'));
501            }
502            break;
503        case 'history':
504            if(actionOK('revisions'))
505                $out .= html_btn('revs',$ID,'o',array('do' => 'revisions'));
506            break;
507        case 'recent':
508            if(actionOK('recent'))
509                $out .= html_btn('recent',$ID,'r',array('do' => 'recent'));
510            break;
511        case 'index':
512            if(actionOK('index'))
513                $out .= html_btn('index',$ID,'x',array('do' => 'index'));
514            break;
515        case 'back':
516            if ($parent = tpl_getparent($ID)) {
517                $out .= html_btn('back',$parent,'b',array('do' => 'show'));
518            }
519            break;
520        case 'top':
521            $out .= html_topbtn();
522            break;
523        case 'login':
524            if($conf['useacl'] && $auth){
525                if(isset($_SERVER['REMOTE_USER'])){
526                    $out .= html_btn('logout',$ID,'',array('do' => 'logout', 'sectok' => getSecurityToken()));
527                }else{
528                    $out .= html_btn('login',$ID,'',array('do' => 'login', 'sectok' => getSecurityToken()));
529                }
530            }
531            break;
532        case 'admin':
533            if($INFO['ismanager']){
534                $out .= html_btn('admin',$ID,'',array('do' => 'admin'));
535            }
536            break;
537        case 'revert':
538            if($INFO['ismanager'] && $REV && $INFO['writable'] && actionOK('revert')){
539                $out .= html_btn('revert',$ID,'',array('do' => 'revert', 'rev' => $REV, 'sectok' => getSecurityToken()));
540            }
541            break;
542        case 'subscribe':
543            if ($conf['useacl'] && $auth && $ACT == 'show' &&
544                    $conf['subscribers'] && isset($_SERVER['REMOTE_USER']) &&
545                    actionOK('subscribe')) {
546                $out .= html_btn('subscribe',$ID,'',array('do' => 'subscribe',));
547            }
548            break;
549        case 'backlink':
550            if(actionOK('backlink'))
551                $out .= html_btn('backlink',$ID,'',array('do' => 'backlink'));
552            break;
553        case 'profile':
554            if($conf['useacl'] && isset($_SERVER['REMOTE_USER']) && $auth &&
555                    $auth->canDo('Profile') && ($ACT!='profile')){
556                $out .= html_btn('profile',$ID,'',array('do' => 'profile'));
557            }
558            break;
559        default:
560            $out .= '[unknown button type]';
561            break;
562    }
563    if ($return) return $out;
564    print $out;
565    return $out ? true : false;
566}
567
568/**
569 * Like the action buttons but links
570 *
571 * Available links are
572 *
573 *  edit    - edit/create/show link
574 *  history - old revisions
575 *  recent  - recent changes
576 *  login   - login/logout link - if ACL enabled
577 *  profile - user profile link (if logged in)
578 *  index   - The index
579 *  admin   - admin page - if enough rights
580 *  top     - a back to top link
581 *  back    - a back to parent link - if available
582 *  backlink - links to the list of backlinks
583 *  subscribe/subscription - subscribe/unsubscribe link
584 *
585 * @author Andreas Gohr <andi@splitbrain.org>
586 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
587 * @see    tpl_button
588 */
589function tpl_actionlink($type,$pre='',$suf='',$inner='',$return=false){
590    global $ID;
591    global $INFO;
592    global $REV;
593    global $ACT;
594    global $conf;
595    global $lang;
596    global $auth;
597
598    // check disabled actions and fix the badly named ones
599    $ctype = $type;
600    if($type == 'history') $ctype='revisions';
601    if(!actionOK($ctype)) return false;
602
603    $out = '';
604    switch($type){
605        case 'edit':
606            // most complicated type - we need to decide on current action
607            if($ACT == 'show' || $ACT == 'search'){
608                if($INFO['writable']){
609                    if(!empty($INFO['draft'])) {
610                        $out .= tpl_link(wl($ID,'do=draft'),
611                                $pre.(($inner)?$inner:$lang['btn_draft']).$suf,
612                                'class="action edit" accesskey="e" rel="nofollow"',1);
613                    } else {
614                        if($INFO['exists']){
615                            $out .= tpl_link(wl($ID,'do=edit&amp;rev='.$REV),
616                                    $pre.(($inner)?$inner:$lang['btn_edit']).$suf,
617                                    'class="action edit" accesskey="e" rel="nofollow"',1);
618                        }else{
619                            $out .= tpl_link(wl($ID,'do=edit&amp;rev='.$REV),
620                                    $pre.(($inner)?$inner:$lang['btn_create']).$suf,
621                                    'class="action create" accesskey="e" rel="nofollow"',1);
622                        }
623                    }
624                }else{
625                    if(actionOK('source')) //pseudo action
626                        $out .= tpl_link(wl($ID,'do=edit&amp;rev='.$REV),
627                                $pre.(($inner)?$inner:$lang['btn_source']).$suf,
628                                'class="action source" accesskey="v" rel="nofollow"',1);
629                }
630            }else{
631                $out .= tpl_link(wl($ID,'do=show'),
632                        $pre.(($inner)?$inner:$lang['btn_show']).$suf,
633                        'class="action show" accesskey="v" rel="nofollow"',1);
634            }
635            break;
636        case 'history':
637            if(actionOK('revisions'))
638                $out .= tpl_link(wl($ID,'do=revisions'),
639                        $pre.(($inner)?$inner:$lang['btn_revs']).$suf,
640                        'class="action revisions" accesskey="o" rel="nofollow"',1);
641            break;
642        case 'recent':
643            if(actionOK('recent'))
644                $out .= tpl_link(wl($ID,'do=recent'),
645                        $pre.(($inner)?$inner:$lang['btn_recent']).$suf,
646                        'class="action recent" accesskey="r" rel="nofollow"',1);
647            break;
648        case 'index':
649            if(actionOK('index'))
650                $out .= tpl_link(wl($ID,'do=index'),
651                        $pre.(($inner)?$inner:$lang['btn_index']).$suf,
652                        'class="action index" accesskey="x" rel="nofollow"',1);
653            break;
654        case 'top':
655            $out .= '<a href="#dokuwiki__top" class="action top" accesskey="x">'.
656                $pre.(($inner)?$inner:$lang['btn_top']).$suf.'</a>';
657            break;
658        case 'back':
659            if ($parent = tpl_getparent($ID)) {
660                $out .= tpl_link(wl($parent,'do=show'),
661                        $pre.(($inner)?$inner:$lang['btn_back']).$suf,
662                        'class="action back" accesskey="b" rel="nofollow"',1);
663            }
664            break;
665        case 'login':
666            if($conf['useacl'] && $auth){
667                if($_SERVER['REMOTE_USER']){
668                    $out .= tpl_link(wl($ID,'do=logout&amp;sectok='.getSecurityToken()),
669                            $pre.(($inner)?$inner:$lang['btn_logout']).$suf,
670                            'class="action logout" rel="nofollow"',1);
671                }else{
672                    $out .= tpl_link(wl($ID,'do=login&amp;sectok='.getSecurityToken()),
673                            $pre.(($inner)?$inner:$lang['btn_login']).$suf,
674                            'class="action login" rel="nofollow"',1);
675                }
676            }
677            break;
678        case 'admin':
679            if($INFO['ismanager']){
680                $out .= tpl_link(wl($ID,'do=admin'),
681                        $pre.(($inner)?$inner:$lang['btn_admin']).$suf,
682                        'class="action admin" rel="nofollow"',1);
683            }
684            break;
685        case 'revert':
686            if($INFO['ismanager'] && $REV && $INFO['writable'] && actionOK('revert')){
687                $out .= tpl_link(wl($ID,array('do' => 'revert', 'rev' => $REV, 'sectok' => getSecurityToken())),
688                        $pre.(($inner)?$inner:$lang['btn_revert']).$suf,
689                        'class="action revert" rel="nofollow"',1);
690            }
691            break;
692        case 'subscribe':
693        case 'subscription':
694            if($conf['useacl'] && $auth && $ACT == 'show' && $conf['subscribers']) {
695                if($_SERVER['REMOTE_USER']){
696                    if(actionOK('subscribe'))
697                        $out .= tpl_link(wl($ID,'do=subscribe'),
698                                $pre.(($inner)?$inner:$lang['btn_subscribe']).$suf,
699                                'class="action subscribe" rel="nofollow"',1);
700                }
701            }
702            break;
703        case 'backlink':
704            if(actionOK('backlink'))
705                $out .= tpl_link(wl($ID,'do=backlink'),
706                        $pre.(($inner)?$inner:$lang['btn_backlink']).$suf,
707                        'class="action backlink" rel="nofollow"',1);
708            break;
709        case 'profile':
710            if($conf['useacl'] && $auth && $_SERVER['REMOTE_USER'] &&
711                    $auth->canDo('Profile') && ($ACT!='profile')){
712                $out .= tpl_link(wl($ID,'do=profile'),
713                        $pre.(($inner)?$inner:$lang['btn_profile']).$suf,
714                        'class="action profile" rel="nofollow"',1);
715            }
716            break;
717        default:
718            $out .= '[unknown link type]';
719            break;
720    }
721    if ($return) return $out;
722    print $out;
723    return $out ? true : false;
724}
725
726/**
727 * Wrapper around tpl_button() and tpl_actionlink()
728 *
729 * @author Anika Henke <anika@selfthinker.org>
730 */
731function tpl_action($type,$link=0,$wrapper=false,$return=false,$pre='',$suf='',$inner='') {
732    $out = '';
733    if ($link) $out .= tpl_actionlink($type,$pre,$suf,$inner,1);
734    else $out .= tpl_button($type,1);
735    if ($out && $wrapper) $out = "<$wrapper>$out</$wrapper>";
736
737    if ($return) return $out;
738    print $out;
739    return $out ? true : false;
740}
741
742/**
743 * Print the search form
744 *
745 * If the first parameter is given a div with the ID 'qsearch_out' will
746 * be added which instructs the ajax pagequicksearch to kick in and place
747 * its output into this div. The second parameter controls the propritary
748 * attribute autocomplete. If set to false this attribute will be set with an
749 * value of "off" to instruct the browser to disable it's own built in
750 * autocompletion feature (MSIE and Firefox)
751 *
752 * @author Andreas Gohr <andi@splitbrain.org>
753 */
754function tpl_searchform($ajax=true,$autocomplete=true){
755    global $lang;
756    global $ACT;
757    global $QUERY;
758
759    // don't print the search form if search action has been disabled
760    if (!actionOk('search')) return false;
761
762    print '<form action="'.wl().'" accept-charset="utf-8" class="search" id="dw__search"><div class="no">';
763    print '<input type="hidden" name="do" value="search" />';
764    print '<input type="text" ';
765    if($ACT == 'search') print 'value="'.htmlspecialchars($QUERY).'" ';
766    if(!$autocomplete) print 'autocomplete="off" ';
767    print 'id="qsearch__in" accesskey="f" name="id" class="edit" title="[F]" />';
768    print '<input type="submit" value="'.$lang['btn_search'].'" class="button" title="'.$lang['btn_search'].'" />';
769    if($ajax) print '<div id="qsearch__out" class="ajax_qsearch JSpopup"></div>';
770    print '</div></form>';
771    return true;
772}
773
774/**
775 * Print the breadcrumbs trace
776 *
777 * @author Andreas Gohr <andi@splitbrain.org>
778 */
779function tpl_breadcrumbs($sep='&raquo;'){
780    global $lang;
781    global $conf;
782
783    //check if enabled
784    if(!$conf['breadcrumbs']) return false;
785
786    $crumbs = breadcrumbs(); //setup crumb trace
787
788    //reverse crumborder in right-to-left mode, add RLM character to fix heb/eng display mixups
789    if($lang['direction'] == 'rtl') {
790        $crumbs = array_reverse($crumbs,true);
791        $crumbs_sep = ' &#8207;<span class="bcsep">'.$sep.'</span>&#8207; ';
792    } else {
793        $crumbs_sep = ' <span class="bcsep">'.$sep.'</span> ';
794    }
795
796    //render crumbs, highlight the last one
797    print '<span class="bchead">'.$lang['breadcrumb'].':</span>';
798    $last = count($crumbs);
799    $i = 0;
800    foreach ($crumbs as $id => $name){
801        $i++;
802        echo $crumbs_sep;
803        if ($i == $last) print '<span class="curid">';
804        tpl_link(wl($id),hsc($name),'class="breadcrumbs" title="'.$id.'"');
805        if ($i == $last) print '</span>';
806    }
807    return true;
808}
809
810/**
811 * Hierarchical breadcrumbs
812 *
813 * This code was suggested as replacement for the usual breadcrumbs.
814 * It only makes sense with a deep site structure.
815 *
816 * @author Andreas Gohr <andi@splitbrain.org>
817 * @author Nigel McNie <oracle.shinoda@gmail.com>
818 * @author Sean Coates <sean@caedmon.net>
819 * @author <fredrik@averpil.com>
820 * @todo   May behave strangely in RTL languages
821 */
822function tpl_youarehere($sep=' &raquo; '){
823    global $conf;
824    global $ID;
825    global $lang;
826
827    // check if enabled
828    if(!$conf['youarehere']) return false;
829
830    $parts = explode(':', $ID);
831    $count = count($parts);
832
833    if($GLOBALS['ACT'] == 'search')
834    {
835        $parts = array($conf['start']);
836        $count = 1;
837    }
838
839    echo '<span class="bchead">'.$lang['youarehere'].': </span>';
840
841    // always print the startpage
842    $title = useHeading('navigation') ? p_get_first_heading($conf['start']) : $conf['start'];
843    if(!$title) $title = $conf['start'];
844    tpl_link(wl($conf['start']),hsc($title),'title="'.$conf['start'].'"');
845
846    // print intermediate namespace links
847    $part = '';
848    for($i=0; $i<$count - 1; $i++){
849        $part .= $parts[$i].':';
850        $page = $part;
851        resolve_pageid('',$page,$exists);
852        if ($page == $conf['start']) continue; // Skip startpage
853
854        // output
855        echo $sep;
856        if($exists){
857            $title = useHeading('navigation') ? p_get_first_heading($page) : $parts[$i];
858            tpl_link(wl($page),hsc($title),'title="'.$page.'"');
859        }else{
860            tpl_link(wl($page),$parts[$i],'title="'.$page.'" class="wikilink2" rel="nofollow"');
861        }
862    }
863
864    // print current page, skipping start page, skipping for namespace index
865    if(isset($page) && $page==$part.$parts[$i]) return;
866    $page = $part.$parts[$i];
867    if($page == $conf['start']) return;
868    echo $sep;
869    if(page_exists($page)){
870        $title = useHeading('navigation') ? p_get_first_heading($page) : $parts[$i];
871        tpl_link(wl($page),hsc($title),'title="'.$page.'"');
872    }else{
873        tpl_link(wl($page),$parts[$i],'title="'.$page.'" class="wikilink2" rel="nofollow"');
874    }
875    return true;
876}
877
878/**
879 * Print info if the user is logged in
880 * and show full name in that case
881 *
882 * Could be enhanced with a profile link in future?
883 *
884 * @author Andreas Gohr <andi@splitbrain.org>
885 */
886function tpl_userinfo(){
887    global $lang;
888    global $INFO;
889    if(isset($_SERVER['REMOTE_USER'])){
890        print $lang['loggedinas'].': '.$INFO['userinfo']['name'].' ('.$_SERVER['REMOTE_USER'].')';
891                return true;
892                }
893                return false;
894                }
895
896                /**
897                 * Print some info about the current page
898                 *
899                 * @author Andreas Gohr <andi@splitbrain.org>
900                 */
901                function tpl_pageinfo($ret=false){
902                global $conf;
903                global $lang;
904                global $INFO;
905                global $ID;
906
907                // return if we are not allowed to view the page
908                if (!auth_quickaclcheck($ID)) { return false; }
909
910                // prepare date and path
911                $fn = $INFO['filepath'];
912                if(!$conf['fullpath']){
913                    if($INFO['rev']){
914                        $fn = str_replace(fullpath($conf['olddir']).'/','',$fn);
915                    }else{
916                        $fn = str_replace(fullpath($conf['datadir']).'/','',$fn);
917                    }
918                }
919                $fn = utf8_decodeFN($fn);
920                $date = dformat($INFO['lastmod']);
921
922                // print it
923                if($INFO['exists']){
924                    $out = '';
925                    $out .= $fn;
926                    $out .= ' &middot; ';
927                    $out .= $lang['lastmod'];
928                    $out .= ': ';
929                    $out .= $date;
930                    if($INFO['editor']){
931                        $out .= ' '.$lang['by'].' ';
932                        $out .= editorinfo($INFO['editor']);
933                    }else{
934                        $out .= ' ('.$lang['external_edit'].')';
935                                }
936                                if($INFO['locked']){
937                                $out .= ' &middot; ';
938                                $out .= $lang['lockedby'];
939                                $out .= ': ';
940                                $out .= editorinfo($INFO['locked']);
941                                }
942                                if($ret){
943                                return $out;
944                                }else{
945                                echo $out;
946                                return true;
947                                }
948                                }
949                                return false;
950                                }
951
952                                /**
953                                 * Prints or returns the name of the given page (current one if none given).
954                                 *
955                                 * If useheading is enabled this will use the first headline else
956                                 * the given ID is used.
957                                 *
958                                 * @author Andreas Gohr <andi@splitbrain.org>
959                                 */
960function tpl_pagetitle($id=null, $ret=false){
961    global $conf;
962    if(is_null($id)){
963        global $ID;
964        $id = $ID;
965    }
966
967    $name = $id;
968    if (useHeading('navigation')) {
969        $title = p_get_first_heading($id);
970        if ($title) $name = $title;
971    }
972
973    if ($ret) {
974        return hsc($name);
975    } else {
976        print hsc($name);
977        return true;
978    }
979}
980
981/**
982 * Returns the requested EXIF/IPTC tag from the current image
983 *
984 * If $tags is an array all given tags are tried until a
985 * value is found. If no value is found $alt is returned.
986 *
987 * Which texts are known is defined in the functions _exifTagNames
988 * and _iptcTagNames() in inc/jpeg.php (You need to prepend IPTC
989 * to the names of the latter one)
990 *
991 * Only allowed in: detail.php
992 *
993 * @author Andreas Gohr <andi@splitbrain.org>
994 */
995function tpl_img_getTag($tags,$alt='',$src=null){
996    // Init Exif Reader
997    global $SRC;
998
999    if(is_null($src)) $src = $SRC;
1000
1001    static $meta = null;
1002    if(is_null($meta)) $meta = new JpegMeta($src);
1003    if($meta === false) return $alt;
1004    $info = $meta->getField($tags);
1005    if($info == false) return $alt;
1006    return $info;
1007}
1008
1009/**
1010 * Prints the image with a link to the full sized version
1011 *
1012 * Only allowed in: detail.php
1013 */
1014function tpl_img($maxwidth=0,$maxheight=0){
1015    global $IMG;
1016    $w = tpl_img_getTag('File.Width');
1017    $h = tpl_img_getTag('File.Height');
1018
1019    //resize to given max values
1020    $ratio = 1;
1021    if($w >= $h){
1022        if($maxwidth && $w >= $maxwidth){
1023            $ratio = $maxwidth/$w;
1024        }elseif($maxheight && $h > $maxheight){
1025            $ratio = $maxheight/$h;
1026        }
1027    }else{
1028        if($maxheight && $h >= $maxheight){
1029            $ratio = $maxheight/$h;
1030        }elseif($maxwidth && $w > $maxwidth){
1031            $ratio = $maxwidth/$w;
1032        }
1033    }
1034    if($ratio){
1035        $w = floor($ratio*$w);
1036        $h = floor($ratio*$h);
1037    }
1038
1039    //prepare URLs
1040    $url=ml($IMG,array('cache'=>$_REQUEST['cache']));
1041    $src=ml($IMG,array('cache'=>$_REQUEST['cache'],'w'=>$w,'h'=>$h));
1042
1043    //prepare attributes
1044    $alt=tpl_img_getTag('Simple.Title');
1045    $p = array();
1046    if($w) $p['width']  = $w;
1047    if($h) $p['height'] = $h;
1048    $p['class']  = 'img_detail';
1049    if($alt){
1050        $p['alt']   = $alt;
1051        $p['title'] = $alt;
1052    }else{
1053        $p['alt'] = '';
1054    }
1055    $p = buildAttributes($p);
1056
1057    print '<a href="'.$url.'">';
1058    print '<img src="'.$src.'" '.$p.'/>';
1059    print '</a>';
1060    return true;
1061}
1062
1063/**
1064 * This function inserts a 1x1 pixel gif which in reality
1065 * is the indexer function.
1066 *
1067 * Should be called somewhere at the very end of the main.php
1068 * template
1069 */
1070function tpl_indexerWebBug(){
1071    global $ID;
1072    global $INFO;
1073    if(!$INFO['exists']) return false;
1074
1075    if(isHiddenPage($ID)) return false; //no need to index hidden pages
1076
1077    $p = array();
1078    $p['src']    = DOKU_BASE.'lib/exe/indexer.php?id='.rawurlencode($ID).
1079        '&'.time();
1080    $p['width']  = 1;
1081    $p['height'] = 1;
1082    $p['alt']    = '';
1083    $att = buildAttributes($p);
1084    print "<img $att />";
1085    return true;
1086}
1087
1088// configuration methods
1089/**
1090 * tpl_getConf($id)
1091 *
1092 * use this function to access template configuration variables
1093 */
1094function tpl_getConf($id){
1095    global $conf;
1096    global $tpl_configloaded;
1097
1098    $tpl = $conf['template'];
1099
1100    if (!$tpl_configloaded){
1101        $tconf = tpl_loadConfig();
1102        if ($tconf !== false){
1103            foreach ($tconf as $key => $value){
1104                if (isset($conf['tpl'][$tpl][$key])) continue;
1105                $conf['tpl'][$tpl][$key] = $value;
1106            }
1107            $tpl_configloaded = true;
1108        }
1109    }
1110
1111    return $conf['tpl'][$tpl][$id];
1112}
1113
1114/**
1115 * tpl_loadConfig()
1116 * reads all template configuration variables
1117 * this function is automatically called by tpl_getConf()
1118 */
1119function tpl_loadConfig(){
1120
1121    $file = DOKU_TPLINC.'/conf/default.php';
1122    $conf = array();
1123
1124    if (!@file_exists($file)) return false;
1125
1126    // load default config file
1127    include($file);
1128
1129    return $conf;
1130}
1131
1132/**
1133 * prints the "main content" in the mediamanger popup
1134 *
1135 * Depending on the user's actions this may be a list of
1136 * files in a namespace, the meta editing dialog or
1137 * a message of referencing pages
1138 *
1139 * Only allowed in mediamanager.php
1140 *
1141 * @triggers MEDIAMANAGER_CONTENT_OUTPUT
1142 * @param bool $fromajax - set true when calling this function via ajax
1143 * @author Andreas Gohr <andi@splitbrain.org>
1144 */
1145function tpl_mediaContent($fromajax=false){
1146    global $IMG;
1147    global $AUTH;
1148    global $INUSE;
1149    global $NS;
1150    global $JUMPTO;
1151
1152    if(is_array($_REQUEST['do'])){
1153        $do = array_shift(array_keys($_REQUEST['do']));
1154    }else{
1155        $do = $_REQUEST['do'];
1156    }
1157    if(in_array($do,array('save','cancel'))) $do = '';
1158
1159    if(!$do){
1160        if($_REQUEST['edit']){
1161            $do = 'metaform';
1162        }elseif(is_array($INUSE)){
1163            $do = 'filesinuse';
1164        }else{
1165            $do = 'filelist';
1166        }
1167    }
1168
1169    // output the content pane, wrapped in an event.
1170    if(!$fromajax) ptln('<div id="media__content">');
1171    $data = array( 'do' => $do);
1172    $evt = new Doku_Event('MEDIAMANAGER_CONTENT_OUTPUT', $data);
1173    if ($evt->advise_before()) {
1174        $do = $data['do'];
1175        if($do == 'metaform'){
1176            media_metaform($IMG,$AUTH);
1177        }elseif($do == 'filesinuse'){
1178            media_filesinuse($INUSE,$IMG);
1179        }elseif($do == 'filelist'){
1180            media_filelist($NS,$AUTH,$JUMPTO);
1181        }elseif($do == 'searchlist'){
1182            media_searchlist($_REQUEST['q'],$NS,$AUTH);
1183        }else{
1184            msg('Unknown action '.hsc($do),-1);
1185        }
1186    }
1187    $evt->advise_after();
1188    unset($evt);
1189    if(!$fromajax) ptln('</div>');
1190
1191}
1192
1193/**
1194 * prints the namespace tree in the mediamanger popup
1195 *
1196 * Only allowed in mediamanager.php
1197 *
1198 * @author Andreas Gohr <andi@splitbrain.org>
1199 */
1200function tpl_mediaTree(){
1201    global $NS;
1202
1203    ptln('<div id="media__tree">');
1204    media_nstree($NS);
1205    ptln('</div>');
1206}
1207
1208
1209/**
1210 * Print a dropdown menu with all DokuWiki actions
1211 *
1212 * Note: this will not use any pretty URLs
1213 *
1214 * @author Andreas Gohr <andi@splitbrain.org>
1215 */
1216function tpl_actiondropdown($empty='',$button='&gt;'){
1217    global $ID;
1218    global $INFO;
1219    global $REV;
1220    global $ACT;
1221    global $conf;
1222    global $lang;
1223    global $auth;
1224
1225    echo '<form method="post" accept-charset="utf-8">'; #FIXME action
1226        echo '<input type="hidden" name="id" value="'.$ID.'" />';
1227    if($REV) echo '<input type="hidden" name="rev" value="'.$REV.'" />';
1228    echo '<input type="hidden" name="sectok" value="'.getSecurityToken().'" />';
1229
1230    echo '<select name="do" id="action__selector" class="edit">';
1231    echo '<option value="">'.$empty.'</option>';
1232
1233    echo '<optgroup label=" &mdash; ">';
1234    // 'edit' - most complicated type, we need to decide on current action
1235    if($ACT == 'show' || $ACT == 'search'){
1236        if($INFO['writable']){
1237            if(!empty($INFO['draft'])) {
1238                echo '<option value="edit">'.$lang['btn_draft'].'</option>';
1239            } else {
1240                if($INFO['exists']){
1241                    echo '<option value="edit">'.$lang['btn_edit'].'</option>';
1242                }else{
1243                    echo '<option value="edit">'.$lang['btn_create'].'</option>';
1244                }
1245            }
1246        }else if(actionOK('source')) { //pseudo action
1247            echo '<option value="edit">'.$lang['btn_source'].'</option>';
1248        }
1249    }else{
1250        echo '<option value="show">'.$lang['btn_show'].'</option>';
1251    }
1252
1253    echo '<option value="revisions">'.$lang['btn_revs'].'</option>';
1254    if($INFO['ismanager'] && $REV && $INFO['writable'] && actionOK('revert')){
1255        echo '<option value="revert">'.$lang['btn_revert'].'</option>';
1256    }
1257    echo '<option value="backlink">'.$lang['btn_backlink'].'</option>';
1258    echo '</optgroup>';
1259
1260    echo '<optgroup label=" &mdash; ">';
1261    echo '<option value="recent">'.$lang['btn_recent'].'</option>';
1262    echo '<option value="index">'.$lang['btn_index'].'</option>';
1263    echo '</optgroup>';
1264
1265    echo '<optgroup label=" &mdash; ">';
1266    if($conf['useacl'] && $auth){
1267        if($_SERVER['REMOTE_USER']){
1268            echo '<option value="logout">'.$lang['btn_logout'].'</option>';
1269        }else{
1270            echo '<option value="login">'.$lang['btn_login'].'</option>';
1271        }
1272    }
1273
1274    if($conf['useacl'] && $auth && $_SERVER['REMOTE_USER'] &&
1275            $auth->canDo('Profile') && ($ACT!='profile')){
1276        echo '<option value="profile">'.$lang['btn_profile'].'</option>';
1277    }
1278
1279    if($conf['useacl'] && $auth && $ACT == 'show' && $conf['subscribers']){
1280        if($_SERVER['REMOTE_USER']){
1281            echo '<option value="subscribe">'.$lang['btn_subscribe'].'</option>';
1282        }
1283    }
1284
1285    if($INFO['ismanager']){
1286        echo '<option value="admin">'.$lang['btn_admin'].'</option>';
1287    }
1288    echo '</optgroup>';
1289
1290    echo '</select>';
1291    echo '<input type="submit" value="'.$button.'" id="action__selectorbtn" />';
1292    echo '</form>';
1293}
1294
1295/**
1296 * Print a informational line about the used license
1297 *
1298 * @author Andreas Gohr <andi@splitbrain.org>
1299 * @param  string $img    - print image? (|button|badge)
1300 * @param  bool   $return - when true don't print, but return HTML
1301 */
1302function tpl_license($img='badge',$imgonly=false,$return=false){
1303    global $license;
1304    global $conf;
1305    global $lang;
1306    if(!$conf['license']) return '';
1307    if(!is_array($license[$conf['license']])) return '';
1308    $lic = $license[$conf['license']];
1309
1310    $out  = '<div class="license">';
1311    if($img){
1312        $src = license_img($img);
1313        if($src){
1314            $out .= '<a href="'.$lic['url'].'" rel="license"';
1315            if($conf['target']['external']) $out .= ' target="'.$conf['target']['external'].'"';
1316            $out .= '><img src="'.DOKU_BASE.$src.'" class="medialeft lic'.$img.'" alt="'.$lic['name'].'" /></a> ';
1317        }
1318    }
1319    if(!$imgonly) {
1320        $out .= $lang['license'];
1321        $out .= '<a href="'.$lic['url'].'" rel="license" class="urlextern"';
1322        if(isset($conf['target']['external'])) $out .= ' target="'.$conf['target']['external'].'"';
1323        $out .= '>'.$lic['name'].'</a>';
1324    }
1325    $out .= '</div>';
1326
1327    if($return) return $out;
1328    echo $out;
1329}
1330
1331
1332/**
1333 * Includes the rendered XHTML of a given page
1334 *
1335 * This function is useful to populate sidebars or similar features in a
1336 * template
1337 */
1338function tpl_include_page($pageid,$print=true){
1339    global $ID;
1340    $oldid = $ID;
1341    $html = p_wiki_xhtml($pageid,'',false);
1342    $ID = $oldid;
1343
1344    if(!$print) return $html;
1345    echo $html;
1346}
1347
1348/**
1349 * Display the subscribe form
1350 *
1351 * @author Adrian Lang <lang@cosmocode.de>
1352 */
1353function tpl_subscribe() {
1354    global $INFO;
1355    global $ID;
1356    global $lang;
1357
1358    echo p_locale_xhtml('subscr_form');
1359    echo '<h2>' . $lang['subscr_m_current_header'] . '</h2>';
1360    echo '<div class="level2">';
1361    if ($INFO['subscribed'] === false) {
1362        echo '<p>' . $lang['subscr_m_not_subscribed'] . '</p>';
1363    } else {
1364        echo '<ul>';
1365        foreach($INFO['subscribed'] as $sub) {
1366            echo '<li><div class="li">';
1367            if ($sub['target'] !== $ID) {
1368                echo '<code class="ns">'.hsc(prettyprint_id($sub['target'])).'</code>';
1369            } else {
1370                echo '<code class="page">'.hsc(prettyprint_id($sub['target'])).'</code>';
1371            }
1372            $sstl = $lang['subscr_style_'.$sub['style']];
1373            if(!$sstl) $sstl = hsc($sub['style']);
1374            echo ' ('.$sstl.') ';
1375
1376            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>';
1377
1378            echo '</div></li>';
1379        }
1380        echo '</ul>';
1381    }
1382    echo '</div>';
1383
1384    // Add new subscription form
1385    echo '<h2>' . $lang['subscr_m_new_header'] . '</h2>';
1386    echo '<div class="level2">';
1387    $ns = getNS($ID).':';
1388    $targets = array(
1389            $ID => '<code class="page">'.prettyprint_id($ID).'</code>',
1390            $ns => '<code class="ns">'.prettyprint_id($ns).'</code>',
1391            );
1392    $styles = array(
1393            'every'  => $lang['subscr_style_every'],
1394            'digest' => $lang['subscr_style_digest'],
1395            'list'   => $lang['subscr_style_list'],
1396            );
1397
1398    $form = new Doku_Form(array('id' => 'subscribe__form'));
1399    $form->startFieldset($lang['subscr_m_subscribe']);
1400    $form->addRadioSet('sub_target', $targets);
1401    $form->startFieldset($lang['subscr_m_receive']);
1402    $form->addRadioSet('sub_style', $styles);
1403    $form->addHidden('sub_action', 'subscribe');
1404    $form->addHidden('do', 'subscribe');
1405    $form->addHidden('id', $ID);
1406    $form->endFieldset();
1407    $form->addElement(form_makeButton('submit', 'subscribe', $lang['subscr_m_subscribe']));
1408    html_form('SUBSCRIBE', $form);
1409    echo '</div>';
1410}
1411
1412//Setup VIM: ex: et ts=4 enc=utf-8 :
1413
1414