Newer
Older
<?php
/**
* DokuWiki template functions
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Andreas Gohr <andi@splitbrain.org>
*/
* Access a template file
*
* Returns the path to the given file inside the current template, uses
* default template if the custom version doesn't exist.
*
* @author Andreas Gohr <andi@splitbrain.org>
if(@is_readable(DOKU_INC.'lib/tpl/'.$conf['template'].'/'.$file))
return DOKU_INC.'lib/tpl/'.$conf['template'].'/'.$file;
/**
* Convenience function to access template dir from local FS
*
* This replaces the deprecated DOKU_TPLINC constant
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param string $tpl The template to use, default to current one
function tpl_incdir($tpl='') {
if(!$tpl) $tpl = $conf['template'];
return DOKU_INC.'lib/tpl/'.$tpl.'/';
}
/**
* Convenience function to access template dir from web
*
* This replaces the deprecated DOKU_TPL constant
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param string $tpl The template to use, default to current one
if(!$tpl) $tpl = $conf['template'];
return DOKU_BASE.'lib/tpl/'.$tpl.'/';
/**
* Print the content
*
* This function is used for printing all the usual content
* (defined by the global $ACT var) by calling the appropriate
* outputfunction(s) from html.php
*
* Everything that doesn't use the main template file isn't
* handled by this function. ACL stuff is not done here either.
*
* @author Andreas Gohr <andi@splitbrain.org>
* @triggers TPL_ACT_RENDER
* @triggers TPL_CONTENT_DISPLAY
* @param bool $prependTOC should the TOC be displayed here?
* @return bool true if any output
global $ACT;
global $INFO;
$INFO['prependTOC'] = $prependTOC;
trigger_event('TPL_ACT_RENDER', $ACT, 'tpl_content_core');
trigger_event('TPL_CONTENT_DISPLAY', $html_output, 'ptln');
/**
* Default Action of TPL_ACT_RENDER
*
* @return bool
*/
function tpl_content_core() {
global $ACT;
global $TEXT;
global $PRE;
global $SUF;
global $SUM;
global $IDX;
case 'show':
html_show();
break;
/** @noinspection PhpMissingBreakStatementInspection */
case 'locked':
html_locked();
case 'edit':
break;
case 'draft':
html_draft();
break;
case 'search':
html_search();
break;
case 'revisions':
break;
case 'diff':
html_diff();
break;
case 'recent':
$show_changes = $INPUT->str('show_changes');
if (empty($show_changes)) {
$show_changes = get_doku_pref('show_changes', $show_changes);
}
html_recent($INPUT->extract('first')->int('first'), $show_changes);
break;
case 'index':
html_index($IDX); #FIXME can this be pulled from globals? is it sanitized correctly?
case 'backlink':
html_backlinks();
break;
case 'conflict':
html_conflict(con($PRE, $TEXT, $SUF), $SUM);
html_diff(con($PRE, $TEXT, $SUF), false);
break;
case 'login':
html_login();
break;
case 'register':
html_register();
break;
case 'resendpwd':
html_resendpwd();
break;
case 'denied':
break;
case 'profile' :
html_updateprofile();
break;
case 'admin':
tpl_admin();
break;
case 'subscribe':
tpl_subscribe();
break;
$evt = new Doku_Event('TPL_ACT_UNKNOWN', $ACT);
msg("Failed to handle command: ".hsc($ACT), -1);
$evt->advise_after();
unset($evt);
return false;
}
return true;
/**
* Places the TOC where the function is called
*
* If you use this you most probably want to call tpl_content with
* a false argument
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param bool $return Should the TOC be returned instead to be printed?
* @return string
global $TOC;
global $ACT;
global $ID;
global $REV;
global $INFO;
Anika Henke
committed
global $conf;
// if a TOC was prepared in global scope, always use it
$toc = $TOC;
} elseif(($ACT == 'show' || substr($ACT, 0, 6) == 'export') && !$REV && $INFO['exists']) {
$meta = p_get_metadata($ID, '', METADATA_RENDER_USING_CACHE);
$toc = isset($meta['description']['tableofcontents']) ? $meta['description']['tableofcontents'] : null;
if(!$tocok || !is_array($toc) || !$conf['tocminheads'] || count($toc) < $conf['tocminheads']) {
// try to load admin plugin TOC
/** @var $plugin DokuWiki_Admin_Plugin */
if ($plugin = plugin_getRequestAdminPlugin()) {
$toc = $plugin->getTOC();
$TOC = $toc; // avoid later rebuild
}
}
trigger_event('TPL_TOC_RENDER', $toc, null, false);
$html = html_TOC($toc);
if($return) return $html;
echo $html;
/**
* Handle the admin page contents
*
* @author Andreas Gohr <andi@splitbrain.org>
$class = $INPUT->str('page');
if(!empty($class)) {
// attempt to load the plugin
$plugin = plugin_load('admin', $class);
if(!is_array($TOC)) $TOC = $plugin->getTOC(); //if TOC wasn't requested yet
if($INFO['prependTOC']) tpl_toc();
$plugin->html();
/**
* Print the correct HTML meta headers
*
* This has to go into the head section of your template.
*
* @author Andreas Gohr <andi@splitbrain.org>
* @triggers TPL_METAHEADER_OUTPUT
* @param bool $alt Should feeds and alternative format links be added?
* @return bool
global $ID;
global $REV;
global $INFO;
global $JSINFO;
global $ACT;
global $QUERY;
global $lang;
global $conf;
global $updateVersion;
/** @var Input $INPUT */
global $INPUT;
// prepare the head array
$head = array();
// prepare seed for js and css
$tseed = $updateVersion;
$depends = getConfigFiles('main');
$depends[] = DOKU_CONF."tpl/".$conf['template']."/style.ini";
foreach($depends as $f) $tseed .= @filemtime($f);
$tseed = md5($tseed);
$head['meta'][] = array('name'=> 'generator', 'content'=> 'DokuWiki');
if(actionOK('search')) {
$head['link'][] = array(
'rel' => 'search', 'type'=> 'application/opensearchdescription+xml',
'href'=> DOKU_BASE.'lib/exe/opensearch.php', 'title'=> $conf['title']
);
}
$head['link'][] = array('rel'=> 'start', 'href'=> DOKU_BASE);
if(actionOK('index')) {
$head['link'][] = array(
'rel' => 'contents', 'href'=> wl($ID, 'do=index', false, '&'),
'title'=> $lang['btn_index']
);
if(actionOK('rss')) {
$head['link'][] = array(
'rel' => 'alternate', 'type'=> 'application/rss+xml',
'title'=> $lang['btn_recent'], 'href'=> DOKU_BASE.'feed.php'
);
$head['link'][] = array(
'rel' => 'alternate', 'type'=> 'application/rss+xml',
'title'=> $lang['currentns'],
'href' => DOKU_BASE.'feed.php?mode=list&ns='.$INFO['namespace']
);
}
if(($ACT == 'show' || $ACT == 'search') && $INFO['writable']) {
$head['link'][] = array(
'rel' => 'edit',
'title'=> $lang['btn_edit'],
'href' => wl($ID, 'do=edit', false, '&')
);
$head['link'][] = array(
'rel' => 'alternate', 'type'=> 'application/rss+xml',
'title'=> $lang['searchresult'],
'href' => DOKU_BASE.'feed.php?mode=search&q='.$QUERY
);
if(actionOK('export_xhtml')) {
$head['link'][] = array(
'rel' => 'alternate', 'type'=> 'text/html', 'title'=> $lang['plainhtml'],
'href'=> exportlink($ID, 'xhtml', '', false, '&')
);
if(actionOK('export_raw')) {
$head['link'][] = array(
'rel' => 'alternate', 'type'=> 'text/plain', 'title'=> $lang['wikimarkup'],
'href'=> exportlink($ID, 'raw', '', false, '&')
);
// setup robot tags apropriate for different modes
if(($ACT == 'show' || $ACT == 'export_xhtml') && !$REV) {
if($INFO['exists']) {
if((time() - $INFO['lastmod']) >= $conf['indexdelay']) {
$head['meta'][] = array('name'=> 'robots', 'content'=> 'index,follow');
} else {
$head['meta'][] = array('name'=> 'robots', 'content'=> 'noindex,nofollow');
$canonicalUrl = wl($ID, '', true, '&');
if ($ID == $conf['start']) {
$canonicalUrl = DOKU_URL;
}
$head['link'][] = array('rel'=> 'canonical', 'href'=> $canonicalUrl);
} else {
$head['meta'][] = array('name'=> 'robots', 'content'=> 'noindex,follow');
} elseif(defined('DOKU_MEDIADETAIL')) {
$head['meta'][] = array('name'=> 'robots', 'content'=> 'index,follow');
} else {
$head['meta'][] = array('name'=> 'robots', 'content'=> 'noindex,nofollow');
if($ACT == 'show' || $ACT == 'export_xhtml') {
// keywords (explicit or implicit)
if(!empty($INFO['meta']['subject'])) {
$head['meta'][] = array('name'=> 'keywords', 'content'=> join(',', $INFO['meta']['subject']));
} else {
$head['meta'][] = array('name'=> 'keywords', 'content'=> str_replace(':', ',', $ID));
$head['link'][] = array(
'rel' => 'stylesheet', 'type'=> 'text/css',
'href'=> DOKU_BASE.'lib/exe/css.php?t='.rawurlencode($conf['template']).'&tseed='.$tseed
// make $INFO and other vars available to JavaScripts
$script = "var NS='".$INFO['namespace']."';";
if($conf['useacl'] && $INPUT->server->str('REMOTE_USER')) {
$script .= "var SIG='".toolbar_signature()."';";
$script .= 'var JSINFO = '.$json->encode($JSINFO).';';
$head['script'][] = array('type'=> 'text/javascript', '_data'=> $script);
$jquery = getCdnUrls();
foreach($jquery as $src) {
$head['script'][] = array(
'type' => 'text/javascript', 'charset' => 'utf-8', '_data' => '', 'src' => $src
);
}
// load our javascript dispatcher
$head['script'][] = array(
'type'=> 'text/javascript', 'charset'=> 'utf-8', '_data'=> '',
'src' => DOKU_BASE.'lib/exe/js.php'.'?t='.rawurlencode($conf['template']).'&tseed='.$tseed
trigger_event('TPL_METAHEADER_OUTPUT', $head, '_tpl_metaheaders_action', true);
}
/**
* prints the array build by tpl_metaheaders
*
* $data is an array of different header tags. Each tag can have multiple
* instances. Attributes are given as key value pairs. Values will be HTML
* encoded automatically so they should be provided as is in the $data array.
*
* For tags having a body attribute specify the body data in the special
* attribute '_data'. This field will NOT BE ESCAPED automatically.
*
* @author Andreas Gohr <andi@splitbrain.org>
*
* @param array $data
function _tpl_metaheaders_action($data) {
foreach($data as $tag => $inst) {
if($tag == 'script') {
echo "<!--[if gte IE 9]><!-->\n"; // no scripts for old IE
}
echo '<', $tag, ' ', buildAttributes($attr);
if(isset($attr['_data']) || $tag == 'script') {
if($tag == 'script' && $attr['_data'])
$attr['_data'] = "/*<![CDATA[*/".
"\n/*!]]>*/";
echo '>', $attr['_data'], '</', $tag, '>';
} else {
echo '/>';
}
echo "\n";
}
if($tag == 'script') {
echo "<!--<![endif]-->\n";
}
}
/**
* Print a link
*
*
* @author Andreas Gohr <andi@splitbrain.org>
*
* @param string $url
* @param string $name
* @param string $more
* @param bool $return if true return the link html, otherwise print
* @return bool|string html of the link, or true if printed
function tpl_link($url, $name, $more = '', $return = false) {
$out = '<a href="'.$url.'" ';
print $out;
return true;
/**
* Prints a link to a WikiPage
*
* Wrapper around html_wikilink
*
* @author Andreas Gohr <andi@splitbrain.org>
*
* @param string $id page id
* @param string|null $name the name of the link
print '<bdi>'.html_wikilink($id, $name).'</bdi>';
/**
* get the parent page
*
* Tries to find out which page is parent.
* returns false if none is available
*
*
* @param string $id page id
* @return false|string
$pos = strrpos(getNS($id), ':');
$parent = substr($parent, 0, $pos).':';
resolve_pageid('', $parent, $exists);
if($parent == $id) return false;
}
return $parent;
/**
* Print one of the buttons
*
* @author Adrian Lang <mail@adrianlang.de>
* @see tpl_get_action
*
* @param string $type
* @param bool $return
* @return bool|string html, or false if no data, true if printed
function tpl_button($type, $return = false) {
$out = sprintf($data, 'button');
} else {
/**
* @var string $accesskey
* @var string $id
* @var string $method
* @var array $params
*/
$out = html_topbtn();
} else {
$out = html_btn($type, $id, $accesskey, $params, $method);
}
* @author Adrian Lang <mail@adrianlang.de>
* @see tpl_get_action
* @param string $type action command
* @param string $pre prefix of link
* @param string $suf suffix of link
* @param string $inner innerHML of link
* @param bool $return if true it returns html, otherwise prints
* @return bool|string html or false if no data, true if printed
function tpl_actionlink($type, $pre = '', $suf = '', $inner = '', $return = false) {
global $lang;
$data = tpl_get_action($type);
$out = sprintf($data, 'link');
} else {
/**
* @var string $accesskey
* @var string $id
* @var string $method
$linktarget = $id;
} else {
$linktarget = wl($id, $params);
}
if(strpos($caption, '%s')){
$caption = sprintf($caption, $replacement);
}
$akey = $addTitle = '';
if($accesskey) {
$akey = 'accesskey="'.$accesskey.'" ';
Anika Henke
committed
$addTitle = ' ['.strtoupper($accesskey).']';
}
$rel = $nofollow ? 'rel="nofollow" ' : '';
$out = tpl_link(
$linktarget, $pre.(($inner) ? $inner : $caption).$suf,
'class="action '.$type.'" '.
'title="'.hsc($caption).$addTitle.'"', true
* Check the actions and get data for buttons and links
* Available actions are
*
* edit - edit/create/show/draft
* history - old revisions
* recent - recent changes
* login - login/logout - if ACL enabled
* profile - user profile (if logged in)
* index - The index
* admin - admin page - if enough rights
* top - back to top
* back - back to parent - if available
* backlink - links to the list of backlinks
* subscribe/subscription- subscribe/unsubscribe
* @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
* @author Adrian Lang <mail@adrianlang.de>
* @param string $type
* @return array|bool|string
global $ID;
global $INFO;
global $REV;
global $ACT;
/** @var Input $INPUT */
global $INPUT;
// check disabled actions and fix the badly named ones
if ($type == 'subscription') $type = 'subscribe';
$accesskey = null;
$id = $ID;
$method = 'get';
$params = array('do' => $type);
$nofollow = true;
$replacement = '';
$unknown = false;
case 'edit':
// most complicated type - we need to decide on current action
if(!empty($INFO['draft'])) {
if(!$INFO['exists']) {
$type = 'create';
if(!actionOK('source')) return false; //pseudo action
$params['rev'] = $REV;
$params = array('do' => '');
// allow searchbots to get to the sitemap from the homepage (when dokuwiki isn't providing a sitemap.xml)
if ($conf['start'] == $ID && !$conf['sitemap']) {
$nofollow = false;
}
Anika Henke
committed
$accesskey = 't';
$params = array('do' => '');
$params = array('do' => '');
case 'img_backto':
$params = array();
$accesskey = 'b';
$replacement = $ID;
break;
if($INPUT->server->has('REMOTE_USER')) {
case 'register':
if($INPUT->server->str('REMOTE_USER')) {
return false;
}
break;
case 'resendpwd':
if($INPUT->server->str('REMOTE_USER')) {
return false;
}
break;
}
break;
case 'revert':
if(!$INFO['ismanager'] || !$REV || !$INFO['writable']) {
return false;
if(!$INPUT->server->str('REMOTE_USER')) {
}
break;
case 'backlink':
break;
case 'profile':
if(!$INPUT->server->has('REMOTE_USER')) {
$params['ns'] = getNS($ID);
case 'mediaManager':
// View image in media manager
global $IMG;
$imgNS = getNS($IMG);
$authNS = auth_quickaclcheck("$imgNS:*");
if ($authNS < AUTH_UPLOAD) {
return false;
}
$params = array(
'ns' => $imgNS,
'image' => $IMG,
'do' => 'media'
);
//$type = 'media';
break;
//unknown type
$unknown = true;
}
$data = compact('accesskey', 'type', 'id', 'method', 'params', 'nofollow', 'replacement');
$evt = new Doku_Event('TPL_ACTION_GET', $data);
if($evt->advise_before()) {
//handle unknown types
if($unknown) {
$evt->advise_after();
unset($evt);
return $data;
/**
* Wrapper around tpl_button() and tpl_actionlink()
*
* @author Anika Henke <anika@selfthinker.org>
*
* @param string $type action command
* @param bool $link link or form button?
* @param string|bool $wrapper HTML element wrapper
* @param bool $return return or print
* @param string $pre prefix for links
* @param string $suf suffix for links
* @param string $inner inner HTML for links
function tpl_action($type, $link = false, $wrapper = false, $return = false, $pre = '', $suf = '', $inner = '') {
$out = '';
$out .= tpl_actionlink($type, $pre, $suf, $inner, true);
}
if($out && $wrapper) $out = "<$wrapper>$out</$wrapper>";
print $out;
return $out ? true : false;
}
/**
* Print the search form
*
* If the first parameter is given a div with the ID 'qsearch_out' will
* be added which instructs the ajax pagequicksearch to kick in and place
* its output into this div. The second parameter controls the propritary
* attribute autocomplete. If set to false this attribute will be set with an
* value of "off" to instruct the browser to disable it's own built in
* autocompletion feature (MSIE and Firefox)
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param bool $ajax
* @param bool $autocomplete
* @return bool
function tpl_searchform($ajax = true, $autocomplete = true) {
global $lang;
global $ACT;
global $QUERY;
// don't print the search form if search action has been disabled
print '<form action="'.wl().'" accept-charset="utf-8" class="search" id="dw__search" method="get" role="search"><div class="no">';
print '<input type="hidden" name="do" value="search" />';
print '<input type="text" ';
if($ACT == 'search') print 'value="'.htmlspecialchars($QUERY).'" ';
if(!$autocomplete) print 'autocomplete="off" ';
print 'id="qsearch__in" accesskey="f" name="id" class="edit" title="[F]" />';
Anika Henke
committed
print '<button type="submit" title="'.$lang['btn_search'].'">'.$lang['btn_search'].'</button>';
if($ajax) print '<div id="qsearch__out" class="ajax_qsearch JSpopup"></div>';
print '</div></form>';
return true;
}
/**
* Print the breadcrumbs trace
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param string $sep Separator between entries
* @return bool
global $lang;
global $conf;
//check if enabled
if(!$conf['breadcrumbs']) return false;
$crumbs = breadcrumbs(); //setup crumb trace
$crumbs_sep = ' <span class="bcsep">'.$sep.'</span> ';
//render crumbs, highlight the last one
print '<span class="bchead">'.$lang['breadcrumb'].'</span>';
$i = 0;
foreach($crumbs as $id => $name) {
$i++;
echo $crumbs_sep;
if($i == $last) print '<span class="curid">';
tpl_link(wl($id), hsc($name), 'class="breadcrumbs" title="'.$id.'"');
/**
* Hierarchical breadcrumbs
*
* This code was suggested as replacement for the usual breadcrumbs.
* It only makes sense with a deep site structure.
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Nigel McNie <oracle.shinoda@gmail.com>
* @author <fredrik@averpil.com>
* @todo May behave strangely in RTL languages
* @param string $sep Separator between entries
* @return bool
function tpl_youarehere($sep = ' » ') {
global $conf;
global $ID;
global $lang;
// check if enabled
if(!$conf['youarehere']) return false;
$parts = explode(':', $ID);
$count = count($parts);
echo '<span class="bchead">'.$lang['youarehere'].' </span>';
// always print the startpage
echo '<span class="home">';
tpl_pagelink(':'.$conf['start']);
echo '</span>';
// print intermediate namespace links
$part = '';
$part .= $parts[$i].':';
$page = $part;
if($page == $conf['start']) continue; // Skip startpage
// output
echo $sep;
tpl_pagelink($page);
}
// print current page, skipping start page, skipping for namespace index
if(isset($page) && $page == $part.$parts[$i]) return true;
if($page == $conf['start']) return true;
echo $sep;
tpl_pagelink($page);
/**
* Print info if the user is logged in
*
* Could be enhanced with a profile link in future?
*
* @author Andreas Gohr <andi@splitbrain.org>
/** @var Input $INPUT */
global $INPUT;
if($INPUT->server->str('REMOTE_USER')) {
print $lang['loggedinas'].' '.userlink();
/**
* Print some info about the current page
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param bool $ret return content instead of printing it
* @return bool|string