PageRenderTime 61ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/inc/template.php

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