1<?php
2
3use ComboStrap\CacheManager;
4use ComboStrap\DokuPath;
5use ComboStrap\ExceptionCombo;
6use ComboStrap\LogUtility;
7use ComboStrap\PluginUtility;
8use ComboStrap\Resources;
9use ComboStrap\Snippet;
10use ComboStrap\SnippetManager;
11use dokuwiki\Cache\CacheParser;
12
13if (!defined('DOKU_INC')) die();
14
15/**
16 *
17 *
18 * Add the snippet needed by the components
19 *
20 */
21class action_plugin_combo_snippets extends DokuWiki_Action_Plugin
22{
23
24    /**
25     * @var bool - to trace if the header output was called
26     */
27    private $headerOutputWasCalled = false;
28
29    function __construct()
30    {
31        // enable direct access to language strings
32        // ie $this->lang
33        $this->setupLocale();
34    }
35
36    public function register(Doku_Event_Handler $controller)
37    {
38
39        /**
40         * To add the snippets in the header
41         */
42        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'componentSnippetHead', array());
43
44        /**
45         * To add the snippets in the content
46         * if they have not been added to the header
47         *
48         * Not https://www.dokuwiki.org/devel:event:tpl_content_display TPL_ACT_RENDER
49         * or https://www.dokuwiki.org/devel:event:tpl_act_render
50         * because it works only for the main content
51         * in {@link tpl_content()}
52         *
53         * We use
54         * https://www.dokuwiki.org/devel:event:renderer_content_postprocess
55         * that is in {@link p_render()} and takes into account also the slot page.
56         */
57        $controller->register_hook('RENDERER_CONTENT_POSTPROCESS', 'AFTER', $this, 'componentSnippetContent', array());
58
59        /**
60         * To reset the value
61         */
62        $controller->register_hook('DOKUWIKI_DONE', 'BEFORE', $this, 'close', array());
63
64
65    }
66
67    /**
68     * Reset variable
69     * Otherwise in test, when we call it two times, it just fail
70     */
71    function close()
72    {
73
74        $this->headerOutputWasCalled = false;
75
76        /**
77         * Fighting the fact that in 7.2,
78         * there is still a cache
79         */
80        SnippetManager::init();
81
82    }
83
84    /**
85     * Dokuwiki has already a canonical methodology
86     * https://www.dokuwiki.org/canonical
87     *
88     * @param $event
89     */
90    function componentSnippetHead($event)
91    {
92
93
94        global $ID;
95        if (empty($ID)) {
96
97            global $_SERVER;
98            $scriptName = $_SERVER['SCRIPT_NAME'];
99
100            /**
101             * If this is an ajax call, return
102             * only if this not from webcode
103             */
104            if (strpos($scriptName, "/lib/exe/ajax.php") !== false) {
105                global $_REQUEST;
106                $call = $_REQUEST['call'];
107                if ($call != action_plugin_combo_webcode::CALL_ID) {
108                    return;
109                }
110            } else if (!(strpos($scriptName, "/lib/exe/detail.php") !== false)) {
111                /**
112                 * Image page has an header and footer that may needs snippet
113                 * We return only if this is not a image/detail page
114                 */
115                return;
116            }
117        }
118
119        /**
120         * Advertise that the header output was called
121         * If the user is using another template
122         * than strap that does not put the component snippet
123         * in the head
124         * Used in
125         */
126        $this->headerOutputWasCalled = true;
127
128        $snippetManager = PluginUtility::getSnippetManager();
129        $cacheManager = CacheManager::getOrCreate();
130
131        /**
132         * For each processed bar in the page
133         *   * retrieve the snippets from the cache or store the process one
134         *   * add the cache information in meta
135         */
136        $slots = $cacheManager->getXhtmlCacheSlotResultsForRequestedPage();
137        foreach ($slots as $slotId => $servedFromCache) {
138
139            /**
140             * The local file location of the slot
141             */
142            $slotLocalFilePath = DokuPath::createPagePathFromId($slotId)
143                ->toLocalPath()
144                ->toAbsolutePath()
145                ->toString();
146
147            /**
148             * Using a cache parser, set the page id and will trigger
149             * the parser cache use event in order to log/report the cache usage
150             * At {@link action_plugin_combo_cache::logCacheUsage()}
151             */
152            $cache = new CacheParser($slotId, $slotLocalFilePath, "snippet.json");
153            $cache->setEvent('PARSER_CACHE_USE'); // cache parser use already this event, just FYI
154            $dependencies = array(
155                "files" => [
156                    $slotLocalFilePath,
157                    Resources::getComboHome() . "/plugin.info.txt"
158                ]
159            );
160
161            // if the bar was served from the cache
162            if ($servedFromCache && $cache->useCache($dependencies)) {
163
164                // Retrieve snippets from previous run
165                $data = $cache->retrieveCache();
166                if (!empty($data)) {
167
168                    $jsonDecodeSnippets = json_decode($data, true);
169                    $nativeSnippets = [];
170                    foreach ($jsonDecodeSnippets as $type => $snippets) {
171                        foreach ($snippets as $snippetId => $snippetArray) {
172                            try {
173                                $nativeSnippets[$type][$snippetId] = Snippet::createFromJson($snippetArray);
174                            } catch (ExceptionCombo $e) {
175                                LogUtility::msg("The snippet json array cannot be build into a snippet object. " . $e->getMessage());
176                            }
177                        }
178                    }
179                    $snippetManager->addSnippetsFromCacheForBar($slotId, $nativeSnippets);
180
181                }
182
183            } else {
184                $jsonDecodeSnippets = $snippetManager->getSnippetsForBar($slotId);
185                if ($jsonDecodeSnippets !== null) {
186                    $data1 = json_encode($jsonDecodeSnippets);
187                    $cache->storeCache($data1);
188                }
189            }
190
191        }
192
193        /**
194         * Snippets
195         */
196        $allSnippets = $snippetManager->getSnippets();
197        foreach ($allSnippets as $tagType => $tags) {
198
199            foreach ($tags as $tag) {
200                $event->data[$tagType][] = $tag;
201            }
202
203        }
204
205        $snippetManager->close();
206
207    }
208
209    /**
210     * Used if the template does not run the content
211     * before the calling of the header as strap does.
212     *
213     * In this case, the {@link \ComboStrap\SnippetManager::close()} has
214     * not run, and the snippets are still in memory.
215     *
216     * We store them in the HTML and they
217     * follows then the HTML cache of DokuWiki
218     * @param $event
219     */
220    function componentSnippetContent($event)
221    {
222
223        $format = $event->data[0];
224        if ($format !== "xhtml") {
225            return;
226        }
227
228        /**
229         * Run only if the header output was already called
230         */
231        if ($this->headerOutputWasCalled) {
232
233            $snippetManager = PluginUtility::getSnippetManager();
234
235            $xhtmlContent = &$event->data[1];
236            $snippets = $snippetManager->getSnippets();
237            foreach ($snippets as $tagType => $tags) {
238
239                foreach ($tags as $tag) {
240                    $xhtmlContent .= DOKU_LF . "<$tagType";
241                    $attributes = "";
242                    $content = null;
243                    foreach ($tag as $attributeName => $attributeValue) {
244                        if ($attributeName != "_data") {
245                            $attributes .= " $attributeName=\"$attributeValue\"";
246                        } else {
247                            $content = $attributeValue;
248                        }
249                    }
250                    $xhtmlContent .= "$attributes>";
251                    if (!empty($content)) {
252                        $xhtmlContent .= $content;
253                    }
254                    $xhtmlContent .= "</$tagType>" . DOKU_LF;
255                }
256
257            }
258
259            $snippetManager->close();
260
261        }
262
263    }
264
265
266}
267