xref: /plugin/dw2pdf/src/NamespaceCollector.php (revision 4c5691130dbf9035b960f10f5b3cb4b319a026d8)
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