<?php
/**
* Partial Include Plugin: displays parts of a wiki page within another
* Usage:
* {{subpage>page}} for "page" in same namespace
* {{subpage>:page}} for "page" in top namespace
* {{subpage>namespace:page}} for "page" in namespace "namespace"
* {{subpage>.namespace:page}} for "page" in subnamespace "namespace"
* {{subpage>page#n_sections;optional_min_level}} for a section of "page"
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Gwenole Beauchesne <masked-email>
*/
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
/**
* All DokuWiki plugins to extend the parser/rendering mechanism
* need to inherit from this class
*/
class syntax_plugin_partial_include extends DokuWiki_Syntax_Plugin {
/**
* return some info
*/
function getInfo(){
return array(
'author' => 'Gwenole Beauchesne',
'email' => '<masked-email>',
'date' => '2005-11-01',
'name' => 'Partial Include Plugin',
'desc' => 'displays parts of a wiki page within another',
'url' => '',
);
}
/**
* What kind of syntax are we?
*/
function getType(){
return 'substition';
}
/**
* Where to sort in?
*/
function getSort(){
return 309;
}
/**
* Paragraph Type
*/
function getPType(){
return 'block';
}
/**
* Connect pattern to lexer
*/
function connectTo($mode) {
$this->Lexer->addSpecialPattern('\{\{subpage.+?\}\}',$mode,'plugin_partial_include');
}
/**
* Handle the match
*/
function handle($match, $state, $pos, &$handler){
global $ID;
global $filechain;
$match = substr($match,10,-2); // strip markup
$match = preg_split('/\#/u',$match,2); // split hash from filename
resolve_pageid(getNS($ID),$match[0],$exists); // resolve shortcuts
// check for existence and permission
if ((!$exists) || (auth_quickaclcheck($match[0]) < 1)) return false;
// check for and establish start of $filechain
if (!isset($filechain)) $filechain[] = $ID;
// don't allow the same file to be included more than once
if (in_array($match[0], $filechain)) return false;
// add included page to the filechain
$filechain[] = $match[0];
// get sections information
list($num, $level, $type) = explode(';',$match[1],3);
if ($num == 0)
$num = 1;
if ($level == 0)
$level = 1;
if ($type == '')
$type = 'header';
switch ($type) {
case 'header': case 'list': break;
default: return false;
}
return array($match[0], $num, $level, $type);
}
/**
* Create output
*/
function render($mode, &$renderer, $data) {
if($mode == 'xhtml'){
$file = wikiFN($data[0]);
if (!@file_exists($file)) return false;
// get instructions
$instr = p_cached_instructions($file, false);
// filter section
$instr = $this->{'_getSection_'.$data[3]}($data[1], $data[2], $more, $instr);
// correct relative internal links and media
$instr = $this->_correctRelNS($instr, $data[0]);
// render the instructions on the fly
$text = p_render('xhtml',$instr,$info);
// remove toc, section edit buttons and category tags
$patterns = array('!<div class="toc">.*?(</div>\n</div>)!s',
'#<!-- SECTION \[(\d*-\d*)\] -->#e',
'!<div class="category">.*?</div>!s');
$replace = array('','','');
$text = preg_replace($patterns,$replace,$text);
// prevent caching to ensure the included page is always fresh
$renderer->info['cache'] = FALSE;
// embed the included page
$renderer->doc .= $text;
// append continuation link
if ($more) {
$level = $data[2];
$mlink = $renderer->internallink($data[0],'...','','true');
switch ($data[3]) {
case 'header':
$renderer->doc .= "<div class=\"level$level\">[$mlink]</div>";
break;
case 'list': // <ul> is open at this stage
$renderer->doc .= "<li class=\"level$level\"><div class=\"li\">[$mlink]</div></li></ul>";
}
}
return true;
}
return false;
}
/**
* Get N 'header' sections including their subsections
*/
function _getSection_header($max_sections,$min_level,&$more,$instructions){
$more = 0;
$n_sections = 0;
foreach ($instructions as $instruction){
if ($instruction[0] == 'header'){
$level = $instruction[1][1];
if ($level >= $min_level) {
if ($level == $min_level) {
$n_sections += 1;
if ($n_sections > $max_sections) {
$more = 1;
return $i;
}
}
$i[] = $instruction;
} elseif ($n_sections && $level < $min_level)
return $i;
} elseif ($n_sections) {
$i[] = $instruction;
}
}
return $i;
}
/**
* Get N 'list items including their sublists
* XXX factorisation candidate
*/
function _getSection_list($max_sections,$min_level,&$more,$instructions){
$more = 0;
$n_sections = 0;
for ($it = 0; $it < sizeof($instructions); $it++){
$instruction = $instructions[$it];
if ($instruction[0] == 'listitem_open'){
$level = $instruction[1][0];
if ($level >= $min_level) {
if ($level == $min_level) {
$n_sections += 1;
if ($n_sections == 1)
$i[] = $instructions[$it-1];
elseif ($n_sections > $max_sections) {
$more = 1;
return $i;
}
}
$i[] = $instruction;
} elseif ($n_sections && $level < $min_level)
return $i;
} elseif ($n_sections) {
$i[] = $instruction;
}
}
return $i;
}
/**
* Corrects relative internal links and media
*/
function _correctRelNS($instr,$incl){
global $ID;
// check if included page is in same namespace
$iNS = getNS($incl);
if (getNS($ID) == $iNS) return $instr;
// convert internal links and media from relative to absolute
$n = count($instr);
for($i = 0; $i < $n; $i++){
if (substr($instr[$i][0], 0, 8) == 'internal'){
// relative subnamespace
if ($instr[$i][1][0]{0} == '.'){
$instr[$i][1][0] = $iNS.':'.substr($instr[$i][1][0], 1);
// relative link
} elseif (strpos($instr[$i][1][0],':') === false) {
$instr[$i][1][0] = $iNS.':'.$instr[$i][1][0];
}
}
}
return $instr;
}
}
//Setup VIM: ex: et ts=4 enc=utf-8 :