1 <?php
2 
3 namespace ComboStrap;
4 
5 use ComboStrap\Meta\Field\AncestorImage;
6 use ComboStrap\Meta\Field\FeaturedImage;
7 use ComboStrap\TagAttribute\StyleAttribute;
8 use syntax_plugin_combo_iterator;
9 
10 
11 class 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