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