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 $imageFetcher->setRequestedWidth($width); 149 } 150 if ($height !== null) { 151 $imageFetcher->setRequestedHeight($height); 152 } 153 154 break; 155 case PageImageTag::FIRST_TYPE: 156 157 try { 158 $firstImagePath = FirstImage::createForPage($contextPage)->getValue(); 159 } catch (ExceptionNotFound $e) { 160 continue 2; 161 } 162 163 try { 164 $imageFetcher = IFetcherLocalImage::createImageFetchFromPath($firstImagePath); 165 } catch (ExceptionBadArgument|ExceptionBadSyntax|ExceptionNotExists $e) { 166 LogUtility::warning("Error while creating the first image handler for the image ($firstImagePath) and the page ($contextPage). Error: {$e->getMessage()}", self::CANONICAL, $e); 167 } 168 break; 169 case PageImageTag::VIGNETTE_TYPE: 170 try { 171 $imageFetcher = FetcherVignette::createForPage($contextPage); 172 } catch (ExceptionNotFound|ExceptionBadArgument $e) { 173 LogUtility::warning("Error while creating the vignette for the page ($contextPage). Error: {$e->getMessage()}", self::CANONICAL, $e); 174 } 175 break; 176 case PageImageTag::LOGO_TYPE: 177 try { 178 $imageFetcher = FetcherSvg::createSvgFromPath(Site::getLogoAsSvgImage()); 179 } catch (ExceptionNotFound $e) { 180 LogUtility::info("No page image could be find for the page ($path)", PageImageTag::CANONICAL); 181 } 182 break; 183 case PageImageTag::NONE_TYPE: 184 return false; 185 default: 186 LogUtility::error("The image ($pageImageProcessing) is an unknown page image type", PageImageTag::CANONICAL); 187 continue 2; 188 } 189 if ($imageFetcher !== null) { 190 $tagAttributes->addClassName(StyleAttribute::addComboStrapSuffix($pageImageProcessing)); 191 break; 192 } 193 } 194 195 if ($imageFetcher === null) { 196 return false; 197 } 198 199 /** 200 * Final building 201 */ 202 try { 203 $imageFetcher->buildFromTagAttributes($tagAttributes); 204 } catch (ExceptionBadArgument|ExceptionBadSyntax|ExceptionCompile $e) { 205 LogUtility::error("The image could not be build. Error: {$e->getMessage()}", PageImageTag::CANONICAL, $e); 206 } 207 208 /** 209 * Svg 210 */ 211 if ($imageFetcher instanceof FetcherSvg) { 212 213 /** 214 * This is an illustration image 215 * Used by svg to color by default with the primary color for instance 216 * 217 * (Note that because we use icon as page-image type, 218 * the buildFromTagAttributes would have set it to icon) 219 */ 220 $imageFetcher->setRequestedType(FetcherSvg::ILLUSTRATION_TYPE); 221 222 /** 223 * When the width requested is small, no zoom out 224 */ 225 try { 226 $requestedWidth = $imageFetcher->getRequestedWidth(); 227 try { 228 $pixelWidth = ConditionalLength::createFromString($requestedWidth)->toPixelNumber(); 229 if ($pixelWidth < 30) { 230 /** 231 * Icon rendering 232 */ 233 $imageFetcher 234 ->setRequestedZoom(1) 235 ->setRequestedType(FetcherSvg::ICON_TYPE); 236 237 } 238 } catch (ExceptionCompile $e) { 239 LogUtility::msg("The width value ($requestedWidth) could not be translated in pixel value. Error: {$e->getMessage()}"); 240 } 241 } catch (ExceptionNotFound $e) { 242 // no width 243 } 244 245 } 246 247 248 /** 249 * Img/Svg Tag 250 * 251 * Used as an illustration in a card 252 * If the image is too small, we allow that it will stretch 253 * to take the whole space 254 */ 255 if ($data[PluginUtility::CONTEXT] === CardTag::CARD_TAG) { 256 $tagAttributes->addStyleDeclarationIfNotSet("max-width", "100%"); 257 $tagAttributes->addStyleDeclarationIfNotSet("max-height", "unset"); 258 } 259 260 261 $tagAttributes->setType(self::MARKUP); 262 263 try { 264 return MediaMarkup::createFromFetcher($imageFetcher) 265 ->buildFromTagAttributes($tagAttributes) 266 ->toHtml(); 267 } catch (ExceptionCompile $e) { 268 $message = "Error while rendering the page image: {$e->getMessage()}"; 269 LogUtility::error($message, self::CANONICAL, $e); 270 return $message; 271 } 272 273 } 274 275 public static function getDefaultAttributes(): array 276 { 277 return [ 278 MediaMarkup::LINKING_KEY => MediaMarkup::LINKING_NOLINK_VALUE, 279 TagAttributes::TYPE_KEY => self::FEATURED 280 ]; 281 } 282 283 private static function getOrderOfPreference(TagAttributes $tagAttributes): array 284 { 285 // the type is first 286 $type = $tagAttributes->getType(); 287 $orderOfPreference[] = $type; 288 // then the default one 289 $default = $tagAttributes->getValueAndRemoveIfPresent(PageImageTag::DEFAULT_ATTRIBUTE); 290 if ($default === null) { 291 $defaultOrderOfPreference = PageImageTag::DEFAULT_ORDER; 292 } else { 293 $defaultOrderOfPreference = explode("|", $default); 294 } 295 foreach ($defaultOrderOfPreference as $defaultImageOrder) { 296 if ($defaultImageOrder === $type) { 297 continue; 298 } 299 $orderOfPreference[] = $defaultImageOrder; 300 } 301 return $orderOfPreference; 302 } 303 304} 305