xref: /plugin/combo/ComboStrap/FetcherMarkupBuilder.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
1*04fd306cSNickeau<?php
2*04fd306cSNickeau
3*04fd306cSNickeaunamespace ComboStrap;
4*04fd306cSNickeau
5*04fd306cSNickeauuse dokuwiki\Cache\CacheInstructions;
6*04fd306cSNickeauuse dokuwiki\Cache\CacheParser;
7*04fd306cSNickeauuse dokuwiki\Cache\CacheRenderer;
8*04fd306cSNickeau
9*04fd306cSNickeau/**
10*04fd306cSNickeau * Builder class for {@link FetcherMarkup}
11*04fd306cSNickeau * Php does not allow for nested class
12*04fd306cSNickeau * We therefore need to get the builder class out.
13*04fd306cSNickeau *
14*04fd306cSNickeau * We extends just to get access to protected members class
15*04fd306cSNickeau * and to mimic a builder pattern
16*04fd306cSNickeau *
17*04fd306cSNickeau * @internal
18*04fd306cSNickeau */
19*04fd306cSNickeauclass FetcherMarkupBuilder
20*04fd306cSNickeau{
21*04fd306cSNickeau
22*04fd306cSNickeau    /**
23*04fd306cSNickeau     * Private are they may be null
24*04fd306cSNickeau     */
25*04fd306cSNickeau    private ?string $builderMarkupString = null;
26*04fd306cSNickeau    private ?Path $builderMarkupSourcePath = null;
27*04fd306cSNickeau    private ?array $builderRequestedInstructions = null;
28*04fd306cSNickeau
29*04fd306cSNickeau    protected WikiPath $requestedContextPath;
30*04fd306cSNickeau    protected Mime $mime;
31*04fd306cSNickeau    protected bool $deleteRootBlockElement = false;
32*04fd306cSNickeau    protected string $rendererName;
33*04fd306cSNickeau
34*04fd306cSNickeau
35*04fd306cSNickeau    protected bool $isDoc;
36*04fd306cSNickeau    protected array $builderContextData;
37*04fd306cSNickeau    private bool $isCodeStandAloneExecution = false;
38*04fd306cSNickeau    /**
39*04fd306cSNickeau     * @var FetcherMarkup - a parent if any
40*04fd306cSNickeau     */
41*04fd306cSNickeau    private FetcherMarkup $parentMarkupHandler;
42*04fd306cSNickeau
43*04fd306cSNickeau
44*04fd306cSNickeau    public function __construct()
45*04fd306cSNickeau    {
46*04fd306cSNickeau    }
47*04fd306cSNickeau
48*04fd306cSNickeau    /**
49*04fd306cSNickeau     * The local path is part of the key cache and should be the same
50*04fd306cSNickeau     * than dokuwiki
51*04fd306cSNickeau     *
52*04fd306cSNickeau     * For whatever reason, Dokuwiki uses:
53*04fd306cSNickeau     *   * `/` as separator on Windows
54*04fd306cSNickeau     *   * and Windows short path `GERARD~1` not gerardnico
55*04fd306cSNickeau     * See {@link wikiFN()}
56*04fd306cSNickeau     * There is also a cache in the function
57*04fd306cSNickeau     *
58*04fd306cSNickeau     * We can't use our {@link Path} class to be compatible because the
59*04fd306cSNickeau     * path is on windows format without the short path format
60*04fd306cSNickeau     */
61*04fd306cSNickeau    public static function getWikiIdAndLocalFileDokuwikiCompliant(Path $sourcePath): array
62*04fd306cSNickeau    {
63*04fd306cSNickeau
64*04fd306cSNickeau        try {
65*04fd306cSNickeau            $markuSourceWikiPath = $sourcePath->toWikiPath();
66*04fd306cSNickeau
67*04fd306cSNickeau            if ($markuSourceWikiPath->getDrive() === WikiPath::MARKUP_DRIVE) {
68*04fd306cSNickeau                /**
69*04fd306cSNickeau                 * Dokuwiki special function
70*04fd306cSNickeau                 * that should be the same to conform to the cache key
71*04fd306cSNickeau                 */
72*04fd306cSNickeau                $wikiId = $markuSourceWikiPath->getWikiId();
73*04fd306cSNickeau                $localFile = wikiFN($wikiId);
74*04fd306cSNickeau            } else {
75*04fd306cSNickeau                $localFile = $markuSourceWikiPath->toLocalPath();
76*04fd306cSNickeau                $wikiId = $markuSourceWikiPath->toUriString();
77*04fd306cSNickeau            }
78*04fd306cSNickeau        } catch (ExceptionCast $e) {
79*04fd306cSNickeau            $wikiId = $sourcePath->toAbsoluteId();
80*04fd306cSNickeau            try {
81*04fd306cSNickeau                $localFile = $sourcePath->toLocalPath();
82*04fd306cSNickeau            } catch (ExceptionCast $e) {
83*04fd306cSNickeau                throw new ExceptionRuntimeInternal("The source path ({$sourcePath}) is not supported as markup source path.", $e);
84*04fd306cSNickeau            }
85*04fd306cSNickeau        }
86*04fd306cSNickeau        return [$wikiId, $localFile];
87*04fd306cSNickeau    }
88*04fd306cSNickeau
89*04fd306cSNickeau    /**
90*04fd306cSNickeau     * @param string $markupString - the markup is a string format
91*04fd306cSNickeau     * @return FetcherMarkupBuilder
92*04fd306cSNickeau     */
93*04fd306cSNickeau    public function setRequestedMarkupString(string $markupString): FetcherMarkupBuilder
94*04fd306cSNickeau    {
95*04fd306cSNickeau        $this->builderMarkupString = $markupString;
96*04fd306cSNickeau        return $this;
97*04fd306cSNickeau    }
98*04fd306cSNickeau
99*04fd306cSNickeau    /**
100*04fd306cSNickeau     * Delete the first P instructions
101*04fd306cSNickeau     * (The parser will add a p block element)
102*04fd306cSNickeau     * @param bool $b
103*04fd306cSNickeau     * @return $this
104*04fd306cSNickeau     */
105*04fd306cSNickeau    public function setDeleteRootBlockElement(bool $b): FetcherMarkupBuilder
106*04fd306cSNickeau    {
107*04fd306cSNickeau        $this->deleteRootBlockElement = $b;
108*04fd306cSNickeau        return $this;
109*04fd306cSNickeau    }
110*04fd306cSNickeau
111*04fd306cSNickeau    /**
112*04fd306cSNickeau     * The source where the markup is stored (null if dynamic)
113*04fd306cSNickeau     * It's a duplicate of {@link FetcherMarkup::setSourcePath()}
114*04fd306cSNickeau     * @param ?Path $executingPath
115*04fd306cSNickeau     * @return $this
116*04fd306cSNickeau     */
117*04fd306cSNickeau    public function setRequestedExecutingPath(?Path $executingPath): FetcherMarkupBuilder
118*04fd306cSNickeau    {
119*04fd306cSNickeau
120*04fd306cSNickeau        if ($executingPath == null) {
121*04fd306cSNickeau            return $this;
122*04fd306cSNickeau        }
123*04fd306cSNickeau
124*04fd306cSNickeau        try {
125*04fd306cSNickeau            /**
126*04fd306cSNickeau             * Normalize to wiki path if possible
127*04fd306cSNickeau             * Why ?
128*04fd306cSNickeau             * Because the parent path may be used a {@link MarkupCacheDependencies::getValueForKey()  cache key}
129*04fd306cSNickeau             * and they will have different value if the path type is different
130*04fd306cSNickeau             * * With {@link LocalPath Local Path}: `C:\Users\gerardnico\AppData\Local\Temp\dwtests-1676386702.9751\data\pages\ns_without_scope`
131*04fd306cSNickeau             * * With {@link WikiPath Wiki Path}: `ns_without_scope`
132*04fd306cSNickeau             * It will then make the cache file path different (ie the md5 output key is the file name)
133*04fd306cSNickeau             */
134*04fd306cSNickeau            $this->builderMarkupSourcePath = $executingPath->toWikiPath();
135*04fd306cSNickeau        } catch (ExceptionCast $e) {
136*04fd306cSNickeau            $this->builderMarkupSourcePath = $executingPath;
137*04fd306cSNickeau        }
138*04fd306cSNickeau        return $this;
139*04fd306cSNickeau
140*04fd306cSNickeau    }
141*04fd306cSNickeau
142*04fd306cSNickeau    /**
143*04fd306cSNickeau     * The page context in which this fragment was requested
144*04fd306cSNickeau     *
145*04fd306cSNickeau     * Note that it may or may be not the main requested markup page.
146*04fd306cSNickeau     * You can have a markup rendering inside another markup rendering.
147*04fd306cSNickeau     *
148*04fd306cSNickeau     * @param WikiPath $contextPath
149*04fd306cSNickeau     * @return $this
150*04fd306cSNickeau     */
151*04fd306cSNickeau    public function setRequestedContextPath(WikiPath $contextPath): FetcherMarkupBuilder
152*04fd306cSNickeau    {
153*04fd306cSNickeau        $this->requestedContextPath = $contextPath;
154*04fd306cSNickeau        return $this;
155*04fd306cSNickeau    }
156*04fd306cSNickeau
157*04fd306cSNickeau    /**
158*04fd306cSNickeau     */
159*04fd306cSNickeau    public function setRequestedMime(Mime $mime): FetcherMarkupBuilder
160*04fd306cSNickeau    {
161*04fd306cSNickeau        $this->mime = $mime;
162*04fd306cSNickeau        return $this;
163*04fd306cSNickeau    }
164*04fd306cSNickeau
165*04fd306cSNickeau    public function setRequestedMimeToXhtml(): FetcherMarkupBuilder
166*04fd306cSNickeau    {
167*04fd306cSNickeau        try {
168*04fd306cSNickeau            return $this->setRequestedMime(Mime::createFromExtension("xhtml"));
169*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
170*04fd306cSNickeau            throw new ExceptionRuntime("Internal error", 0, $e);
171*04fd306cSNickeau        }
172*04fd306cSNickeau
173*04fd306cSNickeau    }
174*04fd306cSNickeau
175*04fd306cSNickeau
176*04fd306cSNickeau    /**
177*04fd306cSNickeau     * Technically, you could set the mime to whatever you want
178*04fd306cSNickeau     * and still get the instructions via {@link FetcherMarkup::getInstructions()}
179*04fd306cSNickeau     * Setting the mime to instructions will just not do any render processing.
180*04fd306cSNickeau     * @return $this
181*04fd306cSNickeau     */
182*04fd306cSNickeau    public function setRequestedMimeToInstructions(): FetcherMarkupBuilder
183*04fd306cSNickeau    {
184*04fd306cSNickeau        try {
185*04fd306cSNickeau            $this->setRequestedMime(Mime::createFromExtension(MarkupRenderer::INSTRUCTION_EXTENSION));
186*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
187*04fd306cSNickeau            throw new ExceptionRuntime("Internal error: the mime is internal and should be good");
188*04fd306cSNickeau        }
189*04fd306cSNickeau        return $this;
190*04fd306cSNickeau
191*04fd306cSNickeau    }
192*04fd306cSNickeau
193*04fd306cSNickeau
194*04fd306cSNickeau    /**
195*04fd306cSNickeau     * @throws ExceptionNotExists
196*04fd306cSNickeau     */
197*04fd306cSNickeau    public function build(): FetcherMarkup
198*04fd306cSNickeau    {
199*04fd306cSNickeau
200*04fd306cSNickeau        /**
201*04fd306cSNickeau         * One input should be given
202*04fd306cSNickeau         */
203*04fd306cSNickeau        if ($this->builderMarkupSourcePath === null && $this->builderMarkupString === null && $this->builderRequestedInstructions === null) {
204*04fd306cSNickeau            throw new ExceptionRuntimeInternal("A markup source path, a markup string or instructions should be given");
205*04fd306cSNickeau        }
206*04fd306cSNickeau        /**
207*04fd306cSNickeau         * Only one input should be given
208*04fd306cSNickeau         */
209*04fd306cSNickeau        $foundInput = "";
210*04fd306cSNickeau        if ($this->builderMarkupSourcePath !== null) {
211*04fd306cSNickeau            $foundInput = "markup path";
212*04fd306cSNickeau        }
213*04fd306cSNickeau        if ($this->builderMarkupString !== null) {
214*04fd306cSNickeau            if (!empty($foundInput)) {
215*04fd306cSNickeau                throw new ExceptionRuntimeInternal("Only one input should be given, we have found 2 inputs ($foundInput and markup string)");
216*04fd306cSNickeau            }
217*04fd306cSNickeau            $foundInput = "markup string";
218*04fd306cSNickeau
219*04fd306cSNickeau        }
220*04fd306cSNickeau        if ($this->builderRequestedInstructions !== null) {
221*04fd306cSNickeau            if (!empty($foundInput)) {
222*04fd306cSNickeau                throw new ExceptionRuntimeInternal("Only one input should be given, we have found 2 inputs ($foundInput and instructions)");
223*04fd306cSNickeau            }
224*04fd306cSNickeau        }
225*04fd306cSNickeau
226*04fd306cSNickeau        /**
227*04fd306cSNickeau         * Other Mandatory
228*04fd306cSNickeau         */
229*04fd306cSNickeau        if (!isset($this->mime)) {
230*04fd306cSNickeau            throw new ExceptionRuntimeInternal("A mime is mandatory");
231*04fd306cSNickeau        }
232*04fd306cSNickeau        if (!isset($this->requestedContextPath)) {
233*04fd306cSNickeau            throw new ExceptionRuntimeInternal("A context path is mandatory");
234*04fd306cSNickeau        }
235*04fd306cSNickeau
236*04fd306cSNickeau        /**
237*04fd306cSNickeau         * The object type is mandatory
238*04fd306cSNickeau         */
239*04fd306cSNickeau        if (!isset($this->rendererName)) {
240*04fd306cSNickeau            switch ($this->mime->toString()) {
241*04fd306cSNickeau                case Mime::XHTML:
242*04fd306cSNickeau                    /**
243*04fd306cSNickeau                     * ie last name of {@link \Doku_Renderer_xhtml}
244*04fd306cSNickeau                     */
245*04fd306cSNickeau                    $rendererName = MarkupRenderer::XHTML_RENDERER;
246*04fd306cSNickeau                    break;
247*04fd306cSNickeau                case Mime::META:
248*04fd306cSNickeau                    /**
249*04fd306cSNickeau                     * ie last name of {@link \Doku_Renderer_metadata}
250*04fd306cSNickeau                     */
251*04fd306cSNickeau                    $rendererName = "metadata";
252*04fd306cSNickeau                    break;
253*04fd306cSNickeau                case Mime::INSTRUCTIONS:
254*04fd306cSNickeau                    /**
255*04fd306cSNickeau                     * Does not exist yet bu that the future
256*04fd306cSNickeau                     */
257*04fd306cSNickeau                    $rendererName = FetcherMarkupInstructions::NAME;
258*04fd306cSNickeau                    break;
259*04fd306cSNickeau                default:
260*04fd306cSNickeau                    throw new ExceptionRuntimeInternal("A renderer name (ie builder name/output object type) is mandatory");
261*04fd306cSNickeau            }
262*04fd306cSNickeau        } else {
263*04fd306cSNickeau            $rendererName = $this->rendererName;
264*04fd306cSNickeau        }
265*04fd306cSNickeau
266*04fd306cSNickeau        /**
267*04fd306cSNickeau         * Building
268*04fd306cSNickeau         */
269*04fd306cSNickeau        $newFetcherMarkup = new FetcherMarkup();
270*04fd306cSNickeau        $newFetcherMarkup->builderName = $rendererName;
271*04fd306cSNickeau
272*04fd306cSNickeau        $newFetcherMarkup->requestedContextPath = $this->requestedContextPath;
273*04fd306cSNickeau        if ($this->builderMarkupString !== null) {
274*04fd306cSNickeau            $newFetcherMarkup->markupString = $this->builderMarkupString;
275*04fd306cSNickeau        }
276*04fd306cSNickeau        if ($this->builderMarkupSourcePath !== null) {
277*04fd306cSNickeau            $newFetcherMarkup->markupSourcePath = $this->builderMarkupSourcePath;
278*04fd306cSNickeau            if (!FileSystems::exists($this->builderMarkupSourcePath)) {
279*04fd306cSNickeau                /**
280*04fd306cSNickeau                 * Too much edge case for now with dokuwiki
281*04fd306cSNickeau                 * The {@link \Doku_Renderer_metadata} for instance throws an error if the file does not exist
282*04fd306cSNickeau                 * ... etc ....
283*04fd306cSNickeau                 */
284*04fd306cSNickeau                throw new ExceptionNotExists("The executing source file ({$this->builderMarkupSourcePath}) does not exist");
285*04fd306cSNickeau            }
286*04fd306cSNickeau        }
287*04fd306cSNickeau        if ($this->builderRequestedInstructions !== null) {
288*04fd306cSNickeau            $newFetcherMarkup->requestedInstructions = $this->builderRequestedInstructions;
289*04fd306cSNickeau        }
290*04fd306cSNickeau        $newFetcherMarkup->mime = $this->mime;
291*04fd306cSNickeau        $newFetcherMarkup->deleteRootBlockElement = $this->deleteRootBlockElement;
292*04fd306cSNickeau
293*04fd306cSNickeau
294*04fd306cSNickeau        $newFetcherMarkup->isDoc = $this->getIsDocumentExecution();
295*04fd306cSNickeau        if (isset($this->builderContextData)) {
296*04fd306cSNickeau            $newFetcherMarkup->contextData = $this->builderContextData;
297*04fd306cSNickeau        }
298*04fd306cSNickeau
299*04fd306cSNickeau        if (isset($this->parentMarkupHandler)) {
300*04fd306cSNickeau            $newFetcherMarkup->parentMarkupHandler = $this->parentMarkupHandler;
301*04fd306cSNickeau        }
302*04fd306cSNickeau        $newFetcherMarkup->isNonPathStandaloneExecution = $this->isCodeStandAloneExecution;
303*04fd306cSNickeau
304*04fd306cSNickeau
305*04fd306cSNickeau        /**
306*04fd306cSNickeau         * We build the cache dependencies even if there is no source markup path (therefore no cache store)
307*04fd306cSNickeau         * (Why ? for test purpose, where we want to check if the dependencies was applied)
308*04fd306cSNickeau         * !!! Attention, the build of the dependencies should happen after that the markup source path is set !!!
309*04fd306cSNickeau         */
310*04fd306cSNickeau        $newFetcherMarkup->outputCacheDependencies = MarkupCacheDependencies::create($newFetcherMarkup);
311*04fd306cSNickeau
312*04fd306cSNickeau        /**
313*04fd306cSNickeau         * The cache object depends on the running request
314*04fd306cSNickeau         * We build it then just
315*04fd306cSNickeau         *
316*04fd306cSNickeau         * A request is also send by dokuwiki to check the cache validity
317*04fd306cSNickeau         *
318*04fd306cSNickeau         */
319*04fd306cSNickeau        if ($this->builderMarkupSourcePath !== null) {
320*04fd306cSNickeau
321*04fd306cSNickeau
322*04fd306cSNickeau            list($wikiId, $localFile) = self::getWikiIdAndLocalFileDokuwikiCompliant($this->builderMarkupSourcePath);
323*04fd306cSNickeau
324*04fd306cSNickeau            /**
325*04fd306cSNickeau             * Instructions cache
326*04fd306cSNickeau             */
327*04fd306cSNickeau            $newFetcherMarkup->instructionsCache = new CacheInstructions($wikiId, $localFile);
328*04fd306cSNickeau
329*04fd306cSNickeau            /**
330*04fd306cSNickeau             * Content cache
331*04fd306cSNickeau             */
332*04fd306cSNickeau            $extension = $this->mime->getExtension();
333*04fd306cSNickeau            $newFetcherMarkup->contentCache = new CacheRenderer($wikiId, $localFile, $extension);
334*04fd306cSNickeau            $newFetcherMarkup->outputCacheDependencies->rerouteCacheDestination($newFetcherMarkup->contentCache);
335*04fd306cSNickeau
336*04fd306cSNickeau            /**
337*04fd306cSNickeau             * Snippet Cache
338*04fd306cSNickeau             * Snippet.json is data dependent
339*04fd306cSNickeau             *
340*04fd306cSNickeau             * For instance, the carrousel may add glide or grid as snippet. It depends on the the number of backlinks.
341*04fd306cSNickeau             *
342*04fd306cSNickeau             * Therefore the output should be unique by rendered slot
343*04fd306cSNickeau             * Therefore we reroute (recalculate the cache key to the same than the html file)
344*04fd306cSNickeau             */
345*04fd306cSNickeau            $newFetcherMarkup->snippetCache = new CacheParser($wikiId, $localFile, "snippet.json");
346*04fd306cSNickeau            $newFetcherMarkup->outputCacheDependencies->rerouteCacheDestination($newFetcherMarkup->snippetCache);
347*04fd306cSNickeau
348*04fd306cSNickeau
349*04fd306cSNickeau            /**
350*04fd306cSNickeau             * Runtime Meta cache
351*04fd306cSNickeau             * (Technically, it's derived from the instructions)
352*04fd306cSNickeau             */
353*04fd306cSNickeau            $newFetcherMarkup->metaPath = LocalPath::createFromPathString(metaFN($wikiId, '.meta'));
354*04fd306cSNickeau            $newFetcherMarkup->metaCache = new CacheRenderer($wikiId, $localFile, 'metadata');
355*04fd306cSNickeau
356*04fd306cSNickeau        }
357*04fd306cSNickeau
358*04fd306cSNickeau        return $newFetcherMarkup;
359*04fd306cSNickeau
360*04fd306cSNickeau    }
361*04fd306cSNickeau
362*04fd306cSNickeau    public function setRequestedMimeToMetadata(): FetcherMarkupBuilder
363*04fd306cSNickeau    {
364*04fd306cSNickeau        try {
365*04fd306cSNickeau            return $this->setRequestedMime(Mime::createFromExtension(MarkupRenderer::METADATA_EXTENSION));
366*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
367*04fd306cSNickeau            throw new ExceptionRuntime("Internal error", 0, $e);
368*04fd306cSNickeau        }
369*04fd306cSNickeau    }
370*04fd306cSNickeau
371*04fd306cSNickeau    public function setRequestedRenderer(string $rendererName): FetcherMarkupBuilder
372*04fd306cSNickeau    {
373*04fd306cSNickeau        $this->rendererName = $rendererName;
374*04fd306cSNickeau        return $this;
375*04fd306cSNickeau    }
376*04fd306cSNickeau
377*04fd306cSNickeau    public function setRequestedContextPathWithDefault(): FetcherMarkupBuilder
378*04fd306cSNickeau    {
379*04fd306cSNickeau        $executionContext = ExecutionContext::getActualOrCreateFromEnv();
380*04fd306cSNickeau        try {
381*04fd306cSNickeau            // do we have an executing handler
382*04fd306cSNickeau            $this->requestedContextPath = $executionContext
383*04fd306cSNickeau                ->getExecutingMarkupHandler()
384*04fd306cSNickeau                ->getRequestedExecutingPath()
385*04fd306cSNickeau                ->toWikiPath();
386*04fd306cSNickeau        } catch (ExceptionCast|ExceptionNotFound $e) {
387*04fd306cSNickeau            $this->requestedContextPath = $executionContext->getConfig()->getDefaultContextPath();
388*04fd306cSNickeau        }
389*04fd306cSNickeau        return $this;
390*04fd306cSNickeau    }
391*04fd306cSNickeau
392*04fd306cSNickeau    /**
393*04fd306cSNickeau     * @param bool $isDoc - if the markup is a document or a fragment
394*04fd306cSNickeau     * @return $this
395*04fd306cSNickeau     * If the markup is a document, an outline is added, a toc is calculated.
396*04fd306cSNickeau     *
397*04fd306cSNickeau     * The default is execution parameters dependent if not set
398*04fd306cSNickeau     * and is calculated at {@link FetcherMarkupBuilder::getIsDocumentExecution()}
399*04fd306cSNickeau     */
400*04fd306cSNickeau    public function setIsDocument(bool $isDoc): FetcherMarkupBuilder
401*04fd306cSNickeau    {
402*04fd306cSNickeau        $this->isDoc = $isDoc;
403*04fd306cSNickeau        return $this;
404*04fd306cSNickeau    }
405*04fd306cSNickeau
406*04fd306cSNickeau    /**
407*04fd306cSNickeau     * @param array $instructions
408*04fd306cSNickeau     * @return FetcherMarkupBuilder
409*04fd306cSNickeau     */
410*04fd306cSNickeau    public function setRequestedInstructions(array $instructions): FetcherMarkupBuilder
411*04fd306cSNickeau    {
412*04fd306cSNickeau        $this->builderRequestedInstructions = $instructions;
413*04fd306cSNickeau        return $this;
414*04fd306cSNickeau    }
415*04fd306cSNickeau
416*04fd306cSNickeau    /**
417*04fd306cSNickeau     * @param array|null $contextData
418*04fd306cSNickeau     * @return $this
419*04fd306cSNickeau     */
420*04fd306cSNickeau    public function setContextData(?array $contextData): FetcherMarkupBuilder
421*04fd306cSNickeau    {
422*04fd306cSNickeau        if ($contextData == null) {
423*04fd306cSNickeau            return $this;
424*04fd306cSNickeau        }
425*04fd306cSNickeau        $this->builderContextData = $contextData;
426*04fd306cSNickeau        return $this;
427*04fd306cSNickeau    }
428*04fd306cSNickeau
429*04fd306cSNickeau    /**
430*04fd306cSNickeau     * @param bool $isStandAlone
431*04fd306cSNickeau     * @return $this
432*04fd306cSNickeau     *
433*04fd306cSNickeau     * when a execution is not a {@link FetcherMarkup::isPathExecution()}, the snippet will not be stored automatically.
434*04fd306cSNickeau     * To avoid this problem, a warning is send if the calling code does not set explicitly that this is specifically a
435*04fd306cSNickeau     * standalone execution
436*04fd306cSNickeau     */
437*04fd306cSNickeau    public function setIsStandAloneCodeExecution(bool $isStandAlone): FetcherMarkupBuilder
438*04fd306cSNickeau    {
439*04fd306cSNickeau        $this->isCodeStandAloneExecution = $isStandAlone;
440*04fd306cSNickeau        return $this;
441*04fd306cSNickeau    }
442*04fd306cSNickeau
443*04fd306cSNickeau    /**
444*04fd306cSNickeau     * Determins if the run is a fragment or document execution
445*04fd306cSNickeau     *
446*04fd306cSNickeau     * Note: in dokuwiki term, a {@link ExecutionContext::PREVIEW_ACTION}
447*04fd306cSNickeau     * preview action is a fragment
448*04fd306cSNickeau     *
449*04fd306cSNickeau     * @return bool true if this is a document execution
450*04fd306cSNickeau     */
451*04fd306cSNickeau    private function getIsDocumentExecution(): bool
452*04fd306cSNickeau    {
453*04fd306cSNickeau
454*04fd306cSNickeau        if (isset($this->isDoc)) {
455*04fd306cSNickeau            return $this->isDoc;
456*04fd306cSNickeau        }
457*04fd306cSNickeau
458*04fd306cSNickeau        /**
459*04fd306cSNickeau         * By default, a string is not a whole doc
460*04fd306cSNickeau         * (in test, this is almost always the case)
461*04fd306cSNickeau         */
462*04fd306cSNickeau        if ($this->builderMarkupString !== null) {
463*04fd306cSNickeau            return false;
464*04fd306cSNickeau        }
465*04fd306cSNickeau
466*04fd306cSNickeau        /**
467*04fd306cSNickeau         * By default, a instructions array is not a whole doc
468*04fd306cSNickeau         * (in test and rendering, this is almost always the case)
469*04fd306cSNickeau         */
470*04fd306cSNickeau        if ($this->builderRequestedInstructions !== null) {
471*04fd306cSNickeau            return false;
472*04fd306cSNickeau        }
473*04fd306cSNickeau
474*04fd306cSNickeau        try {
475*04fd306cSNickeau
476*04fd306cSNickeau            /**
477*04fd306cSNickeau             * What fucked up is fucked up
478*04fd306cSNickeau             * Fragment such as sidebar may run in their own context (ie when editing for instance)
479*04fd306cSNickeau             * but are not a document
480*04fd306cSNickeau             */
481*04fd306cSNickeau            $executingWikiPath = $this->builderMarkupSourcePath->toWikiPath();
482*04fd306cSNickeau            $isFragmentMarkup = MarkupPath::createPageFromPathObject($executingWikiPath)->isSlot();
483*04fd306cSNickeau            if ($isFragmentMarkup) {
484*04fd306cSNickeau                return false;
485*04fd306cSNickeau            }
486*04fd306cSNickeau
487*04fd306cSNickeau            /**
488*04fd306cSNickeau             * If the context and executing path are:
489*04fd306cSNickeau             * * the same, this is a document run
490*04fd306cSNickeau             * * not the same, this is a fragment run
491*04fd306cSNickeau             */
492*04fd306cSNickeau            if ($this->requestedContextPath->toUriString() !== $executingWikiPath->toUriString()) {
493*04fd306cSNickeau                return false;
494*04fd306cSNickeau            }
495*04fd306cSNickeau
496*04fd306cSNickeau        } catch (ExceptionCast $e) {
497*04fd306cSNickeau            // no executing path, not a wiki path
498*04fd306cSNickeau        }
499*04fd306cSNickeau
500*04fd306cSNickeau        return true;
501*04fd306cSNickeau
502*04fd306cSNickeau    }
503*04fd306cSNickeau
504*04fd306cSNickeau    public function setParentMarkupHandler(FetcherMarkup $parentMarkupHandler): FetcherMarkupBuilder
505*04fd306cSNickeau    {
506*04fd306cSNickeau        $this->parentMarkupHandler = $parentMarkupHandler;
507*04fd306cSNickeau        return $this;
508*04fd306cSNickeau    }
509*04fd306cSNickeau
510*04fd306cSNickeau
511*04fd306cSNickeau}
512