<?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 :