xref: /template/strap/ComboStrap/FetcherPageBundler.php (revision 7dbcdecd15b9c1485d6e26d96fd967e18985d6c4)
104fd306cSNickeau<?php
204fd306cSNickeau
304fd306cSNickeaunamespace ComboStrap;
404fd306cSNickeau
504fd306cSNickeau
604fd306cSNickeauuse ComboStrap\Meta\Field\PageTemplateName;
704fd306cSNickeauuse ComboStrap\Web\Url;
804fd306cSNickeau
904fd306cSNickeau/**
1004fd306cSNickeau * Bundle page from the same namespace
1104fd306cSNickeau * with {@link FetcherPageBundler::getBundledOutline() corrected outline}
1204fd306cSNickeau *
1304fd306cSNickeau * From a wiki app, just add: `?do=combo_pagebundler`
1404fd306cSNickeau *
1504fd306cSNickeau */
1604fd306cSNickeauclass FetcherPageBundler extends IFetcherAbs implements IFetcherString
1704fd306cSNickeau{
1804fd306cSNickeau
1904fd306cSNickeau    use FetcherTraitWikiPath;
2004fd306cSNickeau
2104fd306cSNickeau    const CANONICAL = self::NAME;
2204fd306cSNickeau    const NAME = "pagebundler";
23*7dbcdecdSNico    private ?Outline $bundledOutline = null;
2404fd306cSNickeau
2504fd306cSNickeau    public static function createPageBundler(): FetcherPageBundler
2604fd306cSNickeau    {
2704fd306cSNickeau        return new FetcherPageBundler();
2804fd306cSNickeau    }
2904fd306cSNickeau
3004fd306cSNickeau    public function buildFromUrl(Url $url): FetcherPageBundler
3104fd306cSNickeau    {
3204fd306cSNickeau        /**
3304fd306cSNickeau         * Just to return the good type
3404fd306cSNickeau         */
3504fd306cSNickeau        parent::buildFromUrl($url);
3604fd306cSNickeau        return $this;
3704fd306cSNickeau    }
3804fd306cSNickeau
3904fd306cSNickeau    /**
4004fd306cSNickeau     * @throws ExceptionBadArgument
4104fd306cSNickeau     * @throws ExceptionBadSyntax
4204fd306cSNickeau     * @throws ExceptionNotExists
4304fd306cSNickeau     * @throws ExceptionNotFound
4404fd306cSNickeau     */
4504fd306cSNickeau    public function buildFromTagAttributes(TagAttributes $tagAttributes): FetcherPageBundler
4604fd306cSNickeau    {
4704fd306cSNickeau        parent::buildFromTagAttributes($tagAttributes);
4804fd306cSNickeau        $this->buildOriginalPathFromTagAttributes($tagAttributes);
4904fd306cSNickeau        return $this;
5004fd306cSNickeau    }
5104fd306cSNickeau
5204fd306cSNickeau
5304fd306cSNickeau    function getBuster(): string
5404fd306cSNickeau    {
5504fd306cSNickeau        return "";
5604fd306cSNickeau    }
5704fd306cSNickeau
5804fd306cSNickeau    /**
5904fd306cSNickeau     * @return Mime
6004fd306cSNickeau     */
6104fd306cSNickeau    public function getMime(): Mime
6204fd306cSNickeau    {
6304fd306cSNickeau        return Mime::getHtml();
6404fd306cSNickeau    }
6504fd306cSNickeau
6604fd306cSNickeau    public function getFetcherName(): string
6704fd306cSNickeau    {
6804fd306cSNickeau        return self::NAME;
6904fd306cSNickeau    }
7004fd306cSNickeau
7104fd306cSNickeau    public function getFetchString(): string
7204fd306cSNickeau    {
7304fd306cSNickeau
7404fd306cSNickeau        $outline = $this->getBundledOutline();
7504fd306cSNickeau        $instructionsCalls = $outline->toHtmlSectionOutlineCalls();
7604fd306cSNickeau        $mainContent = MarkupRenderer::createFromInstructions($instructionsCalls)
7704fd306cSNickeau            ->setRequestedExecutingPath($this->getStartPath())
7804fd306cSNickeau            ->setRequestedContextPath($this->getRequestedContextPath())
7904fd306cSNickeau            ->setRequestedMime($this->getMime())
8004fd306cSNickeau            ->getOutput();
8104fd306cSNickeau
8204fd306cSNickeau
8304fd306cSNickeau        $startMarkup = $this->getStartPath();
8404fd306cSNickeau        $title = PageTitle::createForMarkup($startMarkup)->getValueOrDefault();
8504fd306cSNickeau        $lang = Lang::createForMarkup($startMarkup);
8604fd306cSNickeau        try {
8704fd306cSNickeau            $startMarkupWikiPath = WikiPath::createFromPathObject($startMarkup->getPathObject());
8804fd306cSNickeau        } catch (ExceptionBadArgument $e) {
8904fd306cSNickeau            /**
9004fd306cSNickeau             * should not happen as this class accepts only wiki path as {@link FetcherPageBundler::setContextPath() context path}
9104fd306cSNickeau             */
9204fd306cSNickeau            throw new ExceptionRuntimeInternal("We were unable to get the start markup wiki path. Error:{$e->getMessage()}", self::CANONICAL);
9304fd306cSNickeau        }
9404fd306cSNickeau
9504fd306cSNickeau        $layoutName = PageTemplateName::BLANK_TEMPLATE_VALUE;
9604fd306cSNickeau        try {
9704fd306cSNickeau            $toc = Toc::createEmpty()
9804fd306cSNickeau                ->setValue($this->getBundledOutline()->toTocDokuwikiFormat());
9904fd306cSNickeau        } catch (ExceptionBadArgument $e) {
10004fd306cSNickeau            // this is an array
10104fd306cSNickeau            throw new ExceptionRuntimeInternal("The toc could not be created. Error:{$e->getMessage()}", self::CANONICAL, 1, $e);
10204fd306cSNickeau        }
10304fd306cSNickeau        try {
10404fd306cSNickeau            return TemplateForWebPage::create()
10504fd306cSNickeau                ->setRequestedTemplateName($layoutName)
10604fd306cSNickeau                ->setRequestedContextPath($startMarkupWikiPath)
10704fd306cSNickeau                ->setRequestedTitle($title)
10804fd306cSNickeau                ->setRequestedLang($lang)
10904fd306cSNickeau                ->setToc($toc)
11004fd306cSNickeau                ->setIsSocial(false)
11104fd306cSNickeau                ->setRequestedEnableTaskRunner(false)
11204fd306cSNickeau                ->setMainContent($mainContent)
11304fd306cSNickeau                ->render();
11404fd306cSNickeau        } catch (ExceptionBadSyntax|ExceptionNotFound|ExceptionBadArgument $e) {
11504fd306cSNickeau            // layout should be good
11604fd306cSNickeau            throw new ExceptionRuntimeInternal("The $layoutName template returns an error", self::CANONICAL, 1, $e);
11704fd306cSNickeau        }
11804fd306cSNickeau
11904fd306cSNickeau
12004fd306cSNickeau    }
12104fd306cSNickeau
12204fd306cSNickeau    public function getBundledOutline(): Outline
12304fd306cSNickeau    {
12404fd306cSNickeau
12504fd306cSNickeau        if (isset($this->bundledOutline)) {
12604fd306cSNickeau            return $this->bundledOutline;
12704fd306cSNickeau        }
12804fd306cSNickeau
12904fd306cSNickeau        $startPath = $this->getStartPath();
130*7dbcdecdSNico        $actualLevel = 0;
131*7dbcdecdSNico        $this->buildOutlineRecursive($startPath,$actualLevel);
13204fd306cSNickeau
13304fd306cSNickeau        return $this->bundledOutline;
13404fd306cSNickeau
13504fd306cSNickeau    }
13604fd306cSNickeau
13704fd306cSNickeau    /**
13804fd306cSNickeau     * The path from where the bundle should start
13904fd306cSNickeau     * If this is not an index markup, the index markup will be chosen {@link FetcherPageBundler::getStartPath()}
14004fd306cSNickeau     *
14104fd306cSNickeau     * @throws ExceptionBadArgument - if the path is not a {@link WikiPath web path}
14204fd306cSNickeau     */
14304fd306cSNickeau    public function setContextPath(Path $requestedPath): FetcherPageBundler
14404fd306cSNickeau    {
14504fd306cSNickeau        $this->setSourcePath(WikiPath::createFromPathObject($requestedPath));
14604fd306cSNickeau        return $this;
14704fd306cSNickeau    }
14804fd306cSNickeau
14904fd306cSNickeau    private function getRequestedContextPath(): WikiPath
15004fd306cSNickeau    {
15104fd306cSNickeau        return $this->getSourcePath();
15204fd306cSNickeau    }
15304fd306cSNickeau
15404fd306cSNickeau
15504fd306cSNickeau    /**
15604fd306cSNickeau     *
15704fd306cSNickeau     * @return MarkupPath The index path or the request path is none
15804fd306cSNickeau     *
15904fd306cSNickeau     */
16004fd306cSNickeau    private function getStartPath(): MarkupPath
16104fd306cSNickeau    {
16204fd306cSNickeau        $requestedPath = MarkupPath::createPageFromPathObject($this->getRequestedContextPath());
16304fd306cSNickeau        if ($requestedPath->isIndexPage()) {
16404fd306cSNickeau            return $requestedPath;
16504fd306cSNickeau        }
16604fd306cSNickeau        try {
16704fd306cSNickeau            /**
16804fd306cSNickeau             * Parent is an index path in the {@link MarkupFileSystem}
16904fd306cSNickeau             */
17004fd306cSNickeau            return $requestedPath->getParent();
17104fd306cSNickeau        } catch (ExceptionNotFound $e) {
17204fd306cSNickeau            // home markup case (should not happen - home page is a index page)
17304fd306cSNickeau            return $requestedPath;
17404fd306cSNickeau        }
17504fd306cSNickeau
17604fd306cSNickeau    }
17704fd306cSNickeau
17804fd306cSNickeau    /**
17904fd306cSNickeau     * If a page does not have any h1
18004fd306cSNickeau     * (Case of index page for instance)
18104fd306cSNickeau     *
18204fd306cSNickeau     * If this is the case, the outline is broken.
18304fd306cSNickeau     * @param Outline $outline
18404fd306cSNickeau     * @return Outline
18504fd306cSNickeau     */
18604fd306cSNickeau    private function addFirstSectionIfMissing(Outline $outline): Outline
18704fd306cSNickeau    {
18804fd306cSNickeau        $rootOutlineSection = $outline->getRootOutlineSection();
18904fd306cSNickeau        $addFirstSection = false;
19004fd306cSNickeau        try {
19104fd306cSNickeau            $firstChild = $rootOutlineSection->getFirstChild();
19204fd306cSNickeau            if ($firstChild->getLevel() >= 2) {
19304fd306cSNickeau                $addFirstSection = true;
19404fd306cSNickeau            }
19504fd306cSNickeau        } catch (ExceptionNotFound $e) {
19604fd306cSNickeau            $addFirstSection = true;
19704fd306cSNickeau        }
19804fd306cSNickeau        if ($addFirstSection) {
19904fd306cSNickeau            $enterHeading = Call::createComboCall(
20004fd306cSNickeau                HeadingTag::HEADING_TAG,
20104fd306cSNickeau                DOKU_LEXER_ENTER,
20204fd306cSNickeau                array(HeadingTag::LEVEL => 1),
20304fd306cSNickeau                HeadingTag::TYPE_OUTLINE,
20404fd306cSNickeau                null,
20504fd306cSNickeau                null,
20604fd306cSNickeau                null,
20704fd306cSNickeau                \syntax_plugin_combo_xmlblocktag::TAG
20804fd306cSNickeau            );
20904fd306cSNickeau            $title = PageTitle::createForMarkup($outline->getMarkupPath())->getValueOrDefault();
21004fd306cSNickeau            $unmatchedHeading = Call::createComboCall(
21104fd306cSNickeau                HeadingTag::HEADING_TAG,
21204fd306cSNickeau                DOKU_LEXER_UNMATCHED,
21304fd306cSNickeau                [],
21404fd306cSNickeau                null,
21504fd306cSNickeau                $title,
21604fd306cSNickeau                $title,
21704fd306cSNickeau                null,
21804fd306cSNickeau                \syntax_plugin_combo_xmlblocktag::TAG
21904fd306cSNickeau            );
22004fd306cSNickeau            $exitHeading = Call::createComboCall(
22104fd306cSNickeau                HeadingTag::HEADING_TAG,
22204fd306cSNickeau                DOKU_LEXER_EXIT,
22304fd306cSNickeau                array(HeadingTag::LEVEL => 1),
22404fd306cSNickeau                null,
22504fd306cSNickeau                null,
22604fd306cSNickeau                null,
22704fd306cSNickeau                null,
22804fd306cSNickeau                \syntax_plugin_combo_xmlblocktag::TAG
22904fd306cSNickeau            );
23004fd306cSNickeau            $h1Section = OutlineSection::createFromEnterHeadingCall($enterHeading)
23104fd306cSNickeau                ->addHeaderCall($unmatchedHeading)
23204fd306cSNickeau                ->addHeaderCall($exitHeading);
23304fd306cSNickeau            $children = $rootOutlineSection->getChildren();
23404fd306cSNickeau            foreach ($children as $child) {
23504fd306cSNickeau                $child->detachBeforeAppend();
23604fd306cSNickeau                try {
23704fd306cSNickeau                    $h1Section->appendChild($child);
23804fd306cSNickeau                } catch (ExceptionBadState $e) {
23904fd306cSNickeau                    LogUtility::error("An error occurs when trying to move the h2 children below the recreated heading title ($title)", self::CANONICAL);
24004fd306cSNickeau                }
24104fd306cSNickeau            }
24204fd306cSNickeau            /**
24304fd306cSNickeau             * Without h1
24404fd306cSNickeau             * The content is in the root heading
24504fd306cSNickeau             */
24604fd306cSNickeau            foreach ($rootOutlineSection->getContentCalls() as $rootHeadingCall) {
24704fd306cSNickeau                $h1Section->addContentCall($rootHeadingCall);
24804fd306cSNickeau            }
24904fd306cSNickeau            $rootOutlineSection->deleteContentCalls();
25004fd306cSNickeau            try {
25104fd306cSNickeau                $rootOutlineSection->appendChild($h1Section);
25204fd306cSNickeau            } catch (ExceptionBadState $e) {
25304fd306cSNickeau                LogUtility::error("An error occurs when trying to add the recreated title heading ($title) to the root", self::CANONICAL);
25404fd306cSNickeau            }
25504fd306cSNickeau        }
25604fd306cSNickeau        return $outline;
25704fd306cSNickeau    }
25804fd306cSNickeau
25904fd306cSNickeau    public function getLabel(): string
26004fd306cSNickeau    {
26104fd306cSNickeau        return self::CANONICAL;
26204fd306cSNickeau    }
263*7dbcdecdSNico
264*7dbcdecdSNico    private function buildOutlineRecursive(MarkupPath $indexPath, int $actualLevel)
265*7dbcdecdSNico    {
266*7dbcdecdSNico        /**
267*7dbcdecdSNico         * Index Page
268*7dbcdecdSNico         */
269*7dbcdecdSNico        if (FileSystems::exists($indexPath)) {
270*7dbcdecdSNico            $indexOutline = $this->addFirstSectionIfMissing($indexPath->getOutline());
271*7dbcdecdSNico        } else {
272*7dbcdecdSNico            $title = PageTitle::createForMarkup($indexPath)->getValueOrDefault();
273*7dbcdecdSNico            $content = <<<EOF
274*7dbcdecdSNico====== $title ======
275*7dbcdecdSNicoEOF;
276*7dbcdecdSNico            $indexOutline = Outline::createFromMarkup($content, $this->getStartPath(), $this->getRequestedContextPath());
277*7dbcdecdSNico        }
278*7dbcdecdSNico
279*7dbcdecdSNico        if(!$this->bundledOutline){
280*7dbcdecdSNico            $this->bundledOutline = $indexOutline;
281*7dbcdecdSNico        } else {
282*7dbcdecdSNico            Outline::merge($this->bundledOutline, $indexOutline, $actualLevel);
283*7dbcdecdSNico        }
284*7dbcdecdSNico
285*7dbcdecdSNico        /**
286*7dbcdecdSNico         * Children Pages (Same level)
287*7dbcdecdSNico         */
288*7dbcdecdSNico        $childrenPages = MarkupFileSystem::getOrCreate()->getChildren($indexPath, FileSystems::LEAF);
289*7dbcdecdSNico        foreach ($childrenPages as $child) {
290*7dbcdecdSNico            if ($child->isSlot()) {
291*7dbcdecdSNico                continue;
292*7dbcdecdSNico            }
293*7dbcdecdSNico            $outer = $this->addFirstSectionIfMissing($child->getOutline());
294*7dbcdecdSNico            Outline::merge($this->bundledOutline, $outer, $actualLevel);
295*7dbcdecdSNico        }
296*7dbcdecdSNico        $containerPages = MarkupFileSystem::getOrCreate()->getChildren($indexPath, FileSystems::CONTAINER);
297*7dbcdecdSNico        $nextLevel = $actualLevel+1;
298*7dbcdecdSNico        foreach ($containerPages as $child) {
299*7dbcdecdSNico            $this->buildOutlineRecursive($child,$nextLevel);
300*7dbcdecdSNico        }
301*7dbcdecdSNico
302*7dbcdecdSNico    }
30304fd306cSNickeau}
304