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