├── VERSION ├── plugin.info.txt ├── README ├── make_tgz.sh ├── style.css └── syntax.php /VERSION: -------------------------------------------------------------------------------- 1 | 0.5a 2 | -------------------------------------------------------------------------------- /plugin.info.txt: -------------------------------------------------------------------------------- 1 | # General Plugin Info do not edit 2 | base dir 3 | author Jacobus Geluk 4 | email Jacobus.Geluk@gmail.com 5 | date 2014-07-15 6 | name dir 7 | desc Show content of current namespace, including sub namespaces and/or parent/sibling namespaces, in a table or list. 8 | url http://www.dokuwiki.org/plugin:dir 9 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ====== Dir Plugin for DokuWiki ====== 2 | 3 | All documentation for the Dir Plugin is available online at: 4 | 5 | * http://www.dokuwiki.org/plugin:dir 6 | 7 | This plugin requires DokuWiki 2007-06-26 or later. 8 | 9 | This version of Dir Plugin is modified to accept the argument namespacename 10 | to show the namespaces name instead of just "start". 11 | 12 | There's a Google Groups mailing list to discuss all issues: 13 | http://groups.google.com/group/dokuwiki-plugin-dir (outdated) 14 | 15 | GPL 2 License 16 | -------------------------------------------------------------------------------- /make_tgz.sh: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | # 3 | # Create the .tgz file ../plugin-dir.tgz 4 | # 5 | dokuwiki_root=/usr/local/www/dokuwiki 6 | dokuwiki_plugin_root="${dokuwiki_root}/lib/plugins" 7 | plugin_tgz_file="${dokuwiki_plugin_root}/plugin-dir.tgz" 8 | 9 | if [ -f "${plugin_tgz_file}" ] ; then 10 | rm -f "{plugin_tgz_file}" 11 | fi 12 | cd "${dokuwiki_plugin_root}" || exit 1 13 | 14 | # 15 | # Remove the backup files 16 | # 17 | rm -f dir/.* > /dev/null 2>&1 18 | # 19 | # Create the tar file 20 | # 21 | echo "Creating ${plugin_tgz_file}" 22 | tar --create -z --verbose --exclude 'manager.dat' --file "${plugin_tgz_file}" "./dir" 23 | echo rc=$? 24 | # 25 | # List the contents 26 | # 27 | echo "Contents of ${plugin_tgz_file}:" 28 | tar --list --file "${plugin_tgz_file}" 29 | 30 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /* 2 | Most of this style.css is copied from the pagelist plugin, 3 | keep it in line with that plugin... 4 | */ 5 | 6 | div.dokuwiki table.pagelist, 7 | div.dokuwiki table.ul { 8 | border: 0; 9 | padding: 0; 10 | border-spacing: 0; 11 | margin-bottom: 1em; 12 | border-collapse: collapse; 13 | } 14 | 15 | div.dokuwiki table.pagelist tr { 16 | border-top: 1px solid __border__; 17 | border-bottom: 1px solid __border__; 18 | } 19 | 20 | div.dokuwiki table.pagelist th, 21 | div.dokuwiki table.pagelist td { 22 | padding: 1px 1em 1px 0; 23 | } 24 | 25 | div.dokuwiki table.ul th, 26 | div.dokuwiki table.ul td { 27 | padding: 0 1em 0 0; 28 | } 29 | 30 | div.dokuwiki table.ul ul { 31 | margin: 0 0 0 1.5em; 32 | } 33 | 34 | div.dokuwiki table.pagelist th, 35 | div.dokuwiki table.ul th { 36 | background-color: __background_alt__; 37 | } 38 | 39 | div.dokuwiki th.dpage, 40 | div.dokuwiki th.page, 41 | div.dokuwiki th.date, 42 | div.dokuwiki th.user, 43 | div.dokuwiki th.desc, 44 | div.dokuwiki th.comments, 45 | div.dokuwiki th.tags, 46 | div.dokuwiki td.date, 47 | div.dokuwiki td.user, 48 | div.dokuwiki td.desc, 49 | div.dokuwiki td.comments, 50 | div.dokuwiki td.tags { 51 | color: __text_neu__; 52 | font-size: 80%; 53 | } 54 | 55 | div.dokuwiki th.date { 56 | text-align: right; 57 | } 58 | div.dokuwiki td.date { 59 | text-align: right; 60 | } 61 | 62 | div.dokuwiki td.dpage { 63 | vertical-align: top ; 64 | text-align: left ; 65 | } 66 | 67 | div.dokuwiki th.rowno { 68 | color: __text_neu__; 69 | font-size: 80%; 70 | text-align: right ; 71 | width: 5px ; 72 | } 73 | div.dokuwiki td.rowno { 74 | color: __text_neu__; 75 | font-size: 80%; 76 | text-align: right ; 77 | width: 5px ; 78 | } 79 | 80 | /* color for directory names in ucnames option */ 81 | div.dokuwiki td.dpage a.wikilink { 82 | /* color: __extern__; */ /* pre-dokuwiki default template */ 83 | color: __link__; /*dokuwiki template */ 84 | } 85 | -------------------------------------------------------------------------------- /syntax.php: -------------------------------------------------------------------------------- 1 | 7 | * @based_on "pageindex" plugin by Kite 8 | * @based_on "externallink" plugin by Otto Vainio 9 | * @based_on "pagelist" plugin by Esther Brunner 10 | * 11 | * Contributions by: 12 | * 13 | * - Jean-Philippe Prade 14 | * - Gunther Hartmann 15 | * - Sebastian Menge 16 | * - Matthias Schulte 17 | * - Geert Janssens 18 | * - Gerry Weißbach 19 | */ 20 | 21 | if(!defined('DOKU_INC')) { 22 | define ('DOKU_INC', realpath(dirname(__FILE__).'/../../').'/'); 23 | } 24 | if(!defined('DOKU_PLUGIN')) { 25 | define ('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); 26 | } 27 | 28 | require_once (DOKU_PLUGIN.'syntax.php'); 29 | require_once (DOKU_INC.'inc/search.php'); 30 | require_once (DOKU_INC.'inc/pageutils.php'); 31 | 32 | define ("DIR_PLUGIN_PATTERN", "DIR"); 33 | 34 | /** 35 | * The main DIR plugin class... 36 | */ 37 | class syntax_plugin_dir extends DokuWiki_Syntax_Plugin { 38 | var $debug = false; 39 | var $plugins = Array(); 40 | var $opts = Array(); 41 | var $cols = Array(); 42 | var $hdrs = Array(); 43 | var $pages = Array(); 44 | var $includeTags = Array(); 45 | var $excludeTags = Array(); 46 | var $hasTags = false; 47 | var $style = "default"; 48 | var $rdr = NULL; 49 | var $rdrMode = NULL; 50 | var $start = "start"; 51 | var $dformat = NULL; 52 | var $sortKeys = Array(); 53 | var $nbrOfSortKeys = 0; 54 | var $useDefaultTitle = true; 55 | var $modeIsXHTML = false; 56 | var $modeIsLatex = false; 57 | var $processedLatex = false; 58 | var $rowNumber = 0; 59 | var $ucnames = false; 60 | 61 | /** 62 | * Constructor 63 | */ 64 | function syntax_plugin_dir() { 65 | global $conf; 66 | 67 | // 68 | // In the config you can set allowdebug, but you can also 69 | // specify the debug attribute in the ~~DIR~~ line... 70 | // 71 | if($conf ["allowdebug"] == 1) 72 | $this->debug = true; 73 | 74 | $this->start = $conf ["start"]; 75 | $this->dformat = $conf ["dformat"]; 76 | $this->style = $this->getConf("style"); 77 | } 78 | 79 | /** 80 | * return some info 81 | */ 82 | function getInfo() { 83 | return array( 84 | 'author' => 'Jacobus Geluk', 85 | 'email' => 'Jacobus.Geluk@gmail.com', 86 | 'date' => '2008-06-28', 87 | 'name' => 'Dir Plugin', 88 | 'desc' => 'Shows pages in one or namespaces in a table or list', 89 | 'url' => 'http://www.dokuwiki.org/plugin:dir', 90 | ); 91 | } 92 | 93 | /** 94 | * What kind of syntax are we? 95 | */ 96 | function getType() { 97 | return "substition"; 98 | } 99 | 100 | /** 101 | * Just before build in links 102 | */ 103 | function getSort() { 104 | return 299; 105 | } 106 | 107 | /** 108 | * What about paragraphs? 109 | */ 110 | function getPType() { 111 | return "block"; 112 | } 113 | 114 | /** 115 | * Register the ~~DIR~~ verb... 116 | * Supported signatures: 117 | * 118 | * 1. ~~DIR~~ 119 | * 2. ~~DIR:...~~ 120 | * 3. ~~DIR?...~~ 121 | */ 122 | function connectTo($mode) { 123 | $this->Lexer->addSpecialPattern('~~'.DIR_PLUGIN_PATTERN.'~~', $mode, 'plugin_dir'); 124 | $this->Lexer->addSpecialPattern('~~'.DIR_PLUGIN_PATTERN.'[:?][^~]*~~', $mode, 'plugin_dir'); 125 | } 126 | 127 | /** 128 | * Handle the match 129 | */ 130 | function handle($match, $state, $pos, &$handler) { 131 | return preg_replace("%~~".DIR_PLUGIN_PATTERN.":(=(.*))?~~%", "\\2", $match); 132 | } 133 | 134 | /** 135 | * Initialize the current object for each rendering pass 136 | */ 137 | function _initRender($mode, &$renderer) { 138 | $rc = FALSE; 139 | $this->rowNumber = 0; 140 | $this->opts = Array(); 141 | $this->cols = Array(); 142 | $this->hdrs = Array(); 143 | $this->pages = Array(); 144 | $this->hasTags = false; 145 | $this->excludeTags = Array(); 146 | $this->includeTags = Array(); 147 | $this->rdr =& $renderer; 148 | $this->rdrMode = $mode; 149 | 150 | switch($mode) { 151 | case 'latex': 152 | $this->modeIsXHTML = false; 153 | $this->modeIsLatex = true; 154 | $rc = TRUE; 155 | break; 156 | case 'xhtml': 157 | $this->modeIsXHTML = true; 158 | $this->modeIsLatex = false; 159 | $rc = TRUE; 160 | break; 161 | default: 162 | $this->modeIsXHTML = false; 163 | $this->modeIsLatex = false; 164 | } 165 | 166 | return $rc; 167 | } 168 | 169 | /** 170 | * Create output 171 | */ 172 | function render($mode, &$renderer, $data) { 173 | if(!$this->_initRender($mode, $renderer)) return false; 174 | 175 | $rc = $this->_dir($data); 176 | 177 | if($this->modeIsLatex) $this->processedLatex = true; 178 | 179 | $this->_showDebugMsg("Leaving syntax_plugin_dir.render()"); 180 | 181 | return $rc; 182 | } 183 | 184 | /** 185 | * Put a debug message on screen... 186 | */ 187 | function _showDebugMsg($msg) { 188 | if(!$this->debug) return; 189 | 190 | if(is_array($msg)) { 191 | foreach($msg as $index => $m) { 192 | $this->_showDebugMsg("Array [$index]: ".$m); 193 | } 194 | return; 195 | } 196 | 197 | $this->_putNewLine(); 198 | 199 | switch($this->rdrMode) { 200 | case 'xhtml': 201 | $this->_put(DOKU_LF."~~"); 202 | $this->_put(DIR_PLUGIN_PATTERN."~~: ".hsc($msg).""); 203 | break; 204 | case 'latex': 205 | $this->_put(DOKU_LF."~~"); 206 | $this->_put(DIR_PLUGIN_PATTERN."~~: ".$msg); 207 | break; 208 | } 209 | } 210 | 211 | /** 212 | * Load the specified plugin (like the tag or discussion plugin) 213 | */ 214 | function _loadPlugin($plugin) { 215 | if(plugin_isdisabled($plugin)) 216 | return false; 217 | 218 | $plug = plugin_load('helper', $plugin); 219 | 220 | if(!$plug) { 221 | $this->_showDebugMsg("Plugin \"$plugin\" NOT loaded!"); 222 | return false; 223 | } 224 | 225 | $this->plugins [$plugin] = $plug; 226 | $this->_showDebugMsg("Plugin \"$plugin\" loaded!"); 227 | 228 | return true; 229 | } 230 | 231 | /** 232 | * Let another plugin generate the content... 233 | */ 234 | function _pluginCell($plugin, $id) { 235 | $plug = $this->plugins [$plugin]; 236 | 237 | if(!$plug) 238 | return 'Plugin '.$plugin.' not loaded!'; 239 | 240 | $html = $plug->td(cleanID($id)); 241 | 242 | return $html; 243 | } 244 | 245 | /** 246 | * Shows parsed options (in debug mode) 247 | */ 248 | function _parseOptionsShow($data, $dir, $ns) { 249 | if(!$this->debug) return; 250 | 251 | $this->_put(DOKU_LF.""); 252 | $this->_put(DOKU_LF." data = $data"); 253 | $this->_put(DOKU_LF." dir = $dir"); 254 | $this->_put(DOKU_LF." ns = $ns"); 255 | 256 | foreach($this->opts as $key => $opt) { 257 | if(is_array($opt)) { 258 | foreach($opt as $optkey => $optval) { 259 | $this->_put(DOKU_LF." opts[$key][$optkey] = $optval"); 260 | } 261 | } else if(is_bool($opt)) { 262 | $this->_put(DOKU_LF." opts[$key] = ".($opt ? "true" : "false")); 263 | } else { 264 | $this->_put(DOKU_LF." opts[$key] = $opt"); 265 | } 266 | } 267 | $this->_put(DOKU_LF.DOKU_LF."date: ".date("D M j G:i:s T Y")); 268 | $this->_put(DOKU_LF."".DOKU_LF); 269 | } 270 | 271 | /** 272 | * Get the namespace of the parent directory 273 | * (always prefixed and postfixed with a colon, root is ':') 274 | */ 275 | function _getParentNS($id) { 276 | // global $ID ; 277 | $curNS = getNS($id); 278 | 279 | if($curNS == '') return ':'; 280 | 281 | if(substr($curNS, 0, 1) != ':') { 282 | $curNS = ':'.$curNS; 283 | } 284 | 285 | return $curNS.':'; 286 | } 287 | 288 | /** 289 | * Create a fully qualified namespace from the specified one. 290 | * The second parameter must be true when the given namespace 291 | * is never a page id. In that case, the returned namespace 292 | * always ends with a colon. 293 | */ 294 | function _parseNS($ns, $mustBeNSnoPage) { 295 | global $ID; 296 | 297 | if(substr($ns, 0, 2) == '.:') { 298 | $ns = ':'.getNS($ID).substr($ns, 1); 299 | } elseif(substr($ns, 0, 3) == '..:') { 300 | $ns = $this->_getParentNS($ID).substr($ns, 3); 301 | } elseif($ns == '..') { 302 | $ns = $this->_getParentNS($ID); 303 | } elseif(substr($ns, 0, 1) == ':') { 304 | } elseif($ns == '.' || $ns == '*') { 305 | $ns = ':'.getNS($ID); 306 | } else { 307 | $ns = ':'.getNS($ID).':'.$ns; 308 | } 309 | 310 | if($mustBeNSnoPage && substr($ns, -1) <> ':') $ns .= ':'; 311 | 312 | return $ns; 313 | } 314 | 315 | /** 316 | * Convert namespace to its path 317 | */ 318 | function _ns2path($ns) { 319 | global $conf; 320 | 321 | if($ns == ':' || $ns == '') return $conf ['datadir']; 322 | 323 | $ns = trim($ns, ':'); 324 | 325 | $path = $conf ['datadir'].'/'.utf8_encodeFN(str_replace(':', '/', $ns)); 326 | 327 | return $path; 328 | } 329 | 330 | /** 331 | * Initialize the opts array... 332 | */ 333 | function _initOpts($flags) { 334 | $this->opts = array(); 335 | $this->opts ["noheader"] = false; 336 | $this->opts ["collapse"] = false; 337 | $this->opts ["ego"] = false; 338 | $this->opts ["namespacename"] = false; 339 | 340 | $flags = explode('&', $flags); 341 | 342 | foreach($flags as $index => $par) { 343 | $tmp = explode("=", $par); 344 | $key = $tmp [0]; 345 | $val = $tmp [1]; 346 | 347 | switch($key) { 348 | case "skip": 349 | case "cols": 350 | case "hdrs": 351 | case "sort": 352 | case "tag": 353 | $val = explode(';', trim($val, ';')); 354 | $this->_loadPlugin("tag"); 355 | break; 356 | case "noheader": 357 | case "nohead": 358 | case "nohdr": 359 | $key = "noheader"; 360 | $val = true; 361 | break; 362 | case "showheader": 363 | case "header": 364 | $key = "noheader"; 365 | $val = false; 366 | break; 367 | case "collapse": 368 | $key = "collapse"; 369 | $val = true; 370 | break; 371 | case "ego": 372 | $key = "ego"; 373 | $val = true; 374 | break; 375 | case "nodefaulttitle": 376 | case "ndt": 377 | $key = "nodefaulttitle"; 378 | $val = true; 379 | $this->useDefaultTitle = false; 380 | break; 381 | case "widedesc": 382 | $val = true; 383 | break; 384 | case "table": 385 | $this->style = "table"; 386 | break; 387 | case "list": 388 | $this->style = "list"; 389 | break; 390 | case "namespacename": 391 | $key = "namespacename"; 392 | $val = true; 393 | break; 394 | case "ucnames": 395 | $this->ucnames = true; 396 | break; 397 | 398 | case "debug": 399 | $this->debug = true; 400 | break; 401 | case "last": 402 | $key = 'maxrows'; 403 | $val = intval($val); 404 | break; 405 | } 406 | $this->opts [$key] = $val; 407 | } 408 | } 409 | 410 | /** 411 | * Check the supplied column names 412 | */ 413 | function _parseColumnNames() { 414 | if(is_array($this->opts ["cols"])) { 415 | $this->cols = $this->opts ["cols"]; 416 | } else { 417 | $this->cols = Array("page"); 418 | } 419 | 420 | if(count($this->cols) == 0) { 421 | $cols [] = "page"; 422 | $cols [] = "desc"; 423 | } 424 | 425 | $newCols = Array(); 426 | 427 | foreach($this->cols as $index => $col) { 428 | switch($col) { 429 | case "page": 430 | case "desc": 431 | case "user": 432 | case "userid": 433 | case "mdate": 434 | case "cdate": 435 | case "rowno": 436 | break; 437 | case "comments": 438 | $this->_loadPlugin("discussion"); 439 | break; 440 | case "tags": 441 | $this->_loadPlugin("tag"); 442 | break; 443 | case "date": 444 | $col = "mdate"; 445 | break; 446 | case "description": 447 | $col = "desc"; 448 | break; 449 | default: 450 | $this->_showDebugMsg("Unrecognized column name: \"$col\""); 451 | $col = ''; 452 | } 453 | if($col != '') { 454 | $this->_showDebugMsg("Recognized column name: $col"); 455 | $newCols [] = $col; 456 | } 457 | } 458 | $this->cols = $newCols; 459 | if(count($this->opts ["hdrs"]) != count($this->cols)) { 460 | $this->_showDebugMsg( 461 | "The number of specified headers (".count($this->opts ["hdrs"]). 462 | ") is not equal to the number of specified columns (". 463 | count($this->cols).")!" 464 | ); 465 | } 466 | } 467 | 468 | /** 469 | * Check the supplied tags 470 | */ 471 | function _parseTags() { 472 | $this->hasTags = false; 473 | 474 | if(is_array($this->opts ["tag"])) { 475 | foreach($this->opts ["tag"] as $tag) { 476 | if($tag == NULL || $tag == '') 477 | continue; 478 | $tag = mb_convert_case($tag, MB_CASE_LOWER, "UTF-8"); 479 | if(substr($tag, 0, 1) == '!') { 480 | $this->excludeTags [] = substr($tag, 1); 481 | } else { 482 | $this->includeTags [] = $tag; 483 | } 484 | $this->hasTags = true; 485 | } 486 | foreach($this->excludeTags as $tag) { 487 | $this->_showDebugMsg("Specified exclude tag: $tag"); 488 | } 489 | foreach($this->includeTags as $tag) { 490 | $this->_showDebugMsg("Specified include tag: $tag"); 491 | } 492 | } 493 | } 494 | 495 | /** 496 | * Check the supplied sort keys 497 | */ 498 | function _parseSortKeys() { 499 | if(is_array($this->opts ["sort"])) { 500 | $this->sortKeys = $this->opts ["sort"]; 501 | } 502 | 503 | $sortKeys = Array(); 504 | 505 | foreach($this->sortKeys as $index => $sortKey) { 506 | 507 | $array = explode('-', strtolower($sortKey)); 508 | if(count($array) == 1) { 509 | $array = Array($sortKey, "a"); 510 | } 511 | 512 | switch($array [1]) { 513 | case NULL: 514 | case "a": 515 | case "asc": 516 | case "ascending": 517 | $array [1] = false; 518 | break; 519 | case "d": 520 | case "desc": 521 | case "descending": 522 | $array [1] = true; 523 | break; 524 | default: 525 | $this->_showDebugMsg( 526 | "Unrecognized sort column name modifier: ". 527 | $array [1] 528 | ); 529 | $array [1] = false; 530 | } 531 | 532 | switch($array [0]) { 533 | case "page": 534 | case "desc": 535 | case "user": 536 | case "userid": 537 | case "mdate": 538 | case "cdate": 539 | case "rowno": 540 | break; 541 | case "comments": 542 | $this->_loadPlugin("discussion"); 543 | break; 544 | case "tags": 545 | $this->_loadPlugin("tag"); 546 | break; 547 | case "date": 548 | $array [0] = "mdate"; 549 | break; 550 | case "description": 551 | $array [0] = "desc"; 552 | break; 553 | default: 554 | $this->_showDebugMsg( 555 | "Unrecognized sort column name: ".$array [0] 556 | ); 557 | $array [0] = NULL; 558 | } 559 | if($array [0]) { 560 | $this->_showDebugMsg( 561 | "Sort column ".$array [0]." ". 562 | ($array [1] ? "descending" : "ascending") 563 | ); 564 | $sortKeys [] = $array; 565 | } 566 | } 567 | 568 | $this->sortKeys = $sortKeys; 569 | $this->nbrOfSortKeys = count($this->sortKeys); 570 | } 571 | 572 | /** 573 | * Add a page to the collection of $pages. Check first if it should 574 | * not be skipped... 575 | */ 576 | function _addFoundPage(&$data, $ns, $id, $type, $level) { 577 | global $ID; 578 | $fqid = $ns.$id; // Fully qualified id... 579 | 580 | // 581 | // If this file or directory should be skipped, do so 582 | // 583 | switch($type) { 584 | case "f": 585 | if(($fqid == ':'.$ID) && !$this->opts ["ego"]) // If we found ourself, skip it 586 | return false; 587 | $pageName = noNS($id); 588 | if($pageName == $this->start) 589 | return false; 590 | foreach($this->opts ["skipfqid"] as $index => $skipitem) { 591 | if($skipitem) { 592 | if($skipitem == $fqid) { 593 | // 594 | // Remove the skip rule, it has no use any more... 595 | // 596 | $this->opts ["skipfqid"] [$index] = NULL; 597 | $this->_showDebugMsg("Skipping $fqid due to skip rule $skipitem"); 598 | return false; 599 | } 600 | } 601 | } 602 | if($this->opts ["collapse"]) { 603 | // With collapse, only show: 604 | // - pages within the same namespace as the current page 605 | if($this->_getParentNS($fqid) != $this->_getParentNS($ID)) { 606 | return false; 607 | } 608 | } 609 | $linkid = $fqid; 610 | break; 611 | case "d": 612 | $fqid .= ':'; 613 | foreach($this->opts ["skipns"] as $skipitem) { 614 | if($skipitem == $fqid) { 615 | $this->_showDebugMsg("Skipping $fqid due to skip rule $skipitem"); 616 | return false; 617 | } 618 | } 619 | 620 | // Don't add startpages the user isn't authorized to read 621 | if(auth_quickaclcheck(substr($linkid, 1)) < AUTH_READ) 622 | return false; 623 | 624 | if($this->opts ["collapse"]) { 625 | // With collapse, only show: 626 | // - sibling namespaces of the current namespace and it's ancestors 627 | $curPathSplit = explode(":", trim(getNS($ID), ":")); 628 | $fqidPathSplit = explode(":", trim(getNS($fqid), ":")); 629 | 630 | // Find the last parent namespace that matches 631 | // If there is only one more child namespace in the namespace under evaluation, 632 | // Then this is a sibling of one of the parent namespaces of the current page. 633 | // Siblings are ok, grandchild namespaces and below should be skipped (for collapse). 634 | $clevel = 0; 635 | if(count($curPathSplit) > 0) { 636 | while(($clevel < count($fqidPathSplit) - 1) && ($clevel < count($curPathSplit))) { 637 | if($curPathSplit[$clevel] == $fqidPathSplit[$clevel]) { 638 | $clevel++; 639 | } else { 640 | break; 641 | } 642 | } 643 | } 644 | if(count($fqidPathSplit) > $clevel + 1) { 645 | return false; 646 | } 647 | } 648 | 649 | $linkid = $fqid.$this->start; 650 | break; 651 | } 652 | 653 | // $this->_showDebugMsg ("$level $type $ns$id:"); 654 | 655 | if($this->ucnames) { 656 | $fqid = str_replace('_'," ",$fqid); 657 | // $fqid = ltrim($fqid , ':'); 658 | $fqid = preg_replace_callback( 659 | '|:\w|', 660 | function ($matches) { 661 | return strtoupper($matches[0]); 662 | }, 663 | $fqid 664 | ); 665 | $fqid = ucwords($fqid); 666 | } 667 | $data [] = array( 668 | 'id' => $fqid, 669 | 'type' => $type, 670 | 'level' => $level, 671 | 'linkid' => $linkid, 672 | 'timestamp' => NULL 673 | ); 674 | 675 | return true; 676 | } 677 | 678 | /** 679 | * Callback method for the search function in _parseOptions 680 | */ 681 | function _searchDir(&$data, $base, $file, $type, $level, $opts) { 682 | global $ID; 683 | $ns = $opts ["ns"]; 684 | 685 | switch($type) { 686 | case "d": 687 | return $this->_addFoundPage($data, $ns, pathID($file), $type, $level); 688 | case "f": 689 | if(!preg_match('#\.txt$#', $file)) 690 | return false; 691 | //check ACL 692 | $id = pathID($file); 693 | if(auth_quickaclcheck($id) < AUTH_READ) 694 | return false; 695 | $this->_addFoundPage($data, $ns, $id, $type, $level); 696 | } 697 | 698 | return false; 699 | } 700 | 701 | /** 702 | * Parse the options after the ~~DIR: string and 703 | * return true if the table can be generated... 704 | * 705 | * A namespace specifaction should start with a colon and the flags 706 | * should start with a question mark, like this: 707 | * 708 | * ~~DIR[[:][?]]~~ 709 | * 710 | * To not break pages created with an older version of this plugin, this 711 | * syntax is also supported: 712 | * 713 | * ~~DIR:~~ 714 | * 715 | * This assumes that no other colon is put in 716 | */ 717 | function _parseOptions($data) { 718 | global $conf; 719 | global $ID; 720 | $ns = '.'; 721 | $flags = trim($data, '~'); 722 | $flags = substr($flags, strlen(DIR_PLUGIN_PATTERN)); 723 | $flags = trim($flags); 724 | 725 | $this->_showDebugMsg("specified arguments=".$flags); 726 | 727 | if( 728 | substr($flags, 0, 1) == ':' && 729 | strpos(substr($flags, 1), '?') === FALSE && 730 | strpos(substr($flags, 1), ':') === FALSE 731 | ) { 732 | // 733 | // This is the "old" syntax where flags do not start with a question mark 734 | // 735 | $this->_showDebugMsg("parseOptions A"); 736 | $flags = substr($flags, 1); 737 | } else if( 738 | substr($flags, 0, 1) == ':' && 739 | strpos(substr($flags, 1), '?') === FALSE 740 | ) { 741 | // 742 | // There is no questionmark so it's all namespace specification 743 | // 744 | $this->_showDebugMsg("parseOptions B"); 745 | $ns = substr($flags, 1); 746 | $flags = ''; 747 | } else if(substr($flags, 0, 1) == '?') { 748 | $this->_showDebugMsg("parseOptions C"); 749 | $flags = substr($flags, 1); 750 | } else if(strlen($flags) == 0) { 751 | $this->_showDebugMsg("parseOptions D"); 752 | } else if( 753 | strpos(substr($flags, 1), '?') !== FALSE 754 | ) { 755 | $this->_showDebugMsg("parseOptions E"); 756 | $tmp = explode('?', $flags); 757 | 758 | if(count($tmp) == 2) { 759 | $ns = substr($tmp [0], 1); 760 | $flags = $tmp [1]; 761 | } else { 762 | $this->_showDebugMsg("ERROR: Multiple questionmarks are not supported"); 763 | $flags = ''; 764 | } 765 | } else { 766 | $this->_showDebugMsg("parseOptions E"); 767 | $ns = $flags; 768 | $flags = ''; 769 | } 770 | 771 | $this->_showDebugMsg("specified namespace=$ns"); 772 | $this->_showDebugMsg("specified flags=$flags"); 773 | 774 | $ns = $this->_parseNS($ns, true); 775 | 776 | $path = $this->_ns2path($ns); 777 | $this->_showDebugMsg("path=$path"); 778 | 779 | $this->_initOpts($flags); 780 | $this->_parseColumnNames(); 781 | $this->_parseSortKeys(); 782 | $this->_parseTags(); 783 | 784 | // 785 | // Check the column headers 786 | // 787 | $this->hdrs = $this->cols; 788 | if(is_array($this->opts ["hdrs"])) { 789 | foreach($this->opts ["hdrs"] as $index => $hdr) { 790 | $this->hdrs [$index] = $hdr; 791 | } 792 | } 793 | 794 | // 795 | // Check the skip items 796 | // 797 | $this->opts ["skipfqid"] = Array(); 798 | $this->opts ["skipns"] = Array(); 799 | if(is_array($this->opts ["skip"])) { 800 | foreach($this->opts ["skip"] as $skipitem) { 801 | $item = $this->_parseNS($skipitem, false); 802 | if(substr($item, -1) == ":") { 803 | $this->opts ["skipns"] [] = $item; 804 | } else { 805 | $this->opts ["skipfqid"] [] = $item; 806 | } 807 | } 808 | } 809 | 810 | $this->_parseOptionsShow($data, $path, $ns); 811 | 812 | // 813 | // Search the directory $dir, only if the pages array 814 | // is empty, since we can pass here several times (xhtml, latex). 815 | // 816 | $this->_showDebugMsg("Search directory $path"); 817 | $this->_showDebugMsg("for namespace $ns"); 818 | 819 | if(count($this->pages) == 0) { 820 | search( 821 | $this->pages, // results 822 | $path, // folder root 823 | array($this, '_searchDir'), // handler 824 | array('ns' => $ns) // namespace 825 | ); 826 | } 827 | $count = count($this->pages); 828 | 829 | $this->_showDebugMsg("Found ".$count." pages!"); 830 | 831 | if($count == 0) { 832 | $this->_put(DOKU_LF."\t

There are no documents to show.

".DOKU_LF); 833 | return false; 834 | } 835 | 836 | $this->_sortResult(); 837 | if ( !empty($this->opts['maxrows']) && $this->opts['maxrows'] > 0 ) { 838 | $this->pages = array_slice($this->pages, 0, $this->opts['maxrows']); 839 | } 840 | 841 | return true; 842 | } 843 | 844 | /** 845 | * Sort the found pages according to the settings 846 | */ 847 | function _sortResult() { 848 | 849 | if($this->nbrOfSortKeys == 0) 850 | return; 851 | 852 | usort($this->pages, array($this, "_sortPage")); 853 | } 854 | 855 | /** 856 | * Compare function for usort 857 | */ 858 | function _sortPage($a, $b) { 859 | return $this->_sortPageByKey($a, $b, 0); 860 | } 861 | 862 | function _sortPageByKey(&$a, &$b, $index) { 863 | if($index >= $this->nbrOfSortKeys) 864 | return 0; 865 | 866 | $keyType = $this->sortKeys [$index]; 867 | $sortKeyA = $this->_getSortKey($a, $keyType [0]); 868 | $sortKeyB = $this->_getSortKey($b, $keyType [0]); 869 | 870 | if($sortKeyA == $sortKeyB) 871 | return $this->_sortPageByKey($a, $b, $index + 1); 872 | 873 | if($keyType [1]) { 874 | $tmp = $sortKeyA; 875 | $sortKeyA = $sortKeyB; 876 | $sortKeyB = $tmp; 877 | } 878 | 879 | return ($sortKeyA < $sortKeyB) ? -1 : 1; 880 | } 881 | 882 | /** 883 | * Produces a sortable key 884 | */ 885 | function _getSortKey(&$page, $keyType) { 886 | switch($keyType) { 887 | case "page": 888 | return html_wikilink($page ["id"]); 889 | case "desc": 890 | case "widedesc": 891 | return $this->_getMeta($page, "description", "abstract"); 892 | case "mdate": 893 | return $this->_getMeta($page, "date", "modified"); 894 | case "cdate": 895 | return $this->_getMeta($page, "date", "created"); 896 | case "user": 897 | $users = $this->_getMeta($page, "contributor"); 898 | if(is_array($users)) { 899 | $index = 0; 900 | foreach($users as $userid => $user) { 901 | if($user && $user <> "") { 902 | return $user; 903 | } 904 | } 905 | } 906 | return $users; 907 | case "userid": 908 | $users = $this->_getMeta($page, "contributor"); 909 | if(is_array($users)) { 910 | $index = 0; 911 | foreach($users as $userid => $user) { 912 | if($userid && $userid <> "") { 913 | return $userid; 914 | } 915 | } 916 | } 917 | return $users; 918 | case "comments": 919 | return $this->_pluginCell("discussion", $page ["linkid"]); 920 | case "tags": 921 | return $this->_pluginCell("tag", $page ["linkid"]); 922 | case "rowno": 923 | return '0'; 924 | } 925 | 926 | return NULL; 927 | } 928 | 929 | /** 930 | * Generate the content for the cell with the page link... 931 | */ 932 | function _tableCellContentID(&$page) { 933 | $fqid = $page ["id"]; 934 | $tmplvl = $page ["level"] - 1; 935 | $spacerWidth = $tmplvl * 20; 936 | $pageid = $fqid; 937 | $name = NULL; 938 | 939 | if($page ["type"] == 'd') { 940 | if($this->opts ["namespacename"]) { 941 | $pieces = explode(':', trim($pageid, ':')); 942 | $name = array_pop($pieces); 943 | } 944 | $pageid .= ':'.$this->start; 945 | } 946 | 947 | if(!$this->useDefaultTitle) { 948 | $name = explode(':', $fqid); 949 | $name = ucfirst($name [count($name) - 1]); 950 | } 951 | 952 | switch($this->rdrMode) { 953 | case 'latex': 954 | $this->rdr->internallink($pageid, $name); 955 | break; 956 | case 'xhtml': 957 | if($spacerWidth > 0) { 958 | $this->_put('
'); 959 | } 960 | 961 | if($page ["type"] == 'd' && $this->ucnames) { 962 | $dirlnk = html_wikilink($pageid, $name); 963 | $dirlnk = str_replace('wikilink2', 'wikilink',$dirlnk); 964 | $this->_put($dirlnk); 965 | } 966 | else $this->_put(html_wikilink($pageid, $name)); 967 | if($spacerWidth > 0) { 968 | $this->_put('
'); 969 | } 970 | break; 971 | } 972 | } 973 | 974 | /** 975 | * Get default value for an unset element 976 | */ 977 | function _getMeta(&$page, $key1, $key2 = NULL) { 978 | if(!isset ($page ["meta"])) 979 | $page ["meta"] = p_get_metadata($page ["linkid"], false, true); 980 | 981 | // 982 | // Use "created" instead of "modified" if null 983 | // 984 | if( 985 | $key1 == "date" && 986 | $key2 == "modified" && 987 | !isset ($page ["meta"]["date"]["modified"]) 988 | ) { 989 | $key2 = "created"; 990 | } 991 | // 992 | // Return "creator" if "contributor" is null 993 | // 994 | if($key1 == "contributor" && !isset ($page ["meta"]["contributor"])) { 995 | $key1 = "creator"; 996 | } 997 | 998 | if(is_string($key2)) return $page ["meta"] [$key1] [$key2]; 999 | 1000 | return $page ["meta"] [$key1]; 1001 | } 1002 | 1003 | /** 1004 | * Generate the table cell content... 1005 | */ 1006 | function _tableCellContent(&$page, $col) { 1007 | switch($col) { 1008 | case "page": 1009 | $this->_tableCellContentID($page); 1010 | break; 1011 | case "desc": 1012 | case "widedesc": 1013 | $this->_put($this->_getMeta($page, "description", "abstract")); 1014 | break; 1015 | case "mdate": 1016 | $this->_putDate($this->_getMeta($page, "date", "modified")); 1017 | break; 1018 | case "cdate": 1019 | $this->_putDate($this->_getMeta($page, "date", "created")); 1020 | break; 1021 | case "user": 1022 | $users = $this->_getMeta($page, "contributor"); 1023 | if(is_array($users)) { 1024 | $index = 0; 1025 | foreach($users as $userid => $user) { 1026 | if($user && $user <> '') { 1027 | if($index++ > 0) { 1028 | $this->_putNewLine(); 1029 | } 1030 | $this->_put($user); 1031 | } 1032 | } 1033 | } 1034 | break; 1035 | case "userid": 1036 | $users = $this->_getMeta($page, "contributor"); 1037 | if(is_array($users)) { 1038 | $index = 0; 1039 | foreach($users as $userid => $user) { 1040 | if($userid && $userid <> '') { 1041 | if($index++ > 0) { 1042 | $this->_putNewLine(); 1043 | } 1044 | $this->_put($userid); 1045 | } 1046 | } 1047 | } 1048 | break; 1049 | case "comments": 1050 | if(!$this->modeIsLatex) 1051 | $this->_put($this->_pluginCell("discussion", $page ["linkid"])); 1052 | break; 1053 | case "tags": 1054 | if(!$this->modeIsLatex) 1055 | $this->_put($this->_pluginCell("tag", $page ["linkid"])); 1056 | break; 1057 | case "rowno": 1058 | $this->_put($this->rowNumber); 1059 | break; 1060 | default: 1061 | $this->_put($col); 1062 | } 1063 | } 1064 | 1065 | /** 1066 | * Rewrite of renderer->table_open () because of class 1067 | */ 1068 | function _tableOpen() { 1069 | $rdr = $this->rdr; 1070 | 1071 | if($this->modeIsLatex) { 1072 | $rdr->_current_tab_cols = 0; 1073 | if($rdr->info ['usetablefigure'] == "on") { 1074 | $this->_putCmdNl("begin{figure}[h]"); 1075 | } else { 1076 | $this->_putCmdNl("vspace{0.8em}"); 1077 | } 1078 | $rdr->putcmd("begin{tabular}"); 1079 | $rdr->put("{"); 1080 | foreach($this->hdrs as $index => $hdr) { 1081 | $rdr->put("l"); 1082 | if($index + 1 < sizeof($this->hdrs)) 1083 | $rdr->put('|'); 1084 | } 1085 | $rdr->putnl("}"); 1086 | return; 1087 | } 1088 | 1089 | switch($this->style) { 1090 | case "table": 1091 | $class = "inline"; 1092 | break; 1093 | case "list": 1094 | $class = "ul"; 1095 | break; 1096 | default: 1097 | $class = "pagelist"; 1098 | } 1099 | $this->_showDebugMsg("Style=".$this->style." table class=$class"); 1100 | $this->_put(DOKU_LF.''.DOKU_LF); 1101 | } 1102 | 1103 | /** 1104 | * Rewrite of renderer->table_close () 1105 | */ 1106 | function _tableClose() { 1107 | if($this->modeIsLatex) { 1108 | $this->rdr->tabular_close(); 1109 | return; 1110 | } 1111 | 1112 | $this->_put(DOKU_LF.'
'.DOKU_LF); 1113 | 1114 | // Removed table_close() because it add's a too much 1115 | // $this->rdr->table_close () ; 1116 | } 1117 | 1118 | /** 1119 | * Rewrite of renderer->tableheader_open () because of class 1120 | */ 1121 | function _tableHeaderCellOpen($class) { 1122 | if($this->modeIsLatex) 1123 | return; 1124 | 1125 | $this->_put(DOKU_LF.DOKU_TAB.DOKU_TAB.''); 1126 | } 1127 | 1128 | /** 1129 | * Rewrite of renderer->tableheader_close () 1130 | */ 1131 | function _tableHeaderCellClose($index) { 1132 | if($this->modeIsLatex) { 1133 | if(($index + 1) == sizeof($this->hdrs)) { 1134 | $this->rdr->putnl('\\\\'); 1135 | } else { 1136 | $this->rdr->put('&'); 1137 | } 1138 | return; 1139 | } 1140 | return $this->rdr->tableheader_close(); 1141 | } 1142 | 1143 | /** 1144 | * Rewrite of renderer->tablecell_open () because of class 1145 | */ 1146 | function _tableCellOpen($colspan, $class) { 1147 | if($this->modeIsLatex) return; 1148 | 1149 | $this->_put(DOKU_LF.DOKU_TAB.DOKU_TAB.' 1) 1152 | $this->_put(' colspan="'.$colspan.'"'); 1153 | 1154 | $this->_put(">"); 1155 | } 1156 | 1157 | /** 1158 | * Rewrite of renderer->tablecell_close () because of class 1159 | */ 1160 | function _tableCellClose($index) { 1161 | if($this->modeIsLatex) { 1162 | if(($index + 1) == sizeof($this->hdrs)) { 1163 | $this->rdr->putnl('\\\\'); 1164 | } else { 1165 | $this->rdr->put('&'); 1166 | } 1167 | return; 1168 | } 1169 | 1170 | return $this->rdr->tablecell_close(); 1171 | } 1172 | 1173 | /** 1174 | * Return the class name to be used for the showing $col. 1175 | */ 1176 | function _getCellClassForCol($col) { 1177 | switch($col) { 1178 | case "page": 1179 | return "dpage"; 1180 | case "date": 1181 | case "user": 1182 | case "desc": 1183 | case "comments": 1184 | case "tags": 1185 | case "rowno": 1186 | return $col; 1187 | case "mdate": 1188 | case "cdate": 1189 | return "date"; 1190 | case "userid": 1191 | return "user"; 1192 | case "": 1193 | return ""; 1194 | } 1195 | $this->_showDebugMsg("Unknown style class for col $col"); 1196 | return "desc"; 1197 | } 1198 | 1199 | function _tableHeaderRowOpen() { 1200 | if($this->modeIsLatex) { 1201 | $this->_putCmdNl('hline'); 1202 | return; 1203 | } 1204 | 1205 | $this->rdr->tablerow_open(); 1206 | } 1207 | 1208 | function _tableHeaderRowClose() { 1209 | if($this->modeIsLatex) { 1210 | $this->_putCmdNl('hline'); 1211 | return; 1212 | } 1213 | 1214 | $this->rdr->tablerow_close(); 1215 | } 1216 | 1217 | function _tableRowOpen() { 1218 | if($this->modeIsLatex) 1219 | return; 1220 | 1221 | $this->rdr->tablerow_open(); 1222 | } 1223 | 1224 | function _tableRowClose() { 1225 | if($this->modeIsLatex) 1226 | return; 1227 | 1228 | $this->rdr->tablerow_close(); 1229 | } 1230 | 1231 | /** 1232 | * Return true if the tag plugin is not loaded, 1233 | * if the ~~DIR~~ line has no tag attribute or 1234 | * if the given page has one of the specified tags. 1235 | */ 1236 | function _hasTag($page) { 1237 | if(!$this->hasTags) return true; 1238 | 1239 | $plug = $this->plugins ['tag']; 1240 | if(!$plug) return true; 1241 | 1242 | // Get the tags of the current page 1243 | $tmp = $this->_getMeta($page, "subject"); 1244 | 1245 | if(!is_array($tmp)) return false; 1246 | 1247 | $tags = Array(); 1248 | 1249 | // Convert them to lowercase 1250 | foreach($tmp as $tag) { 1251 | $tags [] = mb_convert_case($tag, MB_CASE_LOWER, "UTF-8"); 1252 | } 1253 | 1254 | # 1255 | # If there is an intersection with the exclude tags then we can not show 1256 | # the current document 1257 | # 1258 | if(count($this->excludeTags) > 0) { 1259 | if(count(array_intersect($tags, $this->excludeTags)) > 0) { 1260 | $this->_showDebugMsg('skip'); 1261 | return false; 1262 | } 1263 | } 1264 | 1265 | # If the intersection with the include tags is not equal (in size) to the 1266 | # array of include tags, we must skip the current document. 1267 | if(count($this->includeTags) > 0) { 1268 | $intersection = array_intersect($tags, $this->includeTags); 1269 | if(count($intersection) != count($this->includeTags)) { 1270 | return false; 1271 | } 1272 | } 1273 | 1274 | return true; 1275 | } 1276 | 1277 | /** 1278 | * Generate the actual table content... 1279 | */ 1280 | function _tableContent() { 1281 | $doWideDesc = $this->opts ["widedesc"]; 1282 | 1283 | if(!$this->opts ["noheader"]) { 1284 | $this->_tableHeaderRowOpen(); 1285 | foreach($this->hdrs as $index => $hdr) { 1286 | $this->_tableHeaderCellOpen( 1287 | $this->_getCellClassForCol($this->cols [$index]) 1288 | ); 1289 | $this->_put($hdr); 1290 | $this->_tableHeaderCellClose($index); 1291 | } 1292 | $this->_tableHeaderRowClose(); 1293 | } 1294 | 1295 | foreach($this->pages as $page) { 1296 | 1297 | if(!$this->_hasTag($page)) continue; 1298 | 1299 | $this->rowNumber += 1; 1300 | 1301 | $this->_tableRowOpen(); 1302 | foreach($this->cols as $index => $col) { 1303 | $this->_tableCellOpen(1, $this->_getCellClassForCol($col)); 1304 | $this->_tableCellContent($page, $col); 1305 | $this->_tableCellClose($index); 1306 | } 1307 | $this->_tableRowClose(); 1308 | if($doWideDesc) { 1309 | $this->_tableRowOpen(); 1310 | $this->_tableCellOpen(count($this->cols), "desc"); 1311 | $this->_tableCellContent($page, "widedesc"); 1312 | $this->_tableCellClose(0); 1313 | $this->_tableRowClose(); 1314 | } 1315 | } 1316 | } 1317 | 1318 | /** 1319 | * Write data to the output stream 1320 | */ 1321 | function _put($data) { 1322 | if($data == NULL || $data == '') 1323 | return; 1324 | 1325 | switch($this->rdrMode) { 1326 | case 'xhtml': 1327 | $this->rdr->doc .= $data; 1328 | break; 1329 | case 'latex': 1330 | $this->rdr->put($data); 1331 | break; 1332 | } 1333 | } 1334 | 1335 | /** 1336 | * Write a date to the output stream 1337 | */ 1338 | function _putDate($date) { 1339 | $this->_put(strftime($this->dformat, $date)); 1340 | } 1341 | 1342 | function _putCmdNl($cmd) { 1343 | $this->rdr->putcmdnl($cmd); 1344 | } 1345 | 1346 | function _putNewLine() { 1347 | if($this->modeIsLatex) { 1348 | $this->_putCmdNl('newline'); 1349 | } else { 1350 | $this->_put('
'); 1351 | } 1352 | } 1353 | 1354 | /** 1355 | * Do the real work 1356 | */ 1357 | function _dir($data) { 1358 | if(!$this->_parseOptions($data)) 1359 | return false; 1360 | 1361 | // 1362 | // If we already did the latex pass, skip the xhtml pass 1363 | // 1364 | if($this->processedLatex && $this->rdrMode == 'xhtml') { 1365 | //return false ; 1366 | } 1367 | 1368 | // 1369 | // Generate the actual table... 1370 | // 1371 | $this->_tableOpen(); 1372 | $this->_tableContent(); 1373 | $this->_tableClose(); 1374 | 1375 | return true; 1376 | } 1377 | 1378 | } // syntax_plugin_dir 1379 | 1380 | //Setup VIM: ex: et ts=2 enc=utf-8 : 1381 | --------------------------------------------------------------------------------