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