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