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