1<?php 2if (! class_exists('syntax_plugin_nstoc')) { 3if (! defined('DOKU_PLUGIN')) { 4 if (! defined('DOKU_INC')) { 5 define('DOKU_INC', realpath(dirname(__FILE__) . '/../../') . '/'); 6 } // if 7 define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 8} // if 9// Include parent class: 10require_once(DOKU_PLUGIN . 'syntax.php'); 11// library providing the global 'auth_aclcheck()' function: 12require_once(DOKU_INC . 'inc/auth.php'); 13// library providing the global 'wl()' function: 14require_once(DOKU_INC . 'inc/common.php'); 15// library providing the global 'search()' function: 16require_once(DOKU_INC . 'inc/search.php'); 17// library providing the global 'cleanID()'/'getID()'/'wikiFN()' functions: 18require_once(DOKU_INC . 'inc/pageutils.php'); 19 20/** 21 * <tt>syntax_plugin_nstoc.php </tt>- A PHP4 class that implements 22 * a <tt>DokuWiki</tt> plugin to generate a 23 * <em>namespace table of contents</em>. 24 * 25 * <p> 26 * Usage:<br> 27 * <tt>{{nstoc [namespace [maxdepth]]}}</tt> 28 * </p><pre> 29 * Copyright (C) 2006, 2010 M.Watermann, D-10247 Berlin, FRG 30 * All rights reserved 31 * EMail : <support@mwat.de> 32 * </pre><div class="disclaimer"> 33 * This program is free software; you can redistribute it and/or modify 34 * it under the terms of the GNU General Public License as published by 35 * the Free Software Foundation; either 36 * <a href="http://www.gnu.org/licenses/gpl.html">version 3</a> of the 37 * License, or (at your option) any later version.<br> 38 * This software is distributed in the hope that it will be useful, 39 * but WITHOUT ANY WARRANTY; without even the implied warranty of 40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 41 * General Public License for more details. 42 * </div> 43 * @author <a href="mailto:support@mwat.de">Matthias Watermann</a> 44 * @version <tt>$Id: syntax_plugin_nstoc.php,v 1.17 2010/02/21 14:36:27 matthias Exp $</tt> 45 * @since created 23-Dec-2006 46 */ 47class syntax_plugin_nstoc extends DokuWiki_Syntax_Plugin { 48 49 /** 50 * @privatesection 51 */ 52 //@{ 53 54 /** 55 * Callback function for use by the global <tt>search()</tt> function. 56 * 57 * @private 58 * @see render() 59 */ 60 var $_callback = NULL; 61 62 /** 63 * HTML special characters to replace in <tt>render()</tt>. 64 * 65 * <p> 66 * This property is used to avoid repeated memory allocations 67 * inside the <tt>_doMarkup()</tt> loops. 68 * </p> 69 * @private 70 * @since created 09-Aug-2007 71 * @see _doMarkup() 72 */ 73 var $_Chars = array('&', '<', '>', '"'); 74 75 /** 76 * Entity replacements for HTML special characters. 77 * 78 * <p> 79 * This property is used to avoid repeated memory allocations 80 * inside the <tt>_doMarkup()</tt> loops. 81 * </p> 82 * @private 83 * @since created 09-Aug-2007 84 * @see _doMarkup() 85 */ 86 var $_Ents = array('&', '<', '>', '"'); 87 88 /** 89 * Lookup table for headlines ./. levels. 90 * 91 * @private 92 * @since 12-Aug-2007 93 * @see _getHeadings() 94 */ 95 var $_Hlevels = array('======' => 1, '=====' => 2, 96 '====' => 3, '===' => 4, '==' => 5, '=' => 6); 97 98 /** 99 * Additional markup used with older DokuWiki installations. 100 * 101 * @private 102 * @since created 20-Feb-2007 103 * @see _fixJS() 104 */ 105 var $_JSmarkup = FALSE; 106 107 /** 108 * Prepare the (X)HTML markup. 109 * 110 * <p> 111 * Each entry of the given <tt>$aList</tt> (indexed by <em>page ID</em>) 112 * is expected to be a list of arrays with the respective entry's level 113 * at index <tt>0</tt> (zero) and the headline's text at index 114 * <tt>1</tt> (one) the latter of which is used to construct the 115 * respective hypertext link fragment identifier. 116 * </p> 117 * @param $aList Array The list of headlines in <tt>$aID</tt>. 118 * @return String The list markup to add to the document. 119 * @private 120 * @see render() 121 */ 122 function _doMarkup(&$aList) { 123 $divOpen = array_fill(0, 0xff, 0); //XXX 255 levels as in "handle()" 124 $curLvl = 0; 125 $markup = array(); // buffer to avoid string re-allocations 126 while (list($id, $ul) = each($aList)) { 127 unset($aList[$id]); // free mem 128 $link = '<a class="wikilink1" href="' . wl($id) . '#'; 129 while (list($a, $l) = each($ul)) { 130 if ($curLvl < $l[0]) { 131 // need to open a new level 132 do { 133 if (0 < $divOpen[$curLvl]) { 134 $markup[] = '</div>'; 135 --$divOpen[$curLvl]; 136 } // if 137 ++$curLvl; 138 $markup[] = '<ul class="nstoc"><li class="level' 139 . $curLvl . '"><div class="li">'; 140 ++$divOpen[$curLvl]; 141 } while ($curLvl < $l[0]); 142 } else if ($curLvl > $l[0]) { 143 // need to close the current level 144 do { 145 if (0 < $divOpen[$curLvl]) { 146 $markup[] = '</div>'; 147 --$divOpen[$curLvl]; 148 } // if 149 --$curLvl; 150 $markup[] .= '</li></ul>'; 151 if (0 < $divOpen[$curLvl]) { 152 $markup[] = '</div>'; 153 --$divOpen[$curLvl]; 154 } // if 155 } while ($curLvl > $l[0]); 156 $markup[] = '</li><li class="level' . $curLvl 157 . '"><div class="li">'; 158 ++$divOpen[$curLvl]; 159 } else { 160 // still the current nesting level 161 if (0 < $divOpen[$curLvl]) { 162 $markup[] = '</div>'; 163 } // if 164 $markup[] = '</li><li class="level' . $curLvl 165 . '"><div class="li">'; 166 } // if 167 // Prepare the current link by setting up 168 // the HREF and TITLE attributes as appropriate: 169 $l[0] = str_replace($this->_Chars, $this->_Ents, $l[1]); 170 $markup[] = $link 171 . ltrim(str_replace(':', '', cleanID($l[1])), '0123456789._-') 172 . '" title="' . $l[0] . '">' . $l[0] . '</a>'; 173 } // while 174 } // while 175 176 // Finally close all possibly open DIV/LI/UL elements 177 while (0 < $curLvl) { 178 if (0 < $divOpen[$curLvl]) { 179 $markup[] = '</div>'; 180 } // if 181 $markup[] = '</li></ul>'; 182 --$curLvl; 183 } // while 184 185 // Return the list markup for the current document: 186 return implode('', $markup); 187 } // _doMarkup() 188 189 /** 190 * Add markup to load JavaScript/CSS with older DokuWiki versions. 191 * 192 * @param $aRenderer Object The renderer used. 193 * @private 194 * @since created 20-Feb-2007 195 * @see render() 196 */ 197 function _fixJS(&$aRenderer) { 198 if ($this->_JSmarkup) { 199 return; // Markup already added (or not needed) 200 } // if 201 202 //XXX This test will break if that DokuWiki file gets renamed: 203 if (@file_exists(DOKU_INC . 'lib/exe/js.php')) { 204 // Assuming a fairly recent DokuWiki installation 205 // handling the plugin files on its own. 206 $this->_JSmarkup = TRUE; 207 return; 208 } // if 209 210 $localdir = realpath(dirname(__FILE__)) . '/'; 211 $webdir = DOKU_BASE . 'lib/plugins/nstoc/'; 212 $css = ''; 213 if (file_exists($localdir . 'style.css')) { 214 ob_start(); 215 @include($localdir . 'style.css'); 216 // Remove whitespace from CSS and expand IMG paths: 217 if ($css = preg_replace( 218 array('|\s*/\x2A.*?\x2A/\s*|s', '|\s*([:;\{\},+!])\s*|', 219 '|(?:url\x28\s*)([^/])|', '|^\s*|', '|\s*$|'), 220 array(' ', '\1', 'url(' . $webdir . '\1'), 221 ob_get_contents())) { 222 $css = '<style type="text/css">' . $css . '</style>'; 223 } // if 224 ob_end_clean(); 225 } // if 226 227 $js = (file_exists($localdir . 'script.js')) 228 ? '<script type="text/javascript" src="' 229 . $webdir . 'script.js"></script>' 230 : ''; 231 if ($this->_JSmarkup = $css . $js) { 232 // Place the additional markup at top'o'page: 233 $aRenderer->doc = $this->_JSmarkup 234 . preg_replace('|\s*<p>\s*</p>\s*|', '', $aRenderer->doc); 235 } else { 236 // Neither CSS nor JS files found. 237 // Set member field to skip tests with next call: 238 $this->_JSmarkup = TRUE; 239 } // if 240 } // _fixJS() 241 242 /** 243 * Get a list of the headlines in the given <tt>$aID</tt> page. 244 * 245 * <p> 246 * Each entry of the returned zero-based list is an array with the 247 * respective headline's level at index <tt>0</tt> (zero) 248 * and the headline's text at index <tt>1</tt> (one). 249 * </p> 250 * @param $aID String The wiki ID to process. 251 * @param $aStartLevel Integer The initial namespace depth. 252 * @param $aMaxLevel Integer The max. nesting level allowed. 253 * @param $aDecLevel Integer Number of levels to reduce the computed 254 * level of the returned entries; either <tt>0</tt> (zero) or <tt>1</tt>. 255 * @return Mixed An array (list) of headlines or <tt>FALSE</tt> 256 * if no headline markup was found. 257 * @private 258 * @see render() 259 */ 260 function _getHeadings(&$aID, &$aStartLevel, &$aMaxLevel, &$aDecLevel) { 261 $absLvl = $aStartLevel + $aMaxLevel; 262 // The prepended colon is essential to make sure we're always 263 // starting with level "1" even if processing a page/file in 264 // the root namespace: 265 $cl = substr_count(':' . $aID, ':'); 266 $hits = $result = array(); 267 if ($c = preg_match_all('|\n[ \t]*(={2,6}?)[\t ]*?([^=][^\n]*[^=])\s*?\1|U', 268 "\n" . io_readfile(wikiFN($aID), FALSE), $hits, PREG_SET_ORDER)) { 269 for ($i = 0; $c > $i; ++$i) { 270 if (($l = $cl + $this->_Hlevels[$hits[$i][1]]) 271 && ($l < $absLvl)) { 272 $result[] = array( 273 ($l - $aStartLevel) - $aDecLevel, 274 $hits[$i][2]); 275 } // if 276 unset($hits[$i]); // free mem 277 } // for 278 } // if 279 280 // Return the list only if there was something found: 281 return (0 < count($result)) 282 ? $result 283 : FALSE; 284 } // _getHeadings() 285 286 /** 287 * Resolve the given <tt>$aPath</tt> in relation to the specified 288 * <tt>$aNamespace</tt>. 289 * 290 * <p> 291 * This method tries to resolve <em>relative</em> and <em>absolute</em> 292 * pathnames depending on the given <tt>$aNamespace</tt> value. 293 * </p><p> 294 * Note that this implementation is not bulletproof but just uses 295 * string operations for its intended purpose. 296 * It's called by the public <tt>handle()</tt> method where further 297 * checks are applied. 298 * </p> 299 * @param $aNamespace String The base namespace of <tt>$aPath</tt>: 300 * @param $aPath String The (possibly relative) path to resolve. 301 * @return String The absolute namespace/page name. 302 * @private 303 * @since created 11-Aug-2007 304 * @see handle() 305 * @static 306 */ 307 function _path($aNamespace, $aPath) { 308 // Make sure the NS ends with a colon: 309 if ($len = strlen($aNamespace)) { 310 if (':' != $aNamespace[--$len]) { 311 $aNamespace .= ':'; 312 } // if 313 } else { 314 $aNamespace = ':'; 315 } // if 316 if ($len = strlen($aPath)) { 317 if ('.' == $aPath) { 318 return $aNamespace; 319 } // if 320 // Check for absolute path: 321 if (':' == $aPath[0]) { 322 return $aPath; 323 } // if 324 } else { 325 // Empty path => return current namespace: 326 return $aNamespace; 327 } // if 328 329 // Check for relative paths: 330 if ((1 < $len) 331 && ('.' == $aPath[0])) { 332 if (':' == $aPath[1]) { 333 return syntax_plugin_nstoc::_path($aNamespace, 334 substr($aPath, 2)); 335 } // if 336 337 if ('.' == $aPath{1}) { 338 // We use "preg_split()" instead of "explode()" to 339 // omit empty entries: 340 $path = preg_split('|:|', $aNamespace, -1, PREG_SPLIT_NO_EMPTY); 341 if (count($path)) { 342 // Remove the last NS element: 343 array_pop($path); 344 // Rebuild the whole NS path: 345 $aNamespace = implode(':', $path); 346 return ((2 < $len) && (':' == $aPath[2])) 347 ? syntax_plugin_nstoc::_path($aNamespace, 348 substr($aPath, 3)) 349 : syntax_plugin_nstoc::_path($aNamespace, 350 substr($aPath, 2)); 351 } // if 352 // Trying to go beyond the NS start ... 353 return ':'; 354 } // if 355 } // if 356 357 return $aNamespace . $aPath; 358 } // _path() 359 360 //@} 361 /** 362 * @publicsection 363 */ 364 //@{ 365 366 /** 367 * Tell the parser whether the plugin accepts syntax mode 368 * <tt>$aMode</tt> within its own markup. 369 * 370 * @param $aMode String The requested syntaxmode. 371 * @return Boolean <tt>FALSE</tt> always since no nested markup 372 * is possible with this plugin. 373 * @public 374 */ 375 function accepts($aMode) { 376 return FALSE; 377 } // accepts() 378 379 /** 380 * Connect lookup pattern to lexer. 381 * 382 * @param $aMode String The desired rendermode. 383 * @public 384 * @see render() 385 */ 386 function connectTo($aMode) { 387 $this->Lexer->addSpecialPattern('\x7B\x7Bnstoc\s+[^\}\n\r]*\x7D\x7D', 388 $aMode, 'plugin_nstoc'); 389 } // connectTo() 390 391 /** 392 * Get an associative array with plugin info. 393 * 394 * <p> 395 * The returned array holds the following fields: 396 * <dl> 397 * <dt>author</dt><dd>Author of the plugin</dd> 398 * <dt>email</dt><dd>Email address to contact the author</dd> 399 * <dt>date</dt><dd>Last modified date of the plugin in 400 * <tt>YYYY-MM-DD</tt> format</dd> 401 * <dt>name</dt><dd>Name of the plugin</dd> 402 * <dt>desc</dt><dd>Short description of the plugin (Text only)</dd> 403 * <dt>url</dt><dd>Website with more information on the plugin 404 * (eg. syntax description)</dd> 405 * </dl> 406 * @return Array Information about this plugin class. 407 * @public 408 * @static 409 */ 410 function getInfo() { 411 return array( 412 'author' => 'Matthias Watermann', 413 'email' => 'support@mwat.de', 414 'date' => '2010-02-21', 415 'name' => 'NsToC Syntax Plugin', 416 'desc' => 'Add a namespace\'s table of contents {' 417 . '{nstoc [namespace [maxdepth]]}}', 418 'url' => 'http://www.dokuwiki.org/plugin:nstoc'); 419 } // getInfo() 420 421 /** 422 * Define how this plugin is handled regarding paragraphs. 423 * 424 * @return String <tt>"block"</tt> (open paragraphs need to be closed 425 * before plugin output). 426 * @public 427 * @static 428 */ 429 function getPType() { 430 return 'block'; 431 } // getPType() 432 433 /** 434 * Where to sort in? 435 * 436 * @return Integer <tt>298</tt> 437 * (smaller <tt>Doku_Parser_Mode_internallink</tt>). 438 * @public 439 * @static 440 */ 441 function getSort() { 442 return 298; 443 } // getSort() 444 445 /** 446 * Get the type of syntax this plugin defines. 447 * 448 * @return String <tt>"substition"</tt> (i.e. <em>substitution</em>). 449 * @public 450 * @static 451 */ 452 function getType() { 453 return 'substition'; // sic! should be __substitution__ 454 } // getType() 455 456 /** 457 * Handler to prepare matched data for the rendering process. 458 * 459 * <p> 460 * The <tt>$aState</tt> parameter gives the type of pattern 461 * which triggered the call to this method: 462 * </p><dl> 463 * <dt>DOKU_LEXER_SPECIAL</dt> 464 * <dd>a pattern set by <tt>addSpecialPattern()</tt></dd> 465 * </dl><p> 466 * Any other <tt>$aState</tt> value results in a no-op. 467 * </p> 468 * @param $aMatch String The text matched by the patterns. 469 * @param $aState Integer The lexer state for the match; all states but 470 * DOKU_LEXER_SPECIAL are ignored by this implementation. 471 * @param $aPos Integer The character position of the matched text. 472 * @param $aHandler Object Reference to the Doku_Handler object. 473 * @return Array List of parsed data: Index 474 * <tt>[0]</tt> holds the current <tt>$aState</tt>, 475 * <tt>[1]</tt> the base namespace to process (possibly empty), 476 * <tt>[2]</tt> the allowed nesting depth, 477 * <tt>[3]</tt> the initial nesting depth of the given base namespace 478 * and <tt>[4]</tt> a flag indicating whether to start with a file 479 * (<tt>TRUE</tt>) or directory (<tt>FALSE</tt>). 480 * @public 481 * @see render() 482 * @static 483 */ 484 function handle($aMatch, $aState, $aPos, &$aHandler) { 485 if (DOKU_LEXER_SPECIAL != $aState) { 486 // This causes "render()" to do nothing ... 487 return array(DOKU_LEXER_EXIT); 488 } // if 489 490 // Extract the 0|1|2 arguments: 491 $args = ($aMatch = substr($aMatch, 7, -2)) 492 ? preg_split('|\s+|', $aMatch, -1, PREG_SPLIT_NO_EMPTY) 493 : NULL; 494 switch (count($args)) { 495 case 0: 496 $args = array('', 0); 497 break; 498 case 1: 499 if (is_numeric($args[0])) { 500 // There's a depth value only, make it numeric: 501 $args[1] = $args[0] * 1; 502 $args[0] = ''; 503 } else { 504 $args[0] = str_replace('/', ':', $args[0]); 505 // There's a namespace only, add depth value: 506 $args[1] = 0; 507 } // if 508 break; 509 default: 510 $args[0] = str_replace('/', ':', $args[0]); 511 // Make the (assumed) depth value numeric: 512 $args[1] *= 1; 513 break; 514 } // switch 515 516 // Compute current page and namespace: 517 $current = str_replace('/', ':', getID('id', FALSE)); 518 $dir = ''; 519 for ($f = strlen($current); 0 < $f; --$f) { 520 if (':' == $current[$f]) { 521 $dir = substr($current, 0, $f); 522 break; 523 } // if 524 } // for 525 526 // Resolve paths relative to current namespace 527 $args[0] = syntax_plugin_nstoc::_path($dir, $args[0]); 528 529 // Check whether we've got the index page of a namespace: 530 global $conf; 531 $idx = (empty($conf['start'])) 532 ? 'start' 533 : $conf['start']; 534 if ($args[0] == $idx) { 535 $args[0] = ''; 536 } else { 537 $idx = ':' . $idx; 538 $f = strlen($idx) * -1; 539 if (substr($args[0], $f) == $idx) { 540 $args[0] = substr($args[0], 0, $f); 541 } // if 542 } // if 543 544 $f = 0; // file flag 545 // Now check whether we've got in fact a valid namespace/page: 546 if ($ns = cleanID($args[0])) { 547 // To compute the actual nesting level we have to test 548 // whether the given ID refers to a file or directory. 549 if ($f = file_exists($fn = wikiFN($ns))) { 550 // If there is a file set the flag to FALSE if there's 551 // a directory (i.e. namespace) with the same name: 552 $f = (! is_dir(substr($fn, 0, -4))); 553 } // if 554 // Make the file flag's numeric so it's usable 555 // for computing the actual starting level: 556 $f *= 1; 557 // Compute the initial nesting level: 558 $args[0] = ($f) 559 ? 2 + substr_count($ns, ':') 560 : 1 + substr_count($ns, ':'); 561 } else { 562 // we're in the root namespace either explicitely or 563 // by an argument that resolved to root. 564 $args[0] = 1; 565 } // if 566 567 // Check the allowed nesting level value: 568 if (0 < $args[1]) { 569 if (! $f) { 570 // For directories we need extra levels 571 if ('' == $ns) { 572 ++$args[1]; 573 } else { 574 $args[1] += 2; 575 } // if 576 } // if 577 } else { 578 //XXX In case no depth argument was given we use a 579 // value of 255 which should be reasonably great enough 580 // (see "_doMarkup()"). 581 $args[1] = 0xff; 582 } // if 583 584 // Finally prepare the data used by "render()": 585 return array(DOKU_LEXER_SPECIAL, $ns, $args[1], $args[0], (bool)$f); 586 } // handle() 587 588 /** 589 * Handle the actual output creation. 590 * 591 * <p> 592 * The method checks for the given <tt>$aFormat</tt> and returns 593 * <tt>FALSE</tt> when a format isn't supported. 594 * <tt>$aRenderer</tt> contains a reference to the renderer object 595 * which is currently handling the rendering. 596 * The contents of <tt>$aData</tt> is the return value of the 597 * <tt>handle()</tt> method. 598 * </p><p> 599 * This implementation uses the precomputed values of <tt>$aData</tt> 600 * to generate a list of headlines marked up as a (X)HTML list. 601 * </p> 602 * @param $aFormat String The output format to generate. 603 * @param $aRenderer Object Reference to the <tt>Doku_Renderer_xhtml</tt> 604 * object to use. 605 * @param $aData Array The data created/returned by the 606 * <tt>handle()</tt> method. 607 * @return Boolean <tt>TRUE</tt> if rendered successfully, or 608 * <tt>FALSE</tt> otherwise. 609 * @public 610 * @see handle() 611 */ 612 function render($aFormat, &$aRenderer, &$aData) { 613 if ('xhtml' != $aFormat) { 614 return FALSE; // nothing to do for other formats 615 } // if 616 if (DOKU_LEXER_SPECIAL != $aData[0]) { 617 return TRUE; // nothing to do for other states 618 } // if 619 620 global $conf; 621 $ids = array(); 622 if ($aData[4]) { 623 // It's just a single file to process 624 $ids[0] = $aData[1]; 625 // The var is recycled to hold the level decrement value 626 // used by "_getHeadings()" to compute the actual LI level 627 // attribute: 628 $aData[1] = -1; 629 } else { 630 // Unfortunately the global "search()" function isn't able 631 // to use methods (even static class methods) but insists 632 // on an ordinary function to be passed as a calltime 633 // argument (at least up to DokuWiki 2006-03-05). 634 // To avoid polluting the global namespace even more than 635 // it already is we use a private member function which we 636 // can pass to DokuWiki's global "search()" function. 637 if (! $this->_callback) { 638 $idx = (empty($conf['start'])) 639 ? 'start' 640 : $conf['start']; 641 $iLen = (strlen($idx) + 1) * -1; // "+1" for the NS colon 642 // Here we filter out the "index" pages i.e. pages either 643 // named as configured in the global "$conf['start']" or 644 // with the same name as a sub-directory. 645 $this->_callback = create_function( 646 '&$aData, $aBase, $aFile, $aType, $aLvl, $opts', 647 'if (("f" == $aType) && (".txt" == substr($aFile, -4))' 648 . '&& (! is_dir($aBase . "/" . substr($aFile, 0, -4)))' 649 . '&& ($aFile = pathID($aFile)) && ($aFile != "' . $idx . '")' 650 . '&& (substr($aFile, ' . $iLen . ') != ":' . $idx . '")) {' 651 . '$aData[] = $aFile;}' 652 . 'return TRUE;'); 653 } // if 654 // Call DokuWiki's global search function: 655 if ('' == $aData[1]) { 656 search($ids, $conf['datadir'], $this->_callback, 657 FALSE, $aData[1], 0); 658 $aData[1] = 0; // setup level decrement for "_getHeadings()" 659 } else { 660 search($ids, $conf['datadir'], $this->_callback, 661 FALSE, str_replace(':', '/', $aData[1]), 0); 662 $aData[1] = 1; // setup level decrement for "_getHeadings()" 663 } // if 664 sort($ids); 665 } // if 666 667 global $USERINFO; 668 $g =& $USERINFO['grps']; // Preparing references saves array .. 669 $u =& $_SERVER['REMOTE_USER']; // .. lookups within the loops below. 670 $pages = array(); 671 // To avoid repeated boolean and regEx tests if unneeded 672 // we unroll the loop saving lots of CPU cycles. 673 if (empty($conf['hidepages'])) { 674 while (list($i, $entry) = each($ids)) { 675 unset($ids[$i]); // free mem 676 // Use only pages which are actually 677 // readable for the current user: 678 if ((0 < auth_aclcheck($entry, $u, $g)) 679 && ($i = $this->_getHeadings($entry, $aData[3], 680 $aData[2], $aData[1]))) { 681 $pages[$entry] = $i; 682 } // if 683 } // while 684 unset($entry, $i, $ids); // free mem 685 } else { 686 $re = '/' . $conf['hidepages'] . '/ui'; 687 while (list($i, $entry) = each($ids)) { 688 unset($ids[$i]); // free mem 689 // Use only pages which are actually readable for the 690 // current user and not supposed to be "hidden": 691 if ((0 < auth_aclcheck($entry, $u, $g)) 692 && (! preg_match($re, ':' . $entry)) 693 && ($i = $this->_getHeadings($entry, $aData[3], 694 $aData[2], $aData[1]))) { 695 $pages[$entry] = $i; 696 } // if 697 } // while 698 unset($entry, $i, $ids, $re); // free mem 699 } // if 700 701 if (0 < count($pages)) { 702 $this->_fixJS($aRenderer); // check for old DokuWiki versions 703 $aRenderer->doc .= $this->_doMarkup($pages); 704 } // if 705 706 return TRUE; 707 } // render() 708 709 //@} 710} // class syntax_plugin_nstoc 711} // if 712?> 713