1<?php 2 3 4use ComboStrap\AnalyticsDocument; 5use ComboStrap\CallStack; 6use ComboStrap\DokuFs; 7use ComboStrap\DokuPath; 8use ComboStrap\ExceptionComboRuntime; 9use ComboStrap\Image; 10use ComboStrap\InternetPath; 11use ComboStrap\LogUtility; 12use ComboStrap\MediaLink; 13use ComboStrap\Metadata; 14use ComboStrap\PageImages; 15use ComboStrap\PagePath; 16use ComboStrap\Path; 17use ComboStrap\PluginUtility; 18use ComboStrap\ThirdPartyPlugins; 19 20 21require_once(__DIR__ . '/../ComboStrap/PluginUtility.php'); 22 23 24/** 25 * Media 26 * 27 * Takes over the {@link \dokuwiki\Parsing\ParserMode\Media media mode} 28 * that is processed by {@link Doku_Handler_Parse_Media} 29 * 30 * 31 * 32 * It can be a internal / external media 33 * 34 * 35 * See: 36 * https://developers.google.com/search/docs/advanced/guidelines/google-images 37 */ 38class syntax_plugin_combo_media extends DokuWiki_Syntax_Plugin 39{ 40 41 42 const TAG = "media"; 43 44 /** 45 * Used in the move plugin 46 * !!! The two last word of the plugin class !!! 47 */ 48 const COMPONENT = 'combo_' . self::TAG; 49 50 51 /** 52 * Found at {@link \dokuwiki\Parsing\ParserMode\Media} 53 */ 54 const MEDIA_PATTERN = "\{\{(?:[^>\}]|(?:\}[^\}]))+\}\}"; 55 56 /** 57 * Enable or disable the image 58 */ 59 const CONF_IMAGE_ENABLE = "imageEnable"; 60 61 /** 62 * Svg Rendering error 63 */ 64 const SVG_RENDERING_ERROR_CLASS = "combo-svg-rendering-error"; 65 66 public static function registerFirstMedia(Doku_Renderer_metadata $renderer, $src) 67 { 68 /** 69 * {@link Doku_Renderer_metadata::$firstimage} is unfortunately protected 70 * and {@link Doku_Renderer_metadata::internalmedia()} does not allow svg as first image 71 */ 72 if (!isset($renderer->meta[PageImages::FIRST_IMAGE_META_RELATION])) { 73 $renderer->meta[PageImages::FIRST_IMAGE_META_RELATION] = $src; 74 } 75 76 } 77 78 79 /** 80 * @param $attributes 81 * @param renderer_plugin_combo_analytics $renderer 82 */ 83 public static function updateStatistics($attributes, renderer_plugin_combo_analytics $renderer) 84 { 85 $media = MediaLink::createFromCallStackArray($attributes); 86 $renderer->stats[AnalyticsDocument::MEDIA_COUNT]++; 87 $scheme = $media->getMedia()->getPath()->getScheme(); 88 switch ($scheme) { 89 case DokuFs::SCHEME: 90 $renderer->stats[AnalyticsDocument::INTERNAL_MEDIA_COUNT]++; 91 if (!$media->getMedia()->exists()) { 92 $renderer->stats[AnalyticsDocument::INTERNAL_BROKEN_MEDIA_COUNT]++; 93 } 94 break; 95 case InternetPath::scheme: 96 $renderer->stats[AnalyticsDocument::EXTERNAL_MEDIA_COUNT]++; 97 break; 98 } 99 } 100 101 102 function getType() 103 { 104 return 'formatting'; 105 } 106 107 /** 108 * How Dokuwiki will add P element 109 * 110 * * 'normal' - The plugin can be used inside paragraphs (inline) 111 * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 112 * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 113 * 114 * @see DokuWiki_Syntax_Plugin::getPType() 115 */ 116 function getPType() 117 { 118 /** 119 * An image is not a block (it can be inside paragraph) 120 */ 121 return 'normal'; 122 } 123 124 function getAllowedTypes() 125 { 126 return array('substition', 'formatting', 'disabled'); 127 } 128 129 /** 130 * It should be less than {@link \dokuwiki\Parsing\ParserMode\Media::getSort()} 131 * (It was 320 at the time of writing this code) 132 * @return int 133 * 134 */ 135 function getSort() 136 { 137 return 319; 138 } 139 140 141 function connectTo($mode) 142 { 143 $enable = $this->getConf(self::CONF_IMAGE_ENABLE, 1); 144 if (!$enable) { 145 146 // Inside a card, we need to take over and enable it 147 $modes = [ 148 PluginUtility::getModeFromTag(syntax_plugin_combo_card::TAG), 149 ]; 150 $enable = in_array($mode, $modes); 151 } 152 153 if ($enable) { 154 if ($mode !== PluginUtility::getModeFromPluginName(ThirdPartyPlugins::IMAGE_MAPPING_NAME)) { 155 $this->Lexer->addSpecialPattern(self::MEDIA_PATTERN, $mode, PluginUtility::getModeFromTag($this->getPluginComponent())); 156 } 157 } 158 } 159 160 161 function handle($match, $state, $pos, Doku_Handler $handler): array 162 { 163 164 switch ($state) { 165 166 167 // As this is a container, this cannot happens but yeah, now, you know 168 case DOKU_LEXER_SPECIAL : 169 170 $media = MediaLink::createFromRenderMatch($match); 171 $attributes = $media->toCallStackArray(); 172 173 $callStack = CallStack::createFromHandler($handler); 174 175 /** 176 * Parent 177 */ 178 $parent = $callStack->moveToParent(); 179 $parentTag = ""; 180 if (!empty($parent)) { 181 $parentTag = $parent->getTagName(); 182 if ($parentTag == syntax_plugin_combo_link::TAG) { 183 /** 184 * TODO: should be on the exit tag of the link 185 * The image is in a link, we don't want another link 186 * to the image 187 */ 188 $attributes[MediaLink::LINKING_KEY] = MediaLink::LINKING_NOLINK_VALUE; 189 } 190 } 191 192 return array( 193 PluginUtility::STATE => $state, 194 PluginUtility::ATTRIBUTES => $attributes, 195 PluginUtility::CONTEXT => $parentTag 196 ); 197 198 199 } 200 return array(); 201 202 } 203 204 /** 205 * Render the output 206 * @param string $format 207 * @param Doku_Renderer $renderer 208 * @param array $data - what the function handle() return'ed 209 * @return boolean - rendered correctly? (however, returned value is not used at the moment) 210 * @see DokuWiki_Syntax_Plugin::render() 211 * 212 * 213 */ 214 function render($format, Doku_Renderer $renderer, $data) 215 { 216 217 switch ($format) { 218 219 case 'xhtml': 220 221 /** @var Doku_Renderer_xhtml $renderer */ 222 $attributes = $data[PluginUtility::ATTRIBUTES]; 223 $mediaLink = MediaLink::createFromCallStackArray($attributes, $renderer->date_at); 224 $media = $mediaLink->getMedia(); 225 if ($media->getPath()->getScheme() == DokuFs::SCHEME) { 226 if ($media->getPath()->getMime()->isImage() || $media->getPath()->getExtension() === "svg") { 227 try { 228 $renderer->doc .= $mediaLink->renderMediaTagWithLink(); 229 } catch (RuntimeException $e) { 230 if (PluginUtility::isDevOrTest()) { 231 throw new ExceptionComboRuntime("Media Rendering Error. {$e->getMessage()}", MediaLink::CANONICAL, 0, $e); 232 } else { 233 $errorClass = self::SVG_RENDERING_ERROR_CLASS; 234 $message = "Media ({$media->getPath()}). Error while rendering: {$e->getMessage()}"; 235 $renderer->doc .= "<span class=\"text-alert $errorClass\">" . hsc(trim($message)) . "</span>"; 236 LogUtility::msg($message, LogUtility::LVL_MSG_ERROR, MediaLink::CANONICAL); 237 } 238 } 239 return true; 240 } 241 } 242 243 /** 244 * This is not an local internal media image (a video or an url image) 245 * Dokuwiki takes over 246 */ 247 $type = $attributes[MediaLink::MEDIA_DOKUWIKI_TYPE]; 248 $src = $attributes['src']; 249 $title = $attributes['title']; 250 $align = $attributes['align']; 251 $width = $attributes['width']; 252 $height = $attributes['height']; 253 $cache = $attributes['cache']; 254 if ($cache == null) { 255 // Dokuwiki needs a value 256 // If their is no value it will output it without any value 257 // in the query string. 258 $cache = "cache"; 259 } 260 $linking = $attributes['linking']; 261 switch ($type) { 262 case MediaLink::INTERNAL_MEDIA_CALL_NAME: 263 $renderer->doc .= $renderer->internalmedia($src, $title, $align, $width, $height, $cache, $linking, true); 264 break; 265 case MediaLink::EXTERNAL_MEDIA_CALL_NAME: 266 $renderer->doc .= $renderer->externalmedia($src, $title, $align, $width, $height, $cache, $linking, true); 267 break; 268 default: 269 LogUtility::msg("The dokuwiki media type ($type) is unknown"); 270 break; 271 } 272 273 return true; 274 275 case 276 "metadata": 277 278 /** 279 * Keep track of the metadata 280 * @var Doku_Renderer_metadata $renderer 281 */ 282 $attributes = $data[PluginUtility::ATTRIBUTES]; 283 self::registerImageMeta($attributes, $renderer); 284 return true; 285 286 case renderer_plugin_combo_analytics::RENDERER_FORMAT: 287 288 /** 289 * Special pattern call 290 * @var renderer_plugin_combo_analytics $renderer 291 */ 292 $attributes = $data[PluginUtility::ATTRIBUTES]; 293 self::updateStatistics($attributes, $renderer); 294 return true; 295 296 } 297 // unsupported $mode 298 return false; 299 } 300 301 /** 302 * Update the index for the move plugin 303 * and {@link Metadata::FIRST_IMAGE_META_RELATION} 304 * 305 * @param array $attributes 306 * @param Doku_Renderer_metadata $renderer 307 */ 308 static public function registerImageMeta($attributes, $renderer) 309 { 310 $type = $attributes[MediaLink::MEDIA_DOKUWIKI_TYPE]; 311 $src = $attributes['src']; 312 if ($src == null) { 313 $src = $attributes[PagePath::PROPERTY_NAME]; 314 } 315 $title = $attributes['title']; 316 $align = $attributes['align']; 317 $width = $attributes['width']; 318 $height = $attributes['height']; 319 $cache = $attributes['cache']; // Cache: https://www.dokuwiki.org/images#caching 320 $linking = $attributes['linking']; 321 322 switch ($type) { 323 case MediaLink::INTERNAL_MEDIA_CALL_NAME: 324 if (substr($src, -4) === ".svg") { 325 self::registerFirstMedia($renderer, $src); 326 } 327 $renderer->internalmedia($src, $title, $align, $width, $height, $cache, $linking); 328 break; 329 case MediaLink::EXTERNAL_MEDIA_CALL_NAME: 330 $renderer->externalmedia($src, $title, $align, $width, $height, $cache, $linking); 331 break; 332 default: 333 LogUtility::msg("The dokuwiki media type ($type) for metadata registration is unknown"); 334 break; 335 } 336 337 } 338 339 340} 341 342