xref: /plugin/combo/syntax/media.php (revision 82a60d039cd81033dc8147c27f0a50716b7a5301)
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