*/
// must be run within Dokuwiki
if (!defined('DOKU_INC')) die();
if (!defined('DOKU_LF')) define('DOKU_LF', "\n");
if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");
if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/');
class helper_plugin_include extends DokuWiki_Plugin { // DokuWiki_Helper_Plugin
var $pages = array(); // filechain of included pages
var $page = array(); // associative array with data about the page to include
var $ins = array(); // instructions array
var $doc = ''; // the final output XHTML string
var $mode = 'section'; // inclusion mode: 'page' or 'section'
var $clevel = 0; // current section level
var $firstsec = 0; // show first section only
var $footer = 1; // show metaline below page
var $header = array(); // included page / section header
var $renderer = NULL; // DokuWiki renderer object
// private variables
var $_offset = NULL;
/**
* Constructor loads some config settings
*/
function helper_plugin_include(){
$this->firstsec = $this->getConf('firstseconly');
$this->footer = $this->getConf('showfooter');
}
function getInfo(){
return array(
'author' => 'Esther Brunner',
'email' => 'wikidesign@gmail.com',
'date' => '2007-04-27',
'name' => 'Include Plugin (helper class)',
'desc' => 'Functions to include another page in a wiki page',
'url' => 'http://www.wikidesign/en/plugin/include/start',
);
}
function getMethods(){
$result = array();
$result[] = array(
'name' => 'setPage',
'desc' => 'sets the page to include',
'params' => array("page attributes, 'id' required, 'section' for filtering" => 'array'),
'return' => array('success' => 'boolean'),
);
$result[] = array(
'name' => 'setMode',
'desc' => 'sets inclusion mode: should indention be merged?',
'params' => array("'page' (original) or 'section' (merged indention)" => 'string'),
);
$result[] = array(
'name' => 'setLevel',
'desc' => 'sets the indention for the current section level',
'params' => array('level: 0 to 5' => 'integer'),
'return' => array('success' => 'boolean'),
);
$result[] = array(
'name' => 'setFlags',
'desc' => 'overrides standard values for showfooter and firstseconly settings',
'params' => array('flags' => 'array'),
);
$result[] = array(
'name' => 'renderXHTML',
'desc' => 'renders the XHTML output of the included page',
'params' => array('DokuWiki renderer' => 'object'),
'return' => array('XHTML' => 'string'),
);
return $result;
}
/**
* Sets the page to include if it is not already included (prevent recursion)
*/
function setPage($page){
global $ID;
$id = $page['id'];
$fullid = $id.'#'.$page['section'];
if (!$id) return false; // no page id given
if ($id == $ID) return false; // page can't include itself
// prevent include recursion
if ((isset($this->pages[$id.'#'])) || (isset($this->pages[$fullid]))) return false;
// add the page to the filechain
$this->pages[$fullid] = $page;
$this->page =& $this->pages[$fullid];
return true;
}
/**
* Sets the inclusion mode
*/
function setMode($mode){
$this->mode = $mode;
}
/**
* Sets the right indention for a given section level
*/
function setLevel($level){
if ((is_numeric($level)) && ($level >= 0) && ($level <= 5)){
$this->clevel = $level;
return true;
}
return false;
}
/**
* Overrides standard values for showfooter and firstseconly settings
*/
function setFlags($flags){
foreach ($flags as $flag){
switch ($flag){
case 'footer':
$this->footer = 1;
break;
case 'nofooter':
$this->footer = 0;
break;
case 'firstseconly':
$this->firstsec = 1;
break;
case 'fullpage':
$this->firstsec = 0;
break;
}
}
}
/**
* Builds the XHTML to embed the page to include
*/
function renderXHTML(&$renderer){
if (!$this->page['id']) return ''; // page must be set first
if (!$this->page['exists'] && ($this->page['perm'] < AUTH_CREATE)) return '';
// prepare variables
$this->doc = '';
$this->renderer =& $renderer;
// get instructions and render them on the fly
$this->page['file'] = wikiFN($this->page['id']);
$this->ins = p_cached_instructions($this->page['file']);
// show only a given section?
if ($this->page['section'] && $this->page['exists']) $this->_getSection();
// convert relative links
$this->_convertInstructions();
// render the included page
if ($this->header) $content = 'header['level'].' class="entry-title">'.
'header['level'].'>'.DOKU_LF;
else $content = '';
$content .= '
'.DOKU_LF.
$this->_cleanXHTML(p_render('xhtml', $this->ins, $info)).DOKU_LF.
'
'.DOKU_LF;
// embed the included page
$class = ($this->page['draft'] ? 'include draft' : 'include');
$renderer->doc .= '_showTagLogos().'>'.DOKU_LF;
if (!$this->header && $this->clevel && ($this->mode == 'section'))
$renderer->doc .= '
'.DOKU_LF;
if ((@file_exists(DOKU_PLUGIN.'editsections/action.php'))
&& (!plugin_isdisabled('editsections'))){ // for Edit Section Reorganizer Plugin
$renderer->doc .= $this->_editButton().$content;
} else {
$renderer->doc .= $content.$this->_editButton();
}
if (!$this->header && $this->clevel && ($this->mode == 'section'))
$renderer->doc .= '
'.DOKU_LF; // class="level?"
$renderer->doc .= '
'.DOKU_LF; // class="include hentry"
// output meta line (if wanted) and remove page from filechain
$renderer->doc .= $this->_footer(array_pop($this->pages));
$this->helper_plugin_include();
return $this->doc;
}
/* ---------- Private Methods ---------- */
/**
* Get a section including its subsections
*/
function _getSection(){
foreach ($this->ins as $ins){
if ($ins[0] == 'header'){
// found the right header
if (cleanID($ins[1][0]) == $this->page['section']){
$level = $ins[1][1];
$i[] = $ins;
// next header of the same or higher level -> exit
} elseif ($ins[1][1] <= $level){
$this->ins = $i;
return true;
} elseif (isset($level)){
$i[] = $ins;
}
// add instructions from our section
} elseif (isset($level)){
$i[] = $ins;
}
}
$this->ins = $i;
return true;
}
/**
* Corrects relative internal links and media and
* converts headers of included pages to subheaders of the current page
*/
function _convertInstructions(){
global $ID;
global $conf;
$this->header = array();
$offset = $this->clevel;
if (!$this->page['exists']) return false;
// check if included page is in same namespace
$inclNS = getNS($this->page['id']);
if (getNS($ID) == $inclNS) $convert = false;
else $convert = true;
$n = count($this->ins);
for ($i = 0; $i < $n; $i++){
$current = $this->ins[$i][0];
// convert internal links and media from relative to absolute
if ($convert && (substr($current, 0, 8) == 'internal')){
$this->ins[$i][1][0] = $this->_convertInternalLinks($i, $inclNS);
// set header level to current section level + header level
} elseif ($current == 'header'){
$this->_convertHeaders($i);
// the same for sections
} elseif ($current == 'section_open'){
$this->ins[$i][1][0] = $this->_convertSectionLevel($this->ins[$i][1][0]);
// show only the first section?
} elseif ($this->firstsec && ($current == 'section_close')
&& ($this->ins[$i-1][0] != 'section_open')){
$this->_readMore($i);
return true;
}
}
$this->_finishConvert();
return true;
}
/**
* Convert relative internal links and media
*
* @param integer $i: counter for current instruction
* @param string $ns: namespace of included page
* @return string $link: converted, now absolute link
*/
function _convertInternalLinks($i, $ns){
// relative subnamespace
if ($this->ins[$i][1][0]{0} == '.'){
// parent namespace
if ($this->ins[$i][1][0]{1} == '.')
return getNS($ns).':'.substr($this->ins[$i][1][0], 2);
// current namespace
else
return $ns.':'.substr($this->ins[$i][1][0], 1);
// relative link
} elseif (strpos($this->ins[$i][1][0], ':') === false){
return $ns.':'.$this->ins[$i][1][0];
}
}
/**
* Convert header level and add header to TOC
*
* @param integer $i: counter for current instruction
* @return boolean true
*/
function _convertHeaders($i){
$text = $this->ins[$i][1][0];
$hid = $this->renderer->_headerToLink($text, 'true');
if (empty($this->header)){
$this->_offset = $this->clevel - $this->ins[$i][1][1] + 1;
$level = $this->clevel + 1;
$this->header = array(
'hid' => $hid,
'title' => hsc($text),
'level' => $level
);
unset($this->ins[$i]);
} else {
$level = $this->_convertSectionLevel($this->ins[$i][1][1]);
$this->ins[$i][1][1] = $level;
}
// add TOC item
if (($level >= $conf['toptoclevel']) && ($level <= $conf['maxtoclevel'])){
$this->renderer->toc[] = array(
'hid' => $hid,
'title' => $text,
'type' => 'ul',
'level' => $level - $conf['toptoclevel'] + 1
);
}
return true;
}
/**
* Convert the level of headers and sections
*
* @param integer $in: current level
* @return integer $out: converted level
*/
function _convertSectionLevel($in){
$out = $in + $this->_offset;
if ($out > 5) $out = 5;
return $out;
}
/**
* Adds a read more... link at the bottom of the first section
*
* @param integer $i: counter for current instruction
* @return boolean true
*/
function _readMore($i){
$more = ((is_array($this->ins[$i+1])) && ($this->ins[$i+1][0] != 'document_end'));
if ($this->ins[0][0] == 'document_start') $this->ins = array_slice($this->ins, 1, $i);
else $this->ins = array_slice($this->ins, 0, $i);
if ($more){
array_unshift($this->ins, array('document_start', array(), 0));
$last = array_pop($this->ins);
$this->ins[] = array('p_open', array(), $last[2]);
$this->ins[] = array('internallink',array($this->page['id'], $this->getLang('readmore')),$last[2]);
$this->ins[] = array('p_close', array(), $last[2]);
$this->ins[] = $last;
$this->ins[] = array('document_end', array(), $last[2]);
} else {
$this->_finishConvert();
}
return true;
}
/**
* Adds 'document_start' and 'document_end' instructions if not already there
*/
function _finishConvert(){
if ($this->ins[0][0] != 'document_start'){
array_unshift($this->ins, array('document_start', array(), 0));
$this->ins[] = array('document_end', array(), 0);
}
}
/**
* Remove TOC, section edit buttons and tags
*/
function _cleanXHTML($xhtml){
preg_match('!.*?
!s', $xhtml, $match);
$this->page['tags'] = $match[0];
$replace = array(
'!.*?(
\n)!s' => '', // remove toc
'##e' => '', // remove section edit buttons
'!.*?(
)!s' => '', // remove category tags
);
$xhtml = preg_replace(array_keys($replace), array_values($replace), $xhtml);
return $xhtml;
}
/**
* Optionally display logo for the first tag found in the included page
*/
function _showTagLogos(){
if (!$this->getConf('showtaglogos')) return '';
preg_match_all('/]*title="(.*?)" rel="tag"[^>]*>([^<]*)', $this->page['tags'], $tag);
$logoID = getNS($tag[1][0]).':'.$tag[2][0];
$logosrc = mediaFN($logoID);
$types = array('.png', '.jpg', '.gif'); // auto-detect filetype
foreach ($types as $type){
if (!@file_exists($logosrc.$type)) continue;
$logoID .= $type;
$logosrc .= $type;
list($w, $h, $t, $a) = getimagesize($logosrc);
return ' style="min-height: '.$h.'px">'.
'
page['perm']))
$this->page['perm'] = auth_quickaclcheck($this->page['id']);
if (@file_exists($this->page['file'])){
if (($this->page['perm'] >= AUTH_EDIT) && (is_writable($this->page['file'])))
$action = 'edit';
else return '';
} elseif ($this->page['perm'] >= AUTH_CREATE){
$action = 'create';
}
if ($this->getConf('showeditbtn')){
return ''.DOKU_LF.DOKU_TAB.
html_btn($action, $this->page['id'], '', array('do' => 'edit'), 'post').DOKU_LF.
'
'.DOKU_LF;
} else {
return '';
}
}
/**
* Returns the meta line below the included page
*/
function _footer($page){
global $conf;
if (!$this->footer) return ''; // '
'.DOKU_LF;
$id = $page['id'];
$meta = p_get_metadata($id);
$ret = array();
// permalink
if ($this->getConf('showlink')){
$title = ($page['title'] ? $page['title'] : $meta['title']);
if (!$title) $title = str_replace('_', ' ', noNS($id));
$class = ($page['exists'] ? 'wikilink1' : 'wikilink2');
$link = array(
'url' => wl($id),
'title' => $id,
'name' => hsc($title),
'target' => $conf['target']['wiki'],
'class' => $class.' permalink',
'more' => 'rel="bookmark"',
);
$ret[] = $this->renderer->_formatLink($link);
}
// date
if ($this->getConf('showdate')){
$date = ($page['date'] ? $page['date'] : $meta['date']['created']);
if ($date)
$ret[] = ''.
date($conf['dformat'], $date).
'';
}
// author
if ($this->getConf('showuser')){
$author = ($page['user'] ? $page['user'] : $meta['creator']);
if ($author){
$userpage = cleanID($this->getConf('usernamespace').':'.$author);
resolve_pageid(getNS($ID), $id, $exists);
$class = ($exists ? 'wikilink1' : 'wikilink2');
$link = array(
'url' => wl($userpage),
'title' => $userpage,
'name' => hsc($author),
'target' => $conf['target']['wiki'],
'class' => $class.' url fn',
'pre' => '',
'suf' => '',
);
$ret[] = $this->renderer->_formatLink($link);
}
}
// comments - let Discussion Plugin do the work for us
if (!$page['section'] && $this->getConf('showcomments')
&& (!plugin_isdisabled('discussion'))
&& ($discussion =& plugin_load('helper', 'discussion'))){
$disc = $discussion->td($id);
if ($disc) $ret[] = '';
}
// linkbacks - let Linkback Plugin do the work for us
if (!$page['section'] && $this->getConf('showlinkbacks')
&& (!plugin_isdisabled('linkback'))
&& ($linkback =& plugin_load('helper', 'linkback'))){
$link = $linkback->td($id);
if ($link) $ret[] = ''.$link.'';
}
$ret = implode(' · ', $ret);
// tags
if (($this->getConf('showtags')) && ($page['tags'])){
$ret = $this->page['tags'].$ret;
}
if (!$ret) $ret = ' ';
return ''.DOKU_LF.$ret.DOKU_LF.'
'.DOKU_LF;
}
}
//Setup VIM: ex: et ts=4 enc=utf-8 :