1<?php
2
3
4namespace ComboStrap;
5
6
7use ComboStrap\TagAttribute\Align;
8use ComboStrap\TagAttribute\Animation;
9use ComboStrap\TagAttribute\Shadow;
10use ComboStrap\Web\Url;
11use Doku_Renderer_metadata;
12use Doku_Renderer_xhtml;
13use renderer_plugin_combo_analytics;
14use syntax_plugin_combo_media;
15
16/**
17 * This class represents a media markup:
18 *   - with a {@link MediaMarkup::getFetcher() fetcher}
19 *   - and {@link MediaMarkup::getExtraMediaTagAttributes() tag/styling attributes}
20 *
21 * You can create it:
22 *   * via a {@link MediaMarkup::createFromRef() Markup Ref} (The string ref in the document)
23 *   * via a {@link MediaMarkup::createFromFetcher() Fetcher}
24 *   * via a {@link MediaMarkup::createFromFetchUrl() Fetch Url}
25 *   * via a {@link MediaMarkup::createFromCallStackArray() callstack array} of {@link syntax_plugin_combo_media::render()}
26 *   * via a {@link MediaMarkup::createFromMarkup() string match} of {@link syntax_plugin_combo_media::handle()}
27 *
28 *
29 */
30class MediaMarkup
31{
32
33    /**
34     * No name as this is {{ --- }}
35     */
36    public const TAG = "media";
37
38    /**
39     * The dokuwiki type and mode name
40     * (ie call)
41     *  * ie {@link MediaMarkup::EXTERNAL_MEDIA_CALL_NAME}
42     *  or {@link MediaMarkup::INTERNAL_MEDIA_CALL_NAME}
43     *
44     * The dokuwiki type (internalmedia/externalmedia)
45     *
46     */
47    public const MEDIA_DOKUWIKI_TYPE = 'dokuwiki_media_type';
48    public const EXTERNAL_MEDIA_CALL_NAME = "externalmedia";
49    public const INTERNAL_MEDIA_CALL_NAME = "internalmedia";
50
51    /**
52     * Link value:
53     *   * 'nolink'
54     *   * 'direct': directly to the image
55     *   * 'linkonly': show only a url
56     *   * 'details': go to the details media viewer
57     *
58     * @var
59     */
60    public const LINKING_KEY = 'linking';
61    public const LINKING_DETAILS_VALUE = 'details';
62    public const LINKING_DIRECT_VALUE = 'direct';
63    /**
64     * Only used by Dokuwiki
65     * Contains the path and eventually an anchor
66     * never query parameters
67     */
68    public const DOKUWIKI_SRC = "src";
69    public const LINKING_LINKONLY_VALUE = "linkonly";
70    public const LINKING_NOLINK_VALUE = 'nolink';
71    /**
72     * Default image linking value
73     */
74    public const CONF_DEFAULT_LINKING = "defaultImageLinking";
75
76    const CANONICAL = "media";
77
78    /**
79     * This attributes does not apply
80     * to a fetch (URL)
81     * They are only for the tag (img, svg, ...)
82     * or internal
83     */
84    public const STYLE_ATTRIBUTES = [
85        TagAttributes::TITLE_KEY,
86        Hover::ON_HOVER_ATTRIBUTE,
87        Animation::ON_VIEW_ATTRIBUTE,
88        Shadow::SHADOW_ATT,
89        Opacity::OPACITY_ATTRIBUTE,
90        TagAttributes::CLASS_KEY,
91    ];
92
93    /**
94     * An attribute to set the class of the link if any
95     */
96    public const LINK_CLASS_ATTRIBUTE = "link-class";
97    public static string $MEDIA_QUERY_PARAMETER = "media";
98
99
100    private ?string $align = null;
101    private ?string $label = null;
102    private ?MarkupRef $markupRef = null;
103    private ?string $linking = null;
104    private ?string $lazyLoadMethod = null;
105    private TagAttributes $extraMediaTagAttributes;
106    private ?string $linkingClass = null;
107    private IFetcher $fetcher;
108    private Url $fetchUrl;
109
110    private function __construct()
111    {
112        $this->extraMediaTagAttributes = TagAttributes::createEmpty();
113    }
114
115
116    /**
117     * Private method use {@link MediaMarkup::createFromRef()} to create a media markup via a ref
118     *
119     * Set and parse a media wiki ref that you can found in the first part of a media markup
120     *
121     * @param string $markupRef
122     * @return MediaMarkup
123     * @throws ExceptionBadArgument
124     * @throws ExceptionBadSyntax
125     * @throws ExceptionNotFound
126     */
127    private function setMarkupRef(string $markupRef): MediaMarkup
128    {
129
130        $markupRef = trim($markupRef);
131        $this->markupRef = MarkupRef::createMediaFromRef($markupRef);
132
133        $refUrl = $this->markupRef->getUrl();
134        $this->setUrl($refUrl);
135
136        return $this;
137    }
138
139    /**
140     * @param $callStackArray
141     * @return MediaMarkup
142     * @throws ExceptionBadArgument
143     * @throws ExceptionBadSyntax
144     * @throws ExceptionNotFound
145     * @throws ExceptionNotExists
146     */
147    public static function createFromCallStackArray($callStackArray): MediaMarkup
148    {
149
150        $tagAttributes = TagAttributes::createFromCallStackArray($callStackArray);
151        $ref = $tagAttributes->getValueAndRemoveIfPresent(MarkupRef::REF_ATTRIBUTE);
152        if ($ref === null) {
153            $ref = $tagAttributes->getValueAndRemoveIfPresent(MediaMarkup::DOKUWIKI_SRC);
154            if ($ref === null) {
155                throw new ExceptionBadArgument("The media reference was not found in the callstack array", self::CANONICAL);
156            }
157        }
158        return self::createFromRef($ref)
159            ->buildFromTagAttributes($tagAttributes);
160
161
162    }
163
164    /**
165     * @throws ExceptionBadArgument
166     * @throws ExceptionBadSyntax
167     * @throws ExceptionNotExists
168     * @throws ExceptionNotFound
169     * @throws ExceptionInternal
170     */
171    public static function createFromFetchUrl(Url $fetchUrl): MediaMarkup
172    {
173        return (new MediaMarkup())->setUrl($fetchUrl);
174    }
175
176    public static function createFromFetcher(IFetcher $fetcher): MediaMarkup
177    {
178        return (new MediaMarkup())
179            ->setFetcher($fetcher);
180    }
181
182    public static function renderSpecial(array $data, Doku_Renderer_xhtml $renderer)
183    {
184
185        $callStackArray = $data[PluginUtility::ATTRIBUTES];
186        $display = $callStackArray[Display::DISPLAY] ?? null;
187        if ($display === Display::DISPLAY_NONE_VALUE) {
188            /**
189             * Used primarly to not show the featured images
190             * in the outline {@link Outline::toHtmlSectionOutlineCallsRecurse()}
191             * for item page
192             * But we keep the metadata to move them if any
193             */
194            return false;
195        }
196
197        /** @var Doku_Renderer_xhtml $renderer */
198        try {
199            $mediaMarkup = MediaMarkup::createFromCallStackArray($callStackArray);
200        } catch (ExceptionCompile $e) {
201            return $e->getMessage();
202        }
203
204
205        if (
206            $mediaMarkup->getInternalExternalType() === MediaMarkup::INTERNAL_MEDIA_CALL_NAME
207        ) {
208            try {
209                $isImage = $mediaMarkup->getFetcher()->getMime()->isImage();
210            } catch (\Exception $e) {
211                $isImage = false;
212            }
213            if ($isImage) {
214                try {
215                    return MediaLink::createFromMediaMarkup($mediaMarkup)->renderMediaTag();
216                } catch (ExceptionCompile $e) {
217                    if (PluginUtility::isDevOrTest()) {
218                        throw new ExceptionRuntime("Media Rendering Error. {$e->getMessage()}", MediaLink::CANONICAL, 0, $e);
219                    } else {
220                        $errorClass = syntax_plugin_combo_media::SVG_RENDERING_ERROR_CLASS;
221                        $message = "Media ({$mediaMarkup}). Error while rendering: {$e->getMessage()}";
222                        LogUtility::msg($message, LogUtility::LVL_MSG_ERROR, MediaLink::CANONICAL);
223                        return "<span class=\"text-danger $errorClass\">" . hsc(trim($message)) . "</span>";
224
225                    }
226                }
227            }
228
229        }
230
231
232        /**
233         * This is not an local internal media image (a video or an url image)
234         * Dokuwiki takes over
235         */
236        $mediaType = $mediaMarkup->getInternalExternalType();
237        try {
238            $title = $mediaMarkup->getLabel();
239        } catch (ExceptionNotFound $e) {
240            $title = null;
241        }
242        try {
243            $linking = $mediaMarkup->getLinking();
244        } catch (ExceptionNotFound $e) {
245            $linking = null;
246        }
247        try {
248            $align = $mediaMarkup->getAlign();
249        } catch (ExceptionNotFound $e) {
250            $align = null;
251        }
252        try {
253            /**
254             * We use the markup ref url
255             * because we don't support http/https (external) url
256             * And there is therefore no fetcher available
257             */
258            $markupUrl = $mediaMarkup->getMarkupRef()->getUrl();
259
260        } catch (ExceptionNotFound $e) {
261            // the
262            LogUtility::internalError("As the media markup is created from a markup in the syntax component, it should be available");
263            return "";
264        }
265
266        try {
267            $src = $mediaMarkup->getSrc();
268        } catch (ExceptionNotFound $e) {
269            LogUtility::internalError("For an external markup, the src should not be empty", self::CANONICAL);
270            return "";
271        }
272        try {
273            $isImage = FileSystems::getMime($markupUrl)->isImage();
274        } catch (ExceptionNotFound $e) {
275            $isImage = false;
276        }
277        if ($isImage) {
278            /**
279             * We need to delete the
280             * wXh and other properties
281             * Dokuwiki does not accept it in its function
282             */
283            try {
284                $src = Url::createEmpty()
285                    ->setScheme($markupUrl->getScheme())
286                    ->setHost($markupUrl->getHost())
287                    ->setPath($markupUrl->getPath())
288                    ->toString();
289            } catch (ExceptionNotFound $e) {
290
291            }
292        }
293        try {
294            $width = $markupUrl->getQueryPropertyValue(Dimension::WIDTH_KEY);
295        } catch
296        (ExceptionNotFound $e) {
297            $width = null;
298        }
299        try {
300            $height = $markupUrl->getQueryPropertyValue(Dimension::HEIGHT_KEY);
301        } catch (ExceptionNotFound $e) {
302            $height = null;
303        }
304        try {
305            $cache = $markupUrl->getQueryPropertyValue(IFetcherAbs::CACHE_KEY);
306        } catch (ExceptionNotFound $e) {
307            // Dokuwiki needs a value
308            // If their is no value it will output it without any value
309            // in the query string.
310            $cache = IFetcherAbs::CACHE_DEFAULT_VALUE;
311        }
312        switch ($mediaType) {
313            case MediaMarkup::INTERNAL_MEDIA_CALL_NAME:
314                return $renderer->internalmedia($src, $title, $align, $width, $height, $cache, $linking, true);
315            case MediaMarkup::EXTERNAL_MEDIA_CALL_NAME:
316                return $renderer->externalmedia($src, $title, $align, $width, $height, $cache, $linking, true);
317            default:
318                LogUtility::msg("The dokuwiki media type ($mediaType) is unknown");
319                return "";
320        }
321    }
322
323
324    /**
325     * Compliance: src in dokuwiki is the id and the anchor if any
326     * Dokuwiki does not understand other property and the reference metadata
327     * may not work if we send back the `ref`
328     * @throws ExceptionNotFound
329     */
330    public function getSrc(): string
331    {
332        $internalExternalType = $this->getInternalExternalType();
333        switch ($internalExternalType) {
334            case MediaMarkup::INTERNAL_MEDIA_CALL_NAME:
335                /**
336                 * Absolute id because dokuwiki resolve a relatif id
337                 * to the actual namespace
338                 */
339                $src = $this->getPath()->toAbsoluteId();
340                try {
341                    $src = "$src#{$this->markupRef->getUrl()->getFragment()}";
342                } catch (ExceptionNotFound $e) {
343                    // ok
344                }
345                return $src;
346            case MediaMarkup::EXTERNAL_MEDIA_CALL_NAME:
347                return $this->getMarkupRef()->getRef();
348            default:
349                LogUtility::internalError("The internal/external type value ($internalExternalType) is unknown");
350                return $this->getMarkupRef()->getRef();
351        }
352
353    }
354
355    /**
356     * Media Type Needed by Dokuwiki
357     */
358    public function getInternalExternalType(): string
359    {
360        try {
361            // if there is a path, this is internal
362            // if interwiki this, wiki id, ...
363            $this->markupRef->getPath();
364            return self::INTERNAL_MEDIA_CALL_NAME;
365        } catch (ExceptionNotFound $e) {
366            return self::EXTERNAL_MEDIA_CALL_NAME;
367        }
368
369    }
370
371
372    /**
373     * @throws ExceptionBadSyntax
374     * @throws ExceptionBadArgument
375     * @throws ExceptionNotFound
376     */
377    public static function createFromRef(string $markupRef): MediaMarkup
378    {
379        return (new MediaMarkup())->setMarkupRef($markupRef);
380    }
381
382    /**
383     * Keep track of the metadata
384     * @param array $data
385     * @param Doku_Renderer_metadata $renderer
386     * @return void
387     */
388    public static function metadata(array $data, Doku_Renderer_metadata $renderer)
389    {
390
391        $tagAttributes = $data[PluginUtility::ATTRIBUTES];
392        if ($tagAttributes === null) {
393            // error on handle
394            return;
395        }
396        syntax_plugin_combo_media::registerImageMeta($tagAttributes, $renderer);
397
398    }
399
400    /**
401     * Special pattern call
402     * @param array $data
403     * @param renderer_plugin_combo_analytics $renderer
404     * @return void
405     * @deprecated - for metadata but yeah ...
406     */
407    public static function analytics(array $data, renderer_plugin_combo_analytics $renderer)
408    {
409
410        $tagAttributes = $data[PluginUtility::ATTRIBUTES];
411        syntax_plugin_combo_media::updateStatistics($tagAttributes, $renderer);
412
413    }
414
415
416    /**
417     * @return Url - an url that has query property as a fetch url
418     * It permits to select the fetch class
419     * @deprecated use {@link MediaMarkup::getFetcher()}->getUrl instead
420     */
421    public function getFetchUrl(): Url
422    {
423        return $this->getFetcher()->getFetchUrl();
424    }
425
426
427    /**
428     * @param string $match - the match of the renderer
429     * @throws ExceptionBadSyntax - if no ref was found
430     * @throws ExceptionBadArgument
431     * @throws ExceptionNotFound|ExceptionNotExists
432     * @throws ExceptionInternal
433     */
434    public static function createFromMarkup(string $match): MediaMarkup
435    {
436
437        $mediaMarkup = new MediaMarkup();
438
439        /**
440         *   * Delete the opening and closing character
441         *   * create the url and description
442         */
443        $match = preg_replace(array('/^{{/', '/}}$/u'), '', $match);
444        $parts = explode('|', $match, 2);
445
446        $ref = $parts[0];
447        if ($ref === null) {
448            throw new ExceptionBadSyntax("No ref was found");
449        }
450        $mediaMarkup->setMarkupRef($ref);
451        if (isset($parts[1])) {
452            $mediaMarkup->setLabel($parts[1]);
453        }
454
455
456        /**
457         * Media Alignment
458         */
459        $rightAlign = (bool)preg_match('/^ /', $ref);
460        $leftAlign = (bool)preg_match('/ $/', $ref);
461        $align = null;
462        // Logic = what's that ;)...
463        if ($leftAlign & $rightAlign) {
464            $align = 'center';
465        } else if ($rightAlign) {
466            $align = 'right';
467        } else if ($leftAlign) {
468            $align = 'left';
469        }
470        if ($align !== null) {
471            $mediaMarkup->setAlign($align);
472        }
473
474        return $mediaMarkup;
475
476
477    }
478
479    public function setAlign(string $align): MediaMarkup
480    {
481        $this->align = $align;
482        return $this;
483    }
484
485    public function setLabel(string $label): MediaMarkup
486    {
487        $this->label = $label;
488        return $this;
489    }
490
491    /**
492     * just FYI, not used
493     *
494     * Create an image from dokuwiki {@link Internallink internal call media attributes}
495     *
496     * Dokuwiki extracts already the width, height and align property
497     * @param array $callAttributes
498     * @return MediaMarkup
499     */
500    public static function createFromIndexAttributes(array $callAttributes)
501    {
502        $src = $callAttributes[0];
503        $title = $callAttributes[1];
504        $align = $callAttributes[2];
505        $width = $callAttributes[3];
506        $height = $callAttributes[4];
507        $cache = $callAttributes[5];
508        $linking = $callAttributes[6];
509
510        $ref = "$src?{$width}x$height&$cache";
511        return (new MediaMarkup())
512            ->setMarkupRef($ref)
513            ->setAlign($align)
514            ->setLabel($title)
515            ->setLinking($linking);
516
517    }
518
519    /**
520     * A function to set explicitly which array format
521     * is used in the returned data of a {@link SyntaxPlugin::handle()}
522     * (which ultimately is stored in the {@link CallStack)
523     *
524     * This is to make the difference with the {@link MediaLink::createFromIndexAttributes()}
525     * that is indexed by number (ie without property name)
526     *
527     *
528     * Return the array that is used in the {@link CallStack}
529     *
530     * @return array of key string and value
531     */
532    public function toCallStackArray(): array
533    {
534        /**
535         * We store linking as attribute (to make it possible to change the linking by other plugin)
536         * (ie no linking in heading , ...)
537         */
538        $attributes[MediaMarkup::LINKING_KEY] = $this->linking;
539        $attributes[MarkupRef::REF_ATTRIBUTE] = $this->markupRef->getRef();
540        $attributes[Align::ALIGN_ATTRIBUTE] = $this->align;
541        $attributes[TagAttributes::TITLE_KEY] = $this->label;
542        return $attributes;
543
544    }
545
546    public function setLinking(string $linking): MediaMarkup
547    {
548        $this->linking = $linking;
549        return $this;
550    }
551
552    /**
553     * @throws ExceptionNotFound
554     */
555    public function getLinking(): string
556    {
557        /**
558         * Linking
559         */
560        $linking = $this->linking;
561        if ($linking !== null) {
562            return $linking;
563        }
564        throw new ExceptionNotFound("No linking set");
565
566
567    }
568
569    /**
570     * Align on the url has precedence
571     * if present
572     * @throws ExceptionNotFound
573     */
574    public function getAlign(): string
575    {
576
577        if ($this->align !== null) {
578            return $this->align;
579        }
580        throw new ExceptionNotFound("No align was specified");
581    }
582
583
584    public function toTagAttributes()
585    {
586
587
588        /**
589         * The align attribute on an image parse
590         * is a float right
591         * ComboStrap does a difference between a block right and a float right
592         */
593        try {
594            $align = $this->getAlign();
595            if ($align === "right") {
596                $this->extraMediaTagAttributes->addComponentAttributeValue(FloatAttribute::FLOAT_KEY, "right");
597            } else {
598                $this->extraMediaTagAttributes->addComponentAttributeValue(Align::ALIGN_ATTRIBUTE, $align);
599            }
600        } catch (ExceptionNotFound $e) {
601            // ok
602        }
603
604        return $this->extraMediaTagAttributes;
605
606    }
607
608    /**
609     * @throws ExceptionNotFound
610     */
611    public function getLabel(): string
612    {
613        if (empty($this->label)) {
614            throw new ExceptionNotFound("No label specified");
615        }
616        return $this->label;
617    }
618
619    public
620    function setLazyLoadMethod($false): MediaMarkup
621    {
622        $this->lazyLoadMethod = $false;
623        return $this;
624    }
625
626    /**
627     * @throws ExceptionNotFound
628     */
629    public
630    function getLazyLoadMethod(): string
631    {
632
633        if ($this->lazyLoadMethod !== null) {
634            return $this->lazyLoadMethod;
635        }
636        throw new ExceptionNotFound("Lazy method is not specified");
637
638    }
639
640    public
641    function getLazyLoadMethodOrDefault(): string
642    {
643        try {
644            return $this->getLazyLoadMethod();
645        } catch (ExceptionNotFound $e) {
646            return LazyLoad::getDefault();
647        }
648
649    }
650
651
652    public
653    static function isInternalMediaSyntax($text)
654    {
655        return preg_match(' / ' . syntax_plugin_combo_media::MEDIA_PATTERN . ' / msSi', $text);
656    }
657
658
659    public function isLazy(): bool
660    {
661
662        return $this->getLazyLoadMethodOrDefault() !== LazyLoad::LAZY_LOAD_METHOD_NONE_VALUE;
663
664    }
665
666    public function getExtraMediaTagAttributes(): TagAttributes
667    {
668        try {
669            $this->extraMediaTagAttributes->addComponentAttributeValue(Align::ALIGN_ATTRIBUTE, $this->getAlign());
670        } catch (ExceptionNotFound $e) {
671            // ok
672        }
673        return $this->extraMediaTagAttributes;
674    }
675
676
677    public function __toString()
678    {
679        return $this->toMarkupSyntax();
680    }
681
682    public function setLazyLoad(bool $true): MediaMarkup
683    {
684        if ($true) {
685            $this->lazyLoadMethod = LazyLoad::getDefault();
686        } else {
687            $this->lazyLoadMethod = LazyLoad::LAZY_LOAD_METHOD_NONE_VALUE;
688        }
689        return $this;
690    }
691
692    /**
693     * Get and delete the attribute for the link
694     * (The rest is for the image)
695     */
696    public
697    function getLinkingClass()
698    {
699        return $this->linkingClass;
700    }
701
702    /**
703     * @throws ExceptionNotFound
704     */
705    public function getMarkupRef(): MarkupRef
706    {
707        if ($this->markupRef === null) {
708            throw new ExceptionNotFound("No markup, this media markup was not created from a markup");
709        }
710        return $this->markupRef;
711    }
712
713
714    /**
715     * @param TagAttributes $tagAttributes - the attributes in a tag format
716     * @return $this
717     */
718    public function buildFromTagAttributes(TagAttributes $tagAttributes): MediaMarkup
719    {
720
721        $linking = $tagAttributes->getValueAndRemoveIfPresent(self::LINKING_KEY);
722        if ($linking !== null) {
723            $this->setLinking($linking);
724        }
725        $label = $tagAttributes->getValueAndRemoveIfPresent(TagAttributes::TITLE_KEY);
726        if ($label !== null) {
727            $this->setLabel($label);
728        }
729        $align = $tagAttributes->getValueAndRemoveIfPresent(Align::ALIGN_ATTRIBUTE);
730        if ($align !== null) {
731            $this->setAlign($align);
732        }
733        $lazy = $tagAttributes->getValueAndRemoveIfPresent(LazyLoad::LAZY_LOAD_METHOD);
734        if ($lazy !== null) {
735            $this->setLazyLoadMethod($lazy);
736        }
737
738        /**
739         * dokuwiki attribute
740         */
741        if (isset($this->fetchUrl)) {
742            $width = $tagAttributes->getValueAndRemoveIfPresent(Dimension::WIDTH_KEY);
743            if ($width !== null) {
744                $this->fetchUrl->addQueryParameterIfNotPresent(Dimension::WIDTH_KEY, $width);
745            }
746            $height = $tagAttributes->getValueAndRemoveIfPresent(Dimension::HEIGHT_KEY);
747            if ($height !== null) {
748                $this->fetchUrl->addQueryParameterIfNotPresent(Dimension::HEIGHT_KEY, $height);
749            }
750            $ratio = $tagAttributes->getValueAndRemoveIfPresent(Dimension::RATIO_ATTRIBUTE);
751            if ($ratio !== null) {
752                $this->fetchUrl->addQueryParameterIfNotPresent(Dimension::RATIO_ATTRIBUTE, $ratio);
753            }
754        }
755
756        foreach ($tagAttributes->getComponentAttributes() as $key => $value) {
757            $this->extraMediaTagAttributes->addComponentAttributeValue($key, $value);
758        }
759
760        foreach ($tagAttributes->getStyleDeclarations() as $key => $value) {
761            $this->extraMediaTagAttributes->addStyleDeclarationIfNotSet($key, $value);
762        }
763
764        return $this;
765    }
766
767    /**
768     * @throws ExceptionBadArgument
769     * @throws ExceptionBadSyntax
770     * @throws ExceptionNotExists
771     * @throws ExceptionNotFound
772     */
773    public function toHtml(): string
774    {
775        return MediaLink::createFromMediaMarkup($this)
776            ->renderMediaTag();
777    }
778
779    /**
780     * @throws ExceptionBadSyntax
781     * @throws ExceptionBadArgument
782     * @throws ExceptionNotExists
783     * @throws ExceptionNotFound
784     */
785    public function getMediaLink()
786    {
787        return MediaLink::createFromMediaMarkup($this);
788    }
789
790    private
791    function setLinkingClass($value): MediaMarkup
792    {
793        $this->linkingClass = $value;
794        return $this;
795    }
796
797    /**
798     * @return string the wiki syntax
799     */
800    public function toMarkupSyntax(): string
801    {
802        $descriptionPart = "";
803        try {
804            $descriptionPart = "|" . $this->getLabel();
805        } catch (ExceptionNotFound $e) {
806            // ok
807        }
808        try {
809            $ref = $this->getRef();
810        } catch (ExceptionNotFound $e) {
811            $ref = $this->getFetchUrl()->toString();
812        }
813        return '{{' . $ref . $descriptionPart . '}}';
814    }
815
816    /**
817     *
818     * Private method use {@link MediaMarkup::createFromFetchUrl()} to create a media markup via a Url
819     *
820     * @throws ExceptionNotFound
821     */
822    private function setUrl(Url $fetchUrl): MediaMarkup
823    {
824
825        /**
826         * Tag Attributes
827         */
828        try {
829            $this->align = $fetchUrl->getQueryPropertyValueAndRemoveIfPresent(Align::ALIGN_ATTRIBUTE);
830        } catch (ExceptionNotFound $e) {
831            // ok
832        }
833        try {
834            $this->linking = $fetchUrl->getQueryPropertyValueAndRemoveIfPresent(self::LINKING_KEY);
835        } catch (ExceptionNotFound $e) {
836            // ok
837        }
838        try {
839            $this->lazyLoadMethod = $fetchUrl->getQueryPropertyValueAndRemoveIfPresent(LazyLoad::LAZY_LOAD_METHOD);
840        } catch (ExceptionNotFound $e) {
841            // ok
842        }
843        try {
844            $this->linkingClass = $fetchUrl->getQueryPropertyValueAndRemoveIfPresent(self::LINK_CLASS_ATTRIBUTE);
845        } catch (ExceptionNotFound $e) {
846            // ok
847        }
848
849        foreach (self::STYLE_ATTRIBUTES as $nonUrlAttribute) {
850            try {
851                $value = $fetchUrl->getQueryPropertyValueAndRemoveIfPresent($nonUrlAttribute);
852                $this->extraMediaTagAttributes->addComponentAttributeValue($nonUrlAttribute, $value);
853            } catch (ExceptionNotFound $e) {
854                // ok
855            }
856        }
857
858        $this->fetchUrl = $fetchUrl;
859        return $this;
860    }
861
862    /**
863     * @throws ExceptionNotFound
864     */
865    private function getRef(): string
866    {
867        if ($this->markupRef === null) {
868            throw new ExceptionNotFound("No ref was specified");
869        }
870        return $this->markupRef->getRef();
871    }
872
873    /**
874     * @throws ExceptionNotFound - if this markup does not have a path origin
875     * @deprecated use the {@link self::getFetcher()} instead
876     * A media may be generated (ie {@link FetcherVignette}
877     * therefore the path may be not present
878     *
879     * If you want to known the mime use {@link self::getFetcher()} then {@link IFetcher::getMime()}
880     */
881    public function getPath(): WikiPath
882    {
883        try {
884
885            return $this->getMarkupRef()->getPath();
886
887        } catch (ExceptionNotFound $e) {
888
889            if ($this->fetcher instanceof IFetcherSource) {
890                return $this->fetcher->getSourcePath();
891            }
892            throw $e;
893
894        }
895    }
896
897    /**
898     * Private method use {@link MediaMarkup::createFromFetcher()} to create a media markup via a Fetcher
899     * @param IFetcher $fetcher
900     * @return MediaMarkup
901     */
902    private function setFetcher(IFetcher $fetcher): MediaMarkup
903    {
904        $this->fetcher = $fetcher;
905        return $this;
906    }
907
908
909    /**
910     * @return IFetcher
911     */
912    public function getFetcher(): IFetcher
913    {
914        if (!isset($this->fetcher)) {
915            if (!isset($this->fetchUrl)) {
916                throw new ExceptionRuntimeInternal("No fetcher or url was set");
917            }
918            /**
919             * Fetcher is build later
920             * because for a raster image
921             * actually, we can't built it
922             * if the file does not exists.
923             * It will throw an error immediatly and we may want not.
924             * For resources, we want to build the url even if the image does not exists.
925             */
926            try {
927                $this->fetcher = FetcherSystem::createPathFetcherFromUrl($this->fetchUrl);
928            } catch (ExceptionBadArgument|ExceptionInternal|ExceptionNotFound $e) {
929                try {
930                    // we don't support http fetch
931                    if (!($this->getMarkupRef()->getSchemeType() === MarkupRef::WEB_URI)) {
932                        throw ExceptionRuntimeInternal::withMessageAndError("we don't support http fetch", $e);
933                    }
934                } catch (ExceptionNotFound $e) {
935                    // ok no markup ref
936                }
937            }
938        }
939        return $this->fetcher;
940    }
941
942
943}
944