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