xref: /plugin/combo/syntax/media.php (revision 37748cd8654635afbeca80942126742f0f4cc346)
121913ab3SNickeau<?php
221913ab3SNickeau
321913ab3SNickeau
4e8b2ff59SNickeauuse ComboStrap\Analytics;
5*37748cd8SNickeauuse ComboStrap\CallStack;
623723136Sgerardnicouse ComboStrap\DokuPath;
723723136Sgerardnicouse ComboStrap\LogUtility;
823723136Sgerardnicouse ComboStrap\MediaLink;
921913ab3SNickeauuse ComboStrap\PluginUtility;
10*37748cd8SNickeauuse ComboStrap\ThirdPartyPlugins;
1121913ab3SNickeau
1221913ab3SNickeau
13*37748cd8SNickeaurequire_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
1421913ab3SNickeau
1521913ab3SNickeau
1621913ab3SNickeau/**
1723723136Sgerardnico * Media
1823723136Sgerardnico *
1923723136Sgerardnico * Takes over the {@link \dokuwiki\Parsing\ParserMode\Media media mode}
2023723136Sgerardnico * that is processed by {@link Doku_Handler_Parse_Media}
2123723136Sgerardnico *
2223723136Sgerardnico *
2323723136Sgerardnico *
2423723136Sgerardnico * It can be a internal / external media
25*37748cd8SNickeau *
26*37748cd8SNickeau * See:
27*37748cd8SNickeau * https://developers.google.com/search/docs/advanced/guidelines/google-images
2821913ab3SNickeau */
2921913ab3SNickeauclass syntax_plugin_combo_media extends DokuWiki_Syntax_Plugin
3021913ab3SNickeau{
3121913ab3SNickeau
3221913ab3SNickeau
3321913ab3SNickeau    const TAG = "media";
3421913ab3SNickeau
3521913ab3SNickeau    /**
3621913ab3SNickeau     * Used in the move plugin
3721913ab3SNickeau     * !!! The two last word of the plugin class !!!
3821913ab3SNickeau     */
3921913ab3SNickeau    const COMPONENT = 'combo_' . self::TAG;
4021913ab3SNickeau
4121913ab3SNickeau
4221913ab3SNickeau    /**
4323723136Sgerardnico     * Found at {@link \dokuwiki\Parsing\ParserMode\Media}
4421913ab3SNickeau     */
4523723136Sgerardnico    const MEDIA_PATTERN = "\{\{(?:[^>\}]|(?:\}[^\}]))+\}\}";
4621913ab3SNickeau
47531e725cSNickeau    /**
48531e725cSNickeau     * Enable or disable the image
49531e725cSNickeau     */
50531e725cSNickeau    const CONF_IMAGE_ENABLE = "imageEnable";
51531e725cSNickeau
52*37748cd8SNickeau    /**
53*37748cd8SNickeau     * Svg Rendering error
54*37748cd8SNickeau     */
55*37748cd8SNickeau    const SVG_RENDERING_ERROR_CLASS = "combo-svg-rendering-error";
56*37748cd8SNickeau
57*37748cd8SNickeau
58*37748cd8SNickeau    /**
59*37748cd8SNickeau     * @param $attributes
60*37748cd8SNickeau     * @param renderer_plugin_combo_analytics $renderer
61*37748cd8SNickeau     */
62*37748cd8SNickeau    public static function updateStatistics($attributes, renderer_plugin_combo_analytics $renderer)
63*37748cd8SNickeau    {
64*37748cd8SNickeau        $media = MediaLink::createFromCallStackArray($attributes);
65*37748cd8SNickeau        $renderer->stats[Analytics::MEDIAS_COUNT]++;
66*37748cd8SNickeau        $scheme = $media->getScheme();
67*37748cd8SNickeau        switch ($scheme) {
68*37748cd8SNickeau            case DokuPath::LOCAL_SCHEME:
69*37748cd8SNickeau                $renderer->stats[Analytics::INTERNAL_MEDIAS_COUNT]++;
70*37748cd8SNickeau                if (!$media->exists()) {
71*37748cd8SNickeau                    $renderer->stats[Analytics::INTERNAL_BROKEN_MEDIAS_COUNT]++;
72*37748cd8SNickeau                }
73*37748cd8SNickeau                break;
74*37748cd8SNickeau            case DokuPath::INTERNET_SCHEME:
75*37748cd8SNickeau                $renderer->stats[Analytics::EXTERNAL_MEDIAS_COUNT]++;
76*37748cd8SNickeau                break;
77*37748cd8SNickeau        }
78*37748cd8SNickeau    }
79*37748cd8SNickeau
8021913ab3SNickeau
8121913ab3SNickeau    function getType()
8221913ab3SNickeau    {
8321913ab3SNickeau        return 'formatting';
8421913ab3SNickeau    }
8521913ab3SNickeau
8621913ab3SNickeau    /**
8721913ab3SNickeau     * How Dokuwiki will add P element
8821913ab3SNickeau     *
8921913ab3SNickeau     *  * 'normal' - The plugin can be used inside paragraphs (inline)
9021913ab3SNickeau     *  * 'block'  - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs
9121913ab3SNickeau     *  * 'stack'  - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs
9221913ab3SNickeau     *
9321913ab3SNickeau     * @see DokuWiki_Syntax_Plugin::getPType()
9421913ab3SNickeau     */
9521913ab3SNickeau    function getPType()
9621913ab3SNickeau    {
9723723136Sgerardnico        /**
9823723136Sgerardnico         * An image is not a block (it can be inside paragraph)
9923723136Sgerardnico         */
10021913ab3SNickeau        return 'normal';
10121913ab3SNickeau    }
10221913ab3SNickeau
10321913ab3SNickeau    function getAllowedTypes()
10421913ab3SNickeau    {
10521913ab3SNickeau        return array('substition', 'formatting', 'disabled');
10621913ab3SNickeau    }
10721913ab3SNickeau
10823723136Sgerardnico    /**
10923723136Sgerardnico     * It should be less than {@link \dokuwiki\Parsing\ParserMode\Media::getSort()}
11023723136Sgerardnico     * (It was 320 at the time of writing this code)
11123723136Sgerardnico     * @return int
11223723136Sgerardnico     *
11323723136Sgerardnico     */
11421913ab3SNickeau    function getSort()
11521913ab3SNickeau    {
11623723136Sgerardnico        return 319;
11721913ab3SNickeau    }
11821913ab3SNickeau
11921913ab3SNickeau
12021913ab3SNickeau    function connectTo($mode)
12121913ab3SNickeau    {
122531e725cSNickeau        $enable = $this->getConf(self::CONF_IMAGE_ENABLE, 1);
12321913ab3SNickeau        if (!$enable) {
12421913ab3SNickeau
12521913ab3SNickeau            // Inside a card, we need to take over and enable it
12621913ab3SNickeau            $modes = [
1279337a630SNickeau                PluginUtility::getModeFromTag(syntax_plugin_combo_card::TAG),
12821913ab3SNickeau            ];
12921913ab3SNickeau            $enable = in_array($mode, $modes);
13021913ab3SNickeau        }
13121913ab3SNickeau
13221913ab3SNickeau        if ($enable) {
133*37748cd8SNickeau            if ($mode !== PluginUtility::getModeFromPluginName(ThirdPartyPlugins::IMAGE_MAPPING_NAME)) {
1349337a630SNickeau                $this->Lexer->addSpecialPattern(self::MEDIA_PATTERN, $mode, PluginUtility::getModeFromTag($this->getPluginComponent()));
13521913ab3SNickeau            }
13621913ab3SNickeau        }
137*37748cd8SNickeau    }
13821913ab3SNickeau
13921913ab3SNickeau
14021913ab3SNickeau    function handle($match, $state, $pos, Doku_Handler $handler)
14121913ab3SNickeau    {
14221913ab3SNickeau
14321913ab3SNickeau        switch ($state) {
14421913ab3SNickeau
14521913ab3SNickeau
14621913ab3SNickeau            // As this is a container, this cannot happens but yeah, now, you know
14721913ab3SNickeau            case DOKU_LEXER_SPECIAL :
148*37748cd8SNickeau
14923723136Sgerardnico                $media = MediaLink::createFromRenderMatch($match);
15021913ab3SNickeau                $attributes = $media->toCallStackArray();
151*37748cd8SNickeau
152*37748cd8SNickeau                $callStack = CallStack::createFromHandler($handler);
153*37748cd8SNickeau
154*37748cd8SNickeau                /**
155*37748cd8SNickeau                 * Parent
156*37748cd8SNickeau                 */
157*37748cd8SNickeau                $parent = $callStack->moveToParent();
15821913ab3SNickeau                $parentTag = "";
15921913ab3SNickeau                if (!empty($parent)) {
160*37748cd8SNickeau                    $parentTag = $parent->getTagName();
16121913ab3SNickeau                    if ($parentTag == syntax_plugin_combo_link::TAG) {
16221913ab3SNickeau                        /**
16321913ab3SNickeau                         * The image is in a link, we don't want another link
16421913ab3SNickeau                         * to the image
16521913ab3SNickeau                         */
166a6bf47aaSNickeau                        $attributes[MediaLink::LINKING_KEY] = MediaLink::LINKING_NOLINK_VALUE;
16721913ab3SNickeau                    }
16821913ab3SNickeau                }
169*37748cd8SNickeau
17021913ab3SNickeau                return array(
17121913ab3SNickeau                    PluginUtility::STATE => $state,
17221913ab3SNickeau                    PluginUtility::ATTRIBUTES => $attributes,
173*37748cd8SNickeau                    PluginUtility::CONTEXT => $parentTag
17421913ab3SNickeau                );
17521913ab3SNickeau
17621913ab3SNickeau
17721913ab3SNickeau        }
17821913ab3SNickeau        return array();
17921913ab3SNickeau
18021913ab3SNickeau    }
18121913ab3SNickeau
18221913ab3SNickeau    /**
18321913ab3SNickeau     * Render the output
18421913ab3SNickeau     * @param string $format
18521913ab3SNickeau     * @param Doku_Renderer $renderer
18621913ab3SNickeau     * @param array $data - what the function handle() return'ed
18721913ab3SNickeau     * @return boolean - rendered correctly? (however, returned value is not used at the moment)
18821913ab3SNickeau     * @see DokuWiki_Syntax_Plugin::render()
18921913ab3SNickeau     *
19021913ab3SNickeau     *
19121913ab3SNickeau     */
19221913ab3SNickeau    function render($format, Doku_Renderer $renderer, $data)
19321913ab3SNickeau    {
19421913ab3SNickeau
19521913ab3SNickeau        switch ($format) {
19621913ab3SNickeau
19721913ab3SNickeau            case 'xhtml':
19821913ab3SNickeau
19921913ab3SNickeau                /** @var Doku_Renderer_xhtml $renderer */
20021913ab3SNickeau                $attributes = $data[PluginUtility::ATTRIBUTES];
20123723136Sgerardnico                $media = MediaLink::createFromCallStackArray($attributes);
20223723136Sgerardnico                if ($media->getScheme() == DokuPath::LOCAL_SCHEME) {
20323723136Sgerardnico                    $media = MediaLink::createFromCallStackArray($attributes, $renderer->date_at);
204*37748cd8SNickeau                    if ($media->isImage() || $media->getExtension() === "svg") {
2059337a630SNickeau                        /**
2069337a630SNickeau                         * We don't support crop
2079337a630SNickeau                         */
2089337a630SNickeau                        $crop = false;
2099337a630SNickeau                        if ($media->getRequestedWidth() != null && $media->getRequestedHeight() != null) {
2109337a630SNickeau                            /**
2119337a630SNickeau                             * Width of 0 = resizing by height (supported)
2129337a630SNickeau                             */
2139337a630SNickeau                            if ($media->getRequestedWidth() != "0") {
2149337a630SNickeau                                $crop = true;
2159337a630SNickeau                            }
2169337a630SNickeau                        }
2179337a630SNickeau                        if (!$crop) {
218*37748cd8SNickeau                            try {
21921913ab3SNickeau                                $renderer->doc .= $media->renderMediaTagWithLink();
220*37748cd8SNickeau                            } catch (RuntimeException $e) {
221*37748cd8SNickeau                                $errorClass = self::SVG_RENDERING_ERROR_CLASS;
222*37748cd8SNickeau                                $message = "Media ({$media->getPath()}). Error while rendering: {$e->getMessage()}";
223*37748cd8SNickeau                                $renderer->doc .= "<span class=\"text-alert $errorClass\">" . hsc($message) . "</span>";
224*37748cd8SNickeau                                if(!PluginUtility::isTest()) {
225*37748cd8SNickeau                                    LogUtility::msg($message, LogUtility::LVL_MSG_WARNING, MediaLink::CANONICAL);
226*37748cd8SNickeau                                }
227*37748cd8SNickeau                            }
22823723136Sgerardnico                            return true;
22923723136Sgerardnico                        }
23023723136Sgerardnico                    }
2319337a630SNickeau                }
23221913ab3SNickeau
23321913ab3SNickeau                /**
23423723136Sgerardnico                 * This is not an local internal media image (a video or an url image)
23521913ab3SNickeau                 * Dokuwiki takes over
23621913ab3SNickeau                 */
237531e725cSNickeau                $type = $attributes[MediaLink::MEDIA_DOKUWIKI_TYPE];
23821913ab3SNickeau                $src = $attributes['src'];
23921913ab3SNickeau                $title = $attributes['title'];
24021913ab3SNickeau                $align = $attributes['align'];
24121913ab3SNickeau                $width = $attributes['width'];
24221913ab3SNickeau                $height = $attributes['height'];
24321913ab3SNickeau                $cache = $attributes['cache'];
244a6bf47aaSNickeau                if ($cache == null) {
245a6bf47aaSNickeau                    // Dokuwiki needs a value
246a6bf47aaSNickeau                    // If their is no value it will output it without any value
247a6bf47aaSNickeau                    // in the query string.
248a6bf47aaSNickeau                    $cache = "cache";
249a6bf47aaSNickeau                }
25021913ab3SNickeau                $linking = $attributes['linking'];
25123723136Sgerardnico                switch ($type) {
252531e725cSNickeau                    case MediaLink::INTERNAL_MEDIA_CALL_NAME:
25321913ab3SNickeau                        $renderer->doc .= $renderer->internalmedia($src, $title, $align, $width, $height, $cache, $linking, true);
25423723136Sgerardnico                        break;
255531e725cSNickeau                    case MediaLink::EXTERNAL_MEDIA_CALL_NAME:
25623723136Sgerardnico                        $renderer->doc .= $renderer->externalmedia($src, $title, $align, $width, $height, $cache, $linking, true);
25723723136Sgerardnico                        break;
25823723136Sgerardnico                    default:
25923723136Sgerardnico                        LogUtility::msg("The dokuwiki media type ($type) is unknown");
26023723136Sgerardnico                        break;
26121913ab3SNickeau                }
26221913ab3SNickeau
26323723136Sgerardnico                return true;
26421913ab3SNickeau
2659337a630SNickeau            case
2669337a630SNickeau            "metadata":
26721913ab3SNickeau
26821913ab3SNickeau                /**
26921913ab3SNickeau                 * Keep track of the metadata
27021913ab3SNickeau                 * @var Doku_Renderer_metadata $renderer
27121913ab3SNickeau                 */
272*37748cd8SNickeau                $attributes = $data[PluginUtility::ATTRIBUTES];
27321913ab3SNickeau                self::registerImageMeta($attributes, $renderer);
27423723136Sgerardnico                return true;
27521913ab3SNickeau
276e8b2ff59SNickeau            case renderer_plugin_combo_analytics::RENDERER_FORMAT:
277e8b2ff59SNickeau
278e8b2ff59SNickeau                /**
279e8b2ff59SNickeau                 * Special pattern call
280e8b2ff59SNickeau                 * @var renderer_plugin_combo_analytics $renderer
281e8b2ff59SNickeau                 */
282e8b2ff59SNickeau                $attributes = $data[PluginUtility::ATTRIBUTES];
283*37748cd8SNickeau                self::updateStatistics($attributes, $renderer);
284e8b2ff59SNickeau                return true;
285e8b2ff59SNickeau
28621913ab3SNickeau        }
28721913ab3SNickeau        // unsupported $mode
28821913ab3SNickeau        return false;
28921913ab3SNickeau    }
29021913ab3SNickeau
29121913ab3SNickeau    /**
292*37748cd8SNickeau     * Update the index for the move plugin
293*37748cd8SNickeau     * and {@link Page::FIRST_IMAGE_META_RELATION}
294*37748cd8SNickeau     *
29521913ab3SNickeau     * @param array $attributes
29621913ab3SNickeau     * @param Doku_Renderer_metadata $renderer
29721913ab3SNickeau     */
29821913ab3SNickeau    static public function registerImageMeta($attributes, $renderer)
29921913ab3SNickeau    {
300531e725cSNickeau        $type = $attributes[MediaLink::MEDIA_DOKUWIKI_TYPE];
30121913ab3SNickeau        $src = $attributes['src'];
302531e725cSNickeau        if ($src == null) {
303531e725cSNickeau            $src = $attributes[DokuPath::PATH_ATTRIBUTE];
304531e725cSNickeau        }
30521913ab3SNickeau        $title = $attributes['title'];
30621913ab3SNickeau        $align = $attributes['align'];
30721913ab3SNickeau        $width = $attributes['width'];
30821913ab3SNickeau        $height = $attributes['height'];
30921913ab3SNickeau        $cache = $attributes['cache']; // Cache: https://www.dokuwiki.org/images#caching
31021913ab3SNickeau        $linking = $attributes['linking'];
31123723136Sgerardnico
31223723136Sgerardnico        switch ($type) {
313531e725cSNickeau            case MediaLink::INTERNAL_MEDIA_CALL_NAME:
31421913ab3SNickeau                $renderer->internalmedia($src, $title, $align, $width, $height, $cache, $linking);
31523723136Sgerardnico                break;
316531e725cSNickeau            case MediaLink::EXTERNAL_MEDIA_CALL_NAME:
31723723136Sgerardnico                $renderer->externalmedia($src, $title, $align, $width, $height, $cache, $linking);
31823723136Sgerardnico                break;
31923723136Sgerardnico            default:
32023723136Sgerardnico                LogUtility::msg("The dokuwiki media type ($type)  for metadata registration is unknown");
32123723136Sgerardnico                break;
32223723136Sgerardnico        }
32323723136Sgerardnico
32421913ab3SNickeau    }
32521913ab3SNickeau
32621913ab3SNickeau
32721913ab3SNickeau}
32821913ab3SNickeau
329