121913ab3SNickeau<?php 221913ab3SNickeau 321913ab3SNickeau 4c3437056SNickeauuse ComboStrap\AnalyticsDocument; 537748cd8SNickeauuse ComboStrap\CallStack; 6c3437056SNickeauuse ComboStrap\DokuFs; 782a60d03SNickeauuse ComboStrap\ExceptionComboRuntime; 8c3437056SNickeauuse ComboStrap\InternetPath; 923723136Sgerardnicouse ComboStrap\LogUtility; 1023723136Sgerardnicouse ComboStrap\MediaLink; 11c3437056SNickeauuse ComboStrap\Metadata; 1282a60d03SNickeauuse ComboStrap\PageImages; 13c3437056SNickeauuse ComboStrap\PagePath; 1421913ab3SNickeauuse ComboStrap\PluginUtility; 15*4cadd4f8SNickeauuse ComboStrap\SvgDocument; 16*4cadd4f8SNickeauuse ComboStrap\TagAttributes; 1737748cd8SNickeauuse ComboStrap\ThirdPartyPlugins; 1821913ab3SNickeau 1921913ab3SNickeau 2037748cd8SNickeaurequire_once(__DIR__ . '/../ComboStrap/PluginUtility.php'); 2121913ab3SNickeau 2221913ab3SNickeau 2321913ab3SNickeau/** 2423723136Sgerardnico * Media 2523723136Sgerardnico * 2623723136Sgerardnico * Takes over the {@link \dokuwiki\Parsing\ParserMode\Media media mode} 2723723136Sgerardnico * that is processed by {@link Doku_Handler_Parse_Media} 2823723136Sgerardnico * 2923723136Sgerardnico * 3023723136Sgerardnico * 3123723136Sgerardnico * It can be a internal / external media 3237748cd8SNickeau * 331fa8c418SNickeau * 3437748cd8SNickeau * See: 3537748cd8SNickeau * https://developers.google.com/search/docs/advanced/guidelines/google-images 3621913ab3SNickeau */ 3721913ab3SNickeauclass syntax_plugin_combo_media extends DokuWiki_Syntax_Plugin 3821913ab3SNickeau{ 3921913ab3SNickeau 4021913ab3SNickeau 4121913ab3SNickeau const TAG = "media"; 4221913ab3SNickeau 4321913ab3SNickeau /** 4421913ab3SNickeau * Used in the move plugin 4521913ab3SNickeau * !!! The two last word of the plugin class !!! 4621913ab3SNickeau */ 4721913ab3SNickeau const COMPONENT = 'combo_' . self::TAG; 4821913ab3SNickeau 4921913ab3SNickeau 5021913ab3SNickeau /** 5123723136Sgerardnico * Found at {@link \dokuwiki\Parsing\ParserMode\Media} 5221913ab3SNickeau */ 5323723136Sgerardnico const MEDIA_PATTERN = "\{\{(?:[^>\}]|(?:\}[^\}]))+\}\}"; 5421913ab3SNickeau 55531e725cSNickeau /** 56531e725cSNickeau * Enable or disable the image 57531e725cSNickeau */ 58531e725cSNickeau const CONF_IMAGE_ENABLE = "imageEnable"; 59531e725cSNickeau 6037748cd8SNickeau /** 6137748cd8SNickeau * Svg Rendering error 6237748cd8SNickeau */ 6337748cd8SNickeau const SVG_RENDERING_ERROR_CLASS = "combo-svg-rendering-error"; 6437748cd8SNickeau 65*4cadd4f8SNickeau /** 66*4cadd4f8SNickeau * An attribute to set the class of the link if any 67*4cadd4f8SNickeau */ 68*4cadd4f8SNickeau const LINK_CLASS_ATTRIBUTE = "link-class"; 69*4cadd4f8SNickeau 7082a60d03SNickeau public static function registerFirstMedia(Doku_Renderer_metadata $renderer, $src) 7182a60d03SNickeau { 7282a60d03SNickeau /** 7382a60d03SNickeau * {@link Doku_Renderer_metadata::$firstimage} is unfortunately protected 7482a60d03SNickeau * and {@link Doku_Renderer_metadata::internalmedia()} does not allow svg as first image 7582a60d03SNickeau */ 7682a60d03SNickeau if (!isset($renderer->meta[PageImages::FIRST_IMAGE_META_RELATION])) { 7782a60d03SNickeau $renderer->meta[PageImages::FIRST_IMAGE_META_RELATION] = $src; 7882a60d03SNickeau } 7982a60d03SNickeau 8082a60d03SNickeau } 8182a60d03SNickeau 8237748cd8SNickeau 8337748cd8SNickeau /** 8437748cd8SNickeau * @param $attributes 8537748cd8SNickeau * @param renderer_plugin_combo_analytics $renderer 8637748cd8SNickeau */ 8737748cd8SNickeau public static function updateStatistics($attributes, renderer_plugin_combo_analytics $renderer) 8837748cd8SNickeau { 8937748cd8SNickeau $media = MediaLink::createFromCallStackArray($attributes); 90c3437056SNickeau $renderer->stats[AnalyticsDocument::MEDIA_COUNT]++; 91c3437056SNickeau $scheme = $media->getMedia()->getPath()->getScheme(); 9237748cd8SNickeau switch ($scheme) { 93c3437056SNickeau case DokuFs::SCHEME: 94c3437056SNickeau $renderer->stats[AnalyticsDocument::INTERNAL_MEDIA_COUNT]++; 95c3437056SNickeau if (!$media->getMedia()->exists()) { 96c3437056SNickeau $renderer->stats[AnalyticsDocument::INTERNAL_BROKEN_MEDIA_COUNT]++; 9737748cd8SNickeau } 9837748cd8SNickeau break; 99c3437056SNickeau case InternetPath::scheme: 100c3437056SNickeau $renderer->stats[AnalyticsDocument::EXTERNAL_MEDIA_COUNT]++; 10137748cd8SNickeau break; 10237748cd8SNickeau } 10337748cd8SNickeau } 10437748cd8SNickeau 10521913ab3SNickeau 106*4cadd4f8SNickeau function getType(): string 10721913ab3SNickeau { 10821913ab3SNickeau return 'formatting'; 10921913ab3SNickeau } 11021913ab3SNickeau 11121913ab3SNickeau /** 11221913ab3SNickeau * How Dokuwiki will add P element 11321913ab3SNickeau * 11421913ab3SNickeau * * 'normal' - The plugin can be used inside paragraphs (inline) 11521913ab3SNickeau * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 11621913ab3SNickeau * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 11721913ab3SNickeau * 11821913ab3SNickeau * @see DokuWiki_Syntax_Plugin::getPType() 11921913ab3SNickeau */ 120*4cadd4f8SNickeau function getPType(): string 12121913ab3SNickeau { 12223723136Sgerardnico /** 12323723136Sgerardnico * An image is not a block (it can be inside paragraph) 12423723136Sgerardnico */ 12521913ab3SNickeau return 'normal'; 12621913ab3SNickeau } 12721913ab3SNickeau 128*4cadd4f8SNickeau function getAllowedTypes(): array 12921913ab3SNickeau { 13021913ab3SNickeau return array('substition', 'formatting', 'disabled'); 13121913ab3SNickeau } 13221913ab3SNickeau 13323723136Sgerardnico /** 13423723136Sgerardnico * It should be less than {@link \dokuwiki\Parsing\ParserMode\Media::getSort()} 13523723136Sgerardnico * (It was 320 at the time of writing this code) 13623723136Sgerardnico * @return int 13723723136Sgerardnico * 13823723136Sgerardnico */ 139*4cadd4f8SNickeau function getSort(): int 14021913ab3SNickeau { 14123723136Sgerardnico return 319; 14221913ab3SNickeau } 14321913ab3SNickeau 14421913ab3SNickeau 14521913ab3SNickeau function connectTo($mode) 14621913ab3SNickeau { 147531e725cSNickeau $enable = $this->getConf(self::CONF_IMAGE_ENABLE, 1); 14821913ab3SNickeau if (!$enable) { 14921913ab3SNickeau 15021913ab3SNickeau // Inside a card, we need to take over and enable it 15121913ab3SNickeau $modes = [ 1529337a630SNickeau PluginUtility::getModeFromTag(syntax_plugin_combo_card::TAG), 15321913ab3SNickeau ]; 15421913ab3SNickeau $enable = in_array($mode, $modes); 15521913ab3SNickeau } 15621913ab3SNickeau 15721913ab3SNickeau if ($enable) { 15837748cd8SNickeau if ($mode !== PluginUtility::getModeFromPluginName(ThirdPartyPlugins::IMAGE_MAPPING_NAME)) { 1599337a630SNickeau $this->Lexer->addSpecialPattern(self::MEDIA_PATTERN, $mode, PluginUtility::getModeFromTag($this->getPluginComponent())); 16021913ab3SNickeau } 16121913ab3SNickeau } 16237748cd8SNickeau } 16321913ab3SNickeau 16421913ab3SNickeau 1651fa8c418SNickeau function handle($match, $state, $pos, Doku_Handler $handler): array 16621913ab3SNickeau { 16721913ab3SNickeau 16821913ab3SNickeau switch ($state) { 16921913ab3SNickeau 17021913ab3SNickeau 17121913ab3SNickeau // As this is a container, this cannot happens but yeah, now, you know 17221913ab3SNickeau case DOKU_LEXER_SPECIAL : 17337748cd8SNickeau 174*4cadd4f8SNickeau /** 175*4cadd4f8SNickeau * Note: The type of image for a svg (icon/illustration) is dedicated 176*4cadd4f8SNickeau * by its structure or is expressly set on type 177*4cadd4f8SNickeau */ 17823723136Sgerardnico $media = MediaLink::createFromRenderMatch($match); 17921913ab3SNickeau $attributes = $media->toCallStackArray(); 18037748cd8SNickeau 18137748cd8SNickeau $callStack = CallStack::createFromHandler($handler); 18237748cd8SNickeau 18337748cd8SNickeau /** 18437748cd8SNickeau * Parent 18537748cd8SNickeau */ 18637748cd8SNickeau $parent = $callStack->moveToParent(); 18721913ab3SNickeau $parentTag = ""; 18821913ab3SNickeau if (!empty($parent)) { 18937748cd8SNickeau $parentTag = $parent->getTagName(); 190*4cadd4f8SNickeau if (in_array($parentTag, 191*4cadd4f8SNickeau [syntax_plugin_combo_link::TAG, syntax_plugin_combo_brand::TAG])) { 19221913ab3SNickeau /** 193*4cadd4f8SNickeau * TODO: should be on the exit tag of the link / brand 194*4cadd4f8SNickeau * - The image is in a link, we don't want another link to the image 195*4cadd4f8SNickeau * - In a brand, there is also already a link to the home page, no link to the media 19621913ab3SNickeau */ 197a6bf47aaSNickeau $attributes[MediaLink::LINKING_KEY] = MediaLink::LINKING_NOLINK_VALUE; 19821913ab3SNickeau } 19921913ab3SNickeau } 20037748cd8SNickeau 20121913ab3SNickeau return array( 20221913ab3SNickeau PluginUtility::STATE => $state, 20321913ab3SNickeau PluginUtility::ATTRIBUTES => $attributes, 20437748cd8SNickeau PluginUtility::CONTEXT => $parentTag 20521913ab3SNickeau ); 20621913ab3SNickeau 20721913ab3SNickeau 20821913ab3SNickeau } 20921913ab3SNickeau return array(); 21021913ab3SNickeau 21121913ab3SNickeau } 21221913ab3SNickeau 21321913ab3SNickeau /** 21421913ab3SNickeau * Render the output 21521913ab3SNickeau * @param string $format 21621913ab3SNickeau * @param Doku_Renderer $renderer 21721913ab3SNickeau * @param array $data - what the function handle() return'ed 21821913ab3SNickeau * @return boolean - rendered correctly? (however, returned value is not used at the moment) 21921913ab3SNickeau * @see DokuWiki_Syntax_Plugin::render() 22021913ab3SNickeau * 22121913ab3SNickeau * 22221913ab3SNickeau */ 223*4cadd4f8SNickeau function render($format, Doku_Renderer $renderer, $data): bool 22421913ab3SNickeau { 22521913ab3SNickeau 22621913ab3SNickeau switch ($format) { 22721913ab3SNickeau 22821913ab3SNickeau case 'xhtml': 22921913ab3SNickeau 23021913ab3SNickeau /** @var Doku_Renderer_xhtml $renderer */ 23121913ab3SNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 2321fa8c418SNickeau $mediaLink = MediaLink::createFromCallStackArray($attributes, $renderer->date_at); 233c3437056SNickeau $media = $mediaLink->getMedia(); 234c3437056SNickeau if ($media->getPath()->getScheme() == DokuFs::SCHEME) { 235c3437056SNickeau if ($media->getPath()->getMime()->isImage() || $media->getPath()->getExtension() === "svg") { 23637748cd8SNickeau try { 2371fa8c418SNickeau $renderer->doc .= $mediaLink->renderMediaTagWithLink(); 23837748cd8SNickeau } catch (RuntimeException $e) { 23982a60d03SNickeau if (PluginUtility::isDevOrTest()) { 24082a60d03SNickeau throw new ExceptionComboRuntime("Media Rendering Error. {$e->getMessage()}", MediaLink::CANONICAL, 0, $e); 24182a60d03SNickeau } else { 24237748cd8SNickeau $errorClass = self::SVG_RENDERING_ERROR_CLASS; 24337748cd8SNickeau $message = "Media ({$media->getPath()}). Error while rendering: {$e->getMessage()}"; 2441fa8c418SNickeau $renderer->doc .= "<span class=\"text-alert $errorClass\">" . hsc(trim($message)) . "</span>"; 2451fa8c418SNickeau LogUtility::msg($message, LogUtility::LVL_MSG_ERROR, MediaLink::CANONICAL); 24637748cd8SNickeau } 24782a60d03SNickeau } 24823723136Sgerardnico return true; 24923723136Sgerardnico } 25023723136Sgerardnico } 25121913ab3SNickeau 25221913ab3SNickeau /** 25323723136Sgerardnico * This is not an local internal media image (a video or an url image) 25421913ab3SNickeau * Dokuwiki takes over 25521913ab3SNickeau */ 256531e725cSNickeau $type = $attributes[MediaLink::MEDIA_DOKUWIKI_TYPE]; 25721913ab3SNickeau $src = $attributes['src']; 25821913ab3SNickeau $title = $attributes['title']; 25921913ab3SNickeau $align = $attributes['align']; 26021913ab3SNickeau $width = $attributes['width']; 26121913ab3SNickeau $height = $attributes['height']; 26221913ab3SNickeau $cache = $attributes['cache']; 263a6bf47aaSNickeau if ($cache == null) { 264a6bf47aaSNickeau // Dokuwiki needs a value 265a6bf47aaSNickeau // If their is no value it will output it without any value 266a6bf47aaSNickeau // in the query string. 267a6bf47aaSNickeau $cache = "cache"; 268a6bf47aaSNickeau } 26921913ab3SNickeau $linking = $attributes['linking']; 27023723136Sgerardnico switch ($type) { 271531e725cSNickeau case MediaLink::INTERNAL_MEDIA_CALL_NAME: 27221913ab3SNickeau $renderer->doc .= $renderer->internalmedia($src, $title, $align, $width, $height, $cache, $linking, true); 27323723136Sgerardnico break; 274531e725cSNickeau case MediaLink::EXTERNAL_MEDIA_CALL_NAME: 27523723136Sgerardnico $renderer->doc .= $renderer->externalmedia($src, $title, $align, $width, $height, $cache, $linking, true); 27623723136Sgerardnico break; 27723723136Sgerardnico default: 27823723136Sgerardnico LogUtility::msg("The dokuwiki media type ($type) is unknown"); 27923723136Sgerardnico break; 28021913ab3SNickeau } 28121913ab3SNickeau 28223723136Sgerardnico return true; 28321913ab3SNickeau 2849337a630SNickeau case 2859337a630SNickeau "metadata": 28621913ab3SNickeau 28721913ab3SNickeau /** 28821913ab3SNickeau * Keep track of the metadata 28921913ab3SNickeau * @var Doku_Renderer_metadata $renderer 29021913ab3SNickeau */ 29137748cd8SNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 29221913ab3SNickeau self::registerImageMeta($attributes, $renderer); 29323723136Sgerardnico return true; 29421913ab3SNickeau 295e8b2ff59SNickeau case renderer_plugin_combo_analytics::RENDERER_FORMAT: 296e8b2ff59SNickeau 297e8b2ff59SNickeau /** 298e8b2ff59SNickeau * Special pattern call 299e8b2ff59SNickeau * @var renderer_plugin_combo_analytics $renderer 300e8b2ff59SNickeau */ 301e8b2ff59SNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 30237748cd8SNickeau self::updateStatistics($attributes, $renderer); 303e8b2ff59SNickeau return true; 304e8b2ff59SNickeau 30521913ab3SNickeau } 30621913ab3SNickeau // unsupported $mode 30721913ab3SNickeau return false; 30821913ab3SNickeau } 30921913ab3SNickeau 31021913ab3SNickeau /** 31137748cd8SNickeau * Update the index for the move plugin 312c3437056SNickeau * and {@link Metadata::FIRST_IMAGE_META_RELATION} 31337748cd8SNickeau * 31421913ab3SNickeau * @param array $attributes 31521913ab3SNickeau * @param Doku_Renderer_metadata $renderer 31621913ab3SNickeau */ 31721913ab3SNickeau static public function registerImageMeta($attributes, $renderer) 31821913ab3SNickeau { 319531e725cSNickeau $type = $attributes[MediaLink::MEDIA_DOKUWIKI_TYPE]; 32021913ab3SNickeau $src = $attributes['src']; 321531e725cSNickeau if ($src == null) { 322c3437056SNickeau $src = $attributes[PagePath::PROPERTY_NAME]; 323531e725cSNickeau } 32421913ab3SNickeau $title = $attributes['title']; 32521913ab3SNickeau $align = $attributes['align']; 32621913ab3SNickeau $width = $attributes['width']; 32721913ab3SNickeau $height = $attributes['height']; 32821913ab3SNickeau $cache = $attributes['cache']; // Cache: https://www.dokuwiki.org/images#caching 32921913ab3SNickeau $linking = $attributes['linking']; 33023723136Sgerardnico 33123723136Sgerardnico switch ($type) { 332531e725cSNickeau case MediaLink::INTERNAL_MEDIA_CALL_NAME: 33382a60d03SNickeau if (substr($src, -4) === ".svg") { 33482a60d03SNickeau self::registerFirstMedia($renderer, $src); 33582a60d03SNickeau } 33621913ab3SNickeau $renderer->internalmedia($src, $title, $align, $width, $height, $cache, $linking); 33723723136Sgerardnico break; 338531e725cSNickeau case MediaLink::EXTERNAL_MEDIA_CALL_NAME: 33923723136Sgerardnico $renderer->externalmedia($src, $title, $align, $width, $height, $cache, $linking); 34023723136Sgerardnico break; 34123723136Sgerardnico default: 34223723136Sgerardnico LogUtility::msg("The dokuwiki media type ($type) for metadata registration is unknown"); 34323723136Sgerardnico break; 34423723136Sgerardnico } 34523723136Sgerardnico 34621913ab3SNickeau } 34721913ab3SNickeau 34821913ab3SNickeau 34921913ab3SNickeau} 35021913ab3SNickeau 351