1<?php
2
3use ComboStrap\DokuwikiId;
4use ComboStrap\ExceptionNotFound;
5use ComboStrap\ExceptionReporter;
6use ComboStrap\ExecutionContext;
7use ComboStrap\FetcherAppPages;
8use ComboStrap\FetcherPage;
9use ComboStrap\FetcherRailBar;
10use ComboStrap\FileSystems;
11use ComboStrap\HttpResponseStatus;
12use ComboStrap\Identity;
13use ComboStrap\IFetcher;
14use ComboStrap\LogUtility;
15use ComboStrap\MarkupPath;
16use ComboStrap\Mime;
17use ComboStrap\PluginUtility;
18use ComboStrap\Site;
19use ComboStrap\SiteConfig;
20use ComboStrap\Web\Url;
21use ComboStrap\Web\UrlRewrite;
22
23/**
24 * Implementation of custom do (ie ACT) to output {@link \ComboStrap\IFetcherString}
25 *
26 *
27 *
28 */
29class action_plugin_combo_docustom extends DokuWiki_Action_Plugin
30{
31
32    const DO_PREFIX = "combo_";
33
34    const TEMPLATE_CANONICAL = "template";
35
36    /**
37     * @var bool to avoid recursion that may happen using {@link tpl_content()}
38     */
39    private bool $doCustomActuallyExecuting = false;
40
41    /**
42     * @return bool
43     */
44    public static function isThemeSystemEnabled(): bool
45    {
46        $confValue = SiteConfig::getConfValue(SiteConfig::CONF_ENABLE_THEME_SYSTEM, SiteConfig::CONF_ENABLE_THEME_SYSTEM_DEFAULT);
47        return $confValue === 1;
48    }
49
50    public static function getDoParameterValue(string $fetcherName): string
51    {
52        return self::DO_PREFIX . $fetcherName;
53    }
54
55    /**
56     *
57     * @param Doku_Event_Handler $controller
58     * @return void
59     */
60
61    public function register(\Doku_Event_Handler $controller)
62    {
63        /**
64         * Execute the combo action via an ACTION_ACT_PREPROCESS
65         *
66         * Not via the [TPL_ACT_UNKNOWN](https://www.dokuwiki.org/devel:event:tpl_act_unknown)
67         * because it would otherwise put the content in the middle of the page
68         * as an admin page.
69         *
70         * Not really useful if you want your own layout or do an export
71         */
72        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'executeComboDoAction');
73
74    }
75
76    /**
77     * @param Doku_Event $event
78     * @param $param
79     * @return void
80     *
81     */
82    public function executeComboDoAction(Doku_Event $event, $param)
83    {
84
85        if ($this->doCustomActuallyExecuting) {
86            return;
87        }
88
89        /**
90         * The router may have done a redirection
91         * (The Dokuwiki testRequest does not stop unfortunately)
92         */
93        $executionContext = ExecutionContext::getActualOrCreateFromEnv();
94        $hasEnded = $executionContext
95            ->response()
96            ->hasEnded();
97        if ($hasEnded) {
98            if ($executionContext->isTestRun()) {
99                /**
100                 * This info helps the developer to see
101                 * why nothing happens when it sends two dokuwiki {@link TestRequest}
102                 *
103                 * And not two {@link \ComboStrap\HttpResponse}
104                 * that reinitialize the global scope
105                 */
106                LogUtility::info("ExecuteDoAction: The response has already be send (ended).");
107            }
108            return;
109        }
110
111        $urlRewrite = Site::getUrlRewrite();
112        if ($urlRewrite == UrlRewrite::VALUE_DOKU_REWRITE) {
113            UrlRewrite::sendErrorMessage();
114            return;
115        }
116
117        $action = $event->data;
118
119        $privateRailbar = $executionContext->getConfig()->getBooleanValue(FetcherRailBar::CONF_PRIVATE_RAIL_BAR, FetcherRailBar::CONF_PRIVATE_RAIL_BAR_DEFAULT);
120        $isAnonymous = Identity::isAnonymous();
121        if ($privateRailbar && $isAnonymous) {
122            /**
123             * To avoid the google console error: `Excluded by ‘noindex’ tag`
124             * Example of URL
125             * https://example.com/dat/bobj/central_management_server?tab_files=upload&do=media&tab_details=history&image=db:hana:hdb_thread_stat_systemdb.png&ns=web/resource
126             */
127            $privateAction = [ExecutionContext::MEDIA_ACTION, ExecutionContext::DIFF_ACTION, ExecutionContext::RECENT_ACTION];
128            if (in_array($action, $privateAction)) {
129                $executionContext->response()
130                    ->setStatus(HttpResponseStatus::NOT_AUTHORIZED)
131                    ->end();
132                return;
133            }
134        }
135
136        if (self::isThemeSystemEnabled()) {
137            switch ($action) {
138                case ExecutionContext::SHOW_ACTION:
139                    try {
140                        $id = Url::createFromGetOrPostGlobalVariable()->getPropertyValue(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE);
141                    } catch (ExceptionNotFound $e) {
142                        // should not happen but yeah
143                        return;
144                    }
145                    $path = MarkupPath::createMarkupFromId($id);
146                    if (!FileSystems::exists($path)) {
147                        return;
148                    }
149                    $action = self::getDoParameterValue(FetcherPage::NAME);
150                    break;
151                case ExecutionContext::LOGIN_ACTION:
152                case ExecutionContext::REGISTER_ACTION:
153                case ExecutionContext::RESEND_PWD_ACTION:
154                case ExecutionContext::PROFILE_ACTION:
155                case ExecutionContext::EDIT_ACTION:
156                case ExecutionContext::PREVIEW_ACTION:
157                case ExecutionContext::SEARCH_ACTION:
158                case ExecutionContext::INDEX_ACTION:
159                    //case ExecutionContext::REVISIONS_ACTION:
160                    //case ExecutionContext::DIFF_ACTION: needs styling
161                    $action = self::getDoParameterValue(FetcherAppPages::NAME);
162                    break;
163            }
164        }
165
166
167        if (!$this->isComboDoAction($action)) return;
168
169        /**
170         * To avoid recursion
171         */
172        $this->doCustomActuallyExecuting = true;
173
174
175        try {
176            $fetcherName = $this->getFetcherNameFromAction($action);
177            $url = Url::createFromGetOrPostGlobalVariable()
178                ->addQueryParameter(IFetcher::FETCHER_KEY, $fetcherName);
179            $fetcher = $executionContext->createStringMainFetcherFromRequestedUrl($url);
180            $body = $fetcher->getFetchString();
181            $mime = $fetcher->getMime();
182            $executionContext->response()
183                ->setStatus(HttpResponseStatus::ALL_GOOD)
184                ->setBody($body, $mime)
185                ->end();
186        } catch (\Exception $e) {
187
188
189            $reporterMessage = "An error has occurred during the execution of the action ($action)";
190            $html = ExceptionReporter::createForException($e)
191                ->getHtmlPage($reporterMessage);
192            if (PluginUtility::isDevOrTest()) {
193                // Permits to throw the error to get the stack trace
194                LogUtility::warning($reporterMessage, self::TEMPLATE_CANONICAL, $e);
195            }
196            $executionContext->response()
197                ->setException($e)
198                ->setBody($html, Mime::getHtml())
199                ->end();
200
201        } finally {
202            $this->doCustomActuallyExecuting = false;
203        }
204
205    }
206
207
208    private function isComboDoAction($actionName): bool
209    {
210        return strpos($actionName, self::DO_PREFIX) === 0;
211    }
212
213    private function getFetcherNameFromAction($actionName)
214    {
215        return substr($actionName, strlen(self::DO_PREFIX));
216    }
217
218
219}
220