xref: /plugin/combo/ComboStrap/ExecutionContext.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
1*04fd306cSNickeau<?php
2*04fd306cSNickeau
3*04fd306cSNickeaunamespace ComboStrap;
4*04fd306cSNickeau
5*04fd306cSNickeau
6*04fd306cSNickeauuse ComboStrap\Meta\Store\MetadataDbStore;
7*04fd306cSNickeauuse ComboStrap\Meta\Store\MetadataDokuWikiStore;
8*04fd306cSNickeauuse ComboStrap\Tag\WebCodeTag;
9*04fd306cSNickeauuse ComboStrap\Web\Url;
10*04fd306cSNickeauuse dokuwiki\ActionRouter;
11*04fd306cSNickeauuse dokuwiki\Extension\EventHandler;
12*04fd306cSNickeauuse TestRequest;
13*04fd306cSNickeau
14*04fd306cSNickeau
15*04fd306cSNickeau/**
16*04fd306cSNickeau * An execution object permits to manage the variable state for
17*04fd306cSNickeau * an execution (ie one HTTP request)
18*04fd306cSNickeau *
19*04fd306cSNickeau *
20*04fd306cSNickeau * Note that normally every page has a page context
21*04fd306cSNickeau * meaning that you can go from an admin page to show the page.
22*04fd306cSNickeau *
23*04fd306cSNickeau *
24*04fd306cSNickeau * When an execution context has finished, it should be {@link ExecutionContext::close() closed}
25*04fd306cSNickeau * or destroyed
26*04fd306cSNickeau *
27*04fd306cSNickeau * You can get the actual execution context with {@link ExecutionContext::getActualOrCreateFromEnv()}
28*04fd306cSNickeau *
29*04fd306cSNickeau *
30*04fd306cSNickeau * Same concept than [routing context](https://vertx.io/docs/apidocs/index.html?io/vertx/ext/web/RoutingContext.html)
31*04fd306cSNickeau * (Not yet fully implemented)
32*04fd306cSNickeau * ```java
33*04fd306cSNickeau * if (pet.isPresent())
34*04fd306cSNickeau *    routingContext
35*04fd306cSNickeau *     .response()
36*04fd306cSNickeau *      .setStatusCode(200)
37*04fd306cSNickeau *       .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
38*04fd306cSNickeau *      .end(pet.get().encode()); // (4)
39*04fd306cSNickeau * else
40*04fd306cSNickeau *    routingContext.fail(404, new Exception("Pet not found"));
41*04fd306cSNickeau * }
42*04fd306cSNickeau * ```
43*04fd306cSNickeau */
44*04fd306cSNickeauclass ExecutionContext
45*04fd306cSNickeau{
46*04fd306cSNickeau
47*04fd306cSNickeau    /**
48*04fd306cSNickeau     * Dokuwiki do attribute
49*04fd306cSNickeau     */
50*04fd306cSNickeau    const DO_ATTRIBUTE = "do";
51*04fd306cSNickeau
52*04fd306cSNickeau
53*04fd306cSNickeau    const CANONICAL = "execution-context";
54*04fd306cSNickeau
55*04fd306cSNickeau    /**
56*04fd306cSNickeau     * All action (handler)
57*04fd306cSNickeau     * That's what you will found in the `do` parameters
58*04fd306cSNickeau     */
59*04fd306cSNickeau    const SHOW_ACTION = "show";
60*04fd306cSNickeau    const EDIT_ACTION = "edit";
61*04fd306cSNickeau    /**
62*04fd306cSNickeau     * Preview is also used to
63*04fd306cSNickeau     * set the {@link FetcherMarkup::isFragment()}
64*04fd306cSNickeau     * processing to fragment
65*04fd306cSNickeau     */
66*04fd306cSNickeau    const PREVIEW_ACTION = "preview";
67*04fd306cSNickeau    const ADMIN_ACTION = "admin";
68*04fd306cSNickeau    const DRAFT_ACTION = "draft";
69*04fd306cSNickeau    const SEARCH_ACTION = "search";
70*04fd306cSNickeau    const LOGIN_ACTION = "login";
71*04fd306cSNickeau    const SAVE_ACTION = "save";
72*04fd306cSNickeau    const DRAFT_DEL_ACTION = "draftdel";
73*04fd306cSNickeau    const REDIRECT_ACTION = "redirect";
74*04fd306cSNickeau
75*04fd306cSNickeau    /**
76*04fd306cSNickeau     * private actions does not render a page to be indexed
77*04fd306cSNickeau     * by a search engine (ie no redirect)
78*04fd306cSNickeau     * May be easier, if not `show`, not public
79*04fd306cSNickeau     */
80*04fd306cSNickeau    const PRIVATES_ACTION_NO_REDIRECT = [
81*04fd306cSNickeau        self::EDIT_ACTION,
82*04fd306cSNickeau        self::PREVIEW_ACTION,
83*04fd306cSNickeau        self::ADMIN_ACTION,
84*04fd306cSNickeau        self::DRAFT_ACTION,
85*04fd306cSNickeau        self::DRAFT_DEL_ACTION,
86*04fd306cSNickeau        self::SEARCH_ACTION,
87*04fd306cSNickeau        self::LOGIN_ACTION,
88*04fd306cSNickeau        self::SAVE_ACTION,
89*04fd306cSNickeau        self::REDIRECT_ACTION,
90*04fd306cSNickeau        self::REGISTER_ACTION,
91*04fd306cSNickeau        self::RESEND_PWD_ACTION,
92*04fd306cSNickeau        self::PROFILE_ACTION,
93*04fd306cSNickeau    ];
94*04fd306cSNickeau    const REGISTER_ACTION = "register";
95*04fd306cSNickeau    const RESEND_PWD_ACTION = "resendpwd";
96*04fd306cSNickeau    const PROFILE_ACTION = "profile";
97*04fd306cSNickeau    const REVISIONS_ACTION = "revisions";
98*04fd306cSNickeau    const DIFF_ACTION = "diff";
99*04fd306cSNickeau    const INDEX_ACTION = "index";
100*04fd306cSNickeau
101*04fd306cSNickeau
102*04fd306cSNickeau    /**
103*04fd306cSNickeau     * @var array of objects that are scoped to this request
104*04fd306cSNickeau     */
105*04fd306cSNickeau    private array $executionScopedVariables = [];
106*04fd306cSNickeau
107*04fd306cSNickeau    private CacheManager $cacheManager;
108*04fd306cSNickeau
109*04fd306cSNickeau    private Site $app;
110*04fd306cSNickeau
111*04fd306cSNickeau    /**
112*04fd306cSNickeau     * A root execution context if any
113*04fd306cSNickeau     * Null because you can not unset a static variable
114*04fd306cSNickeau     */
115*04fd306cSNickeau    private static ?ExecutionContext $actualExecutionContext = null;
116*04fd306cSNickeau
117*04fd306cSNickeau    private ?string $capturedGlobalId;
118*04fd306cSNickeau    /**
119*04fd306cSNickeau     * It may be an array when preview/save/cancel
120*04fd306cSNickeau     * @var array|string|null
121*04fd306cSNickeau     */
122*04fd306cSNickeau    private $capturedAct;
123*04fd306cSNickeau
124*04fd306cSNickeau
125*04fd306cSNickeau    private Url $url;
126*04fd306cSNickeau
127*04fd306cSNickeau
128*04fd306cSNickeau    public HttpResponse $response;
129*04fd306cSNickeau
130*04fd306cSNickeau    /**
131*04fd306cSNickeau     * @var IFetcher - the fetcher that takes into account the HTTP request
132*04fd306cSNickeau     */
133*04fd306cSNickeau    private IFetcher $executingMainFetcher;
134*04fd306cSNickeau
135*04fd306cSNickeau    /**
136*04fd306cSNickeau     * @var array - a stack of:
137*04fd306cSNickeau     *   * markup handler executing (ie handler that is taking a markup (file, string) and making it a HTML, pdf, ...)
138*04fd306cSNickeau     *   * and old global environement, $executingId, $contextExecutingId, $act
139*04fd306cSNickeau     *
140*04fd306cSNickeau     * This fetcher is called by the main fetcher or by the {@link self::setExecutingMarkupHandler()}
141*04fd306cSNickeau     */
142*04fd306cSNickeau    private array $executingMarkupHandlerStack = [];
143*04fd306cSNickeau
144*04fd306cSNickeau    /**
145*04fd306cSNickeau     * @var TemplateForWebPage - the page template fetcher running (when a fetcher creates a page, it would uses this fetcher)
146*04fd306cSNickeau     * This class is called by the main fetcher to create a page
147*04fd306cSNickeau     */
148*04fd306cSNickeau    private TemplateForWebPage $executingPageTemplate;
149*04fd306cSNickeau    private string $creationTime;
150*04fd306cSNickeau
151*04fd306cSNickeau
152*04fd306cSNickeau    public function __construct()
153*04fd306cSNickeau    {
154*04fd306cSNickeau
155*04fd306cSNickeau        $this->creationTime = Iso8601Date::createFromNow()->toIsoStringMs();
156*04fd306cSNickeau
157*04fd306cSNickeau        $this->url = Url::createFromGetOrPostGlobalVariable();
158*04fd306cSNickeau
159*04fd306cSNickeau        $this->response = HttpResponse::createFromExecutionContext($this);
160*04fd306cSNickeau
161*04fd306cSNickeau        /**
162*04fd306cSNickeau         * The requested action
163*04fd306cSNickeau         */
164*04fd306cSNickeau        global $ACT;
165*04fd306cSNickeau        $this->capturedAct = $ACT;
166*04fd306cSNickeau        try {
167*04fd306cSNickeau            $urlAct = $this->url->getQueryPropertyValue(self::DO_ATTRIBUTE);
168*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
169*04fd306cSNickeau            /**
170*04fd306cSNickeau             * The value is unknown
171*04fd306cSNickeau             * (in doku.php, the default is `show`,
172*04fd306cSNickeau             * we take the dokuwiki value because the execution context may be
173*04fd306cSNickeau             * created after the dokuwiki init)
174*04fd306cSNickeau             */
175*04fd306cSNickeau            $urlAct = $ACT;
176*04fd306cSNickeau        }
177*04fd306cSNickeau        $ACT = $urlAct;
178*04fd306cSNickeau
179*04fd306cSNickeau        /**
180*04fd306cSNickeau         * The requested id
181*04fd306cSNickeau         */
182*04fd306cSNickeau        global $ID;
183*04fd306cSNickeau        $this->capturedGlobalId = $ID;
184*04fd306cSNickeau        try {
185*04fd306cSNickeau
186*04fd306cSNickeau            $urlId = $this->url->getQueryPropertyValue(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE);
187*04fd306cSNickeau            if (is_array($urlId)) {
188*04fd306cSNickeau                /**
189*04fd306cSNickeau                 * hack because the dokuwiki request global object as `ID` and `id` as array
190*04fd306cSNickeau                 * but our own {@link Url object} don't allow that and makes an array instead
191*04fd306cSNickeau                 * We don't use this data anyway, anymore ...
192*04fd306cSNickeau                 */
193*04fd306cSNickeau                $urlId = $urlId[0];
194*04fd306cSNickeau            }
195*04fd306cSNickeau            $ID = $urlId;
196*04fd306cSNickeau
197*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
198*04fd306cSNickeau            // none
199*04fd306cSNickeau            $ID = null;
200*04fd306cSNickeau        }
201*04fd306cSNickeau
202*04fd306cSNickeau
203*04fd306cSNickeau    }
204*04fd306cSNickeau
205*04fd306cSNickeau
206*04fd306cSNickeau    /**
207*04fd306cSNickeau     * @throws ExceptionNotFound
208*04fd306cSNickeau     */
209*04fd306cSNickeau    public static function getExecutionContext(): ExecutionContext
210*04fd306cSNickeau    {
211*04fd306cSNickeau        if (!isset(self::$actualExecutionContext)) {
212*04fd306cSNickeau            throw new ExceptionNotFound("No root context");
213*04fd306cSNickeau        }
214*04fd306cSNickeau        return self::$actualExecutionContext;
215*04fd306cSNickeau    }
216*04fd306cSNickeau
217*04fd306cSNickeau    /**
218*04fd306cSNickeau     * Utility class to set the requested id (used only in test,
219*04fd306cSNickeau     * normally the environment is set from global PHP environment variable
220*04fd306cSNickeau     * that get the HTTP request
221*04fd306cSNickeau     * @param string $requestedId
222*04fd306cSNickeau     * @return ExecutionContext
223*04fd306cSNickeau     * @deprecated use {@link self::setDefaultContextPath()} if you want to set a context path
224*04fd306cSNickeau     * without using a {@link TemplateForWebPage} or {@link FetcherMarkup}
225*04fd306cSNickeau     */
226*04fd306cSNickeau    public static function getOrCreateFromRequestedWikiId(string $requestedId): ExecutionContext
227*04fd306cSNickeau    {
228*04fd306cSNickeau
229*04fd306cSNickeau        return self::getActualOrCreateFromEnv()
230*04fd306cSNickeau            ->setDefaultContextPath(WikiPath::createMarkupPathFromId($requestedId));
231*04fd306cSNickeau
232*04fd306cSNickeau    }
233*04fd306cSNickeau
234*04fd306cSNickeau
235*04fd306cSNickeau    /**
236*04fd306cSNickeau     * @return ExecutionContext
237*04fd306cSNickeau     * @deprecated uses {@link self::createBlank()} instead
238*04fd306cSNickeau     */
239*04fd306cSNickeau    public static function createFromEnvironmentVariable(): ExecutionContext
240*04fd306cSNickeau    {
241*04fd306cSNickeau        return self::createBlank();
242*04fd306cSNickeau    }
243*04fd306cSNickeau
244*04fd306cSNickeau
245*04fd306cSNickeau    public static function createBlank(): ExecutionContext
246*04fd306cSNickeau    {
247*04fd306cSNickeau
248*04fd306cSNickeau        if (self::$actualExecutionContext !== null) {
249*04fd306cSNickeau            throw new ExceptionRuntimeInternal("The previous root context should be closed first");
250*04fd306cSNickeau        }
251*04fd306cSNickeau        $rootExecutionContext = (new ExecutionContext());
252*04fd306cSNickeau        self::$actualExecutionContext = $rootExecutionContext;
253*04fd306cSNickeau        return $rootExecutionContext;
254*04fd306cSNickeau
255*04fd306cSNickeau    }
256*04fd306cSNickeau
257*04fd306cSNickeau    /**
258*04fd306cSNickeau     * @return ExecutionContext - return the actual context or create a new one from the environment
259*04fd306cSNickeau     */
260*04fd306cSNickeau    public static function getActualOrCreateFromEnv(): ExecutionContext
261*04fd306cSNickeau    {
262*04fd306cSNickeau        try {
263*04fd306cSNickeau            return self::getExecutionContext();
264*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
265*04fd306cSNickeau            return self::createBlank();
266*04fd306cSNickeau        }
267*04fd306cSNickeau    }
268*04fd306cSNickeau
269*04fd306cSNickeau    /**
270*04fd306cSNickeau     * We create the id manager in the execution
271*04fd306cSNickeau     * context
272*04fd306cSNickeau     * (because in case a user choose to not use templating, the {@link FetcherMarkup}
273*04fd306cSNickeau     * is not available)
274*04fd306cSNickeau     * And all dynamic component such as {@link \syntax_plugin_combo_dropdown} would not
275*04fd306cSNickeau     * work anymore.
276*04fd306cSNickeau     *
277*04fd306cSNickeau     * @return IdManager
278*04fd306cSNickeau     */
279*04fd306cSNickeau    public function getIdManager(): IdManager
280*04fd306cSNickeau    {
281*04fd306cSNickeau        if (!isset($this->idManager)) {
282*04fd306cSNickeau            $this->idManager = new IdManager($this);
283*04fd306cSNickeau        }
284*04fd306cSNickeau        return $this->idManager;
285*04fd306cSNickeau    }
286*04fd306cSNickeau
287*04fd306cSNickeau    /**
288*04fd306cSNickeau     * Return the actual context path
289*04fd306cSNickeau     */
290*04fd306cSNickeau    public function getContextNamespacePath(): WikiPath
291*04fd306cSNickeau    {
292*04fd306cSNickeau        $requestedPath = $this->getContextPath();
293*04fd306cSNickeau        try {
294*04fd306cSNickeau            return $requestedPath->getParent();
295*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
296*04fd306cSNickeau            // root
297*04fd306cSNickeau            return $requestedPath;
298*04fd306cSNickeau        }
299*04fd306cSNickeau
300*04fd306cSNickeau    }
301*04fd306cSNickeau
302*04fd306cSNickeau
303*04fd306cSNickeau    /**
304*04fd306cSNickeau     * @throws ExceptionNotFound
305*04fd306cSNickeau     */
306*04fd306cSNickeau    public function getExecutingWikiId(): string
307*04fd306cSNickeau    {
308*04fd306cSNickeau        global $ID;
309*04fd306cSNickeau        if (empty($ID)) {
310*04fd306cSNickeau            throw new ExceptionNotFound("No executing id was found");
311*04fd306cSNickeau        }
312*04fd306cSNickeau        return $ID;
313*04fd306cSNickeau    }
314*04fd306cSNickeau
315*04fd306cSNickeau
316*04fd306cSNickeau    /**
317*04fd306cSNickeau     * @return void close the execution context
318*04fd306cSNickeau     */
319*04fd306cSNickeau    public function close()
320*04fd306cSNickeau    {
321*04fd306cSNickeau
322*04fd306cSNickeau        /**
323*04fd306cSNickeau         * Check that this execution context was not closed
324*04fd306cSNickeau         */
325*04fd306cSNickeau        if (self::$actualExecutionContext->creationTime !== $this->creationTime) {
326*04fd306cSNickeau            throw new ExceptionRuntimeInternal("This execution context was already closed");
327*04fd306cSNickeau        }
328*04fd306cSNickeau
329*04fd306cSNickeau        /**
330*04fd306cSNickeau         * Restore the global $conf of dokuwiki
331*04fd306cSNickeau         */
332*04fd306cSNickeau        $this->getApp()->getConfig()->restoreConfigState();
333*04fd306cSNickeau
334*04fd306cSNickeau        /** global dokuwiki messages variable */
335*04fd306cSNickeau        global $MSG;
336*04fd306cSNickeau        unset($MSG);
337*04fd306cSNickeau
338*04fd306cSNickeau        /**
339*04fd306cSNickeau         * Environment restoration
340*04fd306cSNickeau         * Execution context, change for now only this
341*04fd306cSNickeau         * global variables
342*04fd306cSNickeau         */
343*04fd306cSNickeau        global $ACT;
344*04fd306cSNickeau        $ACT = $this->getCapturedAct();
345*04fd306cSNickeau        global $ID;
346*04fd306cSNickeau        $ID = $this->getCapturedRunningId();
347*04fd306cSNickeau        global $TOC;
348*04fd306cSNickeau        unset($TOC);
349*04fd306cSNickeau
350*04fd306cSNickeau        // global scope store
351*04fd306cSNickeau        MetadataDbStore::resetAll();
352*04fd306cSNickeau        MetadataDokuWikiStore::unsetGlobalVariables();
353*04fd306cSNickeau
354*04fd306cSNickeau        // Router: dokuwiki global
355*04fd306cSNickeau        // reset event handler
356*04fd306cSNickeau        global $EVENT_HANDLER;
357*04fd306cSNickeau        $EVENT_HANDLER = new EventHandler();
358*04fd306cSNickeau        /**
359*04fd306cSNickeau         * We can't give the getInstance, a true value
360*04fd306cSNickeau         * because it will otherwise start the routing process
361*04fd306cSNickeau         * {@link ActionRouter::getInstance()}
362*04fd306cSNickeau         */
363*04fd306cSNickeau
364*04fd306cSNickeau
365*04fd306cSNickeau        /**
366*04fd306cSNickeau         * Close execution variables
367*04fd306cSNickeau         * (and therefore also {@link Sqlite}
368*04fd306cSNickeau         */
369*04fd306cSNickeau        $this->closeExecutionVariables();
370*04fd306cSNickeau
371*04fd306cSNickeau        /**
372*04fd306cSNickeau         * Is this really needed ?
373*04fd306cSNickeau         * as we unset the execution context below
374*04fd306cSNickeau         */
375*04fd306cSNickeau        unset($this->executingMainFetcher);
376*04fd306cSNickeau        unset($this->executingMarkupHandlerStack);
377*04fd306cSNickeau        unset($this->cacheManager);
378*04fd306cSNickeau        unset($this->idManager);
379*04fd306cSNickeau
380*04fd306cSNickeau        /**
381*04fd306cSNickeau         * Deleting
382*04fd306cSNickeau         */
383*04fd306cSNickeau        self::$actualExecutionContext = null;
384*04fd306cSNickeau
385*04fd306cSNickeau
386*04fd306cSNickeau    }
387*04fd306cSNickeau
388*04fd306cSNickeau
389*04fd306cSNickeau    public function getCapturedRunningId(): ?string
390*04fd306cSNickeau    {
391*04fd306cSNickeau        return $this->capturedGlobalId;
392*04fd306cSNickeau    }
393*04fd306cSNickeau
394*04fd306cSNickeau    public function getCapturedAct()
395*04fd306cSNickeau    {
396*04fd306cSNickeau        return $this->capturedAct;
397*04fd306cSNickeau    }
398*04fd306cSNickeau
399*04fd306cSNickeau    public function getCacheManager(): CacheManager
400*04fd306cSNickeau    {
401*04fd306cSNickeau        $root = self::$actualExecutionContext;
402*04fd306cSNickeau        if (!isset($root->cacheManager)) {
403*04fd306cSNickeau            $root->cacheManager = new CacheManager($this);
404*04fd306cSNickeau        }
405*04fd306cSNickeau        return $root->cacheManager;
406*04fd306cSNickeau
407*04fd306cSNickeau    }
408*04fd306cSNickeau
409*04fd306cSNickeau    /**
410*04fd306cSNickeau     * Return the root path if nothing is found
411*04fd306cSNickeau     */
412*04fd306cSNickeau    public function getRequestedPath(): WikiPath
413*04fd306cSNickeau    {
414*04fd306cSNickeau        /**
415*04fd306cSNickeau         * Do we have a template page executing ?
416*04fd306cSNickeau         */
417*04fd306cSNickeau        try {
418*04fd306cSNickeau            return $this->getExecutingPageTemplate()
419*04fd306cSNickeau                ->getRequestedContextPath();
420*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
421*04fd306cSNickeau            try {
422*04fd306cSNickeau                /**
423*04fd306cSNickeau                 * Case when the main handler
424*04fd306cSNickeau                 * run the main content before
425*04fd306cSNickeau                 * to inject it in the template page
426*04fd306cSNickeau                 * {@link TemplateForWebPage::render()}
427*04fd306cSNickeau                 */
428*04fd306cSNickeau                return $this->getExecutingMarkupHandler()
429*04fd306cSNickeau                    ->getRequestedContextPath();
430*04fd306cSNickeau            } catch (ExceptionNotFound $e) {
431*04fd306cSNickeau
432*04fd306cSNickeau
433*04fd306cSNickeau                /**
434*04fd306cSNickeau                 * not a template engine running
435*04fd306cSNickeau                 * The id notion is a little bit everywhere
436*04fd306cSNickeau                 * That's why we just don't check the action ($ACT)
437*04fd306cSNickeau                 *
438*04fd306cSNickeau                 * Example:
439*04fd306cSNickeau                 * * `id` may be asked by acl to determine the right
440*04fd306cSNickeau                 * * ...
441*04fd306cSNickeau                 */
442*04fd306cSNickeau                global $INPUT;
443*04fd306cSNickeau                $inputId = $INPUT->str("id");
444*04fd306cSNickeau                if (!empty($inputId)) {
445*04fd306cSNickeau                    return WikiPath::createMarkupPathFromId($inputId);
446*04fd306cSNickeau                }
447*04fd306cSNickeau
448*04fd306cSNickeau                global $ID;
449*04fd306cSNickeau                if (!empty($ID)) {
450*04fd306cSNickeau                    return WikiPath::createMarkupPathFromId($ID);
451*04fd306cSNickeau                }
452*04fd306cSNickeau
453*04fd306cSNickeau                /**
454*04fd306cSNickeau                 * This should be less used
455*04fd306cSNickeau                 * but shows where the requested id is spilled in dokuwiki
456*04fd306cSNickeau                 *
457*04fd306cSNickeau                 * If the component is in a sidebar, we don't want the ID of the sidebar
458*04fd306cSNickeau                 * but the ID of the page.
459*04fd306cSNickeau                 */
460*04fd306cSNickeau                global $INFO;
461*04fd306cSNickeau                if ($INFO !== null) {
462*04fd306cSNickeau                    $callingId = $INFO['id'];
463*04fd306cSNickeau                    if (!empty($callingId)) {
464*04fd306cSNickeau                        return WikiPath::createMarkupPathFromId($callingId);
465*04fd306cSNickeau                    }
466*04fd306cSNickeau                }
467*04fd306cSNickeau
468*04fd306cSNickeau                /**
469*04fd306cSNickeau                 * This is the case with event triggered
470*04fd306cSNickeau                 * before DokuWiki such as
471*04fd306cSNickeau                 * https://www.dokuwiki.org/devel:event:init_lang_load
472*04fd306cSNickeau                 * REQUEST is a mixed of post and get parameters
473*04fd306cSNickeau                 */
474*04fd306cSNickeau                global $_REQUEST;
475*04fd306cSNickeau                if (isset($_REQUEST[DokuwikiId::DOKUWIKI_ID_ATTRIBUTE])) {
476*04fd306cSNickeau                    $requestId = $_REQUEST[DokuwikiId::DOKUWIKI_ID_ATTRIBUTE];
477*04fd306cSNickeau                    if (!empty($requestId)) {
478*04fd306cSNickeau                        return WikiPath::createMarkupPathFromId($requestId);
479*04fd306cSNickeau                    }
480*04fd306cSNickeau                }
481*04fd306cSNickeau
482*04fd306cSNickeau                // not that show action is the default even if it's not set
483*04fd306cSNickeau                // we can't then control if the id should exists or not
484*04fd306cSNickeau                // markup based on string (test) or snippet of code
485*04fd306cSNickeau                // return the default context path (ie the root page)
486*04fd306cSNickeau                return $this->getConfig()->getDefaultContextPath();
487*04fd306cSNickeau            }
488*04fd306cSNickeau
489*04fd306cSNickeau        }
490*04fd306cSNickeau
491*04fd306cSNickeau    }
492*04fd306cSNickeau
493*04fd306cSNickeau    /**
494*04fd306cSNickeau     * @throws ExceptionNotFound
495*04fd306cSNickeau     */
496*04fd306cSNickeau    public function &getRuntimeObject(string $objectIdentifier)
497*04fd306cSNickeau    {
498*04fd306cSNickeau        if (isset($this->executionScopedVariables[$objectIdentifier])) {
499*04fd306cSNickeau            return $this->executionScopedVariables[$objectIdentifier];
500*04fd306cSNickeau        }
501*04fd306cSNickeau        throw new ExceptionNotFound("No object $objectIdentifier found");
502*04fd306cSNickeau    }
503*04fd306cSNickeau
504*04fd306cSNickeau    public function setRuntimeObject($objectIdentifier, &$object): ExecutionContext
505*04fd306cSNickeau    {
506*04fd306cSNickeau        $this->executionScopedVariables[$objectIdentifier] = &$object;
507*04fd306cSNickeau        return $this;
508*04fd306cSNickeau    }
509*04fd306cSNickeau
510*04fd306cSNickeau    public function getUrl(): Url
511*04fd306cSNickeau    {
512*04fd306cSNickeau        return $this->url;
513*04fd306cSNickeau    }
514*04fd306cSNickeau
515*04fd306cSNickeau
516*04fd306cSNickeau    /**
517*04fd306cSNickeau     * @param string $key
518*04fd306cSNickeau     * @param $value
519*04fd306cSNickeau     * @param string|null $pluginNamespace - if null, stored in the global conf namespace
520*04fd306cSNickeau     * @return $this
521*04fd306cSNickeau     * @deprecated use {@link SiteConfig::setConf()} instead
522*04fd306cSNickeau     */
523*04fd306cSNickeau    public function setConf(string $key, $value, ?string $pluginNamespace = PluginUtility::PLUGIN_BASE_NAME): ExecutionContext
524*04fd306cSNickeau    {
525*04fd306cSNickeau        $this->getApp()->getConfig()->setConf($key, $value, $pluginNamespace);
526*04fd306cSNickeau        return $this;
527*04fd306cSNickeau    }
528*04fd306cSNickeau
529*04fd306cSNickeau    /**
530*04fd306cSNickeau     * @param string $key
531*04fd306cSNickeau     * @param string|null $default
532*04fd306cSNickeau     * @return mixed|null
533*04fd306cSNickeau     * @deprecated use
534*04fd306cSNickeau     */
535*04fd306cSNickeau    public function getConfValue(string $key, string $default = null)
536*04fd306cSNickeau    {
537*04fd306cSNickeau        return $this->getApp()->getConfig()->getValue($key, $default);
538*04fd306cSNickeau    }
539*04fd306cSNickeau
540*04fd306cSNickeau    public function setRuntimeBoolean(string $key, bool $b): ExecutionContext
541*04fd306cSNickeau    {
542*04fd306cSNickeau        $this->executionScopedVariables[$key] = $b;
543*04fd306cSNickeau        return $this;
544*04fd306cSNickeau    }
545*04fd306cSNickeau
546*04fd306cSNickeau    /**
547*04fd306cSNickeau     * @throws ExceptionNotFound
548*04fd306cSNickeau     */
549*04fd306cSNickeau    public function getRuntimeBoolean(string $name): bool
550*04fd306cSNickeau    {
551*04fd306cSNickeau        $var = $this->executionScopedVariables[$name];
552*04fd306cSNickeau        if (!isset($var)) {
553*04fd306cSNickeau            throw new ExceptionNotFound("No $name runtime env was found");
554*04fd306cSNickeau        }
555*04fd306cSNickeau        return DataType::toBoolean($var);
556*04fd306cSNickeau    }
557*04fd306cSNickeau
558*04fd306cSNickeau    /**
559*04fd306cSNickeau     * @return $this
560*04fd306cSNickeau     * @deprecated uses {@link SiteConfig::setCacheXhtmlOn()}
561*04fd306cSNickeau     */
562*04fd306cSNickeau    public function setCacheXhtmlOn(): ExecutionContext
563*04fd306cSNickeau    {
564*04fd306cSNickeau        $this->getApp()->getConfig()->setCacheXhtmlOn();
565*04fd306cSNickeau        return $this;
566*04fd306cSNickeau    }
567*04fd306cSNickeau
568*04fd306cSNickeau    /**
569*04fd306cSNickeau     *
570*04fd306cSNickeau     * @return $this
571*04fd306cSNickeau     * @deprecated use the {@link SiteConfig::setConsoleOn} instead
572*04fd306cSNickeau     */
573*04fd306cSNickeau    public function setConsoleOn(): ExecutionContext
574*04fd306cSNickeau    {
575*04fd306cSNickeau        $this->getApp()->getConfig()->setCacheXhtmlOn();
576*04fd306cSNickeau        return $this;
577*04fd306cSNickeau    }
578*04fd306cSNickeau
579*04fd306cSNickeau    public function setConsoleOff(): ExecutionContext
580*04fd306cSNickeau    {
581*04fd306cSNickeau        $this->getConfig()->setConsoleOff();
582*04fd306cSNickeau        return $this;
583*04fd306cSNickeau    }
584*04fd306cSNickeau
585*04fd306cSNickeau    /**
586*04fd306cSNickeau     * @return $this
587*04fd306cSNickeau     * @deprecated use {@link SiteConfig::setDisableThemeSystem()}
588*04fd306cSNickeau     */
589*04fd306cSNickeau    public function setDisableTemplating(): ExecutionContext
590*04fd306cSNickeau    {
591*04fd306cSNickeau        $this->getApp()->getConfig()->setDisableThemeSystem();
592*04fd306cSNickeau        return $this;
593*04fd306cSNickeau    }
594*04fd306cSNickeau
595*04fd306cSNickeau
596*04fd306cSNickeau    /**
597*04fd306cSNickeau     * @return bool
598*04fd306cSNickeau     * @deprecated use the {@link SiteConfig::isConsoleOn()} instead
599*04fd306cSNickeau     */
600*04fd306cSNickeau    public function isConsoleOn(): bool
601*04fd306cSNickeau    {
602*04fd306cSNickeau        return $this->getApp()->getConfig()->isConsoleOn();
603*04fd306cSNickeau    }
604*04fd306cSNickeau
605*04fd306cSNickeau
606*04fd306cSNickeau    /**
607*04fd306cSNickeau     * Dokuwiki handler name
608*04fd306cSNickeau     * @return array|mixed|string
609*04fd306cSNickeau     */
610*04fd306cSNickeau    public function getExecutingAction()
611*04fd306cSNickeau    {
612*04fd306cSNickeau        global $ACT;
613*04fd306cSNickeau        return $ACT;
614*04fd306cSNickeau    }
615*04fd306cSNickeau
616*04fd306cSNickeau    public function setLogExceptionToError(): ExecutionContext
617*04fd306cSNickeau    {
618*04fd306cSNickeau        $this->getConfig()->setLogExceptionToError();
619*04fd306cSNickeau        return $this;
620*04fd306cSNickeau    }
621*04fd306cSNickeau
622*04fd306cSNickeau    /**
623*04fd306cSNickeau     * @return SnippetSystem
624*04fd306cSNickeau     * It's not attached to the {@link FetcherMarkup}
625*04fd306cSNickeau     * because the user may choose to not use it (ie {@link SiteConfig::isThemeSystemEnabled()}
626*04fd306cSNickeau     */
627*04fd306cSNickeau    public function getSnippetSystem(): SnippetSystem
628*04fd306cSNickeau    {
629*04fd306cSNickeau        return SnippetSystem::getFromContext();
630*04fd306cSNickeau    }
631*04fd306cSNickeau
632*04fd306cSNickeau    /**
633*04fd306cSNickeau     * @return bool - does the action create a publication (render a page)
634*04fd306cSNickeau     */
635*04fd306cSNickeau    public function isPublicationAction(): bool
636*04fd306cSNickeau    {
637*04fd306cSNickeau
638*04fd306cSNickeau        $act = $this->getExecutingAction();
639*04fd306cSNickeau        if (in_array($act, self::PRIVATES_ACTION_NO_REDIRECT)) {
640*04fd306cSNickeau            return false;
641*04fd306cSNickeau        }
642*04fd306cSNickeau
643*04fd306cSNickeau        return true;
644*04fd306cSNickeau
645*04fd306cSNickeau    }
646*04fd306cSNickeau
647*04fd306cSNickeau    public function setEnableSectionEditing(): ExecutionContext
648*04fd306cSNickeau    {
649*04fd306cSNickeau        $this->setConf('maxseclevel', 999, null);
650*04fd306cSNickeau        return $this;
651*04fd306cSNickeau    }
652*04fd306cSNickeau
653*04fd306cSNickeau    /**
654*04fd306cSNickeau     * @param string $value
655*04fd306cSNickeau     * @return $this
656*04fd306cSNickeau     * @deprecated use the {@link SiteConfig::setCanonicalUrlType()} instead
657*04fd306cSNickeau     */
658*04fd306cSNickeau    public function setCanonicalUrlType(string $value): ExecutionContext
659*04fd306cSNickeau    {
660*04fd306cSNickeau        $this->getConfig()->setCanonicalUrlType($value);
661*04fd306cSNickeau        return $this;
662*04fd306cSNickeau    }
663*04fd306cSNickeau
664*04fd306cSNickeau    public function setUseHeadingAsTitle(): ExecutionContext
665*04fd306cSNickeau    {
666*04fd306cSNickeau        // https://www.dokuwiki.org/config:useheading
667*04fd306cSNickeau        $this->setConf('useheading', 1, null);
668*04fd306cSNickeau        return $this;
669*04fd306cSNickeau    }
670*04fd306cSNickeau
671*04fd306cSNickeau    public function response(): HttpResponse
672*04fd306cSNickeau    {
673*04fd306cSNickeau        return $this->response;
674*04fd306cSNickeau    }
675*04fd306cSNickeau
676*04fd306cSNickeau    /**
677*04fd306cSNickeau     * @param string|null $executingId
678*04fd306cSNickeau     * @return void
679*04fd306cSNickeau     */
680*04fd306cSNickeau    private function setExecutingId(?string $executingId): void
681*04fd306cSNickeau    {
682*04fd306cSNickeau        global $ID;
683*04fd306cSNickeau        if ($executingId == null) {
684*04fd306cSNickeau            // ID should not be null
685*04fd306cSNickeau            // to be able to check the ACL
686*04fd306cSNickeau            return;
687*04fd306cSNickeau        }
688*04fd306cSNickeau        $executingId = WikiPath::removeRootSepIfPresent($executingId);
689*04fd306cSNickeau        $ID = $executingId;
690*04fd306cSNickeau    }
691*04fd306cSNickeau
692*04fd306cSNickeau    public function setConfGlobal(string $key, string $value): ExecutionContext
693*04fd306cSNickeau    {
694*04fd306cSNickeau        $this->setConf($key, $value, null);
695*04fd306cSNickeau        return $this;
696*04fd306cSNickeau    }
697*04fd306cSNickeau
698*04fd306cSNickeau    /**
699*04fd306cSNickeau     * @return bool - if this execution is a test running
700*04fd306cSNickeau     */
701*04fd306cSNickeau    public function isTestRun(): bool
702*04fd306cSNickeau    {
703*04fd306cSNickeau        /**
704*04fd306cSNickeau         * Test Requested is loaded only in a test run
705*04fd306cSNickeau         * Does not exist in a normal installation
706*04fd306cSNickeau         * and is not found, triggering an exception
707*04fd306cSNickeau         */
708*04fd306cSNickeau        if (class_exists('TestRequest')) {
709*04fd306cSNickeau            $testRequest = TestRequest::getRunning();
710*04fd306cSNickeau            return $testRequest !== null;
711*04fd306cSNickeau        }
712*04fd306cSNickeau        return false;
713*04fd306cSNickeau
714*04fd306cSNickeau    }
715*04fd306cSNickeau
716*04fd306cSNickeau    private function setExecutingAction(?string $runningAct): ExecutionContext
717*04fd306cSNickeau    {
718*04fd306cSNickeau        global $ACT;
719*04fd306cSNickeau        $ACT = $runningAct;
720*04fd306cSNickeau        return $this;
721*04fd306cSNickeau    }
722*04fd306cSNickeau
723*04fd306cSNickeau
724*04fd306cSNickeau    /**
725*04fd306cSNickeau     * Set the main fetcher, the entry point of the request (ie the url of the browser)
726*04fd306cSNickeau     * that will return a string
727*04fd306cSNickeau     * @throws ExceptionBadArgument
728*04fd306cSNickeau     * @throws ExceptionInternal
729*04fd306cSNickeau     * @throws ExceptionNotFound
730*04fd306cSNickeau     */
731*04fd306cSNickeau    public function createStringMainFetcherFromRequestedUrl(Url $fetchUrl): IFetcherString
732*04fd306cSNickeau    {
733*04fd306cSNickeau        $this->executingMainFetcher = FetcherSystem::createFetcherStringFromUrl($fetchUrl);
734*04fd306cSNickeau        return $this->executingMainFetcher;
735*04fd306cSNickeau    }
736*04fd306cSNickeau
737*04fd306cSNickeau
738*04fd306cSNickeau    /**
739*04fd306cSNickeau     * Set the main fetcher (with the url of the browser)
740*04fd306cSNickeau     * that will return a path (image, ...)
741*04fd306cSNickeau     * @throws ExceptionBadArgument
742*04fd306cSNickeau     * @throws ExceptionInternal
743*04fd306cSNickeau     * @throws ExceptionNotFound
744*04fd306cSNickeau     */
745*04fd306cSNickeau    public function createPathMainFetcherFromUrl(Url $fetchUrl): IFetcherPath
746*04fd306cSNickeau    {
747*04fd306cSNickeau        $this->executingMainFetcher = FetcherSystem::createPathFetcherFromUrl($fetchUrl);
748*04fd306cSNickeau        return $this->executingMainFetcher;
749*04fd306cSNickeau    }
750*04fd306cSNickeau
751*04fd306cSNickeau    public function closeMainExecutingFetcher(): ExecutionContext
752*04fd306cSNickeau    {
753*04fd306cSNickeau        unset($this->executingMainFetcher);
754*04fd306cSNickeau        /**
755*04fd306cSNickeau         * Snippet are not yet fully coupled to the {@link FetcherMarkup}
756*04fd306cSNickeau         */
757*04fd306cSNickeau        $this->closeAndRemoveRuntimeVariableIfExists(Snippet::CANONICAL);
758*04fd306cSNickeau        return $this;
759*04fd306cSNickeau    }
760*04fd306cSNickeau
761*04fd306cSNickeau    /**
762*04fd306cSNickeau     * This function sets the markup running context object globally,
763*04fd306cSNickeau     * so that code may access it via this global variable
764*04fd306cSNickeau     * (Fighting dokuwiki global scope)
765*04fd306cSNickeau     * @param FetcherMarkup $markupHandler
766*04fd306cSNickeau     * @return $this
767*04fd306cSNickeau     */
768*04fd306cSNickeau    public function setExecutingMarkupHandler(FetcherMarkup $markupHandler): ExecutionContext
769*04fd306cSNickeau    {
770*04fd306cSNickeau
771*04fd306cSNickeau
772*04fd306cSNickeau
773*04fd306cSNickeau        /**
774*04fd306cSNickeau         * Act
775*04fd306cSNickeau         */
776*04fd306cSNickeau        $oldAct = $this->getExecutingAction();
777*04fd306cSNickeau        if (!$markupHandler->isPathExecution() && $oldAct !== ExecutionContext::PREVIEW_ACTION) {
778*04fd306cSNickeau            /**
779*04fd306cSNickeau             * Not sure that is is still needed
780*04fd306cSNickeau             * as we have now the notion of document/fragment
781*04fd306cSNickeau             * {@link FetcherMarkup::isDocument()}
782*04fd306cSNickeau             */
783*04fd306cSNickeau            $runningAct = FetcherMarkup::MARKUP_DYNAMIC_EXECUTION_NAME;
784*04fd306cSNickeau            $this->setExecutingAction($runningAct);
785*04fd306cSNickeau        }
786*04fd306cSNickeau
787*04fd306cSNickeau        /**
788*04fd306cSNickeau         * Id
789*04fd306cSNickeau         */
790*04fd306cSNickeau        try {
791*04fd306cSNickeau            $oldExecutingId = $this->getExecutingWikiId();
792*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
793*04fd306cSNickeau            $oldExecutingId = null;
794*04fd306cSNickeau        }
795*04fd306cSNickeau        try {
796*04fd306cSNickeau
797*04fd306cSNickeau            $executingPath = $markupHandler->getRequestedExecutingPath();
798*04fd306cSNickeau            $executingId = $executingPath->toAbsoluteId();
799*04fd306cSNickeau            $this->setExecutingId($executingId);
800*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
801*04fd306cSNickeau            // no executing path dynamic markup execution
802*04fd306cSNickeau        }
803*04fd306cSNickeau
804*04fd306cSNickeau        /**
805*04fd306cSNickeau         * $INFO  (Fragment run, ...)
806*04fd306cSNickeau         * We don't use {@link pageinfo()} for now
807*04fd306cSNickeau         * We just advertise if this is a fragment run
808*04fd306cSNickeau         * via the `id`
809*04fd306cSNickeau         */
810*04fd306cSNickeau        global $INFO;
811*04fd306cSNickeau        $oldContextId = $INFO['id'];
812*04fd306cSNickeau        if ($markupHandler->isFragment()) {
813*04fd306cSNickeau            $contextPath = $markupHandler->getRequestedContextPath();
814*04fd306cSNickeau            $INFO['id'] = $contextPath->getWikiId();
815*04fd306cSNickeau        }
816*04fd306cSNickeau
817*04fd306cSNickeau        /**
818*04fd306cSNickeau         * Call to Fetcher Markup can be recursive,
819*04fd306cSNickeau         * we try to break a loop
820*04fd306cSNickeau         *
821*04fd306cSNickeau         * Note that the same object may call recursively:
822*04fd306cSNickeau         * * the {@link FetcherMarkup::processMetaEventually()} metadata may call the {@link FetcherMarkup::getInstructions() instructions},
823*04fd306cSNickeau         */
824*04fd306cSNickeau        $id = $markupHandler->getId();
825*04fd306cSNickeau        if(array_key_exists($id,$this->executingMarkupHandlerStack)){
826*04fd306cSNickeau            LogUtility::internalError("The markup ($id) is already executing");
827*04fd306cSNickeau            $id = "$id-already-in-stack";
828*04fd306cSNickeau        }
829*04fd306cSNickeau        $this->executingMarkupHandlerStack[$id] = [$markupHandler, $oldExecutingId, $oldContextId, $oldAct];
830*04fd306cSNickeau        return $this;
831*04fd306cSNickeau    }
832*04fd306cSNickeau
833*04fd306cSNickeau    public
834*04fd306cSNickeau    function closeExecutingMarkupHandler(): ExecutionContext
835*04fd306cSNickeau    {
836*04fd306cSNickeau        /** @noinspection PhpUnusedLocalVariableInspection */
837*04fd306cSNickeau        [$markupHandler, $oldExecutingId, $oldContextId, $oldAct] = array_pop($this->executingMarkupHandlerStack);
838*04fd306cSNickeau
839*04fd306cSNickeau        $this
840*04fd306cSNickeau            ->setExecutingAction($oldAct)
841*04fd306cSNickeau            ->setExecutingId($oldExecutingId);
842*04fd306cSNickeau
843*04fd306cSNickeau        global $INFO;
844*04fd306cSNickeau        if ($oldExecutingId === null) {
845*04fd306cSNickeau            unset($INFO['id']);
846*04fd306cSNickeau        } else {
847*04fd306cSNickeau            $INFO['id'] = $oldContextId;
848*04fd306cSNickeau        }
849*04fd306cSNickeau        return $this;
850*04fd306cSNickeau    }
851*04fd306cSNickeau
852*04fd306cSNickeau
853*04fd306cSNickeau    /**
854*04fd306cSNickeau     * @throws ExceptionNotFound - if there is no markup handler execution running
855*04fd306cSNickeau     */
856*04fd306cSNickeau    public
857*04fd306cSNickeau    function getExecutingMarkupHandler(): FetcherMarkup
858*04fd306cSNickeau    {
859*04fd306cSNickeau        $count = count($this->executingMarkupHandlerStack);
860*04fd306cSNickeau        if ($count >= 1) {
861*04fd306cSNickeau            return $this->executingMarkupHandlerStack[array_key_last($this->executingMarkupHandlerStack)][0];
862*04fd306cSNickeau        }
863*04fd306cSNickeau        throw new ExceptionNotFound("No markup handler running");
864*04fd306cSNickeau    }
865*04fd306cSNickeau
866*04fd306cSNickeau    /**
867*04fd306cSNickeau     * @throws ExceptionNotFound - if there is no parent markup handler execution found
868*04fd306cSNickeau     */
869*04fd306cSNickeau    public
870*04fd306cSNickeau    function getExecutingParentMarkupHandler(): FetcherMarkup
871*04fd306cSNickeau    {
872*04fd306cSNickeau        return $this->getExecutingMarkupHandler()->getParent();
873*04fd306cSNickeau    }
874*04fd306cSNickeau
875*04fd306cSNickeau    /**
876*04fd306cSNickeau     * This function sets the default context path.
877*04fd306cSNickeau     *
878*04fd306cSNickeau     * Mostly used in test, to determine relative path
879*04fd306cSNickeau     * when testing {@link LinkMarkup} and {@link WikiPath}
880*04fd306cSNickeau     * and not use a {@link FetcherMarkup}
881*04fd306cSNickeau     *
882*04fd306cSNickeau     * @param WikiPath $contextPath - a markup file context path used (not a namespace)
883*04fd306cSNickeau     * @return $this
884*04fd306cSNickeau     * @deprecated
885*04fd306cSNickeau     */
886*04fd306cSNickeau    public
887*04fd306cSNickeau    function setDefaultContextPath(WikiPath $contextPath): ExecutionContext
888*04fd306cSNickeau    {
889*04fd306cSNickeau        $this->getConfig()->setDefaultContextPath($contextPath);
890*04fd306cSNickeau        return $this;
891*04fd306cSNickeau    }
892*04fd306cSNickeau
893*04fd306cSNickeau
894*04fd306cSNickeau    /**
895*04fd306cSNickeau     * @return WikiPath - the context path is a markup file that gives context.
896*04fd306cSNickeau     * Ie this is the equivalent of the current directory.
897*04fd306cSNickeau     * When a link/path is empty or relative, the program will check for the context path
898*04fd306cSNickeau     * to calculate the absolute path
899*04fd306cSNickeau     */
900*04fd306cSNickeau    public
901*04fd306cSNickeau    function getContextPath(): WikiPath
902*04fd306cSNickeau    {
903*04fd306cSNickeau
904*04fd306cSNickeau        try {
905*04fd306cSNickeau
906*04fd306cSNickeau            /**
907*04fd306cSNickeau             * Do we a fetcher markup running ?
908*04fd306cSNickeau             * (It's first as we may change it
909*04fd306cSNickeau             * for a slot for instance)
910*04fd306cSNickeau             */
911*04fd306cSNickeau            return $this
912*04fd306cSNickeau                ->getExecutingMarkupHandler()
913*04fd306cSNickeau                ->getRequestedContextPath();
914*04fd306cSNickeau
915*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
916*04fd306cSNickeau            try {
917*04fd306cSNickeau
918*04fd306cSNickeau                /**
919*04fd306cSNickeau                 * Do we have a template page executing ?
920*04fd306cSNickeau                 */
921*04fd306cSNickeau                return $this->getExecutingPageTemplate()
922*04fd306cSNickeau                    ->getRequestedContextPath();
923*04fd306cSNickeau
924*04fd306cSNickeau            } catch (ExceptionNotFound $e) {
925*04fd306cSNickeau
926*04fd306cSNickeau                /**
927*04fd306cSNickeau                 * Hack, hack, hack
928*04fd306cSNickeau                 * In  preview mode, the context path is the last visited page
929*04fd306cSNickeau                 * for a slot
930*04fd306cSNickeau                 */
931*04fd306cSNickeau                global $ACT;
932*04fd306cSNickeau                if ($ACT === ExecutionContext::PREVIEW_ACTION) {
933*04fd306cSNickeau                    global $ID;
934*04fd306cSNickeau                    if (!empty($ID)) {
935*04fd306cSNickeau                        try {
936*04fd306cSNickeau                            $markupPath = MarkupPath::createMarkupFromId($ID);
937*04fd306cSNickeau                            if ($markupPath->isSlot()) {
938*04fd306cSNickeau                                return SlotSystem::getContextPath()->toWikiPath();
939*04fd306cSNickeau                            }
940*04fd306cSNickeau                        } catch (ExceptionCast|ExceptionNotFound $e) {
941*04fd306cSNickeau                            // ok
942*04fd306cSNickeau                        }
943*04fd306cSNickeau                    }
944*04fd306cSNickeau                }
945*04fd306cSNickeau
946*04fd306cSNickeau                /**
947*04fd306cSNickeau                 * Nope ? This is a dokuwiki run (admin page, ...)
948*04fd306cSNickeau                 */
949*04fd306cSNickeau                return $this->getConfig()->getDefaultContextPath();
950*04fd306cSNickeau
951*04fd306cSNickeau            }
952*04fd306cSNickeau
953*04fd306cSNickeau        }
954*04fd306cSNickeau
955*04fd306cSNickeau
956*04fd306cSNickeau    }
957*04fd306cSNickeau
958*04fd306cSNickeau
959*04fd306cSNickeau    /**
960*04fd306cSNickeau     * @return WikiPath
961*04fd306cSNickeau     * @deprecated uses {@link SiteConfig::getDefaultContextPath()}
962*04fd306cSNickeau     */
963*04fd306cSNickeau    public
964*04fd306cSNickeau    function getDefaultContextPath(): WikiPath
965*04fd306cSNickeau    {
966*04fd306cSNickeau        return $this->getConfig()->getDefaultContextPath();
967*04fd306cSNickeau    }
968*04fd306cSNickeau
969*04fd306cSNickeau    /**
970*04fd306cSNickeau     * The page global context object
971*04fd306cSNickeau     * @throws ExceptionNotFound
972*04fd306cSNickeau     */
973*04fd306cSNickeau    public
974*04fd306cSNickeau    function getExecutingPageTemplate(): TemplateForWebPage
975*04fd306cSNickeau    {
976*04fd306cSNickeau        if (isset($this->executingPageTemplate)) {
977*04fd306cSNickeau            return $this->executingPageTemplate;
978*04fd306cSNickeau        }
979*04fd306cSNickeau        throw new ExceptionNotFound("No page template execution running");
980*04fd306cSNickeau    }
981*04fd306cSNickeau
982*04fd306cSNickeau    /**
983*04fd306cSNickeau     * Set the page template that is executing.
984*04fd306cSNickeau     * It's the context object for all page related
985*04fd306cSNickeau     * (mostly header event)
986*04fd306cSNickeau     * @param TemplateForWebPage $pageTemplate
987*04fd306cSNickeau     * @return $this
988*04fd306cSNickeau     */
989*04fd306cSNickeau    public
990*04fd306cSNickeau    function setExecutingPageTemplate(TemplateForWebPage $pageTemplate): ExecutionContext
991*04fd306cSNickeau    {
992*04fd306cSNickeau        $this->executingPageTemplate = $pageTemplate;
993*04fd306cSNickeau        return $this;
994*04fd306cSNickeau    }
995*04fd306cSNickeau
996*04fd306cSNickeau    public
997*04fd306cSNickeau    function closeExecutingPageTemplate(): ExecutionContext
998*04fd306cSNickeau    {
999*04fd306cSNickeau        unset($this->executingPageTemplate);
1000*04fd306cSNickeau        return $this;
1001*04fd306cSNickeau    }
1002*04fd306cSNickeau
1003*04fd306cSNickeau    public
1004*04fd306cSNickeau    function getApp(): Site
1005*04fd306cSNickeau    {
1006*04fd306cSNickeau        if (isset($this->app)) {
1007*04fd306cSNickeau            return $this->app;
1008*04fd306cSNickeau        }
1009*04fd306cSNickeau        $this->app = new Site($this);
1010*04fd306cSNickeau        return $this->app;
1011*04fd306cSNickeau    }
1012*04fd306cSNickeau
1013*04fd306cSNickeau    /**
1014*04fd306cSNickeau     * @return SiteConfig short utility function to get access to the global app config
1015*04fd306cSNickeau     */
1016*04fd306cSNickeau    public
1017*04fd306cSNickeau    function getConfig(): SiteConfig
1018*04fd306cSNickeau    {
1019*04fd306cSNickeau        return $this->getApp()->getConfig();
1020*04fd306cSNickeau    }
1021*04fd306cSNickeau
1022*04fd306cSNickeau    /**
1023*04fd306cSNickeau     * @throws ExceptionNotFound - when there is no executing id (markup execution)
1024*04fd306cSNickeau     */
1025*04fd306cSNickeau    public
1026*04fd306cSNickeau    function getExecutingWikiPath(): WikiPath
1027*04fd306cSNickeau    {
1028*04fd306cSNickeau        try {
1029*04fd306cSNickeau            return $this->getExecutingMarkupHandler()
1030*04fd306cSNickeau                ->getRequestedExecutingPath()
1031*04fd306cSNickeau                ->toWikiPath();
1032*04fd306cSNickeau        } catch (ExceptionCast|ExceptionNotFound $e) {
1033*04fd306cSNickeau            // Execution without templating (ie without fetcher markup)
1034*04fd306cSNickeau            return WikiPath::createMarkupPathFromId($this->getExecutingWikiId());
1035*04fd306cSNickeau        }
1036*04fd306cSNickeau
1037*04fd306cSNickeau    }
1038*04fd306cSNickeau
1039*04fd306cSNickeau    /**
1040*04fd306cSNickeau     * @return array - data in context
1041*04fd306cSNickeau     * This is the central point to get data in context as there is no
1042*04fd306cSNickeau     * content object in dokuwiki
1043*04fd306cSNickeau     *
1044*04fd306cSNickeau     * It takes care of returning the context path
1045*04fd306cSNickeau     * (in case of slot via the {@link self::getContextPath()}
1046*04fd306cSNickeau     */
1047*04fd306cSNickeau    public
1048*04fd306cSNickeau    function getContextData(): array
1049*04fd306cSNickeau    {
1050*04fd306cSNickeau
1051*04fd306cSNickeau        try {
1052*04fd306cSNickeau
1053*04fd306cSNickeau            /**
1054*04fd306cSNickeau             * Context data may be dynamically given
1055*04fd306cSNickeau             * by the {@link \syntax_plugin_combo_iterator}
1056*04fd306cSNickeau             */
1057*04fd306cSNickeau            return $this
1058*04fd306cSNickeau                ->getExecutingMarkupHandler()
1059*04fd306cSNickeau                ->getContextData();
1060*04fd306cSNickeau
1061*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
1062*04fd306cSNickeau
1063*04fd306cSNickeau            /**
1064*04fd306cSNickeau             * Preview / slot
1065*04fd306cSNickeau             */
1066*04fd306cSNickeau            return MarkupPath::createPageFromPathObject($this->getContextPath())->getMetadataForRendering();
1067*04fd306cSNickeau
1068*04fd306cSNickeau        }
1069*04fd306cSNickeau
1070*04fd306cSNickeau    }
1071*04fd306cSNickeau
1072*04fd306cSNickeau    /**
1073*04fd306cSNickeau     * This method will delete the global identifier
1074*04fd306cSNickeau     * and call the 'close' method if the method exists.
1075*04fd306cSNickeau     * @param string $globalObjectIdentifier
1076*04fd306cSNickeau     * @return void
1077*04fd306cSNickeau     */
1078*04fd306cSNickeau    public
1079*04fd306cSNickeau    function closeAndRemoveRuntimeVariableIfExists(string $globalObjectIdentifier)
1080*04fd306cSNickeau    {
1081*04fd306cSNickeau
1082*04fd306cSNickeau        if (!isset($this->executionScopedVariables[$globalObjectIdentifier])) {
1083*04fd306cSNickeau            return;
1084*04fd306cSNickeau        }
1085*04fd306cSNickeau
1086*04fd306cSNickeau        /**
1087*04fd306cSNickeau         * Get the object references
1088*04fd306cSNickeau         */
1089*04fd306cSNickeau        $object = &$this->executionScopedVariables[$globalObjectIdentifier];
1090*04fd306cSNickeau
1091*04fd306cSNickeau
1092*04fd306cSNickeau        /**
1093*04fd306cSNickeau         * Call the close method
1094*04fd306cSNickeau         */
1095*04fd306cSNickeau        if (is_object($object)) {
1096*04fd306cSNickeau            if (method_exists($object, 'close')) {
1097*04fd306cSNickeau                $object->close();
1098*04fd306cSNickeau            }
1099*04fd306cSNickeau        }
1100*04fd306cSNickeau
1101*04fd306cSNickeau        /**
1102*04fd306cSNickeau         * Close it really by setting null
1103*04fd306cSNickeau         *
1104*04fd306cSNickeau         * (Forwhatever reason, sqlite closing in php
1105*04fd306cSNickeau         * is putting the variable to null)
1106*04fd306cSNickeau         */
1107*04fd306cSNickeau        $object = null;
1108*04fd306cSNickeau
1109*04fd306cSNickeau        /**
1110*04fd306cSNickeau         * Delete it from the array
1111*04fd306cSNickeau         */
1112*04fd306cSNickeau        unset($this->executionScopedVariables[$globalObjectIdentifier]);
1113*04fd306cSNickeau
1114*04fd306cSNickeau    }
1115*04fd306cSNickeau
1116*04fd306cSNickeau    /**
1117*04fd306cSNickeau     * Close all execution variables
1118*04fd306cSNickeau     */
1119*04fd306cSNickeau    public
1120*04fd306cSNickeau    function closeExecutionVariables(): ExecutionContext
1121*04fd306cSNickeau    {
1122*04fd306cSNickeau        $scopedVariables = array_keys($this->executionScopedVariables);
1123*04fd306cSNickeau        foreach ($scopedVariables as $executionScopedVariableKey) {
1124*04fd306cSNickeau            $this->closeAndRemoveRuntimeVariableIfExists($executionScopedVariableKey);
1125*04fd306cSNickeau        }
1126*04fd306cSNickeau        return $this;
1127*04fd306cSNickeau    }
1128*04fd306cSNickeau
1129*04fd306cSNickeau    public
1130*04fd306cSNickeau    function __toString()
1131*04fd306cSNickeau    {
1132*04fd306cSNickeau        return $this->creationTime;
1133*04fd306cSNickeau    }
1134*04fd306cSNickeau
1135*04fd306cSNickeau    public
1136*04fd306cSNickeau    function isExecutingPageTemplate(): bool
1137*04fd306cSNickeau    {
1138*04fd306cSNickeau        try {
1139*04fd306cSNickeau            $this->getExecutingPageTemplate();
1140*04fd306cSNickeau            return true;
1141*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
1142*04fd306cSNickeau            return false;
1143*04fd306cSNickeau        }
1144*04fd306cSNickeau    }
1145*04fd306cSNickeau
1146*04fd306cSNickeau    public
1147*04fd306cSNickeau    function hasExecutingMarkupHandler(): bool
1148*04fd306cSNickeau    {
1149*04fd306cSNickeau        try {
1150*04fd306cSNickeau            $this->getExecutingMarkupHandler();
1151*04fd306cSNickeau            return true;
1152*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
1153*04fd306cSNickeau            return false;
1154*04fd306cSNickeau        }
1155*04fd306cSNickeau    }
1156*04fd306cSNickeau
1157*04fd306cSNickeau
1158*04fd306cSNickeau}
1159