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