1<?php 2 3namespace ComboStrap; 4 5 6class MarkupRenderer 7{ 8 9 /** 10 * Setting the mime to instructions will just not do any render processing. 11 * Just parse tree/instructions processing if needed 12 */ 13 public const INSTRUCTION_EXTENSION = "i"; 14 15 const CANONICAL = "markup:renderer"; 16 const XHTML_RENDERER = "xhtml"; 17 const DEFAULT_RENDERER = self::XHTML_RENDERER; 18 const METADATA_EXTENSION = "meta"; 19 /** 20 * @var string source of the renderer is a markup (and not instructions) 21 */ 22 private string $markupSource; 23 /** 24 * @var array source of the rendere is instructions 25 */ 26 private array $instructionsSource; 27 28 private Mime $requestedMime; 29 30 /** 31 * @var mixed 32 */ 33 private $cacheAfterRendering = true; 34 private string $renderer; 35 36 /** 37 * @var WikiPath the context path 38 * May be null (ie markup rendering without any context such as webcode) 39 */ 40 private WikiPath $requestedContextPath; 41 42 /** 43 * @var ?Path the path from where the instructions/markup where created 44 * This is mandatory to add cache dependency informations 45 * and set the path that is executing 46 */ 47 private ?Path $executingPath; 48 49 50 /** 51 * @param string $markup 52 * @param Path|null $executingPath - the source of the markup - may be null (case of webcode) 53 * @param WikiPath|null $contextPath - the requested markup path - may be null (case of webcode) 54 * @return MarkupRenderer 55 */ 56 public static function createFromMarkup(string $markup, ?Path $executingPath, ?WikiPath $contextPath): MarkupRenderer 57 { 58 $markupRenderer = (new MarkupRenderer()) 59 ->setMarkup($markup); 60 if ($executingPath != null) { 61 $markupRenderer->setRequestedExecutingPath($executingPath); 62 } 63 if ($contextPath != null) { 64 $markupRenderer->setRequestedContextPath($contextPath); 65 } 66 return $markupRenderer; 67 68 } 69 70 private function setMarkup(string $markup): MarkupRenderer 71 { 72 $this->markupSource = $markup; 73 return $this; 74 } 75 76 public static function createFromMarkupInstructions($instructions, FetcherMarkup $fetcherMarkup): MarkupRenderer 77 { 78 return (new MarkupRenderer()) 79 ->setInstructions($instructions) 80 ->setRequestedContextPath($fetcherMarkup->getRequestedContextPath()) 81 ->setRequestedExecutingPath($fetcherMarkup->getExecutingPathOrNull()); 82 } 83 84 public static function createFromInstructions($instructions): MarkupRenderer 85 { 86 return (new MarkupRenderer())->setInstructions($instructions); 87 } 88 89 90 public function setRequestedMimeToInstruction(): MarkupRenderer 91 { 92 try { 93 $this->setRequestedMime(Mime::createFromExtension(self::INSTRUCTION_EXTENSION)); 94 } catch (ExceptionNotFound $e) { 95 throw new ExceptionRuntime("Internal error: the mime is internal and should be good"); 96 } 97 return $this; 98 99 } 100 101 public function setRequestedMime(Mime $requestedMime): MarkupRenderer 102 { 103 $this->requestedMime = $requestedMime; 104 return $this; 105 } 106 107 public function getOutput() 108 { 109 110 $extension = $this->requestedMime->getExtension(); 111 switch ($extension) { 112 case self::INSTRUCTION_EXTENSION: 113 /** 114 * Get the instructions adapted from {@link p_cached_instructions()} 115 * 116 * Note that this code may not run at first rendering 117 * 118 * Why ? 119 * Because dokuwiki asks first page information 120 * via the {@link pageinfo()} method. 121 * This function then render the metadata (ie {@link p_render_metadata()} and therefore will trigger 122 * the rendering with this function 123 * ```p_cached_instructions(wikiFN($id),false,$id)``` 124 * 125 * The best way to manipulate the instructions is not before but after 126 * the parsing. See {@link \action_plugin_combo_instructionspostprocessing} 127 * 128 */ 129 return p_get_instructions($this->markupSource); 130 131 default: 132 /** 133 * The code below is adapted from {@link p_cached_output()} 134 * $ret = p_cached_output($file, 'xhtml', $pageid); 135 */ 136 if (!isset($this->instructionsSource)) { 137 $executingPath = null; 138 if (isset($this->executingPath)) { 139 $executingPath = $this->executingPath; 140 } 141 142 $contextPath = null; 143 if (isset($this->requestedContextPath)) { 144 $contextPath = $this->requestedContextPath; 145 } 146 147 $this->instructionsSource = MarkupRenderer::createFromMarkup($this->markupSource, $executingPath, $contextPath) 148 ->setRequestedMimeToInstruction() 149 ->getOutput(); 150 } 151 152 /** 153 * Render 154 */ 155 $result = p_render($this->getRendererNameOrDefault(), $this->instructionsSource, $info); 156 $this->cacheAfterRendering = $info['cache'] !== null ? $info['cache'] : false; 157 return $result; 158 159 } 160 161 162 } 163 164 private function setInstructions(array $instructions): MarkupRenderer 165 { 166 $this->instructionsSource = $instructions; 167 return $this; 168 } 169 170 171 function getRendererNameOrDefault(): string 172 { 173 if (isset($this->renderer)) { 174 return $this->renderer; 175 } 176 /** 177 * Note: This value is passed to {@link p_get_renderer} to get the renderer class 178 */ 179 return self::DEFAULT_RENDERER; 180 } 181 182 public function setRendererName(string $rendererName): MarkupRenderer 183 { 184 $this->renderer = $rendererName; 185 return $this; 186 } 187 188 public function getCacheAfterRendering() 189 { 190 return $this->cacheAfterRendering; 191 } 192 193 /** 194 * The page context in which this markup was requested 195 * @param WikiPath $path 196 * @return $this 197 */ 198 public function setRequestedContextPath(WikiPath $path): MarkupRenderer 199 { 200 $this->requestedContextPath = $path; 201 return $this; 202 } 203 204 public function setRequestedMimeToXhtml(): MarkupRenderer 205 { 206 try { 207 return $this->setRequestedMime(Mime::createFromExtension("xhtml")); 208 } catch (ExceptionNotFound $e) { 209 throw new ExceptionRuntime("Internal error", 0, $e); 210 } 211 } 212 213 /** 214 * @throws ExceptionNotFound 215 */ 216 private function getRequestedContextPath(): WikiPath 217 { 218 219 if (!isset($this->requestedContextPath)) { 220 throw new ExceptionNotFound("No requested context path"); 221 } 222 return $this->requestedContextPath; 223 224 } 225 226 public function setRequestedExecutingPath(?Path $executingPath): MarkupRenderer 227 { 228 $this->executingPath = $executingPath; 229 return $this; 230 } 231 232 233} 234