12bef96e6SAndreas Gohr<?php 22bef96e6SAndreas Gohr 32bef96e6SAndreas Gohrnamespace dokuwiki\plugin\dw2pdf\src; 42bef96e6SAndreas Gohr 52bef96e6SAndreas Gohruse dokuwiki\Extension\Event; 62bef96e6SAndreas Gohr 72bef96e6SAndreas Gohrclass NamespaceCollector extends AbstractCollector 82bef96e6SAndreas Gohr{ 92bef96e6SAndreas Gohr protected string $namespace; 102bef96e6SAndreas Gohr protected $sortorder; 112bef96e6SAndreas Gohr protected int $depth; 122bef96e6SAndreas Gohr protected array $excludePages; 132bef96e6SAndreas Gohr protected array $excludeNamespaces; 142bef96e6SAndreas Gohr 152bef96e6SAndreas Gohr 162bef96e6SAndreas Gohr /** 172bef96e6SAndreas Gohr * Initialize variables from global input 182bef96e6SAndreas Gohr * 192bef96e6SAndreas Gohr * @return void 20*4c569113SAndreas Gohr * @throws ExportException When the requested namespace does not exist 212bef96e6SAndreas Gohr */ 222bef96e6SAndreas Gohr protected function initVars(): void 232bef96e6SAndreas Gohr { 24decfd8d6SAndreas Gohr $config = $this->getConfig(); 252bef96e6SAndreas Gohr 26decfd8d6SAndreas Gohr $this->namespace = $config->getBookNamespace(); 27decfd8d6SAndreas Gohr $this->sortorder = $config->getBookSortOrder(); 28decfd8d6SAndreas Gohr $this->depth = $config->getBookNamespaceDepth(); 292bef96e6SAndreas Gohr if ($this->depth < 0) $this->depth = 0; 30decfd8d6SAndreas Gohr $this->excludePages = $config->getBookExcludedPages(); 31decfd8d6SAndreas Gohr $this->excludeNamespaces = $config->getBookExcludedNamespaces(); 322bef96e6SAndreas Gohr 332bef96e6SAndreas Gohr // check namespace exists 342bef96e6SAndreas Gohr $nsdir = dirname(wikiFN($this->namespace . ':dummy')); 35*4c569113SAndreas Gohr if (!@is_dir($nsdir)) throw new ExportException('needns'); 362bef96e6SAndreas Gohr } 372bef96e6SAndreas Gohr 382bef96e6SAndreas Gohr /** 392bef96e6SAndreas Gohr * @inheritdoc 402bef96e6SAndreas Gohr * @triggers DW2PDF_NAMESPACEEXPORT_SORT 41*4c569113SAndreas Gohr * @throws ExportException When the requested namespace does not exist 422bef96e6SAndreas Gohr * @todo currently we do not support the 'at' parameter. We would need to search pages in the attic for this. 432bef96e6SAndreas Gohr */ 442bef96e6SAndreas Gohr protected function collect(): array 452bef96e6SAndreas Gohr { 462bef96e6SAndreas Gohr global $conf; 472bef96e6SAndreas Gohr 482bef96e6SAndreas Gohr $this->initVars(); 492bef96e6SAndreas Gohr 502bef96e6SAndreas Gohr //page search 512bef96e6SAndreas Gohr $result = []; 522bef96e6SAndreas Gohr $opts = ['depth' => $this->depth]; //recursive all levels 532bef96e6SAndreas Gohr $dir = utf8_encodeFN(str_replace(':', '/', $this->namespace)); 542bef96e6SAndreas Gohr search($result, $conf['datadir'], 'search_allpages', $opts, $dir); 552bef96e6SAndreas Gohr 562bef96e6SAndreas Gohr // remove excluded pages and namespaces 572bef96e6SAndreas Gohr $result = $this->excludePages($result); 582bef96e6SAndreas Gohr 592bef96e6SAndreas Gohr 602bef96e6SAndreas Gohr 612bef96e6SAndreas Gohr // Sort pages, let plugins modify sorting 622bef96e6SAndreas Gohr $eventData = ['pages' => &$result, 'sort' => $this->sortorder]; 632bef96e6SAndreas Gohr $event = new Event('DW2PDF_NAMESPACEEXPORT_SORT', $eventData); 642bef96e6SAndreas Gohr if ($event->advise_before()) { 652bef96e6SAndreas Gohr $result = $this->sortPages($result); 662bef96e6SAndreas Gohr } 672bef96e6SAndreas Gohr $event->advise_after(); 682bef96e6SAndreas Gohr 692bef96e6SAndreas Gohr // extract page ids 702bef96e6SAndreas Gohr $pages = array_column($result, 'id'); 712bef96e6SAndreas Gohr 722bef96e6SAndreas Gohr // if a there is a namespace start page outside the namespace, add it at the beginning 732bef96e6SAndreas Gohr if ($this->namespace !== '') { 742bef96e6SAndreas Gohr if (!in_array($this->namespace . ':' . $conf['start'], $pages, true)) { 752bef96e6SAndreas Gohr if (file_exists(wikiFN(rtrim($this->namespace, ':')))) { 762bef96e6SAndreas Gohr array_unshift($pages, rtrim($this->namespace, ':')); 772bef96e6SAndreas Gohr } 782bef96e6SAndreas Gohr } 792bef96e6SAndreas Gohr } 802bef96e6SAndreas Gohr 812bef96e6SAndreas Gohr return $pages; 822bef96e6SAndreas Gohr } 832bef96e6SAndreas Gohr 842bef96e6SAndreas Gohr /** 852bef96e6SAndreas Gohr * Remove excluded pages and namespaces from the given list of pages 862bef96e6SAndreas Gohr * 872bef96e6SAndreas Gohr * @param array $pages The list of pages as returned by search() 882bef96e6SAndreas Gohr * @return array The filtered list of pages 892bef96e6SAndreas Gohr */ 902bef96e6SAndreas Gohr protected function excludePages(array $pages) 912bef96e6SAndreas Gohr { 925340eaffSAndreas Gohr $pages = array_filter($pages, fn($page) => !in_array($page['id'], $this->excludePages)); 932bef96e6SAndreas Gohr $pages = array_filter($pages, function ($page) { 942bef96e6SAndreas Gohr foreach ($this->excludeNamespaces as $ns) { 955340eaffSAndreas Gohr if (str_starts_with($page['id'], $ns . ':')) { 962bef96e6SAndreas Gohr return false; 972bef96e6SAndreas Gohr } 982bef96e6SAndreas Gohr } 992bef96e6SAndreas Gohr return true; 1002bef96e6SAndreas Gohr }); 1012bef96e6SAndreas Gohr return $pages; 1022bef96e6SAndreas Gohr } 1032bef96e6SAndreas Gohr 1042bef96e6SAndreas Gohr /** 1052bef96e6SAndreas Gohr * Sort the given list of pages according to the selected sort order 1062bef96e6SAndreas Gohr * 1072bef96e6SAndreas Gohr * @param array $pages The list of pages as returned by search() 1082bef96e6SAndreas Gohr * @return array The sorted list of pages 1092bef96e6SAndreas Gohr */ 1102bef96e6SAndreas Gohr protected function sortPages(array $pages): array 1112bef96e6SAndreas Gohr { 1122bef96e6SAndreas Gohr $sortoptions = ['pagename', 'date', 'natural']; 1132bef96e6SAndreas Gohr if (!in_array($this->sortorder, $sortoptions)) { 1142bef96e6SAndreas Gohr $this->sortorder = 'natural'; 1152bef96e6SAndreas Gohr } 1162bef96e6SAndreas Gohr 1172bef96e6SAndreas Gohr if ($this->sortorder == 'date') { 1182bef96e6SAndreas Gohr usort($pages, [$this, 'cbDateSort']); 1192bef96e6SAndreas Gohr } else { 1202bef96e6SAndreas Gohr usort($pages, [$this, 'cbPagenameSort']); 1212bef96e6SAndreas Gohr } 1222bef96e6SAndreas Gohr 1232bef96e6SAndreas Gohr return $pages; 1242bef96e6SAndreas Gohr } 1252bef96e6SAndreas Gohr 1262bef96e6SAndreas Gohr /** 1272bef96e6SAndreas Gohr * usort callback to sort by file lastmodified time 1282bef96e6SAndreas Gohr * 1292bef96e6SAndreas Gohr * @param array $a 1302bef96e6SAndreas Gohr * @param array $b 1312bef96e6SAndreas Gohr * @return int 1322bef96e6SAndreas Gohr */ 1332bef96e6SAndreas Gohr public function cbDateSort($a, $b) 1342bef96e6SAndreas Gohr { 1352bef96e6SAndreas Gohr if ($b['rev'] < $a['rev']) { 1362bef96e6SAndreas Gohr return -1; 1372bef96e6SAndreas Gohr } 1382bef96e6SAndreas Gohr if ($b['rev'] > $a['rev']) { 1392bef96e6SAndreas Gohr return 1; 1402bef96e6SAndreas Gohr } 1412bef96e6SAndreas Gohr return strcmp($b['id'], $a['id']); 1422bef96e6SAndreas Gohr } 1432bef96e6SAndreas Gohr 1442bef96e6SAndreas Gohr /** 1452bef96e6SAndreas Gohr * usort callback to sort by page id 1462bef96e6SAndreas Gohr * @param array $a 1472bef96e6SAndreas Gohr * @param array $b 1482bef96e6SAndreas Gohr * @return int 1492bef96e6SAndreas Gohr */ 1502bef96e6SAndreas Gohr public function cbPagenameSort($a, $b) 1512bef96e6SAndreas Gohr { 1522bef96e6SAndreas Gohr global $conf; 1532bef96e6SAndreas Gohr 1542bef96e6SAndreas Gohr $partsA = explode(':', $a['id']); 1552bef96e6SAndreas Gohr $countA = count($partsA); 1562bef96e6SAndreas Gohr $partsB = explode(':', $b['id']); 1572bef96e6SAndreas Gohr $countB = count($partsB); 1582bef96e6SAndreas Gohr $max = max($countA, $countB); 1592bef96e6SAndreas Gohr 1602bef96e6SAndreas Gohr 1612bef96e6SAndreas Gohr // compare namepsace by namespace 1622bef96e6SAndreas Gohr for ($i = 0; $i < $max; $i++) { 1632bef96e6SAndreas Gohr $partA = $partsA[$i] ?: null; 1642bef96e6SAndreas Gohr $partB = $partsB[$i] ?: null; 1652bef96e6SAndreas Gohr 1662bef96e6SAndreas Gohr // have we reached the page level? 1672bef96e6SAndreas Gohr if ($i === ($countA - 1) || $i === ($countB - 1)) { 1682bef96e6SAndreas Gohr // start page first 1692bef96e6SAndreas Gohr if ($partA == $conf['start']) { 1702bef96e6SAndreas Gohr return -1; 1712bef96e6SAndreas Gohr } 1722bef96e6SAndreas Gohr if ($partB == $conf['start']) { 1732bef96e6SAndreas Gohr return 1; 1742bef96e6SAndreas Gohr } 1752bef96e6SAndreas Gohr } 1762bef96e6SAndreas Gohr 1772bef96e6SAndreas Gohr // prefer page over namespace 1782bef96e6SAndreas Gohr if ($partA === $partB) { 1792bef96e6SAndreas Gohr if (!isset($partsA[$i + 1])) { 1802bef96e6SAndreas Gohr return -1; 1812bef96e6SAndreas Gohr } 1822bef96e6SAndreas Gohr if (!isset($partsB[$i + 1])) { 1832bef96e6SAndreas Gohr return 1; 1842bef96e6SAndreas Gohr } 1852bef96e6SAndreas Gohr continue; 1862bef96e6SAndreas Gohr } 1872bef96e6SAndreas Gohr 1882bef96e6SAndreas Gohr 1892bef96e6SAndreas Gohr // simply compare 1902bef96e6SAndreas Gohr return strnatcmp($partA, $partB); 1912bef96e6SAndreas Gohr } 1922bef96e6SAndreas Gohr 1932bef96e6SAndreas Gohr return strnatcmp($a['id'], $b['id']); 1942bef96e6SAndreas Gohr } 1952bef96e6SAndreas Gohr} 196