namespace#depth | print numbers how_deep absolute }} * * print: * print optimization * numbers + how_deep: * numbering for headers(1.1.3 hallo...) * absolute: * absolute indentation of headers, corresponding to file-depth in namespace * or (default) relative indentation */ class syntax_plugin_composer extends DokuWiki_Syntax_Plugin { /** * how to handle

*/ function getPType(){ return 'block'; } /** * what other types are allowed iside */ function getAllowedTypes() { return array('substition'); } /** * What kind of syntax are we? */ function getType(){ return 'substition'; } /** * Where to sort in? */ function getSort(){ return 311; } /** * Connect pattern to lexer */ function connectTo($mode) { $this->Lexer->addSpecialPattern("{{compose>.*?}}",$mode,'plugin_composer'); } /** * Handle the match */ function handle($match, $state, $pos, &$handler){ global $conf; $opt = array( "level" => 0, "nons" => false, "relative" => false, "print" => false, "numbers" => false, "numbers_depth" => 2, "resort" => false ); $givenOptString = ""; $givenOpt = array(); $content = ""; $match = preg_replace("/\{\{compose\>(.*)\}\}/", "$1", $match); $nsOpt = preg_split("/\|/", $match); if (count($nsOpt) == 1) { $ns = $nsOpt[0]; } else { $ns = $nsOpt[0]; $givenOptString = $nsOpt[1]; $givenOpt = preg_split("/ /", $givenOptString); } $nsLevel = preg_split("/#/", $ns); if (count($nsLevel) > 1) { $ns = $nsLevel[0]; $opt["level"] = $nsLevel[1]; } if (in_array("absolute", $givenOpt)) { if ($conf["useslashes"]) { $opt["relative"] = substr_count($ns, "/") + 1; } else { $opt["relative"] = substr_count($ns, ":") + 1; } } if (in_array("nons", $givenOpt)) { $opt["nons"] = true; } if (in_array("print", $givenOpt)) { $opt["split_too_long"] = true; } // Do we have the numbers option without a level specification? if (in_array("numbers", $givenOpt)) { $opt["numbers"] = true; } // Do we have the numbers option with the level specification? if (preg_match("/numbers:(\d)*/", $givenOptString, $matches)) { $opt["numbers"] = true; $opt["numbers_depth"] = $matches[1]; } if (in_array("resort", $givenOpt)) { $opt["resort"] = true; } return array( 'namespace'=> $ns, 'options' => $opt ); } /** * Create output */ function render($mode, &$renderer, $data) { if($mode == 'xhtml'){ //for header numbering $header_count = array( 1=>0, 2=>0, 3=>0, 4=>0, 5=>0, ); $data['header_count'] = &$header_count; // get all files in the supplied namespace, with their level $file_data = $this->_get_file_data($data); /* * Resort results to optimize output * Sort by levels, then first sort the directories, * than the start page and then the files */ if ($data["options"]["resort"]) { usort($file_data, "composer_sort_filedata"); } foreach($file_data as $file) { // build the output $this->_render_output($renderer, $file, $data); } return true; } return false; } /* * Get all files within the namespace */ function _get_file_data($data) { global $conf; $options = $data['options']; $ns = $data['namespace']; $resolvedPage = ""; $pageExists = false; // Reformat namespace to filename // namespace options . or .. $pageFile = wikiFN($ns); if (!file_exists($pageFile)) { // Try adding the start page if ($conf["useslash"]) { $ns .= "/"; } else { $ns .= ":"; } $ns .= $conf['start']; $pageFile = wikiFN($ns); if (!file_exists($pageFile)) { return false; } } $dirname = dirname($pageFile); $dirname = str_replace($conf['datadir'], "", $dirname); $file_data = array( 'start_lvl' => substr_count($dirname, DIRECTORY_SEPARATOR), 'blocked' => array(), //blocked files ); // find all matching files search( $file_data, $conf['datadir'], 'composer_search_index', $options, $dirname ); // clean the file_data from non-files (for safety) unset($file_data['start_lvl']); unset($file_data['blocked']); return $file_data; } /* * Render the output */ function _render_output(&$renderer, $file_data, $o){ // file attributes $id = $file_data['id']; $open = $file_data['open']; $content = ""; // build with relative indentation $clevel = $file_data['level']; if($o['options']['relative']){ // Relative identation $clevel -= $o['options']['relative']; } // Don't open the file (in case of directories and nons on) if(!$open) { return false; } // Convert a:b -> home/data/pages/a/b $file = wikiFN($id); // Get data(in instructions format) from $file (dont use cache: false) $instr = p_cached_instructions($file, false); // Page was not empty if (!empty($instr)) { // Fix relative links and lower headers of included pages //we dont need this option for conversion unset($o['options']['relative']); $instr = $this->_convertInstructions( $instr, $id, $renderer, $clevel, $o ); $info = array(); // Render page $content = p_render('xhtml', $instr, $info); // Remove TOC`s, section edit buttons and tags $content = $this->_cleanXHTML($content); } // Embed the included page $renderer->doc .= '

'; // Add an anchor to find start of a inserted page $id = str_replace(":", "_", $file_data['id']); $renderer->doc .= ""; $renderer->doc .= $content; $renderer->doc .= '
'; return true; } /* * Corrects relative internal links and media and * converts headers of included pages to subheaders of the current page */ function _convertInstructions($instr, $incl, &$renderer, $clevel, $o){ global $ID; // check if included page is already in output namespace $iNS = getNS($incl); $iID = getNS($ID); //the content belongs to the original page if ($iID == $iNS) { $convert = false; //just leave as it is } else { // The content was newly included, and is not original page content $convert = true; //convert content } for ($i = 0; $i < count($instr); $i++){ if ($convert) { if((substr($instr[$i][0], 0, 8) == 'internal')){ // Internal links(links inside this wiki) an relative links $this->_convert_link($renderer,$instr[$i],$iNS,$iID,$o); } elseif ($instr[$i][0] == 'header'){ // Set header level to current section level + header level $this->_convert_header($renderer,$instr[$i],$clevel,$o); } elseif ($instr[$i][0] == 'section_open'){ // The same for sections $level = $instr[$i][1][0] + $clevel; if ($level > 5) { $level = 5; } $instr[$i][1][0] = $level; } } // Split long lines? if($o['split_too_long'] && $instr[$i][0] == 'code') { $instr[$i][1][0] = wordwrap($instr[$i][1][0],70,"\n"); } } // If its the document start, cut off the document information if ($instr[0][0] == 'document_start') { return array_slice($instr, 1, -1); } else { return $instr; } } /* * convert header of given instruction */ function _convert_header(&$renderer,&$instr,$clevel,$o) { global $conf; $level = $instr[1][1] + $clevel; // If a header level gets "lower" than 5 if ($level > 5) { $level = 5; } $instr[1][1] = $level; // Number headers if($o['numbers']) { // Number in front of header $number = ""; // Reset lower levels for($x = $level+1; $x <= 5; $x++) { $o['header_count'][$x] = 0; } // Raise this level by 1 $o['header_count'][$level]++; // If the level is high (1=high) if($level <= $o['numbering_depth']) { //build the number for($x = 1; $x <= $level; $x++) { $number .= $o['header_count'][$x] . "."; } } //save $instr[1][0] = $number . " " . $instr[1][0]; } // add TOC items if ($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']){ $text = $instr[1][0]; $header_id = $renderer->_headerToLink($text, 'true'); //add item to TOC $renderer->toc[] = array( 'hid' => $header_id, 'title' => $text, 'type' => 'ul', 'level' => $level-$conf['toptoclevel']+1, ); } } /* * Convert link of given instruction */ function _convert_link(&$renderer, &$instr, $iNS, $id, $o) { // Relative subnamespace if ($instr[1][0]{0} == '.'){ // Build complete address $instr[1][0] = $iNS.':'.substr($instr[1][0], 1); } else if (strpos($instr[1][0],':') === false) { //relative link $instr[1][0] = $iNS.':'.$instr[1][0]; } // link to another page, but within our included namespace // if the link starts with our namespace if(strpos($instr[1][0], $o['start_ns']) === 0) { //if the link is inside the included depth of pages if($o['depth'] === 0 || substr_count($o['start_ns'],":") + $o['depth'] >= substr_count($instr[1][0],":") ) { // Convert to internal link if(strpos($instr[1][0],"#") !== false) { // Get last part a:b#c -> c $levels = preg_split("/#/",$instr[1][0]); // $id we are in atm + # + id of anchor $instr[1][0] = $id . "#" . $levels[sizeof($levels)-1]; } else { // a:b:c /** * $id we are in atm + # + full id of page to find(-> * unique id) not possible to use a:b:c for id --> : -> _ */ $instr[1][0] = $id . "#" . str_replace( ":", "_", $instr[1][0] ); // If page-link has no name if(empty($instr[1][1])) { // TODO: get the real name of the page :( $name = preg_split("/_/",$instr[1][0]); // Last part of id -> name $instr[1][1] = $name[sizeof($name)-1]; } } } } } /** * Remove TOC, section edit buttons and tags */ function _cleanXHTML($xhtml){ $replace = array( '!
.*?(
\n)!s' => '', // remove TOCs '##e' => '', // remove section edit buttons '!
.*?(
)!s' => '' // remove category tags ); $xhtml = preg_replace( array_keys($replace), array_values($replace), $xhtml ); return $xhtml; } } /* * evaluate the files presented by search * * modify $data according to our wishes */ function composer_search_index(&$data, $base, $file, $type, $lvl, $opts) { // include in result ? $return = true; // Directories if ($type == 'd'){ //if max-level is reached, don't return them if ($opts['level'] == $lvl) { $return = false; } //add the directory name to list of blocked files //so that file with same name will not be included $data['blocked'][] = $file . ".txt"; //if we don't want namespace-nodes in the resultset if ($opts['nons']) return $return; } elseif($type == 'f' && !preg_match('#\.txt$#',$file)){ // Don't add files, that end in txt return false; } $id = pathID($file); // Check hiddens if($type == 'f' && isHiddenPage($id)){ return false; } // Check ACL (for namespaces too) if(auth_quickaclcheck($id) < AUTH_READ){ //we are not allowed to read return false; } //check if this files was blocked by us if($type == 'f' && in_array($file,$data['blocked'])){ return false; } // add the start level(ns we build) to the current level $lvl += $data['start_lvl']; //pack it up $data[]=array( 'id' => $id, 'type' => $type, //which type is it ? 'level' => $lvl, //show on which level ? 'open' => $return, //open it ? ); return $return; } /** * User defined sort function to sort by level, than by directory, * than by start.txt and then by filename * * @param $a Array left side sort argument * @param $b Array right side sort argument */ function composer_sort_filedata($a, $b) { global $conf; // Sort by level if ($a["level"] < $b["level"]) { return -1; } else if ($a["level"] > $b["level"]) { return 1; } else { // Level is the same. Sort by directories if (($a["type"] == "d") and ($b["type"] == "f")) { return -1; } else if (($a["type"] == "f") and ($b["type"] == "d")) { return 1; } else if (($a["type"] == "d") and ($b["type"] == "d")) { // Both are directories. Sort by id return strcmp($a["id"], $b["id"]); } else { // Both are files. Sort by start.txt if (stristr($a["id"], $conf["start"]) && !stristr($b["id"], $conf["start"]) ) { return -1; } else if (!stristr($a["id"], $conf["start"]) && stristr($b["id"], $conf["start"]) ) { return 1; } else { // No page is a start page or both, sort by id return strcmp($a["id"], $b["id"]); } } } }