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