xref: /template/strap/ComboStrap/FetcherPageBundler.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
1*04fd306cSNickeau<?php
2*04fd306cSNickeau
3*04fd306cSNickeaunamespace ComboStrap;
4*04fd306cSNickeau
5*04fd306cSNickeau
6*04fd306cSNickeauuse ComboStrap\Meta\Field\PageTemplateName;
7*04fd306cSNickeauuse ComboStrap\Web\Url;
8*04fd306cSNickeau
9*04fd306cSNickeau/**
10*04fd306cSNickeau * Bundle page from the same namespace
11*04fd306cSNickeau * with {@link FetcherPageBundler::getBundledOutline() corrected outline}
12*04fd306cSNickeau *
13*04fd306cSNickeau * From a wiki app, just add: `?do=combo_pagebundler`
14*04fd306cSNickeau *
15*04fd306cSNickeau */
16*04fd306cSNickeauclass FetcherPageBundler extends IFetcherAbs implements IFetcherString
17*04fd306cSNickeau{
18*04fd306cSNickeau
19*04fd306cSNickeau    use FetcherTraitWikiPath;
20*04fd306cSNickeau
21*04fd306cSNickeau    const CANONICAL = self::NAME;
22*04fd306cSNickeau    const NAME = "pagebundler";
23*04fd306cSNickeau    private Outline $bundledOutline;
24*04fd306cSNickeau
25*04fd306cSNickeau    public static function createPageBundler(): FetcherPageBundler
26*04fd306cSNickeau    {
27*04fd306cSNickeau        return new FetcherPageBundler();
28*04fd306cSNickeau    }
29*04fd306cSNickeau
30*04fd306cSNickeau    public function buildFromUrl(Url $url): FetcherPageBundler
31*04fd306cSNickeau    {
32*04fd306cSNickeau        /**
33*04fd306cSNickeau         * Just to return the good type
34*04fd306cSNickeau         */
35*04fd306cSNickeau        parent::buildFromUrl($url);
36*04fd306cSNickeau        return $this;
37*04fd306cSNickeau    }
38*04fd306cSNickeau
39*04fd306cSNickeau    /**
40*04fd306cSNickeau     * @throws ExceptionBadArgument
41*04fd306cSNickeau     * @throws ExceptionBadSyntax
42*04fd306cSNickeau     * @throws ExceptionNotExists
43*04fd306cSNickeau     * @throws ExceptionNotFound
44*04fd306cSNickeau     */
45*04fd306cSNickeau    public function buildFromTagAttributes(TagAttributes $tagAttributes): FetcherPageBundler
46*04fd306cSNickeau    {
47*04fd306cSNickeau        parent::buildFromTagAttributes($tagAttributes);
48*04fd306cSNickeau        $this->buildOriginalPathFromTagAttributes($tagAttributes);
49*04fd306cSNickeau        return $this;
50*04fd306cSNickeau    }
51*04fd306cSNickeau
52*04fd306cSNickeau
53*04fd306cSNickeau    function getBuster(): string
54*04fd306cSNickeau    {
55*04fd306cSNickeau        return "";
56*04fd306cSNickeau    }
57*04fd306cSNickeau
58*04fd306cSNickeau    /**
59*04fd306cSNickeau     * @return Mime
60*04fd306cSNickeau     */
61*04fd306cSNickeau    public function getMime(): Mime
62*04fd306cSNickeau    {
63*04fd306cSNickeau        return Mime::getHtml();
64*04fd306cSNickeau    }
65*04fd306cSNickeau
66*04fd306cSNickeau    public function getFetcherName(): string
67*04fd306cSNickeau    {
68*04fd306cSNickeau        return self::NAME;
69*04fd306cSNickeau    }
70*04fd306cSNickeau
71*04fd306cSNickeau    public function getFetchString(): string
72*04fd306cSNickeau    {
73*04fd306cSNickeau
74*04fd306cSNickeau        $outline = $this->getBundledOutline();
75*04fd306cSNickeau        $instructionsCalls = $outline->toHtmlSectionOutlineCalls();
76*04fd306cSNickeau        $mainContent = MarkupRenderer::createFromInstructions($instructionsCalls)
77*04fd306cSNickeau            ->setRequestedExecutingPath($this->getStartPath())
78*04fd306cSNickeau            ->setRequestedContextPath($this->getRequestedContextPath())
79*04fd306cSNickeau            ->setRequestedMime($this->getMime())
80*04fd306cSNickeau            ->getOutput();
81*04fd306cSNickeau
82*04fd306cSNickeau
83*04fd306cSNickeau        $startMarkup = $this->getStartPath();
84*04fd306cSNickeau        $title = PageTitle::createForMarkup($startMarkup)->getValueOrDefault();
85*04fd306cSNickeau        $lang = Lang::createForMarkup($startMarkup);
86*04fd306cSNickeau        try {
87*04fd306cSNickeau            $startMarkupWikiPath = WikiPath::createFromPathObject($startMarkup->getPathObject());
88*04fd306cSNickeau        } catch (ExceptionBadArgument $e) {
89*04fd306cSNickeau            /**
90*04fd306cSNickeau             * should not happen as this class accepts only wiki path as {@link FetcherPageBundler::setContextPath() context path}
91*04fd306cSNickeau             */
92*04fd306cSNickeau            throw new ExceptionRuntimeInternal("We were unable to get the start markup wiki path. Error:{$e->getMessage()}", self::CANONICAL);
93*04fd306cSNickeau        }
94*04fd306cSNickeau
95*04fd306cSNickeau        $layoutName = PageTemplateName::BLANK_TEMPLATE_VALUE;
96*04fd306cSNickeau        try {
97*04fd306cSNickeau            $toc = Toc::createEmpty()
98*04fd306cSNickeau                ->setValue($this->getBundledOutline()->toTocDokuwikiFormat());
99*04fd306cSNickeau        } catch (ExceptionBadArgument $e) {
100*04fd306cSNickeau            // this is an array
101*04fd306cSNickeau            throw new ExceptionRuntimeInternal("The toc could not be created. Error:{$e->getMessage()}", self::CANONICAL, 1, $e);
102*04fd306cSNickeau        }
103*04fd306cSNickeau        try {
104*04fd306cSNickeau            return TemplateForWebPage::create()
105*04fd306cSNickeau                ->setRequestedTemplateName($layoutName)
106*04fd306cSNickeau                ->setRequestedContextPath($startMarkupWikiPath)
107*04fd306cSNickeau                ->setRequestedTitle($title)
108*04fd306cSNickeau                ->setRequestedLang($lang)
109*04fd306cSNickeau                ->setToc($toc)
110*04fd306cSNickeau                ->setIsSocial(false)
111*04fd306cSNickeau                ->setRequestedEnableTaskRunner(false)
112*04fd306cSNickeau                ->setMainContent($mainContent)
113*04fd306cSNickeau                ->render();
114*04fd306cSNickeau        } catch (ExceptionBadSyntax|ExceptionNotFound|ExceptionBadArgument $e) {
115*04fd306cSNickeau            // layout should be good
116*04fd306cSNickeau            throw new ExceptionRuntimeInternal("The $layoutName template returns an error", self::CANONICAL, 1, $e);
117*04fd306cSNickeau        }
118*04fd306cSNickeau
119*04fd306cSNickeau
120*04fd306cSNickeau    }
121*04fd306cSNickeau
122*04fd306cSNickeau    public function getBundledOutline(): Outline
123*04fd306cSNickeau    {
124*04fd306cSNickeau
125*04fd306cSNickeau        if (isset($this->bundledOutline)) {
126*04fd306cSNickeau            return $this->bundledOutline;
127*04fd306cSNickeau        }
128*04fd306cSNickeau
129*04fd306cSNickeau        $startPath = $this->getStartPath();
130*04fd306cSNickeau        if (FileSystems::exists($startPath)) {
131*04fd306cSNickeau            $indexOutline = $this->addFirstSectionIfMissing($startPath->getOutline());
132*04fd306cSNickeau        } else {
133*04fd306cSNickeau            $title = PageTitle::createForMarkup($startPath)->getValueOrDefault();
134*04fd306cSNickeau            $content = <<<EOF
135*04fd306cSNickeau====== $title ======
136*04fd306cSNickeauEOF;
137*04fd306cSNickeau            $indexOutline = Outline::createFromMarkup($content, $this->getStartPath(), $this->getRequestedContextPath());
138*04fd306cSNickeau        }
139*04fd306cSNickeau
140*04fd306cSNickeau        $childrenPages = MarkupFileSystem::getOrCreate()->getChildren($startPath, FileSystems::LEAF);
141*04fd306cSNickeau        foreach ($childrenPages as $child) {
142*04fd306cSNickeau            $outer = $this->addFirstSectionIfMissing($child->getOutline());
143*04fd306cSNickeau            Outline::merge($indexOutline, $outer);
144*04fd306cSNickeau        }
145*04fd306cSNickeau        $this->bundledOutline = $indexOutline;
146*04fd306cSNickeau
147*04fd306cSNickeau        return $this->bundledOutline;
148*04fd306cSNickeau
149*04fd306cSNickeau    }
150*04fd306cSNickeau
151*04fd306cSNickeau    /**
152*04fd306cSNickeau     * The path from where the bundle should start
153*04fd306cSNickeau     * If this is not an index markup, the index markup will be chosen {@link FetcherPageBundler::getStartPath()}
154*04fd306cSNickeau     *
155*04fd306cSNickeau     * @throws ExceptionBadArgument - if the path is not a {@link WikiPath web path}
156*04fd306cSNickeau     */
157*04fd306cSNickeau    public function setContextPath(Path $requestedPath): FetcherPageBundler
158*04fd306cSNickeau    {
159*04fd306cSNickeau        $this->setSourcePath(WikiPath::createFromPathObject($requestedPath));
160*04fd306cSNickeau        return $this;
161*04fd306cSNickeau    }
162*04fd306cSNickeau
163*04fd306cSNickeau    private function getRequestedContextPath(): WikiPath
164*04fd306cSNickeau    {
165*04fd306cSNickeau        return $this->getSourcePath();
166*04fd306cSNickeau    }
167*04fd306cSNickeau
168*04fd306cSNickeau
169*04fd306cSNickeau    /**
170*04fd306cSNickeau     *
171*04fd306cSNickeau     * @return MarkupPath The index path or the request path is none
172*04fd306cSNickeau     *
173*04fd306cSNickeau     */
174*04fd306cSNickeau    private function getStartPath(): MarkupPath
175*04fd306cSNickeau    {
176*04fd306cSNickeau        $requestedPath = MarkupPath::createPageFromPathObject($this->getRequestedContextPath());
177*04fd306cSNickeau        if ($requestedPath->isIndexPage()) {
178*04fd306cSNickeau            return $requestedPath;
179*04fd306cSNickeau        }
180*04fd306cSNickeau        try {
181*04fd306cSNickeau            /**
182*04fd306cSNickeau             * Parent is an index path in the {@link MarkupFileSystem}
183*04fd306cSNickeau             */
184*04fd306cSNickeau            return $requestedPath->getParent();
185*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
186*04fd306cSNickeau            // home markup case (should not happen - home page is a index page)
187*04fd306cSNickeau            return $requestedPath;
188*04fd306cSNickeau        }
189*04fd306cSNickeau
190*04fd306cSNickeau    }
191*04fd306cSNickeau
192*04fd306cSNickeau    /**
193*04fd306cSNickeau     * If a page does not have any h1
194*04fd306cSNickeau     * (Case of index page for instance)
195*04fd306cSNickeau     *
196*04fd306cSNickeau     * If this is the case, the outline is broken.
197*04fd306cSNickeau     * @param Outline $outline
198*04fd306cSNickeau     * @return Outline
199*04fd306cSNickeau     */
200*04fd306cSNickeau    private function addFirstSectionIfMissing(Outline $outline): Outline
201*04fd306cSNickeau    {
202*04fd306cSNickeau        $rootOutlineSection = $outline->getRootOutlineSection();
203*04fd306cSNickeau        $addFirstSection = false;
204*04fd306cSNickeau        try {
205*04fd306cSNickeau            $firstChild = $rootOutlineSection->getFirstChild();
206*04fd306cSNickeau            if ($firstChild->getLevel() >= 2) {
207*04fd306cSNickeau                $addFirstSection = true;
208*04fd306cSNickeau            }
209*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
210*04fd306cSNickeau            $addFirstSection = true;
211*04fd306cSNickeau        }
212*04fd306cSNickeau        if ($addFirstSection) {
213*04fd306cSNickeau            $enterHeading = Call::createComboCall(
214*04fd306cSNickeau                HeadingTag::HEADING_TAG,
215*04fd306cSNickeau                DOKU_LEXER_ENTER,
216*04fd306cSNickeau                array(HeadingTag::LEVEL => 1),
217*04fd306cSNickeau                HeadingTag::TYPE_OUTLINE,
218*04fd306cSNickeau                null,
219*04fd306cSNickeau                null,
220*04fd306cSNickeau                null,
221*04fd306cSNickeau                \syntax_plugin_combo_xmlblocktag::TAG
222*04fd306cSNickeau            );
223*04fd306cSNickeau            $title = PageTitle::createForMarkup($outline->getMarkupPath())->getValueOrDefault();
224*04fd306cSNickeau            $unmatchedHeading = Call::createComboCall(
225*04fd306cSNickeau                HeadingTag::HEADING_TAG,
226*04fd306cSNickeau                DOKU_LEXER_UNMATCHED,
227*04fd306cSNickeau                [],
228*04fd306cSNickeau                null,
229*04fd306cSNickeau                $title,
230*04fd306cSNickeau                $title,
231*04fd306cSNickeau                null,
232*04fd306cSNickeau                \syntax_plugin_combo_xmlblocktag::TAG
233*04fd306cSNickeau            );
234*04fd306cSNickeau            $exitHeading = Call::createComboCall(
235*04fd306cSNickeau                HeadingTag::HEADING_TAG,
236*04fd306cSNickeau                DOKU_LEXER_EXIT,
237*04fd306cSNickeau                array(HeadingTag::LEVEL => 1),
238*04fd306cSNickeau                null,
239*04fd306cSNickeau                null,
240*04fd306cSNickeau                null,
241*04fd306cSNickeau                null,
242*04fd306cSNickeau                \syntax_plugin_combo_xmlblocktag::TAG
243*04fd306cSNickeau            );
244*04fd306cSNickeau            $h1Section = OutlineSection::createFromEnterHeadingCall($enterHeading)
245*04fd306cSNickeau                ->addHeaderCall($unmatchedHeading)
246*04fd306cSNickeau                ->addHeaderCall($exitHeading);
247*04fd306cSNickeau            $children = $rootOutlineSection->getChildren();
248*04fd306cSNickeau            foreach ($children as $child) {
249*04fd306cSNickeau                $child->detachBeforeAppend();
250*04fd306cSNickeau                try {
251*04fd306cSNickeau                    $h1Section->appendChild($child);
252*04fd306cSNickeau                } catch (ExceptionBadState $e) {
253*04fd306cSNickeau                    LogUtility::error("An error occurs when trying to move the h2 children below the recreated heading title ($title)", self::CANONICAL);
254*04fd306cSNickeau                }
255*04fd306cSNickeau            }
256*04fd306cSNickeau            /**
257*04fd306cSNickeau             * Without h1
258*04fd306cSNickeau             * The content is in the root heading
259*04fd306cSNickeau             */
260*04fd306cSNickeau            foreach ($rootOutlineSection->getContentCalls() as $rootHeadingCall) {
261*04fd306cSNickeau                $h1Section->addContentCall($rootHeadingCall);
262*04fd306cSNickeau            }
263*04fd306cSNickeau            $rootOutlineSection->deleteContentCalls();
264*04fd306cSNickeau            try {
265*04fd306cSNickeau                $rootOutlineSection->appendChild($h1Section);
266*04fd306cSNickeau            } catch (ExceptionBadState $e) {
267*04fd306cSNickeau                LogUtility::error("An error occurs when trying to add the recreated title heading ($title) to the root", self::CANONICAL);
268*04fd306cSNickeau            }
269*04fd306cSNickeau        }
270*04fd306cSNickeau        return $outline;
271*04fd306cSNickeau    }
272*04fd306cSNickeau
273*04fd306cSNickeau    public function getLabel(): string
274*04fd306cSNickeau    {
275*04fd306cSNickeau        return self::CANONICAL;
276*04fd306cSNickeau    }
277*04fd306cSNickeau}
278