namespace = $namespace; $this->maxdepth = $maxdepth; } /** @inheritdoc */ public function generate(): void { $this->generated = true; $this->top = new Top(); // add directly to top or add the namespace under the top element? if ($this->hasFlag(self::FLAG_SELF_TOP)) { $parent = $this->createNamespaceNode($this->namespace, noNS($this->namespace)); $parent->setParent($this->top); } else { if ($this->hasFlag(self::FLAG_NS_AS_STARTPAGE)) { // do not add the namespace's own startpage in this mode $this->startpages[$this->getStartpage($this->namespace)] = 1; } $parent = $this->top; } // if FLAG_SELF_TOP, we need to run a recursion decision on the parent if ($parent instanceof Top || $this->applyRecursionDecision($parent, 0)) { $dir = $this->namespacePath($this->namespace); $this->createHierarchy($parent, $dir, $this->maxdepth); } // if FLAG_SELF_TOP, we need to add the parent to the top if (!$parent instanceof Top) { $this->addNodeToHierarchy($this->top, $parent); } } /** * Recursive function to create the page hierarchy * * @param AbstractNode $parent results are added as children to this element * @param string $dir The directory relative to the page directory * @param int $depth Current depth, recursion stops at 0 * @return void */ protected function createHierarchy(AbstractNode $parent, string $dir, int $depth) { // Process namespaces (subdirectories) if ($this->hasNotFlag(self::FLAG_NO_NS)) { $this->processNamespaces($parent, $dir, $depth); } // Process pages (files) if ($this->hasNotFlag(self::FLAG_NO_PAGES)) { $this->processPages($parent, $dir); } } /** * Process namespaces (subdirectories) and add them to the hierarchy * * @param AbstractNode $parent Parent node to add children to * @param string $dir Current directory path * @param int $depth Current depth level * @return void */ protected function processNamespaces(AbstractNode $parent, string $dir, int $depth) { global $conf; $base = $conf['datadir'] . '/'; $dirs = glob($base . $dir . '/*', GLOB_ONLYDIR); foreach ($dirs as $subdir) { $subdir = basename($subdir); $id = pathID($dir . '/' . $subdir); $node = $this->createNamespaceNode($id, $subdir); // Recurse into subdirectory if depth and filter allows if ($depth !== 0 && $this->applyRecursionDecision($node, $this->maxdepth - $depth)) { $this->createHierarchy($node, $dir . '/' . $subdir, $depth - 1); } // Add to hierarchy $this->addNodeToHierarchy($parent, $node); } } /** * Create a namespace node based on the flags * * @param string $id * @param string $title * @return AbstractNode */ protected function createNamespaceNode(string $id, string $title): AbstractNode { if ($this->hasFlag(self::FLAG_NS_AS_STARTPAGE)) { $ns = $id; $id = $this->getStartpage($id); // use the start page for the namespace $this->startpages[$id] = 1; // mark as seen $node = new WikiStartpage($id, $title, $ns); } else { $node = new WikiNamespace($id, $title); } return $node; } /** * Process pages (files) and add them to the hierarchy * * @param AbstractNode $parent Parent node to add children to * @param string $dir Current directory path * @return void */ protected function processPages(AbstractNode $parent, string $dir) { global $conf; $base = $conf['datadir'] . '/'; $files = glob($base . $dir . '/*.txt'); foreach ($files as $file) { $file = basename($file); $id = pathID($dir . '/' . $file); // Skip already shown start pages if (isset($this->startpages[$id])) { continue; } $page = new WikiPage($id, $file); // Add to hierarchy $this->addNodeToHierarchy($parent, $page); } } /** * Run custom node processor and add it to the hierarchy * * @param AbstractNode $parent Parent node * @param AbstractNode $node Node to add * @return void */ protected function addNodeToHierarchy(AbstractNode $parent, AbstractNode $node): void { $node->setParent($parent); // set the parent even when not added, yet $node = $this->applyNodeProcessor($node); if ($node instanceof AbstractNode) { $parent->addChild($node); } } /** * Get the start page for the given namespace * * @param string $ns The namespace to get the start page for * @return string The start page id */ protected function getStartpage(string $ns): string { $id = $ns . ':'; return (new PageResolver(''))->resolveId($id); } /** * Get the file path for the given namespace relative to the page directory * * @param string $namespace * @return string */ protected function namespacePath(string $namespace): string { global $conf; $base = $conf['datadir'] . '/'; $dir = wikiFN($namespace . ':xxx'); $dir = substr($dir, strlen($base)); $dir = dirname($dir); // remove the 'xxx' part if ($dir === '.') $dir = ''; // dirname returns '.' for root namespace return $dir; } /** @inheritdoc */ protected function applyRecursionDecision(AbstractNode $node, int $depth): bool { // automatically skip hidden elements unless disabled by flag if (!$this->hasNotFlag(self::FLAG_KEEP_HIDDEN) && isHiddenPage($node->getId())) { return false; } return parent::applyRecursionDecision($node, $depth); } /** @inheritdoc */ protected function applyNodeProcessor(AbstractNode $node): ?AbstractNode { // automatically skip hidden elements unless disabled by flag if (!$this->hasNotFlag(self::FLAG_KEEP_HIDDEN) && isHiddenPage($node->getId())) { return null; } return parent::applyNodeProcessor($node); } }