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