xref: /plugin/combo/ComboStrap/ExecutionContext.php (revision 49b8fb2440a8b019e2a082c98b66ddbf7fc707db)
104fd306cSNickeau<?php
204fd306cSNickeau
304fd306cSNickeaunamespace ComboStrap;
404fd306cSNickeau
504fd306cSNickeau
604fd306cSNickeauuse ComboStrap\Meta\Store\MetadataDbStore;
704fd306cSNickeauuse ComboStrap\Meta\Store\MetadataDokuWikiStore;
804fd306cSNickeauuse ComboStrap\Tag\WebCodeTag;
904fd306cSNickeauuse ComboStrap\Web\Url;
1004fd306cSNickeauuse dokuwiki\ActionRouter;
1104fd306cSNickeauuse dokuwiki\Extension\EventHandler;
1204fd306cSNickeauuse TestRequest;
1304fd306cSNickeau
1404fd306cSNickeau
1504fd306cSNickeau/**
1604fd306cSNickeau * An execution object permits to manage the variable state for
1704fd306cSNickeau * an execution (ie one HTTP request)
1804fd306cSNickeau *
1904fd306cSNickeau *
2004fd306cSNickeau * Note that normally every page has a page context
2104fd306cSNickeau * meaning that you can go from an admin page to show the page.
2204fd306cSNickeau *
2304fd306cSNickeau *
2404fd306cSNickeau * When an execution context has finished, it should be {@link ExecutionContext::close() closed}
2504fd306cSNickeau * or destroyed
2604fd306cSNickeau *
2704fd306cSNickeau * You can get the actual execution context with {@link ExecutionContext::getActualOrCreateFromEnv()}
2804fd306cSNickeau *
2904fd306cSNickeau *
3004fd306cSNickeau * Same concept than [routing context](https://vertx.io/docs/apidocs/index.html?io/vertx/ext/web/RoutingContext.html)
3104fd306cSNickeau * (Not yet fully implemented)
3204fd306cSNickeau * ```java
3304fd306cSNickeau * if (pet.isPresent())
3404fd306cSNickeau *    routingContext
3504fd306cSNickeau *     .response()
3604fd306cSNickeau *      .setStatusCode(200)
3704fd306cSNickeau *       .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
3804fd306cSNickeau *      .end(pet.get().encode()); // (4)
3904fd306cSNickeau * else
4004fd306cSNickeau *    routingContext.fail(404, new Exception("Pet not found"));
4104fd306cSNickeau * }
4204fd306cSNickeau * ```
4304fd306cSNickeau */
4404fd306cSNickeauclass ExecutionContext
4504fd306cSNickeau{
4604fd306cSNickeau
4704fd306cSNickeau    /**
4804fd306cSNickeau     * Dokuwiki do attribute
4904fd306cSNickeau     */
5004fd306cSNickeau    const DO_ATTRIBUTE = "do";
5104fd306cSNickeau
5204fd306cSNickeau
5304fd306cSNickeau    const CANONICAL = "execution-context";
5404fd306cSNickeau
5504fd306cSNickeau    /**
5604fd306cSNickeau     * All action (handler)
5704fd306cSNickeau     * That's what you will found in the `do` parameters
5804fd306cSNickeau     */
5904fd306cSNickeau    const SHOW_ACTION = "show";
6004fd306cSNickeau    const EDIT_ACTION = "edit";
6104fd306cSNickeau    /**
6204fd306cSNickeau     * Preview is also used to
6304fd306cSNickeau     * set the {@link FetcherMarkup::isFragment()}
6404fd306cSNickeau     * processing to fragment
6504fd306cSNickeau     */
6604fd306cSNickeau    const PREVIEW_ACTION = "preview";
6704fd306cSNickeau    const ADMIN_ACTION = "admin";
6804fd306cSNickeau    const DRAFT_ACTION = "draft";
6904fd306cSNickeau    const SEARCH_ACTION = "search";
7004fd306cSNickeau    const LOGIN_ACTION = "login";
7104fd306cSNickeau    const SAVE_ACTION = "save";
7204fd306cSNickeau    const DRAFT_DEL_ACTION = "draftdel";
7304fd306cSNickeau    const REDIRECT_ACTION = "redirect";
7404fd306cSNickeau
7504fd306cSNickeau    /**
7604fd306cSNickeau     * private actions does not render a page to be indexed
7704fd306cSNickeau     * by a search engine (ie no redirect)
7804fd306cSNickeau     * May be easier, if not `show`, not public
7904fd306cSNickeau     */
8004fd306cSNickeau    const PRIVATES_ACTION_NO_REDIRECT = [
8104fd306cSNickeau        self::EDIT_ACTION,
8204fd306cSNickeau        self::PREVIEW_ACTION,
8304fd306cSNickeau        self::ADMIN_ACTION,
8404fd306cSNickeau        self::DRAFT_ACTION,
8504fd306cSNickeau        self::DRAFT_DEL_ACTION,
8604fd306cSNickeau        self::SEARCH_ACTION,
8704fd306cSNickeau        self::LOGIN_ACTION,
8804fd306cSNickeau        self::SAVE_ACTION,
8904fd306cSNickeau        self::REDIRECT_ACTION,
9004fd306cSNickeau        self::REGISTER_ACTION,
9104fd306cSNickeau        self::RESEND_PWD_ACTION,
9204fd306cSNickeau        self::PROFILE_ACTION,
9304fd306cSNickeau    ];
9404fd306cSNickeau    const REGISTER_ACTION = "register";
9504fd306cSNickeau    const RESEND_PWD_ACTION = "resendpwd";
9604fd306cSNickeau    const PROFILE_ACTION = "profile";
9704fd306cSNickeau    const REVISIONS_ACTION = "revisions";
9804fd306cSNickeau    const DIFF_ACTION = "diff";
9904fd306cSNickeau    const INDEX_ACTION = "index";
10004fd306cSNickeau
101*49b8fb24Sgerardnico    /**
102*49b8fb24Sgerardnico     * The media manager
103*49b8fb24Sgerardnico     */
104*49b8fb24Sgerardnico    const MEDIA_ACTION = "media";
105*49b8fb24Sgerardnico    /**
106*49b8fb24Sgerardnico     * The recent changes
107*49b8fb24Sgerardnico     */
108*49b8fb24Sgerardnico    const RECENT_ACTION = "recent";
109*49b8fb24Sgerardnico
11004fd306cSNickeau
11104fd306cSNickeau    /**
11204fd306cSNickeau     * @var array of objects that are scoped to this request
11304fd306cSNickeau     */
11404fd306cSNickeau    private array $executionScopedVariables = [];
11504fd306cSNickeau
11604fd306cSNickeau    private CacheManager $cacheManager;
11704fd306cSNickeau
11870bbd7f1Sgerardnico    private IdManager $idManager;
11970bbd7f1Sgerardnico
12004fd306cSNickeau    private Site $app;
12104fd306cSNickeau
12204fd306cSNickeau    /**
12304fd306cSNickeau     * A root execution context if any
12404fd306cSNickeau     * Null because you can not unset a static variable
12504fd306cSNickeau     */
12604fd306cSNickeau    private static ?ExecutionContext $actualExecutionContext = null;
12704fd306cSNickeau
12804fd306cSNickeau    private ?string $capturedGlobalId;
12904fd306cSNickeau    /**
13004fd306cSNickeau     * It may be an array when preview/save/cancel
13104fd306cSNickeau     * @var array|string|null
13204fd306cSNickeau     */
13304fd306cSNickeau    private $capturedAct;
13404fd306cSNickeau
13504fd306cSNickeau
13604fd306cSNickeau    private Url $url;
13704fd306cSNickeau
13804fd306cSNickeau
13904fd306cSNickeau    public HttpResponse $response;
14004fd306cSNickeau
14104fd306cSNickeau    /**
14204fd306cSNickeau     * @var IFetcher - the fetcher that takes into account the HTTP request
14304fd306cSNickeau     */
14404fd306cSNickeau    private IFetcher $executingMainFetcher;
14504fd306cSNickeau
14604fd306cSNickeau    /**
14704fd306cSNickeau     * @var array - a stack of:
14804fd306cSNickeau     *   * markup handler executing (ie handler that is taking a markup (file, string) and making it a HTML, pdf, ...)
14904fd306cSNickeau     *   * and old global environement, $executingId, $contextExecutingId, $act
15004fd306cSNickeau     *
15104fd306cSNickeau     * This fetcher is called by the main fetcher or by the {@link self::setExecutingMarkupHandler()}
15204fd306cSNickeau     */
15304fd306cSNickeau    private array $executingMarkupHandlerStack = [];
15404fd306cSNickeau
15504fd306cSNickeau    /**
15604fd306cSNickeau     * @var TemplateForWebPage - the page template fetcher running (when a fetcher creates a page, it would uses this fetcher)
15704fd306cSNickeau     * This class is called by the main fetcher to create a page
15804fd306cSNickeau     */
15904fd306cSNickeau    private TemplateForWebPage $executingPageTemplate;
16004fd306cSNickeau    private string $creationTime;
16104fd306cSNickeau
16204fd306cSNickeau
16304fd306cSNickeau    public function __construct()
16404fd306cSNickeau    {
16504fd306cSNickeau
16604fd306cSNickeau        $this->creationTime = Iso8601Date::createFromNow()->toIsoStringMs();
16704fd306cSNickeau
16804fd306cSNickeau        $this->url = Url::createFromGetOrPostGlobalVariable();
16904fd306cSNickeau
17004fd306cSNickeau        $this->response = HttpResponse::createFromExecutionContext($this);
17104fd306cSNickeau
17204fd306cSNickeau        /**
17304fd306cSNickeau         * The requested action
17404fd306cSNickeau         */
17504fd306cSNickeau        global $ACT;
17604fd306cSNickeau        $this->capturedAct = $ACT;
17704fd306cSNickeau        try {
17804fd306cSNickeau            $urlAct = $this->url->getQueryPropertyValue(self::DO_ATTRIBUTE);
17904fd306cSNickeau        } catch (ExceptionNotFound $e) {
18004fd306cSNickeau            /**
18104fd306cSNickeau             * The value is unknown
18204fd306cSNickeau             * (in doku.php, the default is `show`,
18304fd306cSNickeau             * we take the dokuwiki value because the execution context may be
18404fd306cSNickeau             * created after the dokuwiki init)
18504fd306cSNickeau             */
18604fd306cSNickeau            $urlAct = $ACT;
18704fd306cSNickeau        }
18804fd306cSNickeau        $ACT = $urlAct;
18904fd306cSNickeau
19004fd306cSNickeau        /**
19104fd306cSNickeau         * The requested id
19204fd306cSNickeau         */
19304fd306cSNickeau        global $ID;
19404fd306cSNickeau        $this->capturedGlobalId = $ID;
19504fd306cSNickeau        try {
19604fd306cSNickeau
19704fd306cSNickeau            $urlId = $this->url->getQueryPropertyValue(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE);
19804fd306cSNickeau            if (is_array($urlId)) {
19904fd306cSNickeau                /**
20004fd306cSNickeau                 * hack because the dokuwiki request global object as `ID` and `id` as array
20104fd306cSNickeau                 * but our own {@link Url object} don't allow that and makes an array instead
20204fd306cSNickeau                 * We don't use this data anyway, anymore ...
20304fd306cSNickeau                 */
20404fd306cSNickeau                $urlId = $urlId[0];
20504fd306cSNickeau            }
20604fd306cSNickeau            $ID = $urlId;
20704fd306cSNickeau
20804fd306cSNickeau        } catch (ExceptionNotFound $e) {
20904fd306cSNickeau            // none
21004fd306cSNickeau            $ID = null;
21104fd306cSNickeau        }
21204fd306cSNickeau
21304fd306cSNickeau
21404fd306cSNickeau    }
21504fd306cSNickeau
21604fd306cSNickeau
21704fd306cSNickeau    /**
21804fd306cSNickeau     * @throws ExceptionNotFound
21904fd306cSNickeau     */
22004fd306cSNickeau    public static function getExecutionContext(): ExecutionContext
22104fd306cSNickeau    {
22204fd306cSNickeau        if (!isset(self::$actualExecutionContext)) {
22304fd306cSNickeau            throw new ExceptionNotFound("No root context");
22404fd306cSNickeau        }
22504fd306cSNickeau        return self::$actualExecutionContext;
22604fd306cSNickeau    }
22704fd306cSNickeau
22804fd306cSNickeau    /**
22904fd306cSNickeau     * Utility class to set the requested id (used only in test,
23004fd306cSNickeau     * normally the environment is set from global PHP environment variable
23104fd306cSNickeau     * that get the HTTP request
23204fd306cSNickeau     * @param string $requestedId
23304fd306cSNickeau     * @return ExecutionContext
23404fd306cSNickeau     * @deprecated use {@link self::setDefaultContextPath()} if you want to set a context path
23504fd306cSNickeau     * without using a {@link TemplateForWebPage} or {@link FetcherMarkup}
23604fd306cSNickeau     */
23704fd306cSNickeau    public static function getOrCreateFromRequestedWikiId(string $requestedId): ExecutionContext
23804fd306cSNickeau    {
23904fd306cSNickeau
24004fd306cSNickeau        return self::getActualOrCreateFromEnv()
24104fd306cSNickeau            ->setDefaultContextPath(WikiPath::createMarkupPathFromId($requestedId));
24204fd306cSNickeau
24304fd306cSNickeau    }
24404fd306cSNickeau
24504fd306cSNickeau
24604fd306cSNickeau    /**
24704fd306cSNickeau     * @return ExecutionContext
24804fd306cSNickeau     * @deprecated uses {@link self::createBlank()} instead
24904fd306cSNickeau     */
25004fd306cSNickeau    public static function createFromEnvironmentVariable(): ExecutionContext
25104fd306cSNickeau    {
25204fd306cSNickeau        return self::createBlank();
25304fd306cSNickeau    }
25404fd306cSNickeau
25504fd306cSNickeau
25604fd306cSNickeau    public static function createBlank(): ExecutionContext
25704fd306cSNickeau    {
25804fd306cSNickeau
25904fd306cSNickeau        if (self::$actualExecutionContext !== null) {
26004fd306cSNickeau            throw new ExceptionRuntimeInternal("The previous root context should be closed first");
26104fd306cSNickeau        }
26204fd306cSNickeau        $rootExecutionContext = (new ExecutionContext());
26304fd306cSNickeau        self::$actualExecutionContext = $rootExecutionContext;
26404fd306cSNickeau        return $rootExecutionContext;
26504fd306cSNickeau
26604fd306cSNickeau    }
26704fd306cSNickeau
26804fd306cSNickeau    /**
26904fd306cSNickeau     * @return ExecutionContext - return the actual context or create a new one from the environment
27004fd306cSNickeau     */
27104fd306cSNickeau    public static function getActualOrCreateFromEnv(): ExecutionContext
27204fd306cSNickeau    {
27304fd306cSNickeau        try {
27404fd306cSNickeau            return self::getExecutionContext();
27504fd306cSNickeau        } catch (ExceptionNotFound $e) {
27604fd306cSNickeau            return self::createBlank();
27704fd306cSNickeau        }
27804fd306cSNickeau    }
27904fd306cSNickeau
28004fd306cSNickeau    /**
28104fd306cSNickeau     * We create the id manager in the execution
28204fd306cSNickeau     * context
28304fd306cSNickeau     * (because in case a user choose to not use templating, the {@link FetcherMarkup}
28404fd306cSNickeau     * is not available)
28504fd306cSNickeau     * And all dynamic component such as {@link \syntax_plugin_combo_dropdown} would not
28604fd306cSNickeau     * work anymore.
28704fd306cSNickeau     *
28804fd306cSNickeau     * @return IdManager
28904fd306cSNickeau     */
29004fd306cSNickeau    public function getIdManager(): IdManager
29104fd306cSNickeau    {
29204fd306cSNickeau        if (!isset($this->idManager)) {
29304fd306cSNickeau            $this->idManager = new IdManager($this);
29404fd306cSNickeau        }
29504fd306cSNickeau        return $this->idManager;
29604fd306cSNickeau    }
29704fd306cSNickeau
29804fd306cSNickeau    /**
29904fd306cSNickeau     * Return the actual context path
30004fd306cSNickeau     */
30104fd306cSNickeau    public function getContextNamespacePath(): WikiPath
30204fd306cSNickeau    {
30304fd306cSNickeau        $requestedPath = $this->getContextPath();
30404fd306cSNickeau        try {
30504fd306cSNickeau            return $requestedPath->getParent();
30604fd306cSNickeau        } catch (ExceptionNotFound $e) {
30704fd306cSNickeau            // root
30804fd306cSNickeau            return $requestedPath;
30904fd306cSNickeau        }
31004fd306cSNickeau
31104fd306cSNickeau    }
31204fd306cSNickeau
31304fd306cSNickeau
31404fd306cSNickeau    /**
31504fd306cSNickeau     * @throws ExceptionNotFound
31604fd306cSNickeau     */
31704fd306cSNickeau    public function getExecutingWikiId(): string
31804fd306cSNickeau    {
31904fd306cSNickeau        global $ID;
32004fd306cSNickeau        if (empty($ID)) {
32104fd306cSNickeau            throw new ExceptionNotFound("No executing id was found");
32204fd306cSNickeau        }
32304fd306cSNickeau        return $ID;
32404fd306cSNickeau    }
32504fd306cSNickeau
32604fd306cSNickeau
32704fd306cSNickeau    /**
32804fd306cSNickeau     * @return void close the execution context
32904fd306cSNickeau     */
33004fd306cSNickeau    public function close()
33104fd306cSNickeau    {
33204fd306cSNickeau
33304fd306cSNickeau        /**
33404fd306cSNickeau         * Check that this execution context was not closed
33504fd306cSNickeau         */
33604fd306cSNickeau        if (self::$actualExecutionContext->creationTime !== $this->creationTime) {
33704fd306cSNickeau            throw new ExceptionRuntimeInternal("This execution context was already closed");
33804fd306cSNickeau        }
33904fd306cSNickeau
34004fd306cSNickeau        /**
34104fd306cSNickeau         * Restore the global $conf of dokuwiki
34204fd306cSNickeau         */
34304fd306cSNickeau        $this->getApp()->getConfig()->restoreConfigState();
34404fd306cSNickeau
34504fd306cSNickeau        /** global dokuwiki messages variable */
34604fd306cSNickeau        global $MSG;
34704fd306cSNickeau        unset($MSG);
34804fd306cSNickeau
34904fd306cSNickeau        /**
35004fd306cSNickeau         * Environment restoration
35104fd306cSNickeau         * Execution context, change for now only this
35204fd306cSNickeau         * global variables
35304fd306cSNickeau         */
35404fd306cSNickeau        global $ACT;
35504fd306cSNickeau        $ACT = $this->getCapturedAct();
35604fd306cSNickeau        global $ID;
35704fd306cSNickeau        $ID = $this->getCapturedRunningId();
35804fd306cSNickeau        global $TOC;
35904fd306cSNickeau        unset($TOC);
36004fd306cSNickeau
36104fd306cSNickeau        // global scope store
36204fd306cSNickeau        MetadataDbStore::resetAll();
36304fd306cSNickeau        MetadataDokuWikiStore::unsetGlobalVariables();
36404fd306cSNickeau
36504fd306cSNickeau        // Router: dokuwiki global
36604fd306cSNickeau        // reset event handler
36704fd306cSNickeau        global $EVENT_HANDLER;
36804fd306cSNickeau        $EVENT_HANDLER = new EventHandler();
36904fd306cSNickeau        /**
37004fd306cSNickeau         * We can't give the getInstance, a true value
37104fd306cSNickeau         * because it will otherwise start the routing process
37204fd306cSNickeau         * {@link ActionRouter::getInstance()}
37304fd306cSNickeau         */
37404fd306cSNickeau
37504fd306cSNickeau
37604fd306cSNickeau        /**
37704fd306cSNickeau         * Close execution variables
37804fd306cSNickeau         * (and therefore also {@link Sqlite}
37904fd306cSNickeau         */
38004fd306cSNickeau        $this->closeExecutionVariables();
38104fd306cSNickeau
38204fd306cSNickeau        /**
38304fd306cSNickeau         * Is this really needed ?
38404fd306cSNickeau         * as we unset the execution context below
38504fd306cSNickeau         */
38604fd306cSNickeau        unset($this->executingMainFetcher);
38704fd306cSNickeau        unset($this->executingMarkupHandlerStack);
38804fd306cSNickeau        unset($this->cacheManager);
38904fd306cSNickeau        unset($this->idManager);
39004fd306cSNickeau
39104fd306cSNickeau        /**
39204fd306cSNickeau         * Deleting
39304fd306cSNickeau         */
39404fd306cSNickeau        self::$actualExecutionContext = null;
39504fd306cSNickeau
39604fd306cSNickeau
39704fd306cSNickeau    }
39804fd306cSNickeau
39904fd306cSNickeau
40004fd306cSNickeau    public function getCapturedRunningId(): ?string
40104fd306cSNickeau    {
40204fd306cSNickeau        return $this->capturedGlobalId;
40304fd306cSNickeau    }
40404fd306cSNickeau
40504fd306cSNickeau    public function getCapturedAct()
40604fd306cSNickeau    {
40704fd306cSNickeau        return $this->capturedAct;
40804fd306cSNickeau    }
40904fd306cSNickeau
41004fd306cSNickeau    public function getCacheManager(): CacheManager
41104fd306cSNickeau    {
41204fd306cSNickeau        $root = self::$actualExecutionContext;
41304fd306cSNickeau        if (!isset($root->cacheManager)) {
41404fd306cSNickeau            $root->cacheManager = new CacheManager($this);
41504fd306cSNickeau        }
41604fd306cSNickeau        return $root->cacheManager;
41704fd306cSNickeau
41804fd306cSNickeau    }
41904fd306cSNickeau
42004fd306cSNickeau    /**
42104fd306cSNickeau     * Return the root path if nothing is found
42204fd306cSNickeau     */
42304fd306cSNickeau    public function getRequestedPath(): WikiPath
42404fd306cSNickeau    {
42504fd306cSNickeau        /**
42604fd306cSNickeau         * Do we have a template page executing ?
42704fd306cSNickeau         */
42804fd306cSNickeau        try {
42904fd306cSNickeau            return $this->getExecutingPageTemplate()
43004fd306cSNickeau                ->getRequestedContextPath();
43104fd306cSNickeau        } catch (ExceptionNotFound $e) {
43204fd306cSNickeau            try {
43304fd306cSNickeau                /**
43404fd306cSNickeau                 * Case when the main handler
43504fd306cSNickeau                 * run the main content before
43604fd306cSNickeau                 * to inject it in the template page
43704fd306cSNickeau                 * {@link TemplateForWebPage::render()}
43804fd306cSNickeau                 */
43904fd306cSNickeau                return $this->getExecutingMarkupHandler()
44004fd306cSNickeau                    ->getRequestedContextPath();
44104fd306cSNickeau            } catch (ExceptionNotFound $e) {
44204fd306cSNickeau
44304fd306cSNickeau
44404fd306cSNickeau                /**
44504fd306cSNickeau                 * not a template engine running
44604fd306cSNickeau                 * The id notion is a little bit everywhere
44704fd306cSNickeau                 * That's why we just don't check the action ($ACT)
44804fd306cSNickeau                 *
44904fd306cSNickeau                 * Example:
45004fd306cSNickeau                 * * `id` may be asked by acl to determine the right
45104fd306cSNickeau                 * * ...
45204fd306cSNickeau                 */
45304fd306cSNickeau                global $INPUT;
45404fd306cSNickeau                $inputId = $INPUT->str("id");
45504fd306cSNickeau                if (!empty($inputId)) {
45604fd306cSNickeau                    return WikiPath::createMarkupPathFromId($inputId);
45704fd306cSNickeau                }
45804fd306cSNickeau
45904fd306cSNickeau                global $ID;
46004fd306cSNickeau                if (!empty($ID)) {
46104fd306cSNickeau                    return WikiPath::createMarkupPathFromId($ID);
46204fd306cSNickeau                }
46304fd306cSNickeau
46404fd306cSNickeau                /**
46504fd306cSNickeau                 * This should be less used
46604fd306cSNickeau                 * but shows where the requested id is spilled in dokuwiki
46704fd306cSNickeau                 *
46804fd306cSNickeau                 * If the component is in a sidebar, we don't want the ID of the sidebar
46904fd306cSNickeau                 * but the ID of the page.
47004fd306cSNickeau                 */
47104fd306cSNickeau                global $INFO;
47204fd306cSNickeau                if ($INFO !== null) {
47370bbd7f1Sgerardnico                    $callingId = $INFO['id'] ?? null;
47404fd306cSNickeau                    if (!empty($callingId)) {
47504fd306cSNickeau                        return WikiPath::createMarkupPathFromId($callingId);
47604fd306cSNickeau                    }
47704fd306cSNickeau                }
47804fd306cSNickeau
47904fd306cSNickeau                /**
48004fd306cSNickeau                 * This is the case with event triggered
48104fd306cSNickeau                 * before DokuWiki such as
48204fd306cSNickeau                 * https://www.dokuwiki.org/devel:event:init_lang_load
48304fd306cSNickeau                 * REQUEST is a mixed of post and get parameters
48404fd306cSNickeau                 */
48504fd306cSNickeau                global $_REQUEST;
48604fd306cSNickeau                if (isset($_REQUEST[DokuwikiId::DOKUWIKI_ID_ATTRIBUTE])) {
48704fd306cSNickeau                    $requestId = $_REQUEST[DokuwikiId::DOKUWIKI_ID_ATTRIBUTE];
48804fd306cSNickeau                    if (!empty($requestId)) {
48904fd306cSNickeau                        return WikiPath::createMarkupPathFromId($requestId);
49004fd306cSNickeau                    }
49104fd306cSNickeau                }
49204fd306cSNickeau
49304fd306cSNickeau                // not that show action is the default even if it's not set
49404fd306cSNickeau                // we can't then control if the id should exists or not
49504fd306cSNickeau                // markup based on string (test) or snippet of code
49604fd306cSNickeau                // return the default context path (ie the root page)
49704fd306cSNickeau                return $this->getConfig()->getDefaultContextPath();
49804fd306cSNickeau            }
49904fd306cSNickeau
50004fd306cSNickeau        }
50104fd306cSNickeau
50204fd306cSNickeau    }
50304fd306cSNickeau
50404fd306cSNickeau    /**
50504fd306cSNickeau     * @throws ExceptionNotFound
50604fd306cSNickeau     */
50704fd306cSNickeau    public function &getRuntimeObject(string $objectIdentifier)
50804fd306cSNickeau    {
50904fd306cSNickeau        if (isset($this->executionScopedVariables[$objectIdentifier])) {
51004fd306cSNickeau            return $this->executionScopedVariables[$objectIdentifier];
51104fd306cSNickeau        }
51204fd306cSNickeau        throw new ExceptionNotFound("No object $objectIdentifier found");
51304fd306cSNickeau    }
51404fd306cSNickeau
51504fd306cSNickeau    public function setRuntimeObject($objectIdentifier, &$object): ExecutionContext
51604fd306cSNickeau    {
51704fd306cSNickeau        $this->executionScopedVariables[$objectIdentifier] = &$object;
51804fd306cSNickeau        return $this;
51904fd306cSNickeau    }
52004fd306cSNickeau
52104fd306cSNickeau    public function getUrl(): Url
52204fd306cSNickeau    {
52304fd306cSNickeau        return $this->url;
52404fd306cSNickeau    }
52504fd306cSNickeau
52604fd306cSNickeau
52704fd306cSNickeau    /**
52804fd306cSNickeau     * @param string $key
52904fd306cSNickeau     * @param $value
53004fd306cSNickeau     * @param string|null $pluginNamespace - if null, stored in the global conf namespace
53104fd306cSNickeau     * @return $this
53204fd306cSNickeau     * @deprecated use {@link SiteConfig::setConf()} instead
53304fd306cSNickeau     */
53404fd306cSNickeau    public function setConf(string $key, $value, ?string $pluginNamespace = PluginUtility::PLUGIN_BASE_NAME): ExecutionContext
53504fd306cSNickeau    {
53604fd306cSNickeau        $this->getApp()->getConfig()->setConf($key, $value, $pluginNamespace);
53704fd306cSNickeau        return $this;
53804fd306cSNickeau    }
53904fd306cSNickeau
54004fd306cSNickeau    /**
54104fd306cSNickeau     * @param string $key
54204fd306cSNickeau     * @param string|null $default
54304fd306cSNickeau     * @return mixed|null
54404fd306cSNickeau     * @deprecated use
54504fd306cSNickeau     */
54604fd306cSNickeau    public function getConfValue(string $key, string $default = null)
54704fd306cSNickeau    {
54804fd306cSNickeau        return $this->getApp()->getConfig()->getValue($key, $default);
54904fd306cSNickeau    }
55004fd306cSNickeau
55104fd306cSNickeau    public function setRuntimeBoolean(string $key, bool $b): ExecutionContext
55204fd306cSNickeau    {
55304fd306cSNickeau        $this->executionScopedVariables[$key] = $b;
55404fd306cSNickeau        return $this;
55504fd306cSNickeau    }
55604fd306cSNickeau
55704fd306cSNickeau    /**
55804fd306cSNickeau     * @throws ExceptionNotFound
55904fd306cSNickeau     */
56004fd306cSNickeau    public function getRuntimeBoolean(string $name): bool
56104fd306cSNickeau    {
56270bbd7f1Sgerardnico        $var = $this->executionScopedVariables[$name] ?? null;
56304fd306cSNickeau        if (!isset($var)) {
56404fd306cSNickeau            throw new ExceptionNotFound("No $name runtime env was found");
56504fd306cSNickeau        }
56604fd306cSNickeau        return DataType::toBoolean($var);
56704fd306cSNickeau    }
56804fd306cSNickeau
56904fd306cSNickeau    /**
57004fd306cSNickeau     * @return $this
57104fd306cSNickeau     * @deprecated uses {@link SiteConfig::setCacheXhtmlOn()}
57204fd306cSNickeau     */
57304fd306cSNickeau    public function setCacheXhtmlOn(): ExecutionContext
57404fd306cSNickeau    {
57504fd306cSNickeau        $this->getApp()->getConfig()->setCacheXhtmlOn();
57604fd306cSNickeau        return $this;
57704fd306cSNickeau    }
57804fd306cSNickeau
57904fd306cSNickeau    /**
58004fd306cSNickeau     *
58104fd306cSNickeau     * @return $this
58204fd306cSNickeau     * @deprecated use the {@link SiteConfig::setConsoleOn} instead
58304fd306cSNickeau     */
58404fd306cSNickeau    public function setConsoleOn(): ExecutionContext
58504fd306cSNickeau    {
58604fd306cSNickeau        $this->getApp()->getConfig()->setCacheXhtmlOn();
58704fd306cSNickeau        return $this;
58804fd306cSNickeau    }
58904fd306cSNickeau
59004fd306cSNickeau    public function setConsoleOff(): ExecutionContext
59104fd306cSNickeau    {
59204fd306cSNickeau        $this->getConfig()->setConsoleOff();
59304fd306cSNickeau        return $this;
59404fd306cSNickeau    }
59504fd306cSNickeau
59604fd306cSNickeau    /**
59704fd306cSNickeau     * @return $this
59804fd306cSNickeau     * @deprecated use {@link SiteConfig::setDisableThemeSystem()}
59904fd306cSNickeau     */
60004fd306cSNickeau    public function setDisableTemplating(): ExecutionContext
60104fd306cSNickeau    {
60204fd306cSNickeau        $this->getApp()->getConfig()->setDisableThemeSystem();
60304fd306cSNickeau        return $this;
60404fd306cSNickeau    }
60504fd306cSNickeau
60604fd306cSNickeau
60704fd306cSNickeau    /**
60804fd306cSNickeau     * @return bool
60904fd306cSNickeau     * @deprecated use the {@link SiteConfig::isConsoleOn()} instead
61004fd306cSNickeau     */
61104fd306cSNickeau    public function isConsoleOn(): bool
61204fd306cSNickeau    {
61304fd306cSNickeau        return $this->getApp()->getConfig()->isConsoleOn();
61404fd306cSNickeau    }
61504fd306cSNickeau
61604fd306cSNickeau
61704fd306cSNickeau    /**
61804fd306cSNickeau     * Dokuwiki handler name
61904fd306cSNickeau     * @return array|mixed|string
62004fd306cSNickeau     */
62104fd306cSNickeau    public function getExecutingAction()
62204fd306cSNickeau    {
62304fd306cSNickeau        global $ACT;
62404fd306cSNickeau        return $ACT;
62504fd306cSNickeau    }
62604fd306cSNickeau
62704fd306cSNickeau    public function setLogExceptionToError(): ExecutionContext
62804fd306cSNickeau    {
62904fd306cSNickeau        $this->getConfig()->setLogExceptionToError();
63004fd306cSNickeau        return $this;
63104fd306cSNickeau    }
63204fd306cSNickeau
63304fd306cSNickeau    /**
63404fd306cSNickeau     * @return SnippetSystem
63504fd306cSNickeau     * It's not attached to the {@link FetcherMarkup}
63604fd306cSNickeau     * because the user may choose to not use it (ie {@link SiteConfig::isThemeSystemEnabled()}
63704fd306cSNickeau     */
63804fd306cSNickeau    public function getSnippetSystem(): SnippetSystem
63904fd306cSNickeau    {
64004fd306cSNickeau        return SnippetSystem::getFromContext();
64104fd306cSNickeau    }
64204fd306cSNickeau
64304fd306cSNickeau    /**
64404fd306cSNickeau     * @return bool - does the action create a publication (render a page)
64504fd306cSNickeau     */
64604fd306cSNickeau    public function isPublicationAction(): bool
64704fd306cSNickeau    {
64804fd306cSNickeau
64904fd306cSNickeau        $act = $this->getExecutingAction();
65004fd306cSNickeau        if (in_array($act, self::PRIVATES_ACTION_NO_REDIRECT)) {
65104fd306cSNickeau            return false;
65204fd306cSNickeau        }
65304fd306cSNickeau
65404fd306cSNickeau        return true;
65504fd306cSNickeau
65604fd306cSNickeau    }
65704fd306cSNickeau
65804fd306cSNickeau    public function setEnableSectionEditing(): ExecutionContext
65904fd306cSNickeau    {
66004fd306cSNickeau        $this->setConf('maxseclevel', 999, null);
66104fd306cSNickeau        return $this;
66204fd306cSNickeau    }
66304fd306cSNickeau
66404fd306cSNickeau    /**
66504fd306cSNickeau     * @param string $value
66604fd306cSNickeau     * @return $this
66704fd306cSNickeau     * @deprecated use the {@link SiteConfig::setCanonicalUrlType()} instead
66804fd306cSNickeau     */
66904fd306cSNickeau    public function setCanonicalUrlType(string $value): ExecutionContext
67004fd306cSNickeau    {
67104fd306cSNickeau        $this->getConfig()->setCanonicalUrlType($value);
67204fd306cSNickeau        return $this;
67304fd306cSNickeau    }
67404fd306cSNickeau
67504fd306cSNickeau    public function setUseHeadingAsTitle(): ExecutionContext
67604fd306cSNickeau    {
67704fd306cSNickeau        // https://www.dokuwiki.org/config:useheading
67804fd306cSNickeau        $this->setConf('useheading', 1, null);
67904fd306cSNickeau        return $this;
68004fd306cSNickeau    }
68104fd306cSNickeau
68204fd306cSNickeau    public function response(): HttpResponse
68304fd306cSNickeau    {
68404fd306cSNickeau        return $this->response;
68504fd306cSNickeau    }
68604fd306cSNickeau
68704fd306cSNickeau    /**
68804fd306cSNickeau     * @param string|null $executingId
68904fd306cSNickeau     * @return void
69004fd306cSNickeau     */
69104fd306cSNickeau    private function setExecutingId(?string $executingId): void
69204fd306cSNickeau    {
69304fd306cSNickeau        global $ID;
69404fd306cSNickeau        if ($executingId == null) {
69504fd306cSNickeau            // ID should not be null
69604fd306cSNickeau            // to be able to check the ACL
69704fd306cSNickeau            return;
69804fd306cSNickeau        }
69904fd306cSNickeau        $executingId = WikiPath::removeRootSepIfPresent($executingId);
70004fd306cSNickeau        $ID = $executingId;
70104fd306cSNickeau    }
70204fd306cSNickeau
70304fd306cSNickeau    public function setConfGlobal(string $key, string $value): ExecutionContext
70404fd306cSNickeau    {
70504fd306cSNickeau        $this->setConf($key, $value, null);
70604fd306cSNickeau        return $this;
70704fd306cSNickeau    }
70804fd306cSNickeau
70904fd306cSNickeau    /**
71004fd306cSNickeau     * @return bool - if this execution is a test running
71104fd306cSNickeau     */
71204fd306cSNickeau    public function isTestRun(): bool
71304fd306cSNickeau    {
71404fd306cSNickeau        /**
71504fd306cSNickeau         * Test Requested is loaded only in a test run
71604fd306cSNickeau         * Does not exist in a normal installation
71704fd306cSNickeau         * and is not found, triggering an exception
71804fd306cSNickeau         */
71904fd306cSNickeau        if (class_exists('TestRequest')) {
72004fd306cSNickeau            $testRequest = TestRequest::getRunning();
72104fd306cSNickeau            return $testRequest !== null;
72204fd306cSNickeau        }
72304fd306cSNickeau        return false;
72404fd306cSNickeau
72504fd306cSNickeau    }
72604fd306cSNickeau
72704fd306cSNickeau    private function setExecutingAction(?string $runningAct): ExecutionContext
72804fd306cSNickeau    {
72904fd306cSNickeau        global $ACT;
73004fd306cSNickeau        $ACT = $runningAct;
73104fd306cSNickeau        return $this;
73204fd306cSNickeau    }
73304fd306cSNickeau
73404fd306cSNickeau
73504fd306cSNickeau    /**
73604fd306cSNickeau     * Set the main fetcher, the entry point of the request (ie the url of the browser)
73704fd306cSNickeau     * that will return a string
73804fd306cSNickeau     * @throws ExceptionBadArgument
73904fd306cSNickeau     * @throws ExceptionInternal
74004fd306cSNickeau     * @throws ExceptionNotFound
74104fd306cSNickeau     */
74204fd306cSNickeau    public function createStringMainFetcherFromRequestedUrl(Url $fetchUrl): IFetcherString
74304fd306cSNickeau    {
74404fd306cSNickeau        $this->executingMainFetcher = FetcherSystem::createFetcherStringFromUrl($fetchUrl);
74504fd306cSNickeau        return $this->executingMainFetcher;
74604fd306cSNickeau    }
74704fd306cSNickeau
74804fd306cSNickeau
74904fd306cSNickeau    /**
75004fd306cSNickeau     * Set the main fetcher (with the url of the browser)
75104fd306cSNickeau     * that will return a path (image, ...)
75204fd306cSNickeau     * @throws ExceptionBadArgument
75304fd306cSNickeau     * @throws ExceptionInternal
75404fd306cSNickeau     * @throws ExceptionNotFound
75504fd306cSNickeau     */
75604fd306cSNickeau    public function createPathMainFetcherFromUrl(Url $fetchUrl): IFetcherPath
75704fd306cSNickeau    {
75804fd306cSNickeau        $this->executingMainFetcher = FetcherSystem::createPathFetcherFromUrl($fetchUrl);
75904fd306cSNickeau        return $this->executingMainFetcher;
76004fd306cSNickeau    }
76104fd306cSNickeau
76204fd306cSNickeau    public function closeMainExecutingFetcher(): ExecutionContext
76304fd306cSNickeau    {
76404fd306cSNickeau        unset($this->executingMainFetcher);
76504fd306cSNickeau        /**
76604fd306cSNickeau         * Snippet are not yet fully coupled to the {@link FetcherMarkup}
76704fd306cSNickeau         */
76804fd306cSNickeau        $this->closeAndRemoveRuntimeVariableIfExists(Snippet::CANONICAL);
76904fd306cSNickeau        return $this;
77004fd306cSNickeau    }
77104fd306cSNickeau
77204fd306cSNickeau    /**
77304fd306cSNickeau     * This function sets the markup running context object globally,
77404fd306cSNickeau     * so that code may access it via this global variable
77504fd306cSNickeau     * (Fighting dokuwiki global scope)
77604fd306cSNickeau     * @param FetcherMarkup $markupHandler
77704fd306cSNickeau     * @return $this
77804fd306cSNickeau     */
77904fd306cSNickeau    public function setExecutingMarkupHandler(FetcherMarkup $markupHandler): ExecutionContext
78004fd306cSNickeau    {
78104fd306cSNickeau
78204fd306cSNickeau
78304fd306cSNickeau        /**
78404fd306cSNickeau         * Act
78504fd306cSNickeau         */
78604fd306cSNickeau        $oldAct = $this->getExecutingAction();
78704fd306cSNickeau        if (!$markupHandler->isPathExecution() && $oldAct !== ExecutionContext::PREVIEW_ACTION) {
78804fd306cSNickeau            /**
78904fd306cSNickeau             * Not sure that is is still needed
79004fd306cSNickeau             * as we have now the notion of document/fragment
79104fd306cSNickeau             * {@link FetcherMarkup::isDocument()}
79204fd306cSNickeau             */
79304fd306cSNickeau            $runningAct = FetcherMarkup::MARKUP_DYNAMIC_EXECUTION_NAME;
79404fd306cSNickeau            $this->setExecutingAction($runningAct);
79504fd306cSNickeau        }
79604fd306cSNickeau
79704fd306cSNickeau        /**
79804fd306cSNickeau         * Id
79904fd306cSNickeau         */
80004fd306cSNickeau        try {
80104fd306cSNickeau            $oldExecutingId = $this->getExecutingWikiId();
80204fd306cSNickeau        } catch (ExceptionNotFound $e) {
80304fd306cSNickeau            $oldExecutingId = null;
80404fd306cSNickeau        }
80504fd306cSNickeau        try {
80604fd306cSNickeau
80704fd306cSNickeau            $executingPath = $markupHandler->getRequestedExecutingPath();
80804fd306cSNickeau            $executingId = $executingPath->toAbsoluteId();
80904fd306cSNickeau            $this->setExecutingId($executingId);
81004fd306cSNickeau        } catch (ExceptionNotFound $e) {
81104fd306cSNickeau            // no executing path dynamic markup execution
81204fd306cSNickeau        }
81304fd306cSNickeau
81404fd306cSNickeau        /**
81504fd306cSNickeau         * $INFO  (Fragment run, ...)
81604fd306cSNickeau         * We don't use {@link pageinfo()} for now
81704fd306cSNickeau         * We just advertise if this is a fragment run
81804fd306cSNickeau         * via the `id`
81904fd306cSNickeau         */
82004fd306cSNickeau        global $INFO;
82194bd6462Sgerardnico        $oldContextId = $INFO['id'] ?? null;
82204fd306cSNickeau        if ($markupHandler->isFragment()) {
82304fd306cSNickeau            $contextPath = $markupHandler->getRequestedContextPath();
82470bbd7f1Sgerardnico            $wikiId = $contextPath->getWikiId();
82570bbd7f1Sgerardnico            $INFO['id'] = $wikiId;
82670bbd7f1Sgerardnico            $INFO['namespace'] = getNS($wikiId); // php8 Undefined array key
82704fd306cSNickeau        }
82804fd306cSNickeau
82904fd306cSNickeau        /**
83004fd306cSNickeau         * Call to Fetcher Markup can be recursive,
83104fd306cSNickeau         * we try to break a loop
83204fd306cSNickeau         *
83304fd306cSNickeau         * Note that the same object may call recursively:
83404fd306cSNickeau         * * the {@link FetcherMarkup::processMetaEventually()} metadata may call the {@link FetcherMarkup::getInstructions() instructions},
83504fd306cSNickeau         */
83604fd306cSNickeau        $id = $markupHandler->getId();
83704fd306cSNickeau        if (array_key_exists($id, $this->executingMarkupHandlerStack)) {
83804fd306cSNickeau            LogUtility::internalError("The markup ($id) is already executing");
83904fd306cSNickeau            $id = "$id-already-in-stack";
84004fd306cSNickeau        }
84104fd306cSNickeau        $this->executingMarkupHandlerStack[$id] = [$markupHandler, $oldExecutingId, $oldContextId, $oldAct];
84204fd306cSNickeau        return $this;
84304fd306cSNickeau    }
84404fd306cSNickeau
84504fd306cSNickeau    public
84604fd306cSNickeau    function closeExecutingMarkupHandler(): ExecutionContext
84704fd306cSNickeau    {
84804fd306cSNickeau        /** @noinspection PhpUnusedLocalVariableInspection */
84904fd306cSNickeau        [$markupHandler, $oldExecutingId, $oldContextId, $oldAct] = array_pop($this->executingMarkupHandlerStack);
85004fd306cSNickeau
85104fd306cSNickeau        $this
85204fd306cSNickeau            ->setExecutingAction($oldAct)
85304fd306cSNickeau            ->setExecutingId($oldExecutingId);
85404fd306cSNickeau
85504fd306cSNickeau        global $INFO;
85604fd306cSNickeau        if ($oldExecutingId === null) {
85704fd306cSNickeau            unset($INFO['id']);
85804fd306cSNickeau        } else {
85904fd306cSNickeau            $INFO['id'] = $oldContextId;
86070bbd7f1Sgerardnico            $INFO['namespace'] = getNS($oldContextId);
86104fd306cSNickeau        }
86204fd306cSNickeau        return $this;
86304fd306cSNickeau    }
86404fd306cSNickeau
86504fd306cSNickeau
86604fd306cSNickeau    /**
86704fd306cSNickeau     * @throws ExceptionNotFound - if there is no markup handler execution running
86804fd306cSNickeau     */
86904fd306cSNickeau    public
87004fd306cSNickeau    function getExecutingMarkupHandler(): FetcherMarkup
87104fd306cSNickeau    {
87204fd306cSNickeau        $count = count($this->executingMarkupHandlerStack);
87304fd306cSNickeau        if ($count >= 1) {
87404fd306cSNickeau            return $this->executingMarkupHandlerStack[array_key_last($this->executingMarkupHandlerStack)][0];
87504fd306cSNickeau        }
87604fd306cSNickeau        throw new ExceptionNotFound("No markup handler running");
87704fd306cSNickeau    }
87804fd306cSNickeau
87904fd306cSNickeau    /**
88004fd306cSNickeau     * @throws ExceptionNotFound - if there is no parent markup handler execution found
88104fd306cSNickeau     */
88204fd306cSNickeau    public
88304fd306cSNickeau    function getExecutingParentMarkupHandler(): FetcherMarkup
88404fd306cSNickeau    {
88504fd306cSNickeau        return $this->getExecutingMarkupHandler()->getParent();
88604fd306cSNickeau    }
88704fd306cSNickeau
88804fd306cSNickeau    /**
88904fd306cSNickeau     * This function sets the default context path.
89004fd306cSNickeau     *
89104fd306cSNickeau     * Mostly used in test, to determine relative path
89204fd306cSNickeau     * when testing {@link LinkMarkup} and {@link WikiPath}
89304fd306cSNickeau     * and not use a {@link FetcherMarkup}
89404fd306cSNickeau     *
89504fd306cSNickeau     * @param WikiPath $contextPath - a markup file context path used (not a namespace)
89604fd306cSNickeau     * @return $this
89704fd306cSNickeau     * @deprecated
89804fd306cSNickeau     */
89904fd306cSNickeau    public
90004fd306cSNickeau    function setDefaultContextPath(WikiPath $contextPath): ExecutionContext
90104fd306cSNickeau    {
90204fd306cSNickeau        $this->getConfig()->setDefaultContextPath($contextPath);
90304fd306cSNickeau        return $this;
90404fd306cSNickeau    }
90504fd306cSNickeau
90604fd306cSNickeau
90704fd306cSNickeau    /**
90804fd306cSNickeau     * @return WikiPath - the context path is a markup file that gives context.
90904fd306cSNickeau     * Ie this is the equivalent of the current directory.
91004fd306cSNickeau     * When a link/path is empty or relative, the program will check for the context path
91104fd306cSNickeau     * to calculate the absolute path
91204fd306cSNickeau     */
91304fd306cSNickeau    public
91404fd306cSNickeau    function getContextPath(): WikiPath
91504fd306cSNickeau    {
91604fd306cSNickeau
91704fd306cSNickeau        try {
91804fd306cSNickeau
91904fd306cSNickeau            /**
92004fd306cSNickeau             * Do we a fetcher markup running ?
92104fd306cSNickeau             * (It's first as we may change it
92204fd306cSNickeau             * for a slot for instance)
92304fd306cSNickeau             */
92404fd306cSNickeau            return $this
92504fd306cSNickeau                ->getExecutingMarkupHandler()
92604fd306cSNickeau                ->getRequestedContextPath();
92704fd306cSNickeau
92804fd306cSNickeau        } catch (ExceptionNotFound $e) {
92904fd306cSNickeau            try {
93004fd306cSNickeau
93104fd306cSNickeau                /**
93204fd306cSNickeau                 * Do we have a template page executing ?
93304fd306cSNickeau                 */
93404fd306cSNickeau                return $this->getExecutingPageTemplate()
93504fd306cSNickeau                    ->getRequestedContextPath();
93604fd306cSNickeau
93704fd306cSNickeau            } catch (ExceptionNotFound $e) {
93804fd306cSNickeau
93904fd306cSNickeau                /**
94004fd306cSNickeau                 * Hack, hack, hack
94104fd306cSNickeau                 * In  preview mode, the context path is the last visited page
94204fd306cSNickeau                 * for a slot
94304fd306cSNickeau                 */
94404fd306cSNickeau                global $ACT;
94504fd306cSNickeau                if ($ACT === ExecutionContext::PREVIEW_ACTION) {
94604fd306cSNickeau                    global $ID;
94704fd306cSNickeau                    if (!empty($ID)) {
94804fd306cSNickeau                        try {
94904fd306cSNickeau                            $markupPath = MarkupPath::createMarkupFromId($ID);
95004fd306cSNickeau                            if ($markupPath->isSlot()) {
95104fd306cSNickeau                                return SlotSystem::getContextPath()->toWikiPath();
95204fd306cSNickeau                            }
95304fd306cSNickeau                        } catch (ExceptionCast|ExceptionNotFound $e) {
95404fd306cSNickeau                            // ok
95504fd306cSNickeau                        }
95604fd306cSNickeau                    }
95704fd306cSNickeau                }
95804fd306cSNickeau
95904fd306cSNickeau                /**
96004fd306cSNickeau                 * Nope ? This is a dokuwiki run (admin page, ...)
96104fd306cSNickeau                 */
96204fd306cSNickeau                return $this->getConfig()->getDefaultContextPath();
96304fd306cSNickeau
96404fd306cSNickeau            }
96504fd306cSNickeau
96604fd306cSNickeau        }
96704fd306cSNickeau
96804fd306cSNickeau
96904fd306cSNickeau    }
97004fd306cSNickeau
97104fd306cSNickeau
97204fd306cSNickeau    /**
97304fd306cSNickeau     * @return WikiPath
97404fd306cSNickeau     * @deprecated uses {@link SiteConfig::getDefaultContextPath()}
97504fd306cSNickeau     */
97604fd306cSNickeau    public
97704fd306cSNickeau    function getDefaultContextPath(): WikiPath
97804fd306cSNickeau    {
97904fd306cSNickeau        return $this->getConfig()->getDefaultContextPath();
98004fd306cSNickeau    }
98104fd306cSNickeau
98204fd306cSNickeau    /**
98304fd306cSNickeau     * The page global context object
98404fd306cSNickeau     * @throws ExceptionNotFound
98504fd306cSNickeau     */
98604fd306cSNickeau    public
98704fd306cSNickeau    function getExecutingPageTemplate(): TemplateForWebPage
98804fd306cSNickeau    {
98904fd306cSNickeau        if (isset($this->executingPageTemplate)) {
99004fd306cSNickeau            return $this->executingPageTemplate;
99104fd306cSNickeau        }
99204fd306cSNickeau        throw new ExceptionNotFound("No page template execution running");
99304fd306cSNickeau    }
99404fd306cSNickeau
99504fd306cSNickeau    /**
99604fd306cSNickeau     * Set the page template that is executing.
99704fd306cSNickeau     * It's the context object for all page related
99804fd306cSNickeau     * (mostly header event)
99904fd306cSNickeau     * @param TemplateForWebPage $pageTemplate
100004fd306cSNickeau     * @return $this
100104fd306cSNickeau     */
100204fd306cSNickeau    public
100304fd306cSNickeau    function setExecutingPageTemplate(TemplateForWebPage $pageTemplate): ExecutionContext
100404fd306cSNickeau    {
100504fd306cSNickeau        $this->executingPageTemplate = $pageTemplate;
100604fd306cSNickeau        return $this;
100704fd306cSNickeau    }
100804fd306cSNickeau
100904fd306cSNickeau    public
101004fd306cSNickeau    function closeExecutingPageTemplate(): ExecutionContext
101104fd306cSNickeau    {
101204fd306cSNickeau        unset($this->executingPageTemplate);
101304fd306cSNickeau        return $this;
101404fd306cSNickeau    }
101504fd306cSNickeau
101604fd306cSNickeau    public
101704fd306cSNickeau    function getApp(): Site
101804fd306cSNickeau    {
101904fd306cSNickeau        if (isset($this->app)) {
102004fd306cSNickeau            return $this->app;
102104fd306cSNickeau        }
102204fd306cSNickeau        $this->app = new Site($this);
102304fd306cSNickeau        return $this->app;
102404fd306cSNickeau    }
102504fd306cSNickeau
102604fd306cSNickeau    /**
102704fd306cSNickeau     * @return SiteConfig short utility function to get access to the global app config
102804fd306cSNickeau     */
102904fd306cSNickeau    public
103004fd306cSNickeau    function getConfig(): SiteConfig
103104fd306cSNickeau    {
103204fd306cSNickeau        return $this->getApp()->getConfig();
103304fd306cSNickeau    }
103404fd306cSNickeau
103504fd306cSNickeau    /**
103604fd306cSNickeau     * @throws ExceptionNotFound - when there is no executing id (markup execution)
103704fd306cSNickeau     */
103804fd306cSNickeau    public
103904fd306cSNickeau    function getExecutingWikiPath(): WikiPath
104004fd306cSNickeau    {
104104fd306cSNickeau        try {
104204fd306cSNickeau            return $this->getExecutingMarkupHandler()
104304fd306cSNickeau                ->getRequestedExecutingPath()
104404fd306cSNickeau                ->toWikiPath();
104504fd306cSNickeau        } catch (ExceptionCast|ExceptionNotFound $e) {
104604fd306cSNickeau            // Execution without templating (ie without fetcher markup)
104704fd306cSNickeau            return WikiPath::createMarkupPathFromId($this->getExecutingWikiId());
104804fd306cSNickeau        }
104904fd306cSNickeau
105004fd306cSNickeau    }
105104fd306cSNickeau
105204fd306cSNickeau    /**
105304fd306cSNickeau     * @return array - data in context
105404fd306cSNickeau     * This is the central point to get data in context as there is no
105504fd306cSNickeau     * content object in dokuwiki
105604fd306cSNickeau     *
105704fd306cSNickeau     * It takes care of returning the context path
105804fd306cSNickeau     * (in case of slot via the {@link self::getContextPath()}
105904fd306cSNickeau     */
106004fd306cSNickeau    public
106104fd306cSNickeau    function getContextData(): array
106204fd306cSNickeau    {
106304fd306cSNickeau
106404fd306cSNickeau        try {
106504fd306cSNickeau
106604fd306cSNickeau            /**
106704fd306cSNickeau             * Context data may be dynamically given
106804fd306cSNickeau             * by the {@link \syntax_plugin_combo_iterator}
106904fd306cSNickeau             */
107004fd306cSNickeau            return $this
107104fd306cSNickeau                ->getExecutingMarkupHandler()
107204fd306cSNickeau                ->getContextData();
107304fd306cSNickeau
107404fd306cSNickeau        } catch (ExceptionNotFound $e) {
107504fd306cSNickeau
107604fd306cSNickeau            /**
107704fd306cSNickeau             * Preview / slot
107804fd306cSNickeau             */
107904fd306cSNickeau            return MarkupPath::createPageFromPathObject($this->getContextPath())->getMetadataForRendering();
108004fd306cSNickeau
108104fd306cSNickeau        }
108204fd306cSNickeau
108304fd306cSNickeau    }
108404fd306cSNickeau
108504fd306cSNickeau    /**
108604fd306cSNickeau     * This method will delete the global identifier
108704fd306cSNickeau     * and call the 'close' method if the method exists.
108804fd306cSNickeau     * @param string $globalObjectIdentifier
108904fd306cSNickeau     * @return void
109004fd306cSNickeau     */
109104fd306cSNickeau    public
109204fd306cSNickeau    function closeAndRemoveRuntimeVariableIfExists(string $globalObjectIdentifier)
109304fd306cSNickeau    {
109404fd306cSNickeau
109504fd306cSNickeau        if (!isset($this->executionScopedVariables[$globalObjectIdentifier])) {
109604fd306cSNickeau            return;
109704fd306cSNickeau        }
109804fd306cSNickeau
109904fd306cSNickeau        /**
110004fd306cSNickeau         * Get the object references
110104fd306cSNickeau         */
110204fd306cSNickeau        $object = &$this->executionScopedVariables[$globalObjectIdentifier];
110304fd306cSNickeau
110404fd306cSNickeau
110504fd306cSNickeau        /**
110604fd306cSNickeau         * Call the close method
110704fd306cSNickeau         */
110804fd306cSNickeau        if (is_object($object)) {
110904fd306cSNickeau            if (method_exists($object, 'close')) {
111004fd306cSNickeau                $object->close();
111104fd306cSNickeau            }
111204fd306cSNickeau        }
111304fd306cSNickeau
111404fd306cSNickeau        /**
111504fd306cSNickeau         * Close it really by setting null
111604fd306cSNickeau         *
111704fd306cSNickeau         * (Forwhatever reason, sqlite closing in php
111804fd306cSNickeau         * is putting the variable to null)
111904fd306cSNickeau         */
112004fd306cSNickeau        $object = null;
112104fd306cSNickeau
112204fd306cSNickeau        /**
112304fd306cSNickeau         * Delete it from the array
112404fd306cSNickeau         */
112504fd306cSNickeau        unset($this->executionScopedVariables[$globalObjectIdentifier]);
112604fd306cSNickeau
112704fd306cSNickeau    }
112804fd306cSNickeau
112904fd306cSNickeau    /**
113004fd306cSNickeau     * Close all execution variables
113104fd306cSNickeau     */
113204fd306cSNickeau    public
113304fd306cSNickeau    function closeExecutionVariables(): ExecutionContext
113404fd306cSNickeau    {
113504fd306cSNickeau        $scopedVariables = array_keys($this->executionScopedVariables);
113604fd306cSNickeau        foreach ($scopedVariables as $executionScopedVariableKey) {
113704fd306cSNickeau            $this->closeAndRemoveRuntimeVariableIfExists($executionScopedVariableKey);
113804fd306cSNickeau        }
113904fd306cSNickeau        return $this;
114004fd306cSNickeau    }
114104fd306cSNickeau
114204fd306cSNickeau    public
114304fd306cSNickeau    function __toString()
114404fd306cSNickeau    {
114504fd306cSNickeau        return $this->creationTime;
114604fd306cSNickeau    }
114704fd306cSNickeau
114804fd306cSNickeau    public
114904fd306cSNickeau    function isExecutingPageTemplate(): bool
115004fd306cSNickeau    {
115104fd306cSNickeau        try {
115204fd306cSNickeau            $this->getExecutingPageTemplate();
115304fd306cSNickeau            return true;
115404fd306cSNickeau        } catch (ExceptionNotFound $e) {
115504fd306cSNickeau            return false;
115604fd306cSNickeau        }
115704fd306cSNickeau    }
115804fd306cSNickeau
115904fd306cSNickeau    public
116004fd306cSNickeau    function hasExecutingMarkupHandler(): bool
116104fd306cSNickeau    {
116204fd306cSNickeau        try {
116304fd306cSNickeau            $this->getExecutingMarkupHandler();
116404fd306cSNickeau            return true;
116504fd306cSNickeau        } catch (ExceptionNotFound $e) {
116604fd306cSNickeau            return false;
116704fd306cSNickeau        }
116804fd306cSNickeau    }
116904fd306cSNickeau
117004fd306cSNickeau
117104fd306cSNickeau}
1172