121913ab3SNickeau<?php 221913ab3SNickeau 321913ab3SNickeau 4c3437056SNickeauuse ComboStrap\AnalyticsDocument; 537748cd8SNickeauuse ComboStrap\CallStack; 6c3437056SNickeauuse ComboStrap\DokuFs; 723723136Sgerardnicouse ComboStrap\DokuPath; 8*82a60d03SNickeauuse ComboStrap\ExceptionComboRuntime; 91fa8c418SNickeauuse ComboStrap\Image; 10c3437056SNickeauuse ComboStrap\InternetPath; 1123723136Sgerardnicouse ComboStrap\LogUtility; 1223723136Sgerardnicouse ComboStrap\MediaLink; 13c3437056SNickeauuse ComboStrap\Metadata; 14*82a60d03SNickeauuse ComboStrap\PageImages; 15c3437056SNickeauuse ComboStrap\PagePath; 16c3437056SNickeauuse ComboStrap\Path; 1721913ab3SNickeauuse ComboStrap\PluginUtility; 1837748cd8SNickeauuse ComboStrap\ThirdPartyPlugins; 1921913ab3SNickeau 2021913ab3SNickeau 2137748cd8SNickeaurequire_once(__DIR__ . '/../ComboStrap/PluginUtility.php'); 2221913ab3SNickeau 2321913ab3SNickeau 2421913ab3SNickeau/** 2523723136Sgerardnico * Media 2623723136Sgerardnico * 2723723136Sgerardnico * Takes over the {@link \dokuwiki\Parsing\ParserMode\Media media mode} 2823723136Sgerardnico * that is processed by {@link Doku_Handler_Parse_Media} 2923723136Sgerardnico * 3023723136Sgerardnico * 3123723136Sgerardnico * 3223723136Sgerardnico * It can be a internal / external media 3337748cd8SNickeau * 341fa8c418SNickeau * 3537748cd8SNickeau * See: 3637748cd8SNickeau * https://developers.google.com/search/docs/advanced/guidelines/google-images 3721913ab3SNickeau */ 3821913ab3SNickeauclass syntax_plugin_combo_media extends DokuWiki_Syntax_Plugin 3921913ab3SNickeau{ 4021913ab3SNickeau 4121913ab3SNickeau 4221913ab3SNickeau const TAG = "media"; 4321913ab3SNickeau 4421913ab3SNickeau /** 4521913ab3SNickeau * Used in the move plugin 4621913ab3SNickeau * !!! The two last word of the plugin class !!! 4721913ab3SNickeau */ 4821913ab3SNickeau const COMPONENT = 'combo_' . self::TAG; 4921913ab3SNickeau 5021913ab3SNickeau 5121913ab3SNickeau /** 5223723136Sgerardnico * Found at {@link \dokuwiki\Parsing\ParserMode\Media} 5321913ab3SNickeau */ 5423723136Sgerardnico const MEDIA_PATTERN = "\{\{(?:[^>\}]|(?:\}[^\}]))+\}\}"; 5521913ab3SNickeau 56531e725cSNickeau /** 57531e725cSNickeau * Enable or disable the image 58531e725cSNickeau */ 59531e725cSNickeau const CONF_IMAGE_ENABLE = "imageEnable"; 60531e725cSNickeau 6137748cd8SNickeau /** 6237748cd8SNickeau * Svg Rendering error 6337748cd8SNickeau */ 6437748cd8SNickeau const SVG_RENDERING_ERROR_CLASS = "combo-svg-rendering-error"; 6537748cd8SNickeau 66*82a60d03SNickeau public static function registerFirstMedia(Doku_Renderer_metadata $renderer, $src) 67*82a60d03SNickeau { 68*82a60d03SNickeau /** 69*82a60d03SNickeau * {@link Doku_Renderer_metadata::$firstimage} is unfortunately protected 70*82a60d03SNickeau * and {@link Doku_Renderer_metadata::internalmedia()} does not allow svg as first image 71*82a60d03SNickeau */ 72*82a60d03SNickeau if (!isset($renderer->meta[PageImages::FIRST_IMAGE_META_RELATION])) { 73*82a60d03SNickeau $renderer->meta[PageImages::FIRST_IMAGE_META_RELATION] = $src; 74*82a60d03SNickeau } 75*82a60d03SNickeau 76*82a60d03SNickeau } 77*82a60d03SNickeau 7837748cd8SNickeau 7937748cd8SNickeau /** 8037748cd8SNickeau * @param $attributes 8137748cd8SNickeau * @param renderer_plugin_combo_analytics $renderer 8237748cd8SNickeau */ 8337748cd8SNickeau public static function updateStatistics($attributes, renderer_plugin_combo_analytics $renderer) 8437748cd8SNickeau { 8537748cd8SNickeau $media = MediaLink::createFromCallStackArray($attributes); 86c3437056SNickeau $renderer->stats[AnalyticsDocument::MEDIA_COUNT]++; 87c3437056SNickeau $scheme = $media->getMedia()->getPath()->getScheme(); 8837748cd8SNickeau switch ($scheme) { 89c3437056SNickeau case DokuFs::SCHEME: 90c3437056SNickeau $renderer->stats[AnalyticsDocument::INTERNAL_MEDIA_COUNT]++; 91c3437056SNickeau if (!$media->getMedia()->exists()) { 92c3437056SNickeau $renderer->stats[AnalyticsDocument::INTERNAL_BROKEN_MEDIA_COUNT]++; 9337748cd8SNickeau } 9437748cd8SNickeau break; 95c3437056SNickeau case InternetPath::scheme: 96c3437056SNickeau $renderer->stats[AnalyticsDocument::EXTERNAL_MEDIA_COUNT]++; 9737748cd8SNickeau break; 9837748cd8SNickeau } 9937748cd8SNickeau } 10037748cd8SNickeau 10121913ab3SNickeau 10221913ab3SNickeau function getType() 10321913ab3SNickeau { 10421913ab3SNickeau return 'formatting'; 10521913ab3SNickeau } 10621913ab3SNickeau 10721913ab3SNickeau /** 10821913ab3SNickeau * How Dokuwiki will add P element 10921913ab3SNickeau * 11021913ab3SNickeau * * 'normal' - The plugin can be used inside paragraphs (inline) 11121913ab3SNickeau * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 11221913ab3SNickeau * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 11321913ab3SNickeau * 11421913ab3SNickeau * @see DokuWiki_Syntax_Plugin::getPType() 11521913ab3SNickeau */ 11621913ab3SNickeau function getPType() 11721913ab3SNickeau { 11823723136Sgerardnico /** 11923723136Sgerardnico * An image is not a block (it can be inside paragraph) 12023723136Sgerardnico */ 12121913ab3SNickeau return 'normal'; 12221913ab3SNickeau } 12321913ab3SNickeau 12421913ab3SNickeau function getAllowedTypes() 12521913ab3SNickeau { 12621913ab3SNickeau return array('substition', 'formatting', 'disabled'); 12721913ab3SNickeau } 12821913ab3SNickeau 12923723136Sgerardnico /** 13023723136Sgerardnico * It should be less than {@link \dokuwiki\Parsing\ParserMode\Media::getSort()} 13123723136Sgerardnico * (It was 320 at the time of writing this code) 13223723136Sgerardnico * @return int 13323723136Sgerardnico * 13423723136Sgerardnico */ 13521913ab3SNickeau function getSort() 13621913ab3SNickeau { 13723723136Sgerardnico return 319; 13821913ab3SNickeau } 13921913ab3SNickeau 14021913ab3SNickeau 14121913ab3SNickeau function connectTo($mode) 14221913ab3SNickeau { 143531e725cSNickeau $enable = $this->getConf(self::CONF_IMAGE_ENABLE, 1); 14421913ab3SNickeau if (!$enable) { 14521913ab3SNickeau 14621913ab3SNickeau // Inside a card, we need to take over and enable it 14721913ab3SNickeau $modes = [ 1489337a630SNickeau PluginUtility::getModeFromTag(syntax_plugin_combo_card::TAG), 14921913ab3SNickeau ]; 15021913ab3SNickeau $enable = in_array($mode, $modes); 15121913ab3SNickeau } 15221913ab3SNickeau 15321913ab3SNickeau if ($enable) { 15437748cd8SNickeau if ($mode !== PluginUtility::getModeFromPluginName(ThirdPartyPlugins::IMAGE_MAPPING_NAME)) { 1559337a630SNickeau $this->Lexer->addSpecialPattern(self::MEDIA_PATTERN, $mode, PluginUtility::getModeFromTag($this->getPluginComponent())); 15621913ab3SNickeau } 15721913ab3SNickeau } 15837748cd8SNickeau } 15921913ab3SNickeau 16021913ab3SNickeau 1611fa8c418SNickeau function handle($match, $state, $pos, Doku_Handler $handler): array 16221913ab3SNickeau { 16321913ab3SNickeau 16421913ab3SNickeau switch ($state) { 16521913ab3SNickeau 16621913ab3SNickeau 16721913ab3SNickeau // As this is a container, this cannot happens but yeah, now, you know 16821913ab3SNickeau case DOKU_LEXER_SPECIAL : 16937748cd8SNickeau 17023723136Sgerardnico $media = MediaLink::createFromRenderMatch($match); 17121913ab3SNickeau $attributes = $media->toCallStackArray(); 17237748cd8SNickeau 17337748cd8SNickeau $callStack = CallStack::createFromHandler($handler); 17437748cd8SNickeau 17537748cd8SNickeau /** 17637748cd8SNickeau * Parent 17737748cd8SNickeau */ 17837748cd8SNickeau $parent = $callStack->moveToParent(); 17921913ab3SNickeau $parentTag = ""; 18021913ab3SNickeau if (!empty($parent)) { 18137748cd8SNickeau $parentTag = $parent->getTagName(); 18221913ab3SNickeau if ($parentTag == syntax_plugin_combo_link::TAG) { 18321913ab3SNickeau /** 1841fa8c418SNickeau * TODO: should be on the exit tag of the link 18521913ab3SNickeau * The image is in a link, we don't want another link 18621913ab3SNickeau * to the image 18721913ab3SNickeau */ 188a6bf47aaSNickeau $attributes[MediaLink::LINKING_KEY] = MediaLink::LINKING_NOLINK_VALUE; 18921913ab3SNickeau } 19021913ab3SNickeau } 19137748cd8SNickeau 19221913ab3SNickeau return array( 19321913ab3SNickeau PluginUtility::STATE => $state, 19421913ab3SNickeau PluginUtility::ATTRIBUTES => $attributes, 19537748cd8SNickeau PluginUtility::CONTEXT => $parentTag 19621913ab3SNickeau ); 19721913ab3SNickeau 19821913ab3SNickeau 19921913ab3SNickeau } 20021913ab3SNickeau return array(); 20121913ab3SNickeau 20221913ab3SNickeau } 20321913ab3SNickeau 20421913ab3SNickeau /** 20521913ab3SNickeau * Render the output 20621913ab3SNickeau * @param string $format 20721913ab3SNickeau * @param Doku_Renderer $renderer 20821913ab3SNickeau * @param array $data - what the function handle() return'ed 20921913ab3SNickeau * @return boolean - rendered correctly? (however, returned value is not used at the moment) 21021913ab3SNickeau * @see DokuWiki_Syntax_Plugin::render() 21121913ab3SNickeau * 21221913ab3SNickeau * 21321913ab3SNickeau */ 21421913ab3SNickeau function render($format, Doku_Renderer $renderer, $data) 21521913ab3SNickeau { 21621913ab3SNickeau 21721913ab3SNickeau switch ($format) { 21821913ab3SNickeau 21921913ab3SNickeau case 'xhtml': 22021913ab3SNickeau 22121913ab3SNickeau /** @var Doku_Renderer_xhtml $renderer */ 22221913ab3SNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 2231fa8c418SNickeau $mediaLink = MediaLink::createFromCallStackArray($attributes, $renderer->date_at); 224c3437056SNickeau $media = $mediaLink->getMedia(); 225c3437056SNickeau if ($media->getPath()->getScheme() == DokuFs::SCHEME) { 226c3437056SNickeau if ($media->getPath()->getMime()->isImage() || $media->getPath()->getExtension() === "svg") { 22737748cd8SNickeau try { 2281fa8c418SNickeau $renderer->doc .= $mediaLink->renderMediaTagWithLink(); 22937748cd8SNickeau } catch (RuntimeException $e) { 230*82a60d03SNickeau if (PluginUtility::isDevOrTest()) { 231*82a60d03SNickeau throw new ExceptionComboRuntime("Media Rendering Error. {$e->getMessage()}", MediaLink::CANONICAL, 0, $e); 232*82a60d03SNickeau } else { 23337748cd8SNickeau $errorClass = self::SVG_RENDERING_ERROR_CLASS; 23437748cd8SNickeau $message = "Media ({$media->getPath()}). Error while rendering: {$e->getMessage()}"; 2351fa8c418SNickeau $renderer->doc .= "<span class=\"text-alert $errorClass\">" . hsc(trim($message)) . "</span>"; 2361fa8c418SNickeau LogUtility::msg($message, LogUtility::LVL_MSG_ERROR, MediaLink::CANONICAL); 23737748cd8SNickeau } 238*82a60d03SNickeau } 23923723136Sgerardnico return true; 24023723136Sgerardnico } 24123723136Sgerardnico } 24221913ab3SNickeau 24321913ab3SNickeau /** 24423723136Sgerardnico * This is not an local internal media image (a video or an url image) 24521913ab3SNickeau * Dokuwiki takes over 24621913ab3SNickeau */ 247531e725cSNickeau $type = $attributes[MediaLink::MEDIA_DOKUWIKI_TYPE]; 24821913ab3SNickeau $src = $attributes['src']; 24921913ab3SNickeau $title = $attributes['title']; 25021913ab3SNickeau $align = $attributes['align']; 25121913ab3SNickeau $width = $attributes['width']; 25221913ab3SNickeau $height = $attributes['height']; 25321913ab3SNickeau $cache = $attributes['cache']; 254a6bf47aaSNickeau if ($cache == null) { 255a6bf47aaSNickeau // Dokuwiki needs a value 256a6bf47aaSNickeau // If their is no value it will output it without any value 257a6bf47aaSNickeau // in the query string. 258a6bf47aaSNickeau $cache = "cache"; 259a6bf47aaSNickeau } 26021913ab3SNickeau $linking = $attributes['linking']; 26123723136Sgerardnico switch ($type) { 262531e725cSNickeau case MediaLink::INTERNAL_MEDIA_CALL_NAME: 26321913ab3SNickeau $renderer->doc .= $renderer->internalmedia($src, $title, $align, $width, $height, $cache, $linking, true); 26423723136Sgerardnico break; 265531e725cSNickeau case MediaLink::EXTERNAL_MEDIA_CALL_NAME: 26623723136Sgerardnico $renderer->doc .= $renderer->externalmedia($src, $title, $align, $width, $height, $cache, $linking, true); 26723723136Sgerardnico break; 26823723136Sgerardnico default: 26923723136Sgerardnico LogUtility::msg("The dokuwiki media type ($type) is unknown"); 27023723136Sgerardnico break; 27121913ab3SNickeau } 27221913ab3SNickeau 27323723136Sgerardnico return true; 27421913ab3SNickeau 2759337a630SNickeau case 2769337a630SNickeau "metadata": 27721913ab3SNickeau 27821913ab3SNickeau /** 27921913ab3SNickeau * Keep track of the metadata 28021913ab3SNickeau * @var Doku_Renderer_metadata $renderer 28121913ab3SNickeau */ 28237748cd8SNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 28321913ab3SNickeau self::registerImageMeta($attributes, $renderer); 28423723136Sgerardnico return true; 28521913ab3SNickeau 286e8b2ff59SNickeau case renderer_plugin_combo_analytics::RENDERER_FORMAT: 287e8b2ff59SNickeau 288e8b2ff59SNickeau /** 289e8b2ff59SNickeau * Special pattern call 290e8b2ff59SNickeau * @var renderer_plugin_combo_analytics $renderer 291e8b2ff59SNickeau */ 292e8b2ff59SNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 29337748cd8SNickeau self::updateStatistics($attributes, $renderer); 294e8b2ff59SNickeau return true; 295e8b2ff59SNickeau 29621913ab3SNickeau } 29721913ab3SNickeau // unsupported $mode 29821913ab3SNickeau return false; 29921913ab3SNickeau } 30021913ab3SNickeau 30121913ab3SNickeau /** 30237748cd8SNickeau * Update the index for the move plugin 303c3437056SNickeau * and {@link Metadata::FIRST_IMAGE_META_RELATION} 30437748cd8SNickeau * 30521913ab3SNickeau * @param array $attributes 30621913ab3SNickeau * @param Doku_Renderer_metadata $renderer 30721913ab3SNickeau */ 30821913ab3SNickeau static public function registerImageMeta($attributes, $renderer) 30921913ab3SNickeau { 310531e725cSNickeau $type = $attributes[MediaLink::MEDIA_DOKUWIKI_TYPE]; 31121913ab3SNickeau $src = $attributes['src']; 312531e725cSNickeau if ($src == null) { 313c3437056SNickeau $src = $attributes[PagePath::PROPERTY_NAME]; 314531e725cSNickeau } 31521913ab3SNickeau $title = $attributes['title']; 31621913ab3SNickeau $align = $attributes['align']; 31721913ab3SNickeau $width = $attributes['width']; 31821913ab3SNickeau $height = $attributes['height']; 31921913ab3SNickeau $cache = $attributes['cache']; // Cache: https://www.dokuwiki.org/images#caching 32021913ab3SNickeau $linking = $attributes['linking']; 32123723136Sgerardnico 32223723136Sgerardnico switch ($type) { 323531e725cSNickeau case MediaLink::INTERNAL_MEDIA_CALL_NAME: 324*82a60d03SNickeau if (substr($src, -4) === ".svg") { 325*82a60d03SNickeau self::registerFirstMedia($renderer, $src); 326*82a60d03SNickeau } 32721913ab3SNickeau $renderer->internalmedia($src, $title, $align, $width, $height, $cache, $linking); 32823723136Sgerardnico break; 329531e725cSNickeau case MediaLink::EXTERNAL_MEDIA_CALL_NAME: 33023723136Sgerardnico $renderer->externalmedia($src, $title, $align, $width, $height, $cache, $linking); 33123723136Sgerardnico break; 33223723136Sgerardnico default: 33323723136Sgerardnico LogUtility::msg("The dokuwiki media type ($type) for metadata registration is unknown"); 33423723136Sgerardnico break; 33523723136Sgerardnico } 33623723136Sgerardnico 33721913ab3SNickeau } 33821913ab3SNickeau 33921913ab3SNickeau 34021913ab3SNickeau} 34121913ab3SNickeau 342