xref: /plugin/combo/syntax/media.php (revision 1fa8c418ed5809db58049141be41b7738471dd32)
1<?php
2
3
4use ComboStrap\Analytics;
5use ComboStrap\CallStack;
6use ComboStrap\DokuPath;
7use ComboStrap\Image;
8use ComboStrap\LogUtility;
9use ComboStrap\MediaLink;
10use ComboStrap\PluginUtility;
11use ComboStrap\ThirdPartyPlugins;
12
13
14require_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
15
16
17/**
18 * Media
19 *
20 * Takes over the {@link \dokuwiki\Parsing\ParserMode\Media media mode}
21 * that is processed by {@link Doku_Handler_Parse_Media}
22 *
23 *
24 *
25 * It can be a internal / external media
26 *
27 *
28 * See:
29 * https://developers.google.com/search/docs/advanced/guidelines/google-images
30 */
31class syntax_plugin_combo_media extends DokuWiki_Syntax_Plugin
32{
33
34
35    const TAG = "media";
36
37    /**
38     * Used in the move plugin
39     * !!! The two last word of the plugin class !!!
40     */
41    const COMPONENT = 'combo_' . self::TAG;
42
43
44    /**
45     * Found at {@link \dokuwiki\Parsing\ParserMode\Media}
46     */
47    const MEDIA_PATTERN = "\{\{(?:[^>\}]|(?:\}[^\}]))+\}\}";
48
49    /**
50     * Enable or disable the image
51     */
52    const CONF_IMAGE_ENABLE = "imageEnable";
53
54    /**
55     * Svg Rendering error
56     */
57    const SVG_RENDERING_ERROR_CLASS = "combo-svg-rendering-error";
58
59
60    /**
61     * @param $attributes
62     * @param renderer_plugin_combo_analytics $renderer
63     */
64    public static function updateStatistics($attributes, renderer_plugin_combo_analytics $renderer)
65    {
66        $media = MediaLink::createFromCallStackArray($attributes);
67        $renderer->stats[Analytics::MEDIA_COUNT]++;
68        $scheme = $media->getDefaultImage()->getScheme();
69        switch ($scheme) {
70            case DokuPath::LOCAL_SCHEME:
71                $renderer->stats[Analytics::INTERNAL_MEDIA_COUNT]++;
72                if (!$media->getDefaultImage()->exists()) {
73                    $renderer->stats[Analytics::INTERNAL_BROKEN_MEDIA_COUNT]++;
74                }
75                break;
76            case DokuPath::INTERNET_SCHEME:
77                $renderer->stats[Analytics::EXTERNAL_MEDIA_COUNT]++;
78                break;
79        }
80    }
81
82
83    function getType()
84    {
85        return 'formatting';
86    }
87
88    /**
89     * How Dokuwiki will add P element
90     *
91     *  * 'normal' - The plugin can be used inside paragraphs (inline)
92     *  * 'block'  - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs
93     *  * 'stack'  - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs
94     *
95     * @see DokuWiki_Syntax_Plugin::getPType()
96     */
97    function getPType()
98    {
99        /**
100         * An image is not a block (it can be inside paragraph)
101         */
102        return 'normal';
103    }
104
105    function getAllowedTypes()
106    {
107        return array('substition', 'formatting', 'disabled');
108    }
109
110    /**
111     * It should be less than {@link \dokuwiki\Parsing\ParserMode\Media::getSort()}
112     * (It was 320 at the time of writing this code)
113     * @return int
114     *
115     */
116    function getSort()
117    {
118        return 319;
119    }
120
121
122    function connectTo($mode)
123    {
124        $enable = $this->getConf(self::CONF_IMAGE_ENABLE, 1);
125        if (!$enable) {
126
127            // Inside a card, we need to take over and enable it
128            $modes = [
129                PluginUtility::getModeFromTag(syntax_plugin_combo_card::TAG),
130            ];
131            $enable = in_array($mode, $modes);
132        }
133
134        if ($enable) {
135            if ($mode !== PluginUtility::getModeFromPluginName(ThirdPartyPlugins::IMAGE_MAPPING_NAME)) {
136                $this->Lexer->addSpecialPattern(self::MEDIA_PATTERN, $mode, PluginUtility::getModeFromTag($this->getPluginComponent()));
137            }
138        }
139    }
140
141
142    function handle($match, $state, $pos, Doku_Handler $handler): array
143    {
144
145        switch ($state) {
146
147
148            // As this is a container, this cannot happens but yeah, now, you know
149            case DOKU_LEXER_SPECIAL :
150
151                $media = MediaLink::createFromRenderMatch($match);
152                $attributes = $media->toCallStackArray();
153
154                $callStack = CallStack::createFromHandler($handler);
155
156                /**
157                 * Parent
158                 */
159                $parent = $callStack->moveToParent();
160                $parentTag = "";
161                if (!empty($parent)) {
162                    $parentTag = $parent->getTagName();
163                    if ($parentTag == syntax_plugin_combo_link::TAG) {
164                        /**
165                         * TODO: should be on the exit tag of the link
166                         * The image is in a link, we don't want another link
167                         * to the image
168                         */
169                        $attributes[MediaLink::LINKING_KEY] = MediaLink::LINKING_NOLINK_VALUE;
170                    }
171                }
172
173                return array(
174                    PluginUtility::STATE => $state,
175                    PluginUtility::ATTRIBUTES => $attributes,
176                    PluginUtility::CONTEXT => $parentTag
177                );
178
179
180        }
181        return array();
182
183    }
184
185    /**
186     * Render the output
187     * @param string $format
188     * @param Doku_Renderer $renderer
189     * @param array $data - what the function handle() return'ed
190     * @return boolean - rendered correctly? (however, returned value is not used at the moment)
191     * @see DokuWiki_Syntax_Plugin::render()
192     *
193     *
194     */
195    function render($format, Doku_Renderer $renderer, $data)
196    {
197
198        switch ($format) {
199
200            case 'xhtml':
201
202                /** @var Doku_Renderer_xhtml $renderer */
203                $attributes = $data[PluginUtility::ATTRIBUTES];
204                $mediaLink = MediaLink::createFromCallStackArray($attributes);
205                $media = $mediaLink->getMedia();
206                if ($media->getScheme() == DokuPath::LOCAL_SCHEME) {
207                    $mediaLink = MediaLink::createFromCallStackArray($attributes, $renderer->date_at);
208                    if ($media->isImage() || $media->getExtension() === "svg") {
209                        try {
210                            $renderer->doc .= $mediaLink->renderMediaTagWithLink();
211                        } catch (RuntimeException $e) {
212                            $errorClass = self::SVG_RENDERING_ERROR_CLASS;
213                            $message = "Media ({$media->getPath()}). Error while rendering: {$e->getMessage()}";
214                            $renderer->doc .= "<span class=\"text-alert $errorClass\">" . hsc(trim($message)) . "</span>";
215                            LogUtility::msg($message, LogUtility::LVL_MSG_ERROR, MediaLink::CANONICAL);
216                        }
217                        return true;
218                    }
219                }
220
221                /**
222                 * This is not an local internal media image (a video or an url image)
223                 * Dokuwiki takes over
224                 */
225                $type = $attributes[MediaLink::MEDIA_DOKUWIKI_TYPE];
226                $src = $attributes['src'];
227                $title = $attributes['title'];
228                $align = $attributes['align'];
229                $width = $attributes['width'];
230                $height = $attributes['height'];
231                $cache = $attributes['cache'];
232                if ($cache == null) {
233                    // Dokuwiki needs a value
234                    // If their is no value it will output it without any value
235                    // in the query string.
236                    $cache = "cache";
237                }
238                $linking = $attributes['linking'];
239                switch ($type) {
240                    case MediaLink::INTERNAL_MEDIA_CALL_NAME:
241                        $renderer->doc .= $renderer->internalmedia($src, $title, $align, $width, $height, $cache, $linking, true);
242                        break;
243                    case MediaLink::EXTERNAL_MEDIA_CALL_NAME:
244                        $renderer->doc .= $renderer->externalmedia($src, $title, $align, $width, $height, $cache, $linking, true);
245                        break;
246                    default:
247                        LogUtility::msg("The dokuwiki media type ($type) is unknown");
248                        break;
249                }
250
251                return true;
252
253            case
254            "metadata":
255
256                /**
257                 * Keep track of the metadata
258                 * @var Doku_Renderer_metadata $renderer
259                 */
260                $attributes = $data[PluginUtility::ATTRIBUTES];
261                self::registerImageMeta($attributes, $renderer);
262                return true;
263
264            case renderer_plugin_combo_analytics::RENDERER_FORMAT:
265
266                /**
267                 * Special pattern call
268                 * @var renderer_plugin_combo_analytics $renderer
269                 */
270                $attributes = $data[PluginUtility::ATTRIBUTES];
271                self::updateStatistics($attributes, $renderer);
272                return true;
273
274        }
275        // unsupported $mode
276        return false;
277    }
278
279    /**
280     * Update the index for the move plugin
281     * and {@link Page::FIRST_IMAGE_META_RELATION}
282     *
283     * @param array $attributes
284     * @param Doku_Renderer_metadata $renderer
285     */
286    static public function registerImageMeta($attributes, $renderer)
287    {
288        $type = $attributes[MediaLink::MEDIA_DOKUWIKI_TYPE];
289        $src = $attributes['src'];
290        if ($src == null) {
291            $src = $attributes[DokuPath::PATH_ATTRIBUTE];
292        }
293        $title = $attributes['title'];
294        $align = $attributes['align'];
295        $width = $attributes['width'];
296        $height = $attributes['height'];
297        $cache = $attributes['cache']; // Cache: https://www.dokuwiki.org/images#caching
298        $linking = $attributes['linking'];
299
300        switch ($type) {
301            case MediaLink::INTERNAL_MEDIA_CALL_NAME:
302                $renderer->internalmedia($src, $title, $align, $width, $height, $cache, $linking);
303                break;
304            case MediaLink::EXTERNAL_MEDIA_CALL_NAME:
305                $renderer->externalmedia($src, $title, $align, $width, $height, $cache, $linking);
306                break;
307            default:
308                LogUtility::msg("The dokuwiki media type ($type)  for metadata registration is unknown");
309                break;
310        }
311
312    }
313
314
315}
316
317