1<?php 2 3use dokuwiki\Utf8\PhpString; 4use dokuwiki\Utf8\Sort; 5 6/** 7 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 8 * @author Esther Brunner <wikidesign@gmail.com> 9 * @author Gina Häußge <osd@foosel.net> 10 */ 11class helper_plugin_pagelist extends DokuWiki_Plugin 12{ 13 14 /** @var string table style: 'default', 'table', 'list' */ 15 protected $style = ''; 16 /** @var bool whether heading line is shown */ 17 protected $showheader = false; 18 /** @var bool whether first headline/title is shown in the page column */ 19 protected $showfirsthl = false; 20 21 /** 22 * @var array with entries: 'columnname' => bool/int enabled/disable column, something also config setting 23 * @deprecated 2022-08-17 still public, will change to protected. Use addColumn() and modifyColumn() instead. 24 */ 25 public $column = []; 26 /** 27 * @var array with entries: 'columnname' => language strings for table headers as html for in th 28 * @deprecated 2022-08-17 still public, will change to protected, use setHeader() instead. 29 */ 30 public $header = []; 31 32 /** 33 * Associated array, where the keys are the sortkey. 34 * For each row an array is added which must contain at least the key 'id', can further contain as well : 35 * 'title', 'date', 'user', 'desc', 'summary', 'comments', 'tags', 'status' and 'priority', see addPage() for details 36 * @var array[] array of arrays with the entries: 'columnname' => value, or if plugin the html for in cell 37 */ 38 protected $pages = []; 39 40 /** 41 * data of the current processed page row 42 * see addPage() for details 43 * 44 * @var null|array with entries: 'columnname' => value or if plugin html for in cell, null if no lines processed 45 * @deprecated 2022-08-17 still public, will change to protected, use addPage() instead 46 */ 47 public $page = null; 48 49 /** 50 * @var bool enables sorting. If no sortkey was given, 'id' is used. 51 * @deprecated 2022-08-17 still public, will change to protected, use setFlags() instead 52 */ 53 public $sort = false; 54 /** 55 * @var bool Reverses the sort 56 * @deprecated 2022-08-17 still public, will change to protected, use setFlags()instead 57 */ 58 public $rsort = false; 59 60 /** 61 * @var string the item to use as key for sorting 62 */ 63 private $sortKey; 64 /** 65 * @var string let plugins set their own default without already enabling sorting 66 */ 67 private $defaultSortKey = 'id'; 68 69 /** 70 * @var array with entries: 'pluginname' => ['columnname1', 'columnname2'], registers the available columns per plugin 71 */ 72 protected $plugins = []; 73 74 /** @var string final html output */ 75 protected $doc = ''; 76 77 /** @var null|mixed data retrieved from metadata array for the current processed page */ 78 protected $meta = null; 79 80 /** @var array @deprecated 2022-08-17 still used by very old plugins */ 81 public $_meta = null; 82 83 /** @var helper_plugin_pageimage */ 84 protected $pageimage = null; 85 /** @var helper_plugin_discussion */ 86 protected $discussion = null; 87 /** @var helper_plugin_linkback */ 88 protected $linkback = null; 89 /** @var helper_plugin_tag */ 90 protected $tag = null; 91 92 /** 93 * @var int limits the number of rows shown, 0 is all. 94 */ 95 private $limit; 96 97 /** 98 * Constructor gets default preferences 99 * 100 * These can be overriden by plugins using this class 101 */ 102 public function __construct() 103 { 104 $this->style = $this->getConf('style'); //string 105 $this->showheader = $this->getConf('showheader'); //on-off 106 $this->showfirsthl = $this->getConf('showfirsthl'); //on-off 107 $this->sort = $this->getConf('sort'); //on-off 108 $this->rsort = $this->getConf('rsort'); //on-off 109 $this->sortKey = $this->getConf('sortby'); //string 110 if($this->sortKey) { 111 $this->sort = true; 112 } 113 114 $this->plugins = [ 115 'discussion' => ['comments'], 116 'linkback' => ['linkbacks'], 117 'tag' => ['tags'], 118 'pageimage' => ['image'], 119 ]; 120 121 $this->column = [ 122 'page' => true, 123 'date' => $this->getConf('showdate'), //0,1,2 124 'user' => $this->getConf('showuser'), //0,1,2,3,4 125 'desc' => $this->getConf('showdesc'), //0,160,500 126 'summary' => false, 127 'comments' => $this->getConf('showcomments'), //on-off 128 'linkbacks' => $this->getConf('showlinkbacks'), //on-off 129 'tags' => $this->getConf('showtags'), //on-off 130 'image' => $this->getConf('showimage'), //on-off 131 'diff' => $this->getConf('showdiff'), //on-off 132 ]; 133 134 $this->header = []; 135 $this->limit = 0; 136 } 137 138 public function getMethods() 139 { 140 $result = []; 141 $result[] = [ 142 'name' => 'addColumn', 143 'desc' => '(optional) adds an extra column for plugin data', 144 'params' => [ 145 'plugin name' => 'string', 146 'column key' => 'string' 147 ], 148 ]; 149 $result[] = [ 150 'name' => 'modifyColumn', 151 'desc' => '(optional) override value of an existing column, value equal to false disables column', 152 'params' => [ 153 'column key' => 'string', 154 'value' => 'int|bool' 155 ], 156 ]; 157 $result[] = [ 158 'name' => 'setHeader', 159 'desc' => '(optional) Provide header data, if not given default values or [plugin]->th() is used', 160 'params' => [ 161 'column key' => 'string', 162 'value' => 'int|bool' 163 ], 164 ]; 165 $result[] = [ 166 'name' => 'setFlags', 167 'desc' => '(optional) overrides default flags, or en/disable existing columns', 168 'params' => ['flags' => 'array'], 169 'return' => ['success' => 'boolean'], 170 ]; 171 $result[] = [ 172 'name' => 'startList', 173 'desc' => '(required) prepares the table header for the page list', 174 ]; 175 $result[] = [ 176 'name' => 'addPage', 177 'desc' => '(required) adds a page to the list', 178 'params' => ["page attributes, 'id' required, others optional" => 'array'], 179 ]; 180 $result[] = [ 181 'name' => 'finishList', 182 'desc' => '(required) returns the XHTML output', 183 'return' => ['xhtml' => 'string'], 184 ]; 185 return $result; 186 } 187 188 /** 189 * (optional) Adds an extra column named $col for plugin $plugin. 190 * The data for the extra column is provided via: 191 * 1) extra entry to setHeader([]) or addPage([]) 192 * 2) or, alternatively, by the plugin $plugin that implements a helper component with the functions: 193 * - th($col, &$class=null) or th() 194 * - td($id, $col=null, &$class=null) or td($id) 195 * 196 * 197 * @param string $plugin plugin name 198 * @param string $col column name. Assumption: unique between all builtin columns and plugin supplied columns 199 */ 200 public function addColumn($plugin, $col) 201 { 202 //prevent duplicates if adding a column of already listed plugins 203 if (!isset($this->plugins[$plugin]) || !in_array($col, $this->plugins[$plugin])) { 204 $this->plugins[$plugin][] = $col; 205 } 206 $this->column[$col] = true; 207 } 208 209 /** 210 * (optional) Allow to override the column values e.g. to disable a column 211 * 212 * @param string $col column name 213 * @param int|bool $value must evaluate to false/true for dis/enabling column. Sometimes value is used for specific setting 214 * @see $column 215 */ 216 public function modifyColumn($col, $value) 217 { 218 if (isset($this->column[$col])) { 219 $this->column[$col] = $value; 220 } 221 } 222 223 /** 224 * (optional) Provide header data, if not given for built-in columns localized strings are used, or for plugins the th() function 225 * @param array $header entries, if not given default values or plugin->th() is used 226 * @return void 227 * @see $column the keys of $header should match the keys of $column 228 * 229 */ 230 public function setHeader($header) 231 { 232 if (is_array($header)) { 233 $this->header = $header; 234 } 235 } 236 237 /** 238 * (Optional) Overrides standard values for style, showheader and show(column) settings 239 * 240 * @param string[] $flags 241 * possible flags: 242 * for styling: 'default', 'table', 'list', 'simplelist' 243 * for dis/enabling header: '(no)header', and show titel for page column with '(no)firsthl', 244 * for sorting: 'sort', 'rsort', 'nosort', 'sortby=<columnname>' 245 * for dis/enabling columns: accepts keys of $column, e.g. default: '(no)date', 'user', 'desc', 'summary', 246 * 'comments', 'linkbacks', 'tags', 'image', 'diff' 247 * @return bool, false if no array given 248 */ 249 public function setFlags($flags) 250 { 251 if (!is_array($flags)) return false; 252 253 foreach ($flags as $flag) { 254 switch ($flag) { 255 case 'default': 256 $this->style = 'default'; 257 break; 258 case 'table': 259 $this->style = 'table'; 260 break; 261 case 'list': 262 $this->style = 'list'; 263 break; 264 case 'simplelist': 265 $this->style = 'simplelist'; // Displays pagenames only, no other information 266 break; 267 case 'header': 268 $this->showheader = true; 269 break; 270 case 'noheader': 271 $this->showheader = false; 272 break; 273 case 'firsthl': 274 $this->showfirsthl = true; 275 break; 276 case 'nofirsthl': 277 $this->showfirsthl = false; 278 break; 279 case 'sort': 280 $this->sort = true; //sort by pageid 281 $this->rsort = false; 282 break; 283 case 'rsort': 284 $this->sort = true; //reverse sort on key, not sure if that is by pageid 285 $this->rsort = true; 286 break; 287 case 'nosort': 288 $this->sort = false; 289 $this->rsort = false; 290 break; 291 case 'showdiff': 292 $flag = 'diff'; 293 break; 294 } 295 296 // it is not required to set the sort flag, rsort flag will reverse. 297 // $flag should be an existing column, not checked here as addColumn() is maybe called later then setFlags()? 298 if (substr($flag, 0, 7) == 'sortby=') { 299 $this->sortKey = substr($flag, 7); 300 $this->sort = true; 301 } 302 //for plugins to propose a default value for the sortby flag 303 if (substr($flag, 0, 14) == 'defaultsortby=') { 304 $this->defaultSortKey = substr($flag, 14); 305 } 306 if (substr($flag, 0, 6) == 'limit=') { 307 $this->limit = (int) substr($flag, 6); 308 } 309 310 /** @see $column array, enable/disable columns */ 311 if (substr($flag, 0, 2) == 'no') { 312 $value = false; 313 $flag = substr($flag, 2); 314 } else { 315 $value = true; 316 } 317 318 if (isset($this->column[$flag]) && $flag !== 'page') { 319 $this->column[$flag] = $value; 320 } 321 } 322 if ($this->sortKey === '' && $this->sort) { 323 $this->sortKey = $this->defaultSortKey; 324 } 325 return true; 326 } 327 328 /** 329 * (required) Sets the list header 330 * 331 * @param null|string $callerClass 332 * @return bool 333 */ 334 public function startList($callerClass = null) 335 { 336 337 // table style 338 switch ($this->style) { 339 case 'table': 340 $class = 'inline'; 341 break; 342 case 'list': 343 $class = 'ul'; 344 break; 345 case 'simplelist': 346 $class = false; 347 break; 348 default: 349 $class = 'pagelist'; 350 } 351 352 if ($class) { 353 $class .= ' plgn__pglist'; 354 if ($callerClass) { 355 $class .= ' ' . $callerClass; 356 } 357 $this->doc = '<div class="table"><table class="' . $class . '">'; 358 } else { 359 // Simplelist is enabled; Skip header and firsthl 360 $this->showheader = false; 361 $this->showfirsthl = false; 362 363 $this->doc = '<ul>'; 364 } 365 366 $this->page = null; 367 $this->pages = []; 368 369 // check if some plugins are available - if yes, load them! 370 foreach ($this->plugins as $plugin => $columns) { 371 foreach ($columns as $col) { 372 if (!$this->column[$col]) continue; 373 374 if (!$this->$plugin = $this->loadHelper($plugin)) { 375 $this->column[$col] = false; 376 } 377 } 378 } 379 380 // header row 381 if ($this->showheader) { 382 $this->doc .= '<tr>'; 383 $columns = ['page', 'date', 'user', 'desc', 'diff', 'summary']; 384 //image column first 385 if ($this->column['image']) { 386 if (empty($this->header['image'])) { 387 $this->header['image'] = hsc($this->pageimage->th('image')); 388 } 389 $this->doc .= '<th class="images">' . $this->header['image'] . '</th>'; 390 } 391 //pagelist columns 392 foreach ($columns as $col) { 393 if ($this->column[$col]) { 394 if (empty($this->header[$col])) { 395 $this->header[$col] = hsc($this->getLang($col)); 396 } 397 $this->doc .= '<th class="' . $col . '">' . $this->header[$col] . '</th>'; 398 } 399 } 400 //plugin columns 401 foreach ($this->plugins as $plugin => $columns) { 402 foreach ($columns as $col) { 403 if ($this->column[$col] && $col != 'image') { 404 if (empty($this->header[$col])) { 405 $this->header[$col] = hsc($this->$plugin->th($col, $class)); 406 } 407 $this->doc .= '<th class="' . $col . '">' . $this->header[$col] . '</th>'; 408 } 409 } 410 } 411 $this->doc .= '</tr>'; 412 } 413 return true; 414 } 415 416 /** 417 * (required) Add page row to the list, call for every row. In the $page array is 'id' required, other entries are optional. 418 * 419 * @param array $page 420 * 'id' => (required) string page id 421 * 'title' => string First headline, otherwise page id; exception: if titleimage is used this is used for the image title&alt attribute 422 * 'titleimage' => string media id 423 * 'date' => int timestamp of creation date, otherwise modification date (e.g. sometimes needed for plugin) 424 * 'user' => string $meta['creator'] 425 * 'desc' => string $meta['description']['abstract'] 426 * 'description' => string description set via pagelist syntax 427 * 'summary' => string summary of the last change of the page $meta['last_change']['sum'] 428 * 'exists' => bool page_exists($id) 429 * 'perm' => int auth_quickaclcheck($id) 430 * 'draft' => string $meta['type'] set by blog plugin 431 * 'priority' => string priority of task: 'low', 'medium', 'high', 'critical' 432 * 'class' => string class set for each row 433 * 'file' => string wikiFN($id) 434 * 'section' => string id of section, added as #ancher to page url 435 * further key-value pairs for columns set by plugins (optional), if not defined th() and td() of plugin are called 436 * @return bool, false if no id given 437 */ 438 public function addPage($page) 439 { 440 $id = $page['id']; 441 if (!$id) return false; 442 $this->page = $page; 443 $this->meta = null; // do all metadata calls in addPage() 444 445 if ($this->style != 'simplelist') { 446 if (!isset($this->page['draft'])) { 447 $this->page['draft'] = $this->getMeta('type') == 'draft'; 448 } 449 $this->getPageData($id); 450 451 if (!empty($this->column['date'])) { 452 $this->getDate(); 453 } 454 if (!empty($this->column['user'])) { 455 $this->getUser(); 456 } 457 if (!empty($this->column['desc'])) { 458 $this->getDescription(); 459 } 460 if (!empty($this->column['summary'])) { 461 $this->getSummary(); 462 } 463 } 464 465 $sortKey = $this->getSortKey($id); 466 if (!blank($sortKey)) { 467 //unique key needed, otherwise entries are overwritten 468 $sortKey = $this->uniqueKey($sortKey, $this->pages); 469 $this->pages[$sortKey] = $this->page; 470 } else { 471 $this->pages[] = $this->page; 472 } 473 return true; 474 } 475 476 /** 477 * Non-recursive function to check whether an array key is unique 478 * 479 * @param int|string $key 480 * @param array $result 481 * @return float|int|string 482 * 483 * @author Ilya S. Lebedev <ilya@lebedev.net> 484 * @author Esther Brunner <wikidesign@gmail.com> 485 */ 486 protected function uniqueKey($key, $result) 487 { 488 // increase numeric keys by one 489 if (is_numeric($key)) { 490 while (array_key_exists($key, $result)) { 491 $key++; 492 } 493 return $key; 494 495 // append a number to literal keys 496 } else { 497 $num = 0; 498 $testkey = $key; 499 while (array_key_exists($testkey, $result)) { 500 $testkey = $key . $num; 501 $num++; 502 } 503 return $testkey; 504 } 505 } 506 507 /** 508 * Prints html of a list row, call for every row 509 * 510 * @param array $page see for details @see addPage() 511 * @return void 512 */ 513 protected function renderPageRow($page) 514 { 515 $this->page = $page; 516 $this->meta = null; // should not be used here 517 518 $id = $this->page['id']; 519 if ($this->style == 'simplelist') { 520 // simplelist is enabled; just output pagename 521 $this->doc .= '<li>'; 522 if (page_exists($id)) { 523 $class = 'wikilink1'; 524 } else { 525 $class = 'wikilink2'; 526 } 527 528 if (empty($this->page['title'])) { 529 $this->page['title'] = str_replace('_', ' ', noNS($id)); 530 } 531 $title = hsc($this->page['title']); 532 533 $content = '<a href="' . wl($id) . '" class="' . $class . '" title="' . $id . '">' . $title . '</a>'; 534 $this->doc .= $content; 535 $this->doc .= '</li>'; 536 return; 537 } 538 // default pagelist, list or table style: 539 540 // priority and draft 541 $class = ''; 542 if (isset($this->page['priority'])) { 543 $class .= 'priority' . $this->page['priority'] . ' '; 544 } 545 if (!empty($this->page['draft'])) { 546 $class .= 'draft '; 547 } 548 if (!empty($this->page['class'])) { 549 $class .= $this->page['class']; 550 } 551 552 if (!empty($class)) { 553 $class = ' class="' . $class . '"'; 554 } 555 556 $this->doc .= '<tr' . $class . '>'; 557 //image column first 558 if (!empty($this->column['image'])) { 559 $this->printPluginCell('pageimage', 'image', $id); 560 } 561 $this->printPageCell($id); 562 563 if (!empty($this->column['date'])) { 564 $this->printDateCell(); 565 } 566 if (!empty($this->column['user'])) { 567 $this->printUserCell(); 568 } 569 if (!empty($this->column['desc'])) { 570 $this->printDescriptionCell(); 571 } 572 if (!empty($this->column['diff'])) { 573 $this->printDiffCell($id); 574 } 575 if (!empty($this->column['summary'])) { 576 $this->printSummary(); 577 } 578 foreach ($this->plugins as $plugin => $columns) { 579 foreach ($columns as $col) { 580 if (!empty($this->column[$col]) && $col != 'image') { 581 $this->printPluginCell($plugin, $col, $id); 582 } 583 } 584 } 585 $this->doc .= '</tr>'; 586 } 587 588 /** 589 * (required) Sort pages and render these. 590 * Sets the list footer, reset helper to defaults 591 * 592 * @return string html 593 */ 594 public function finishList() 595 { 596 if ($this->sort) { 597 Sort::ksort($this->pages); 598 if ($this->rsort) { 599 $this->pages = array_reverse($this->pages, true); 600 } 601 } 602 603 $cnt = 0; 604 foreach ($this->pages as $page) { 605 $this->renderPageRow($page); 606 607 $cnt++; 608 if($this->limit > 0 && $cnt >= $this->limit){ 609 break; 610 } 611 } 612 613 if ($this->style == 'simplelist') { 614 $this->doc .= '</ul>'; 615 } else { 616 if (!isset($this->page)) { 617 $this->doc = ''; 618 } else { 619 $this->doc .= '</table></div>'; 620 } 621 } 622 623 // reset defaults 624 $this->__construct(); 625 626 return $this->doc; 627 } 628 629 /* ---------- Private Methods ---------- */ 630 631 /** 632 * Page title / link to page 633 * 634 * @param string $id page id displayed in this table row 635 * @return bool whether empty 636 */ 637 protected function printPageCell($id) 638 { 639 if ($this->page['exists']) { 640 $class = 'wikilink1'; 641 } else { 642 $class = 'wikilink2'; 643 } 644 645 // handle image and text titles 646 if (!empty($this->page['titleimage'])) { 647 $title = '<img src="' . ml($this->page['titleimage']) . '" class="media"'; 648 if (!empty($this->page['title'])) { 649 $title .= ' title="' . hsc($this->page['title']) . '" alt="' . hsc($this->page['title']) . '"'; 650 } 651 $title .= ' />'; 652 } else { 653 $title = hsc($this->page['title']); 654 } 655 656 // produce output 657 $section = !empty($this->page['section']) ? '#' . $this->page['section'] : ''; 658 $content = '<a href="' . wl($id) . $section . '" class="' . $class . '" title="' . $id . '" data-wiki-id="' . $id . '">' . $title . '</a>'; 659 if ($this->style == 'list') { 660 $content = '<ul><li>' . $content . '</li></ul>'; 661 } 662 return $this->printCell('page', $content); 663 } 664 665 /** 666 * Date - creation or last modification date if not set otherwise 667 * 668 * @return bool whether empty 669 */ 670 protected function printDateCell() 671 { 672 global $conf; 673 674 if (empty($this->page['date']) || empty($this->page['exists'])) { 675 return $this->printCell('date', ''); 676 } else { 677 return $this->printCell('date', dformat($this->page['date'], $conf['dformat'])); 678 } 679 } 680 681 /** 682 * User - page creator or contributors if not set otherwise 683 * 684 * @return bool whether empty 685 */ 686 protected function printUserCell() 687 { 688 return $this->printCell('user', $this->page['user']); 689 } 690 691 /** 692 * Internal function to get user column as set in 'showuseras' config option. 693 * 694 * @param string $login_name 695 * @return string whether empty 696 */ 697 private function getShowUserAsContent($login_name) 698 { 699 if (function_exists('userlink')) { 700 $content = userlink($login_name); 701 } else { 702 $content = editorinfo($login_name); 703 } 704 return $content; 705 } 706 707 /** 708 * Description - (truncated) auto abstract if not set otherwise 709 * 710 * @return bool whether empty 711 */ 712 protected function printDescriptionCell() 713 { 714 $desc = $this->page['desc']; 715 716 $max = $this->column['desc']; 717 if ($max > 1 && PhpString::strlen($desc) > $max) { 718 $desc = PhpString::substr($desc, 0, $max) . '…'; 719 } 720 return $this->printCell('desc', hsc($desc)); 721 } 722 723 /** 724 * Diff icon / link to diff page 725 * 726 * @param string $id page id displayed in this table row 727 * @return bool whether empty 728 */ 729 protected function printDiffCell($id) 730 { 731 // check for page existence 732 if (!isset($this->page['exists'])) { 733 if (!isset($this->page['file'])) { 734 $this->page['file'] = wikiFN($id); 735 } 736 $this->page['exists'] = @file_exists($this->page['file']); 737 } 738 739 // produce output 740 $url_params = []; 741 $url_params ['do'] = 'diff'; 742 $url = wl($id, $url_params) . (!empty($this->page['section']) ? '#' . $this->page['section'] : ''); 743 $content = '<a href="' . $url . '" class="diff_link"> 744 <img src="' . DOKU_BASE . 'lib/images/diff.png" width="15" height="11" 745 title="' . hsc($this->getLang('diff_title')) . '" alt="' . hsc($this->getLang('diff_alt')) . '"/> 746 </a>'; 747 return $this->printCell('diff', $content); 748 } 749 750 /** 751 * Print the summary from the last page change 752 */ 753 protected function printSummary() 754 { 755 return $this->printCell('summary', hsc($this->page['summary'])); 756 } 757 758 /** 759 * Plugins - respective plugins must be installed! 760 * 761 * @param string $plugin pluginname 762 * @param string $col column name. Before not provided to td of plugin. Since 2022. Allows different columns per plugin. 763 * @param string $id page id displayed in this table row 764 * @return bool whether empty 765 */ 766 protected function printPluginCell($plugin, $col, $id) 767 { 768 if (!isset($this->page[$col])) { 769 $this->page[$col] = $this->$plugin->td($id, $col); 770 } 771 return $this->printCell($col, $this->page[$col]); 772 } 773 774 /** 775 * Produce XHTML cell output 776 * 777 * @param string $class class per td 778 * @param string $content html 779 * @return bool whether empty 780 */ 781 protected function printCell($class, $content) 782 { 783 if (!$content) { 784 $content = ' '; 785 $empty = true; 786 } else { 787 $empty = false; 788 } 789 $this->doc .= '<td class="' . $class . '">' . $content . '</td>'; 790 return $empty; 791 } 792 793 794 /** 795 * Get default value for an unset element 796 * 797 * @param string $key one key of metadata array 798 * @param string $subkey second key as subkey of metadata array 799 * @return false|mixed content of the metadata (sub)array 800 */ 801 protected function getMeta($key, $subkey = null) 802 { 803 if (empty($this->page['exists']) || empty($this->page['id'])) { 804 return false; 805 } 806 if (!isset($this->meta)) { 807 $this->meta = p_get_metadata($this->page['id'], '', METADATA_RENDER_USING_CACHE); 808 } 809 810 if ($subkey === null) { 811 return $this->meta[$key] ?? null; 812 } else { 813 return $this->meta[$key][$subkey] ?? null; 814 } 815 } 816 817 /** 818 * Retrieve page related data 819 * 820 * @param string $id page id 821 */ 822 private function getPageData($id) 823 { 824 // check for page existence 825 if (!isset($this->page['exists'])) { 826 if (!isset($this->page['file'])) { 827 $this->page['file'] = wikiFN($id); 828 } 829 $this->page['exists'] = @file_exists($this->page['file']); 830 } 831 //retrieve title, but not if titleimage which can have eventually its own title 832 if (empty($this->page['titleimage'])) { 833 //not overwrite titles in earlier provided data 834 if (blank($this->page['title']) && $this->showfirsthl) { 835 $this->page['title'] = $this->getMeta('title'); 836 } 837 838 if (blank($this->page['title'])) { 839 $this->page['title'] = str_replace('_', ' ', noNSorNS($id)); 840 } 841 } 842 } 843 844 /** 845 * Retrieve description 846 */ 847 private function getDescription() 848 { 849 if (array_key_exists('desc', $this->page)) return; 850 851 if (strlen($this->page['description']) > 0) { 852 // This condition will become true, when a page-description is given 853 // inside the pagelist plugin syntax-block 854 $desc = $this->page['description']; 855 } else { 856 //supports meta stored by the Description plugin 857 $desc = $this->getMeta('plugin_description', 'keywords'); 858 859 //use otherwise the default dokuwiki abstract 860 if (!$desc) { 861 $desc = $this->getMeta('description', 'abstract'); 862 } 863 if (blank($desc)) { 864 $desc = ''; 865 } 866 } 867 $this->page['desc'] = $desc; 868 } 869 870 private function getSummary() 871 { 872 if (array_key_exists('summary', $this->page)) return; 873 874 $summary = $this->getMeta('last_change', 'sum'); 875 $this->page['summary'] = $summary; 876 } 877 /** 878 * Retrieve user 879 */ 880 private function getUser() 881 { 882 if (array_key_exists('user', $this->page)) return; 883 884 $content = null; 885 switch ($this->column['user']) { 886 case 1: 887 $content = $this->getMeta('creator'); 888 $content = hsc($content); 889 break; 890 case 2: 891 $users = $this->getMeta('contributor'); 892 if (is_array($users)) { 893 $content = join(', ', $users); 894 $content = hsc($content); 895 } 896 break; 897 case 3: 898 $content = $this->getShowUserAsContent($this->getMeta('user')); 899 break; 900 case 4: 901 $users = $this->getMeta('contributor'); 902 if (is_array($users)) { 903 $content = ''; 904 $item = 0; 905 foreach ($users as $userid => $fullname) { 906 $item++; 907 $content .= $this->getShowUserAsContent($userid); 908 if ($item < count($users)) { 909 $content .= ', '; 910 } 911 } 912 } 913 break; 914 } 915 $this->page['user'] = $content; 916 } 917 918 /** 919 * Retrieve date 920 */ 921 private function getDate() 922 { 923 if (empty($this->page['date']) && !empty($this->page['exists'])) { 924 if ($this->column['date'] == 2) { 925 $this->page['date'] = $this->getMeta('date', 'modified'); 926 } else { 927 $this->page['date'] = $this->getMeta('date', 'created'); 928 } 929 } 930 } 931 932 /** 933 * Determines the sortkey if sorting is requested 934 * 935 * @param string $id page id 936 * @return string 937 */ 938 private function getSortKey($id) 939 { 940 $sortKey = ''; 941 if ($this->sortKey !== '') { 942 $sortKey = $this->page[$this->sortKey] ?? false; 943 if ($sortKey === false) { 944 //entry corresponding to sortKey is not yet set 945 if ($this->sortKey == "draft") { 946 $this->page['draft'] = $this->getMeta('type') == 'draft'; 947 } 948 $this->getPageData($id); 949 if ($this->sortKey == "pagename") { 950 $this->page['pagename'] = noNS($id); 951 } 952 if ($this->sortKey == "ns") { 953 // sorts pages before namespaces using a zero byte 954 // see https://github.com/dokufreaks/plugin-tag/commit/7df7f2cb315c5a3a21b9dfacae89bd3ee661c690 955 $pos = strrpos($id, ':'); 956 if ($pos === false) { 957 $sortkey = "\0" . $id; 958 } else { 959 $sortkey = substr_replace($id, "\0\0", $pos, 1); 960 } 961 $this->page['ns'] = str_replace(':', "\0", $sortkey); 962 } 963 if ($this->sortKey == "date") { 964 $this->getDate(); 965 } 966 if ($this->sortKey == "desc") { 967 $this->getDescription(); 968 } 969 if ($this->sortKey == "summary") { 970 $this->getSummary(); 971 } 972 if ($this->sortKey == "user") { 973 $this->getUser(); 974 } 975 foreach ($this->plugins as $plugin => $columns) { 976 foreach ($columns as $col) { 977 if ($this->sortKey == $col) { 978 if (!isset($this->page[$col])) { 979 $this->page[$col] = $this->$plugin->td($id, $col); 980 } 981 } 982 } 983 } 984 $sortKey = $this->page[$this->sortKey] ?? 9999999999999999; //TODO mostly used for non-existing pages. 999 works only for dates? 985 } 986 } 987 return $sortKey; 988 } 989 990} 991