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