1<?php
2
3use ComboStrap\CacheManager;
4use ComboStrap\ExceptionBadState;
5use ComboStrap\ExceptionNotExists;
6use ComboStrap\ExceptionNotFound;
7use ComboStrap\ExecutionContext;
8use ComboStrap\FetcherMarkup;
9use ComboStrap\LogUtility;
10use ComboStrap\PluginUtility;
11use ComboStrap\SnippetSystem;
12
13
14/**
15 *
16 *
17 * Add the snippet needed by the components
18 *
19 */
20class action_plugin_combo_snippets extends DokuWiki_Action_Plugin
21{
22
23    const CLASS_SNIPPET_IN_CONTENT = "snippet-content-combo";
24
25    /**
26     * To known if we needs to put all snippet in the content
27     * or not
28     */
29    const HEAD_EVENT_WAS_CALLED = "head_event_was_called";
30    const CANONICAL = "snippets";
31
32
33    function __construct()
34    {
35        // enable direct access to language strings
36        // ie $this->lang
37        $this->setupLocale();
38    }
39
40    public function register(Doku_Event_Handler $controller)
41    {
42
43        /**
44         * To add the snippets in the header
45         */
46        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'componentSnippetHead', array());
47
48        /**
49         * To add the snippets in the content
50         * if they have not been added to the header
51         *
52         * Not https://www.dokuwiki.org/devel:event:tpl_content_display TPL_ACT_RENDER
53         * or https://www.dokuwiki.org/devel:event:tpl_act_render
54         * because it works only for the main content
55         * in {@link tpl_content()}
56         *
57         * We use
58         * https://www.dokuwiki.org/devel:event:renderer_content_postprocess
59         * that is in {@link p_render()} and takes into account also the slot page.
60         */
61        $controller->register_hook('RENDERER_CONTENT_POSTPROCESS', 'AFTER', $this, 'componentSnippetContent', array());
62
63
64    }
65
66
67    /**
68     *
69     * Add the snippets in the head
70     *
71     * @param $event
72     */
73    function componentSnippetHead($event)
74    {
75
76        /**
77         * Advertise that this event has occurred
78         * In a strap template, this event is last because head are added after content rendering
79         * In another template, this event is first
80         * The function {@link action_plugin_combo_snippets::componentSnippetContent()} used it to determine if
81         * the snippets should be added into the content
82         */
83        ExecutionContext::getActualOrCreateFromEnv()
84            ->setRuntimeBoolean(self::HEAD_EVENT_WAS_CALLED, true);
85
86
87
88        /**
89         * For each processed slot in the execution, retrieve the snippets
90         */
91        $cacheReporters = CacheManager::getFromContextExecution()->getCacheResults();
92        foreach ($cacheReporters as $cacheReporter) {
93
94            foreach ($cacheReporter->getResults() as $report) {
95
96                if ($report->getMode() !== FetcherMarkup::XHTML_MODE) {
97                    continue;
98                }
99                $markupPath = $report->getMarkupPath();
100                try {
101                    $fetcherMarkupForMarkup = $markupPath->createHtmlFetcherWithRequestedPathAsContextPath();
102                } catch (ExceptionNotExists $e) {
103                    LogUtility::internalError("The executing markup path ($markupPath) should exists because it was executed.");
104                    continue;
105                }
106                $fetcherMarkupForMarkup->loadSnippets();
107            }
108
109        }
110
111
112        $snippetSystem = SnippetSystem::getFromContext();
113
114        $snippets = $snippetSystem->getSnippets();
115        foreach ($snippets as $snippet) {
116            /**
117             * In a dokuwiki standard template, head is called
118             * first, then the content, to not add the snippet in the head and in the content
119             * there is an indicator that tracks if the output was asked
120             */
121            if (!$snippet->hasHtmlOutputAlreadyOccurred()) {
122                try {
123                    $tag = $snippet->toDokuWikiArray();
124                } catch (\Exception $e) {
125                    LogUtility::error("We couldn't get the attributes of the snippet ($snippet). It has been skipped. Error: {$e->getMessage()}", self::CANONICAL);
126                    continue;
127                }
128                $tagType = $snippet->getHtmlTag();
129                $event->data[$tagType][] = $tag;
130            }
131
132        }
133
134
135    }
136
137    /**
138     *
139     * This function store the snippets in the HTML content when needed
140     * (mostly admin page or any other template than strap ...)
141     *
142     * This event/function is called first because {@link \ComboStrap\FetcherPage} parse the main markup first (It's the driver)
143     *
144     * In any other template, they follows the creation of the page, the
145     * header are called first, then the content
146     *
147     *
148     * @param $event
149     */
150    function componentSnippetContent($event)
151    {
152
153        /**
154         * Add snippet in the content
155         *  - if the header output was already called
156         *  - if this is not a page rendering (ie an admin rendering)
157         * for instance, the upgrade plugin call {@link p_cached_output()} on local file
158         */
159
160        /**
161         * Dynamic rendering call this event
162         * We don't add any component at this moment
163         */
164        global $ACT;
165        if ($ACT === FetcherMarkup::MARKUP_DYNAMIC_EXECUTION_NAME) {
166            return;
167        }
168
169
170        $format = $event->data[0];
171        if ($format !== "xhtml") {
172            return;
173        }
174
175        $executionContext = ExecutionContext::getActualOrCreateFromEnv();
176
177        try {
178            $headEventWasCalled = $executionContext->getRuntimeBoolean(self::HEAD_EVENT_WAS_CALLED);
179        } catch (ExceptionNotFound $e) {
180            $headEventWasCalled = false;
181        }
182
183        /**
184         * Put snippet in the content
185         * if this is not a show (ie Admin page rendering)
186         *
187         * And if the header output was already called
188         * (case that the template is not strap)
189         */
190        $putAllSnippetsInContent =
191            $headEventWasCalled === true
192            ||
193            ($ACT !== "show" && $ACT !== null);
194        if (!$putAllSnippetsInContent) {
195            return;
196        }
197
198        $snippetManager = PluginUtility::getSnippetManager();
199        $xhtmlContent = &$event->data[1];
200        /**
201         * What fucked up is fucked up
202         *
203         * In admin page, as we don't know the source of the processing text
204         * (It may be a partial (ie markup) to create the admin page
205         * We may have several times the same global request slot
206         *
207         * We can't make the difference.
208         *
209         * For now, we add therefore only the snippet for the slots.
210         * The snippet for the request should have been already added with the
211         * DOKUWIKI_STARTED hook
212         */
213
214        $snippets = $snippetManager->getSnippets();
215        if (sizeof($snippets) > 0) {
216            $class = self::CLASS_SNIPPET_IN_CONTENT;
217            $htmlForSlots = $snippetManager->toHtmlForSlotSnippets();
218            if (empty($htmlForSlots)) {
219                return;
220            }
221            $htmlForSlotsWrapper = <<<EOF
222<div class="$class">
223    {$htmlForSlots}
224</div>
225EOF;
226            $xhtmlContent .= $htmlForSlotsWrapper;
227
228        }
229
230    }
231
232
233}
234