1<?php
2
3namespace ComboStrap;
4
5use ComboStrap\Meta\Field\AncestorImage;
6use ComboStrap\Meta\Field\FeaturedImage;
7use ComboStrap\TagAttribute\StyleAttribute;
8use syntax_plugin_combo_iterator;
9
10
11class PageImageTag
12{
13
14    public const CANONICAL = PageImageTag::TAG;
15    public const VIGNETTE_TYPE = "vignette";
16    public const DEFAULT_ATTRIBUTE = "default";
17    public const MARKUP = "page-image";
18    public const LOGO_TYPE = "logo";
19    public const DEFAULT_ORDER = [
20        PageImageTag::FEATURED,
21        PageImageTag::FIRST_TYPE,
22        PageImageTag::ANCESTOR_TYPE,
23        PageImageTag::ICON_TYPE,
24        PageImageTag::VIGNETTE_TYPE,
25        PageImageTag::LOGO_TYPE
26    ];
27    public const ANCESTOR_TYPE = "ancestor";
28    public const TAG = "pageimage";
29    public const FEATURED = "featured";
30    public const ICON_TYPE = "icon";
31    public const NONE_TYPE = "none";
32    public const FIRST_TYPE = "first";
33
34    public const TYPES = [
35        PageImageTag::FEATURED,
36        PageImageTag::FIRST_TYPE,
37        PageImageTag::VIGNETTE_TYPE,
38        PageImageTag::ANCESTOR_TYPE,
39        PageImageTag::LOGO_TYPE,
40        PageImageTag::ICON_TYPE
41    ];
42    const DEFAULT_ZOOM = -4;
43
44
45    /**
46     * Because the pageimage can also be used
47     * in a template
48     *
49     * The calculation are done in the {@link syntax_plugin_combo_pageimage::render render function}
50     *
51     */
52    public static function handle($tagAttributes, $handler): array
53    {
54
55        /**
56         * Context
57         */
58        $callStack = CallStack::createFromHandler($handler);
59        $context = PageImageTag::TAG;
60        $parent = $callStack->moveToParent();
61        if ($parent !== false) {
62            $context = $parent->getTagName();
63        }
64
65        return array(
66            PluginUtility::ATTRIBUTES => $tagAttributes->toCallStackArray(),
67            PluginUtility::CONTEXT => $context
68        );
69    }
70
71    public static function render(TagAttributes $tagAttributes, array $data)
72    {
73
74
75        /**
76         * Image selection
77         */
78        $path = syntax_plugin_combo_iterator::getContextPathForComponentThatMayBeInFragment($tagAttributes);
79        $contextPage = MarkupPath::createPageFromPathObject($path);
80
81        /**
82         * Zoom applied only to icon
83         * but we get and **delete** it
84         * because it's not a standard html attribute
85         */
86        $zoom = $tagAttributes->getValueAndRemoveIfPresent(Dimension::ZOOM_ATTRIBUTE, self::DEFAULT_ZOOM);
87
88        /**
89         * Image Order of precedence
90         */
91        $order = self::getOrderOfPreference($tagAttributes);
92        $imageFetcher = null;
93        foreach ($order as $pageImageProcessing) {
94            switch ($pageImageProcessing) {
95                case PageImageTag::FEATURED:
96                    try {
97                        $imagePath = FeaturedImage::createFromResourcePage($contextPage)->getValue();
98                    } catch (ExceptionNotFound $e) {
99                        // ok
100                        continue 2;
101                    }
102                    try {
103                        $imageFetcher = IFetcherLocalImage::createImageFetchFromPath($imagePath);
104                    } catch (ExceptionNotExists|ExceptionBadArgument|ExceptionBadSyntax $e) {
105                        LogUtility::warning("Error while creating the fetcher for the feature image ($imagePath) and the page ($contextPage). Error: {$e->getMessage()}", self::CANONICAL, $e);
106                    }
107                    break;
108                case PageImageTag::ANCESTOR_TYPE:
109                case "parent": // old
110                    try {
111                        $ancestor = AncestorImage::createFromResourcePage($contextPage)->getValue();
112                    } catch (ExceptionNotFound $e) {
113                        continue 2;
114                    }
115                    try {
116                        $imageFetcher = IFetcherLocalImage::createImageFetchFromPath($ancestor);
117                    } catch (ExceptionBadArgument|ExceptionBadSyntax|ExceptionNotExists $e) {
118                        LogUtility::warning("Error while creating the ancestor image handler for the image ($ancestor) and the page ($contextPage). Error: {$e->getMessage()}", self::CANONICAL, $e);
119                    }
120                    break;
121                case PageImageTag::ICON_TYPE:
122                    try {
123                        $icon = FeaturedIcon::createForPage($contextPage)->getValueOrDefault();
124                    } catch (ExceptionNotFound $e) {
125                        continue 2;
126                    }
127
128                    $width = $tagAttributes->getValueAndRemoveIfPresent(Dimension::WIDTH_KEY);
129                    $height = $tagAttributes->getValueAndRemoveIfPresent(Dimension::HEIGHT_KEY);
130                    $ratio = $tagAttributes->getValueAndRemoveIfPresent(Dimension::RATIO_ATTRIBUTE);
131                    if ($width === null && $height !== null && $ratio === null) {
132                        $width = $height;
133                    }
134                    if ($width !== null && $height !== null && $ratio === null) {
135                        $height = $width;
136                    }
137                    $imageFetcher = FetcherSvg::createSvgFromPath($icon)
138                        ->setRequestedZoom($zoom);
139
140                    if ($ratio !== null) {
141                        try {
142                            $imageFetcher->setRequestedAspectRatio($ratio);
143                        } catch (ExceptionBadSyntax $e) {
144                            LogUtility::error("The ratio value ($ratio) is not a valid ratio for the icon image ($icon)");
145                        }
146                    }
147                    if ($width !== null) {
148                        try {
149                            $width = ConditionalLength::createFromString($width)->toPixelNumber();
150                            $imageFetcher->setRequestedWidth($width);
151                        } catch (ExceptionBadArgument $e) {
152                            LogUtility::warning("The width ($width) is not a valid length and was discarded", self::CANONICAL, $e);
153                        }
154                    }
155                    if ($height !== null) {
156                        try {
157                            $height = ConditionalLength::createFromString($height)->toPixelNumber();
158                            $imageFetcher->setRequestedHeight($height);
159                        } catch (ExceptionBadArgument $e) {
160                            LogUtility::warning("The height ($height) is not a valid length and was discarded", self::CANONICAL, $e);
161                        }
162                    }
163
164                    break;
165                case PageImageTag::FIRST_TYPE:
166
167                    try {
168                        $firstImagePath = FirstImage::createForPage($contextPage)->getValue();
169                    } catch (ExceptionNotFound $e) {
170                        continue 2;
171                    }
172
173                    try {
174                        $imageFetcher = IFetcherLocalImage::createImageFetchFromPath($firstImagePath);
175                    } catch (ExceptionBadArgument|ExceptionBadSyntax|ExceptionNotExists $e) {
176                        LogUtility::warning("Error while creating the first image handler for the image ($firstImagePath) and the page ($contextPage). Error: {$e->getMessage()}", self::CANONICAL, $e);
177                    }
178                    break;
179                case PageImageTag::VIGNETTE_TYPE:
180                    try {
181                        $imageFetcher = FetcherVignette::createForPage($contextPage);
182                    } catch (ExceptionNotFound|ExceptionBadArgument $e) {
183                        LogUtility::warning("Error while creating the vignette for the page ($contextPage). Error: {$e->getMessage()}", self::CANONICAL, $e);
184                    }
185                    break;
186                case PageImageTag::LOGO_TYPE:
187                    try {
188                        $imageFetcher = FetcherSvg::createSvgFromPath(Site::getLogoAsSvgImage());
189                    } catch (ExceptionNotFound $e) {
190                        LogUtility::info("No page image could be find for the page ($path)", PageImageTag::CANONICAL);
191                    }
192                    break;
193                case PageImageTag::NONE_TYPE:
194                    return false;
195                default:
196                    LogUtility::error("The image ($pageImageProcessing) is an unknown page image type", PageImageTag::CANONICAL);
197                    continue 2;
198            }
199            if ($imageFetcher !== null) {
200                $tagAttributes->addClassName(StyleAttribute::addComboStrapSuffix($pageImageProcessing));
201                break;
202            }
203        }
204
205        if ($imageFetcher === null) {
206            return false;
207        }
208
209        /**
210         * Final building
211         */
212        try {
213            $imageFetcher->buildFromTagAttributes($tagAttributes);
214        } catch (ExceptionBadArgument|ExceptionBadSyntax|ExceptionCompile $e) {
215            LogUtility::error("The image could not be build. Error: {$e->getMessage()}", PageImageTag::CANONICAL, $e);
216        }
217
218        /**
219         * Svg
220         */
221        if ($imageFetcher instanceof FetcherSvg) {
222
223            /**
224             * This is an illustration image
225             * Used by svg to color by default with the primary color for instance
226             *
227             * (Note that because we use icon as page-image type,
228             * the buildFromTagAttributes would have set it to icon)
229             */
230            $imageFetcher->setRequestedType(FetcherSvg::ILLUSTRATION_TYPE);
231
232            /**
233             * When the width requested is small, no zoom out
234             */
235            try {
236                $requestedWidth = $imageFetcher->getRequestedWidth();
237                try {
238                    $pixelWidth = ConditionalLength::createFromString($requestedWidth)->toPixelNumber();
239                    if ($pixelWidth < 30) {
240                        /**
241                         * Icon rendering
242                         */
243                        $imageFetcher
244                            ->setRequestedZoom(1)
245                            ->setRequestedType(FetcherSvg::ICON_TYPE);
246
247                    }
248                } catch (ExceptionCompile $e) {
249                    LogUtility::msg("The width value ($requestedWidth) could not be translated in pixel value. Error: {$e->getMessage()}");
250                }
251            } catch (ExceptionNotFound $e) {
252                // no width
253            }
254
255        }
256
257
258        /**
259         * Img/Svg Tag
260         *
261         * Used as an illustration in a card
262         * If the image is too small, we allow that it will stretch
263         * to take the whole space
264         */
265        if ($data[PluginUtility::CONTEXT] === CardTag::CARD_TAG) {
266            $tagAttributes->addStyleDeclarationIfNotSet("max-width", "100%");
267            $tagAttributes->addStyleDeclarationIfNotSet("max-height", "unset");
268        }
269
270
271        $tagAttributes->setType(self::MARKUP);
272
273        try {
274            return MediaMarkup::createFromFetcher($imageFetcher)
275                ->buildFromTagAttributes($tagAttributes)
276                ->toHtml();
277        } catch (ExceptionCompile $e) {
278            $message = "Error while rendering the page image: {$e->getMessage()}";
279            LogUtility::error($message, self::CANONICAL, $e);
280            return $message;
281        }
282
283    }
284
285    public static function getDefaultAttributes(): array
286    {
287        return [
288            MediaMarkup::LINKING_KEY => MediaMarkup::LINKING_NOLINK_VALUE,
289            TagAttributes::TYPE_KEY => self::FEATURED
290        ];
291    }
292
293    private static function getOrderOfPreference(TagAttributes $tagAttributes): array
294    {
295        // the type is first
296        $type = $tagAttributes->getType();
297        $orderOfPreference[] = $type;
298        // then the default one
299        $default = $tagAttributes->getValueAndRemoveIfPresent(PageImageTag::DEFAULT_ATTRIBUTE);
300        if ($default === null) {
301            $defaultOrderOfPreference = PageImageTag::DEFAULT_ORDER;
302        } else {
303            $defaultOrderOfPreference = explode("|", $default);
304        }
305        foreach ($defaultOrderOfPreference as $defaultImageOrder) {
306            if ($defaultImageOrder === $type) {
307                continue;
308            }
309            $orderOfPreference[] = $defaultImageOrder;
310        }
311        return $orderOfPreference;
312    }
313
314}
315