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