From a82791a7cb67e02eb0a7fe1bee408ba5921c58e5 Mon Sep 17 00:00:00 2001 From: Stefan Seifert Date: Sat, 2 Sep 2023 12:44:13 +0200 Subject: [PATCH 01/10] prepare image v3 --- .../app-root/components/image/v2/README.md | 2 +- .../app-root/components/image/v3/README.md | 26 +++++++++++++++++++ .../app-root/components/image/v3/image.json | 10 +++++++ changes.xml | 6 +++++ .../library/core-content/image/.content.xml | 14 +++++----- src/site/markdown/components.md | 3 +-- 6 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 bundles/core/src/main/webapp/app-root/components/image/v3/README.md create mode 100644 bundles/core/src/main/webapp/app-root/components/image/v3/image.json diff --git a/bundles/core/src/main/webapp/app-root/components/image/v2/README.md b/bundles/core/src/main/webapp/app-root/components/image/v2/README.md index 20e7b61a..fe28556d 100644 --- a/bundles/core/src/main/webapp/app-root/components/image/v2/README.md +++ b/bundles/core/src/main/webapp/app-root/components/image/v2/README.md @@ -1,4 +1,4 @@ -Image (v2) - deprecated +Image (v2) ==== Image component written in HTL that renders an adaptive image. diff --git a/bundles/core/src/main/webapp/app-root/components/image/v3/README.md b/bundles/core/src/main/webapp/app-root/components/image/v3/README.md new file mode 100644 index 00000000..81f8b537 --- /dev/null +++ b/bundles/core/src/main/webapp/app-root/components/image/v3/README.md @@ -0,0 +1,26 @@ +Image (v3) +==== +Image component written in HTL that renders an adaptive image. + +Extends the [Image AEM Sites Core Component][extends-component] + +## Resource Type +``` +wcm-io/wcm/core/components/image/v3/image +``` + +## Enhanced Features + +* Inherits all features from its [super component][extends-component] +* Uses [wcm.io Media Handler][wcmio-handler-media] for processing the image +* There is no support for dynamic media in this component. If you want to use dynamic media, use the "wcm.io Responsive Image" core component. +* Allows to select media format(s) and auto-cropping in the content policy +* Automatically customizes the in-place image editor cropping ratios to the selected/the applications media formats +* Uses the enhanced Media Handler File Upload dialog widget with path field and media format validation +* Uses [wcm.io Link Handler][wcmio-handler-link] for processing the image link +* Uses the Link Handler dialog widget for defining link type and link target + + +[extends-component]: https://github.com/adobe/aem-core-wcm-components/tree/master/content/src/content/jcr_root/apps/core/wcm/components/image/v3/image +[wcmio-handler-media]: https://wcm.io/handler/media/ +[wcmio-handler-link]: https://wcm.io/handler/link/ diff --git a/bundles/core/src/main/webapp/app-root/components/image/v3/image.json b/bundles/core/src/main/webapp/app-root/components/image/v3/image.json new file mode 100644 index 00000000..550b413e --- /dev/null +++ b/bundles/core/src/main/webapp/app-root/components/image/v3/image.json @@ -0,0 +1,10 @@ +{ + "jcr:primaryType": "cq:Component", + "jcr:title": "wcm.io Image (v3)", + "componentGroup": ".wcmio-core-wcm", + "sling:resourceSuperType": "core/wcm/components/image/v3/image", + + /* Fallback mode for Link Handler to support existing content that used a single property name */ + "wcmio:linkTargetUrlFallbackProperty": "linkURL" + +} diff --git a/changes.xml b/changes.xml index e53f3a9f..6171ff48 100644 --- a/changes.xml +++ b/changes.xml @@ -23,6 +23,12 @@ xsi:schemaLocation="http://maven.apache.org/changes/1.0.0 http://maven.apache.org/plugins/maven-changes-plugin/xsd/changes-1.0.0.xsd"> + + + Add Image (v3). + + + Fix detection of teaser actions in Teaser V2. diff --git a/examples/content-packages/examples-sample-content/jcr_root/content/wcmio-core-components-examples/library/core-content/image/.content.xml b/examples/content-packages/examples-sample-content/jcr_root/content/wcmio-core-components-examples/library/core-content/image/.content.xml index fe30ebe6..f97d1631 100644 --- a/examples/content-packages/examples-sample-content/jcr_root/content/wcmio-core-components-examples/library/core-content/image/.content.xml +++ b/examples/content-packages/examples-sample-content/jcr_root/content/wcmio-core-components-examples/library/core-content/image/.content.xml @@ -18,7 +18,7 @@ cq:styleIds="[1644862132301]" jcr:primaryType="nt:unstructured" sling:resourceType="wcm-io/wcm/core/components/text/v2/text" - text="<h1>Image<sub>v2</sub></h1> " + text="<h1>Image<sub>v3</sub></h1> " textIsRich="true"/> + linkURL="https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/image/v3"/> MEDIA _Media Handler * [Title (v3)](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/title/v3) | [v2](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/title/v2) LINK * [Text (v2)](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/text/v2) RICHTEXT LINK -* [Image (v2)](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/image/v2) (deprecated) MEDIA LINK +* [Image (v3)](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/image/v3) | [(v2)](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/image/v2) MEDIA LINK * [Button (v2)](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/button/v2) | [(v1)](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/button/v1) LINK * [Teaser (v2)](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/teaser/v2) | [(v1)](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/teaser/v1) MEDIA LINK RICHTEXT * [List (v4)](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/list/v4) | [(v3)](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/list/v3) | [(v2)](https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/list/v2) LINK @@ -59,4 +59,3 @@ _Enhanced with_ MEDIA _Media Handler **Page Authoring Components** * Sharing (v1) -* Image (v3) From 4e9a5d095cb5151f621a855deeeaf9f55c6530a4 Mon Sep 17 00:00:00 2001 From: Stefan Seifert Date: Sat, 2 Sep 2023 13:45:42 +0200 Subject: [PATCH 02/10] image v3 impl --- .../impl/models/v3/ImageV3Impl.java | 368 ++++++++++++++++++ .../components/image/v3/image/image.html | 34 ++ changes.xml | 2 +- parent/pom.xml | 22 +- 4 files changed, 414 insertions(+), 12 deletions(-) create mode 100644 bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java create mode 100644 bundles/core/src/main/webapp/app-root/components/image/v3/image/image.html diff --git a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java new file mode 100644 index 00000000..9ee5a268 --- /dev/null +++ b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java @@ -0,0 +1,368 @@ +/* + * #%L + * wcm.io + * %% + * Copyright (C) 2023 wcm.io + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package io.wcm.wcm.core.components.impl.models.v3; + +import static com.day.cq.commons.ImageResource.PN_ALT; +import static com.day.cq.commons.jcr.JcrConstants.JCR_TITLE; +import static io.wcm.handler.media.MediaNameConstants.PROP_CSS_CLASS; +import static io.wcm.handler.media.MediaNameConstants.URI_TEMPLATE_PLACEHOLDER_WIDTH; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import javax.annotation.PostConstruct; + +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.models.annotations.Exporter; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy; +import org.apache.sling.models.annotations.injectorspecific.Self; +import org.apache.sling.models.annotations.injectorspecific.ValueMapValue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.adobe.cq.export.json.ComponentExporter; +import com.adobe.cq.export.json.ExporterConstants; +import com.adobe.cq.wcm.core.components.models.Image; +import com.adobe.cq.wcm.core.components.models.ImageArea; +import com.adobe.cq.wcm.core.components.models.datalayer.ImageData; +import com.adobe.cq.wcm.core.components.models.datalayer.builder.AssetDataBuilder; +import com.adobe.cq.wcm.core.components.models.datalayer.builder.DataLayerBuilder; +import com.day.cq.wcm.api.designer.Style; +import com.fasterxml.jackson.annotation.JsonIgnore; + +import io.wcm.handler.link.LinkHandler; +import io.wcm.handler.media.Asset; +import io.wcm.handler.media.Media; +import io.wcm.handler.media.MediaHandler; +import io.wcm.handler.media.Rendition; +import io.wcm.handler.media.UriTemplate; +import io.wcm.handler.media.UriTemplateType; +import io.wcm.handler.media.format.Ratio; +import io.wcm.handler.url.UrlHandler; +import io.wcm.sling.models.annotations.AemObject; +import io.wcm.wcm.core.components.impl.link.LinkWrapper; +import io.wcm.wcm.core.components.impl.models.helpers.AbstractComponentImpl; +import io.wcm.wcm.core.components.impl.models.helpers.ImageAreaV2Impl; +import io.wcm.wcm.core.components.impl.util.ComponentFeatureImageResolver; +import io.wcm.wcm.core.components.impl.util.HandlerUnwrapper; +import io.wcm.wcm.core.components.models.mixin.MediaMixin; + +/** + * wcm.io-based enhancements for {@link Image}: + *
    + *
  • Build image using Media handler
  • + *
  • Build link using Link handler
  • + *
+ */ +@Model(adaptables = SlingHttpServletRequest.class, + adapters = { Image.class, ComponentExporter.class }, + resourceType = ImageV3Impl.RESOURCE_TYPE) +@Exporter( + name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, + extensions = ExporterConstants.SLING_MODEL_EXTENSION) +public class ImageV3Impl extends AbstractComponentImpl implements Image, MediaMixin { + + /** + * Resource type + */ + public static final String RESOURCE_TYPE = "wcm-io/wcm/core/components/image/v3/image"; + private static final String WIDTH_PLACEHOLDER = "{.width}"; + + @AemObject + private Style currentStyle; + @Self + private LinkHandler linkHandler; + @Self + private MediaHandler mediaHandler; + @Self + private UrlHandler urlHandler; + + @ValueMapValue(name = PN_ALT, injectionStrategy = InjectionStrategy.OPTIONAL) + private @Nullable String alt; + @ValueMapValue(name = JCR_TITLE, injectionStrategy = InjectionStrategy.OPTIONAL) + private @Nullable String title; + + @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL) + private @Nullable String imageCrop; + @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL) + private @Nullable String imageRotate; + + private LinkWrapper link; + private Media media; + private String uuid; + private String fileReference; + private boolean displayPopupTitle; + private boolean enableLazyLoading; + private int lazyThreshold; + private boolean isDecorative; + private List areas; + + private List widths = Collections.emptyList(); + private String srcPattern; + + @PostConstruct + private void activate() { + ValueMap properties = resource.getValueMap(); + + // read basic properties + displayPopupTitle = properties.get(PN_DISPLAY_POPUP_TITLE, currentStyle.get(PN_DISPLAY_POPUP_TITLE, true)); + enableLazyLoading = !currentStyle.get(PN_DESIGN_LAZY_LOADING_ENABLED, true); + lazyThreshold = currentStyle.get(PN_DESIGN_LAZY_THRESHOLD, 0); + isDecorative = properties.get(PN_IS_DECORATIVE, currentStyle.get(PN_IS_DECORATIVE, false)); + boolean altFromAsset = properties.get(PN_ALT_VALUE_FROM_DAM, currentStyle.get(PN_ALT_VALUE_FROM_DAM, true)); + + // resolve link - decorative images have no link and no alt text by definition + if (isDecorative) { + link = new LinkWrapper(linkHandler.invalid()); + } + else { + link = new LinkWrapper(HandlerUnwrapper.get(linkHandler, resource).build()); + } + + // resolve media and properties from DAM asset + ComponentFeatureImageResolver imageResolver = new ComponentFeatureImageResolver(resource, getCurrentPage(), currentStyle, mediaHandler) + .targetPage(link.getLinkObject().getTargetPage()) + .mediaHandlerProperty(PROP_CSS_CLASS, "cmp-image__image") + .mediaHandlerProperty("itemprop", "contentUrl"); + String imageTitle = title; + if (displayPopupTitle && imageTitle != null) { + imageResolver.mediaHandlerProperty("title", imageTitle); + } + media = imageResolver.buildMedia(); + + media = HandlerUnwrapper.get(mediaHandler, resource) + .decorative(isDecorative) + .forceAltValueFromAsset(altFromAsset) + .build(); + if (media.isValid() && !media.getRendition().isImage()) { + // no image asset selected (cannot be rendered) - set to invalid + media = mediaHandler.invalid(); + } + if (media.isValid()) { + initPropertiesFromDamAsset(properties); + widths = buildRenditionWidths(); + srcPattern = buildSrcPattern(media.getRendition()); + areas = ImageAreaV2Impl.convertMap(media.getMap()); + } + + } + + /** + * Checks if the resolved media is a DAM asset, and initializes properties from it. + * @param properties Resource properties + */ + private void initPropertiesFromDamAsset(ValueMap properties) { + Asset asset = media.getAsset(); + if (asset != null) { + com.day.cq.dam.api.Asset damAsset = asset.adaptTo(com.day.cq.dam.api.Asset.class); + if (damAsset != null) { + boolean titleFromAsset = properties.get(PN_TITLE_VALUE_FROM_DAM, currentStyle.get(PN_TITLE_VALUE_FROM_DAM, true)); + boolean uuidDisabled = currentStyle.get(PN_UUID_DISABLED, false); + + fileReference = damAsset.getPath(); + alt = asset.getAltText(); + + if (!uuidDisabled) { + uuid = damAsset.getID(); + } + + if (titleFromAsset) { + String assetTitle = asset.getTitle(); + if (StringUtils.isNotEmpty(assetTitle)) { + title = assetTitle; + } + } + } + } + } + + @Override + @NotNull + public Media getMediaObject() { + return media; + } + + @Override + public String getSrc() { + return media.getUrl(); + } + + @Override + public String getAlt() { + return alt; + } + + @Override + public String getTitle() { + return title; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public com.adobe.cq.wcm.core.components.commons.link.Link getImageLink() { + return link.orNull(); + } + + /** + * @deprecated Deprecated in API + */ + @Override + @Deprecated + public String getLink() { + return link.getURL(); + } + + @Override + public boolean displayPopupTitle() { + return displayPopupTitle; + } + + @Override + public String getFileReference() { + return fileReference; + } + + @Override + public boolean isLazyEnabled() { + return enableLazyLoading; + } + + @Override + public int getLazyThreshold() { + return lazyThreshold; + } + + @Override + public String getSrcUriTemplate() { + return srcPattern; + } + + @Override + public int @NotNull [] getWidths() { + return widths.stream() + .mapToInt(Long::intValue) + .toArray(); + } + + @Override + public List getAreas() { + return areas; + } + + @Override + public boolean isDecorative() { + return isDecorative; + } + + @Override + public String getWidth() { + return null; + } + + @Override + public String getHeight() { + return null; + } + + @Override + public @Nullable String getSizes() { + return null; + } + + @Override + public String getSrcset() { + return null; + } + + @Override + public String getSmartCropRendition() { + return null; + } + + @Override + public boolean isDmImage() { + return false; + } + + /** + * @deprecated Deprecated in API + */ + @Override + @Deprecated + public String getJson() { + // not required for image v2 + return null; + } + + + /** + * Build lists of rendition widths based on the resolved media renditions + * (those that share the same ratio as the primary rendition) + * @return Widths + */ + private List buildRenditionWidths() { + double primaryRatio = media.getRendition().getRatio(); + return media.getRenditions().stream() + .filter(rendition -> Ratio.matches(rendition.getRatio(), primaryRatio)) + .map(rendition -> rendition.getWidth()) + .distinct() + .sorted() + .collect(Collectors.toList()); + } + + /** + * Build image url pattern via Media Handler URI template. + * @param rendition Primary rendition + * @return Url pattern + */ + private String buildSrcPattern(Rendition rendition) { + UriTemplate uriTempalte = rendition.getUriTemplate(UriTemplateType.SCALE_WIDTH); + return StringUtils.replace(uriTempalte.getUriTemplate(), URI_TEMPLATE_PLACEHOLDER_WIDTH, WIDTH_PLACEHOLDER); + } + + // --- data layer --- + + @Override + @JsonIgnore + @SuppressWarnings("null") + public @NotNull ImageData getComponentData() { + return DataLayerBuilder.extending(super.getComponentData()).asImageComponent() + .withTitle(this::getTitle) + .withLinkUrl(this::getLink) + .withAssetData(() -> Optional.of(media) + .filter(Media::isValid) + .map(Media::getAsset) + .map(asset -> asset.adaptTo(com.day.cq.dam.api.Asset.class)) + .map(DataLayerBuilder::forAsset) + .map(AssetDataBuilder::build) + .orElse(null)) + .build(); + } + +} diff --git a/bundles/core/src/main/webapp/app-root/components/image/v3/image/image.html b/bundles/core/src/main/webapp/app-root/components/image/v3/image/image.html new file mode 100644 index 00000000..5fc8d75e --- /dev/null +++ b/bundles/core/src/main/webapp/app-root/components/image/v3/image/image.html @@ -0,0 +1,34 @@ + + + diff --git a/changes.xml b/changes.xml index 6171ff48..ad39c420 100644 --- a/changes.xml +++ b/changes.xml @@ -24,7 +24,7 @@ - + Add Image (v3). diff --git a/parent/pom.xml b/parent/pom.xml index 48ca58b2..17bfbf25 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -118,49 +118,49 @@ io.wcm io.wcm.sling.commons - 1.4.0 + 1.6.4 io.wcm io.wcm.handler.commons - 1.4.0 + 1.5.0 io.wcm io.wcm.handler.url - 1.5.4 + 1.10.2 io.wcm io.wcm.handler.link - 1.7.4 + 1.10.2 io.wcm io.wcm.handler.media - 1.13.6 + 1.15.6 io.wcm io.wcm.handler.richtext - 1.5.0 + 1.6.4 io.wcm io.wcm.wcm.commons - 1.9.0 + 1.10.0 io.wcm io.wcm.wcm.ui.granite - 1.8.0 + 1.9.14 io.wcm io.wcm.testing.aem-mock.junit5 - 5.2.2 + 5.3.0 org.apache.sling @@ -170,7 +170,7 @@ org.apache.sling org.apache.sling.testing.caconfig-mock-plugin - 1.5.0 + 1.5.2 io.wcm @@ -196,7 +196,7 @@ org.skyscreamer jsonassert - 1.5.0 + 1.5.1 From d8ff7ccb05ce7c256fbe062b42a7e95c8810d47e Mon Sep 17 00:00:00 2001 From: Stefan Seifert Date: Sat, 2 Sep 2023 14:09:18 +0200 Subject: [PATCH 03/10] edit/design dialogs --- .../app-root/components/image/v3/README.md | 1 - .../app-root/components/image/v3/image.json | 188 +++++++++++++++++- 2 files changed, 187 insertions(+), 2 deletions(-) diff --git a/bundles/core/src/main/webapp/app-root/components/image/v3/README.md b/bundles/core/src/main/webapp/app-root/components/image/v3/README.md index 81f8b537..dbc64c00 100644 --- a/bundles/core/src/main/webapp/app-root/components/image/v3/README.md +++ b/bundles/core/src/main/webapp/app-root/components/image/v3/README.md @@ -13,7 +13,6 @@ wcm-io/wcm/core/components/image/v3/image * Inherits all features from its [super component][extends-component] * Uses [wcm.io Media Handler][wcmio-handler-media] for processing the image -* There is no support for dynamic media in this component. If you want to use dynamic media, use the "wcm.io Responsive Image" core component. * Allows to select media format(s) and auto-cropping in the content policy * Automatically customizes the in-place image editor cropping ratios to the selected/the applications media formats * Uses the enhanced Media Handler File Upload dialog widget with path field and media format validation diff --git a/bundles/core/src/main/webapp/app-root/components/image/v3/image.json b/bundles/core/src/main/webapp/app-root/components/image/v3/image.json index 550b413e..174824b8 100644 --- a/bundles/core/src/main/webapp/app-root/components/image/v3/image.json +++ b/bundles/core/src/main/webapp/app-root/components/image/v3/image.json @@ -5,6 +5,192 @@ "sling:resourceSuperType": "core/wcm/components/image/v3/image", /* Fallback mode for Link Handler to support existing content that used a single property name */ - "wcmio:linkTargetUrlFallbackProperty": "linkURL" + "wcmio:linkTargetUrlFallbackProperty": "linkURL", + + "cq:editConfig": { + "jcr:primaryType": "cq:EditConfig", + "cq:inherit": true, + /* + * Overwrite inplace edit config to: + * - Disable cropping on inline toolbar as it does not support predefined ratios + * - Remove default ratios as they are fetched from assigned media formats + * - Remove image/webp support + */ + "cq:inplaceEditing": { + "jcr:primaryType": "cq:InplaceEditingConfig", + "editorType": "image", + "active": true, + "configPath": "inplaceEditingConfig", + "inplaceEditingConfig": { + "jcr:primaryType": "nt:unstructured", + "plugins": { + "crop": { + "features": "*", + "supportedMimeTypes": ["image/jpeg", "image/png", "image/tiff"] + }, + "flip": { + "features": "-", + "supportedMimeTypes": ["image/jpeg", "image/png", "image/tiff"] + }, + "map": { + "features": "*", + "supportedMimeTypes": ["image/jpeg", "image/png", "image/tiff", "image/svg+xml"] + }, + "rotate": { + "features": "*", + "supportedMimeTypes": ["image/jpeg", "image/png", "image/tiff"] + }, + "zoom": { + "features": "*", + "supportedMimeTypes": ["image/jpeg", "image/png", "image/tiff", "image/svg+xml"] + } + }, + "ui": { + "inline": { + "toolbar": ["rotate#right", "history#undo", "history#redo", "fullscreen#fullscreen", "control#close", "control#finish"], + "replacementToolbars": { + "crop": ["crop#identifier", "crop#unlaunch", "crop#confirm"] + } + }, + "fullscreen": { + "toolbar": { + "left": ["crop#launchwithratio", "rotate#right", "map#launch", "flip#horizontal", "flip#vertical", "zoom#reset100", "zoom#popupslider"], + "right": ["history#undo", "history#redo", "fullscreen#fullscreenexit"] + }, + "replacementToolbars": { + "crop": { + "left": ["crop#identifier"], + "right": ["crop#unlaunch", "crop#confirm"] + }, + "map": { + "left": ["map#rectangle", "map#circle", "map#polygon"], + "right": ["map#unlaunch", "map#confirm"] + } + } + } + } + } + } + }, + + "cq:dialog": { + "jcr:primaryType": "nt:unstructured", + "extraClientlibs": ["io.wcm.wcm.core.components.image.v2.editor"], + "content": { + "items": { + "tabs": { + "items": { + + "asset": { + "items": { + "columns": { + "items": { + "column": { + "items": { + + /* Does not work with media handler */ + "pageImageThumbnail": { + "sling:hideResource": true + }, + + /* Use wcm.io Media Handler FileUpload */ + "file": { + "sling:resourceType": "wcm-io/handler/media/components/granite/form/fileupload", + "allowUpload": "${not empty cqDesign.allowUpload ? cqDesign.allowUpload : true}" + }, + + /* Supported differently with media handler */ + "dynamicmediaGroup": { + "sling:hideResource": true + } + + } + } + } + } + } + }, + + "metadata": { + "items": { + "columns": { + "items": { + "column": { + "items": { + "link": { + /* hide link pagefield and replace it with wcm.io Link dialog */ + "sling:hideResource": true + } + } + } + } + } + } + }, + + /* wcm.io Link Handler Tab */ + "link": { + "sling:resourceType": "granite/ui/components/coral/foundation/include", + "path": "wcm-io/handler/link/components/global/include/linkRefNoTitleTab" + } + + } + } + } + } + }, + + "cq:design_dialog": { + "jcr:primaryType": "nt:unstructured", + "content": { + "items": { + "tabs": { + "items": { + + "properties": { + "items": { + "content": { + "items": { + + "enableDmFeatures": { + /* Dynamic Media gets active automatically with media handler */ + "sling:hideResource": true + }, + + /* Supported differently with media handler */ + "enableAssetDelivery": { + "sling:hideResource": true + }, + "resizeWidth": { + "sling:hideResource": true + }, + "heading": { + "sling:hideResource": true + }, + "responsiveGroup": { + "sling:hideResource": true + }, + + /* Media format definition */ + "mediaFormatSelection": { + "sling:resourceType": "granite/ui/components/coral/foundation/include", + "path": "wcm-io/handler/media/components/global/include/mediaFormatSelection", + "sling:orderBefore": "enableLazyLoading" + }, + + "jpegQuality": { + /* JPEG quality is configured via media handler configuration */ + "sling:hideResource": true + } + } + } + } + } + + } + } + } + } + } } From 6959eff1fb8a3838904868cae634e8058346f0a4 Mon Sep 17 00:00:00 2001 From: Stefan Seifert Date: Sat, 2 Sep 2023 14:42:19 +0200 Subject: [PATCH 04/10] set version to 1.14.0-2.22.6-SNAPSHOT --- bundles/core/pom.xml | 4 ++-- examples/bundles/examples-core/pom.xml | 6 +++--- examples/content-packages/examples-libs/pom.xml | 6 +++--- examples/content-packages/examples-sample-content/pom.xml | 4 ++-- examples/content-packages/examples/pom.xml | 6 +++--- examples/pom.xml | 4 ++-- parent/pom.xml | 4 ++-- pom.xml | 4 ++-- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/bundles/core/pom.xml b/bundles/core/pom.xml index c40103d3..ab80cde6 100644 --- a/bundles/core/pom.xml +++ b/bundles/core/pom.xml @@ -25,13 +25,13 @@ io.wcm io.wcm.wcm.core.components.parent - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT ../../parent/pom.xml io.wcm io.wcm.wcm.core.components - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT jar WCM Core Components diff --git a/examples/bundles/examples-core/pom.xml b/examples/bundles/examples-core/pom.xml index e08eff1e..9607467a 100644 --- a/examples/bundles/examples-core/pom.xml +++ b/examples/bundles/examples-core/pom.xml @@ -25,13 +25,13 @@ io.wcm io.wcm.wcm.core.components.parent - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT ../../../parent/pom.xml io.wcm.samples io.wcm.wcm.core.components.examples-core - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT jar WCM Core Components Examples Core @@ -44,7 +44,7 @@ io.wcm io.wcm.wcm.core.components - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT compile diff --git a/examples/content-packages/examples-libs/pom.xml b/examples/content-packages/examples-libs/pom.xml index c6d56fe9..92ae6a31 100644 --- a/examples/content-packages/examples-libs/pom.xml +++ b/examples/content-packages/examples-libs/pom.xml @@ -25,13 +25,13 @@ io.wcm io.wcm.wcm.core.components.parent - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT ../../../parent/pom.xml io.wcm.samples io.wcm.wcm.core.components.examples-libs - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT content-package WCM Core Components Examples wcm.io Libraries @@ -42,7 +42,7 @@ io.wcm io.wcm.wcm.core.components - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT compile diff --git a/examples/content-packages/examples-sample-content/pom.xml b/examples/content-packages/examples-sample-content/pom.xml index 550286ec..b76c7ad9 100644 --- a/examples/content-packages/examples-sample-content/pom.xml +++ b/examples/content-packages/examples-sample-content/pom.xml @@ -25,13 +25,13 @@ io.wcm io.wcm.wcm.core.components.parent - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT ../../../parent/pom.xml io.wcm.samples io.wcm.wcm.core.components.examples-sample-content - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT content-package WCM Core Components Examples Content diff --git a/examples/content-packages/examples/pom.xml b/examples/content-packages/examples/pom.xml index ac7ec975..054c5f17 100644 --- a/examples/content-packages/examples/pom.xml +++ b/examples/content-packages/examples/pom.xml @@ -25,13 +25,13 @@ io.wcm io.wcm.wcm.core.components.parent - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT ../../../parent/pom.xml io.wcm.samples io.wcm.wcm.core.components.examples - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT content-package WCM Core Components Examples @@ -42,7 +42,7 @@ io.wcm.samples io.wcm.wcm.core.components.examples-core - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT compile diff --git a/examples/pom.xml b/examples/pom.xml index 486b4441..1bce240b 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -25,13 +25,13 @@ io.wcm io.wcm.wcm.core.components.parent - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT ../parent/pom.xml io.wcm.samples io.wcm.wcm.core.components.examples.root - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT pom diff --git a/parent/pom.xml b/parent/pom.xml index 17bfbf25..1461357c 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -31,7 +31,7 @@ io.wcm io.wcm.wcm.core.components.parent - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT pom WCM Core Components @@ -54,7 +54,7 @@ http://localhost:4503 - 2023-08-21T09:25:52Z + 2023-09-02T12:42:05Z diff --git a/pom.xml b/pom.xml index 8bf91704..0ceb612a 100644 --- a/pom.xml +++ b/pom.xml @@ -25,13 +25,13 @@ io.wcm io.wcm.wcm.core.components.parent - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT parent/pom.xml io.wcm io.wcm.wcm.core.components.root - 1.13.3-2.22.6-SNAPSHOT + 1.14.0-2.22.6-SNAPSHOT pom ${site.url}/${site.url.module.prefix}/ From 0bfa8132d437ae947ab181fa0c51bfb9326932c1 Mon Sep 17 00:00:00 2001 From: Stefan Seifert Date: Mon, 4 Sep 2023 12:22:20 +0200 Subject: [PATCH 05/10] add unit tests --- .../impl/models/v3/ImageV3Impl.java | 5 +- .../util/ComponentFeatureImageResolver.java | 12 +- .../impl/models/v2/ImageV2ImplTest.java | 2 +- .../impl/models/v3/ImageV3ImplTest.java | 473 ++++++++++++++++++ .../wcmio/v1/ResponsiveImageV1ImplTest.java | 1 - 5 files changed, 486 insertions(+), 7 deletions(-) create mode 100644 bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java diff --git a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java index 9ee5a268..fd8f4d05 100644 --- a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java +++ b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java @@ -144,6 +144,7 @@ private void activate() { // resolve media and properties from DAM asset ComponentFeatureImageResolver imageResolver = new ComponentFeatureImageResolver(resource, getCurrentPage(), currentStyle, mediaHandler) .targetPage(link.getLinkObject().getTargetPage()) + .altValueFromDam(altFromAsset) .mediaHandlerProperty(PROP_CSS_CLASS, "cmp-image__image") .mediaHandlerProperty("itemprop", "contentUrl"); String imageTitle = title; @@ -152,10 +153,6 @@ private void activate() { } media = imageResolver.buildMedia(); - media = HandlerUnwrapper.get(mediaHandler, resource) - .decorative(isDecorative) - .forceAltValueFromAsset(altFromAsset) - .build(); if (media.isValid() && !media.getRendition().isImage()) { // no image asset selected (cannot be rendered) - set to invalid media = mediaHandler.invalid(); diff --git a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/util/ComponentFeatureImageResolver.java b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/util/ComponentFeatureImageResolver.java index 477a170f..974b9dd1 100644 --- a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/util/ComponentFeatureImageResolver.java +++ b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/util/ComponentFeatureImageResolver.java @@ -57,7 +57,7 @@ public class ComponentFeatureImageResolver { private final boolean imageFromPageImage; private final boolean altValueFromPageImage; - private final boolean altValueFromDam; + private boolean altValueFromDam; private final boolean isDecorative; private final String componentAltText; @@ -105,6 +105,15 @@ public ComponentFeatureImageResolver mediaHandlerProperty(@NotNull String key, @ return this; } + /** + * @param value Alt Value from DAM + * @return self + */ + public ComponentFeatureImageResolver altValueFromDam(boolean value) { + this.altValueFromDam = value; + return this; + } + /** * Build media after resolving feature images and alt. texts. * @return Media @@ -167,6 +176,7 @@ else if (!(altValueFromPageImage || altValueFromDam)) { // otherwise rely to default media handler behavior builder.altText(componentAltText); } + builder.forceAltValueFromAsset(altValueFromDam); // apply custom media handling properties mediaHandlerProperties.entrySet().forEach(entry -> builder.property(entry.getKey(), entry.getValue())); diff --git a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2ImplTest.java b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2ImplTest.java index f38570d8..36cc3ea3 100644 --- a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2ImplTest.java +++ b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2ImplTest.java @@ -201,7 +201,7 @@ void testWithUploadedImage() { } @Test - @SuppressWarnings({ "null", "deprecation" }) + @SuppressWarnings("deprecation") void testWithImageAndLink() { enableDataLayer(context, true); diff --git a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java new file mode 100644 index 00000000..1c74151a --- /dev/null +++ b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java @@ -0,0 +1,473 @@ +/* + * #%L + * wcm.io + * %% + * Copyright (C) 2023 wcm.io + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package io.wcm.wcm.core.components.impl.models.v3; + +import static com.adobe.cq.wcm.core.components.models.Image.PN_ALT_VALUE_FROM_DAM; +import static com.adobe.cq.wcm.core.components.models.Image.PN_DESIGN_LAZY_LOADING_ENABLED; +import static com.adobe.cq.wcm.core.components.models.Image.PN_DESIGN_LAZY_THRESHOLD; +import static com.adobe.cq.wcm.core.components.models.Image.PN_DISPLAY_POPUP_TITLE; +import static com.adobe.cq.wcm.core.components.models.Image.PN_IS_DECORATIVE; +import static com.adobe.cq.wcm.core.components.models.Image.PN_MAP; +import static com.adobe.cq.wcm.core.components.models.Image.PN_TITLE_VALUE_FROM_DAM; +import static com.adobe.cq.wcm.core.components.models.Image.PN_UUID_DISABLED; +import static com.day.cq.commons.ImageResource.PN_ALT; +import static com.day.cq.commons.jcr.JcrConstants.JCR_TITLE; +import static com.day.cq.commons.jcr.JcrConstants.JCR_UUID; +import static com.day.cq.dam.api.DamConstants.DC_DESCRIPTION; +import static com.day.cq.dam.api.DamConstants.DC_TITLE; +import static io.wcm.handler.link.LinkNameConstants.PN_LINK_EXTERNAL_REF; +import static io.wcm.handler.link.LinkNameConstants.PN_LINK_TITLE; +import static io.wcm.handler.media.MediaNameConstants.NN_COMPONENT_MEDIA_RESPONSIVEIMAGE_SIZES; +import static io.wcm.handler.media.MediaNameConstants.NN_MEDIA_INLINE_STANDARD; +import static io.wcm.handler.media.MediaNameConstants.PN_COMPONENT_MEDIA_AUTOCROP; +import static io.wcm.handler.media.MediaNameConstants.PN_COMPONENT_MEDIA_FORMATS; +import static io.wcm.handler.media.MediaNameConstants.PN_COMPONENT_MEDIA_RESPONSIVE_TYPE; +import static io.wcm.handler.media.MediaNameConstants.PN_MEDIA_REF_STANDARD; +import static io.wcm.wcm.core.components.impl.models.helpers.DataLayerTestUtils.enableDataLayer; +import static io.wcm.wcm.core.components.impl.models.v3.ImageV3Impl.RESOURCE_TYPE; +import static io.wcm.wcm.core.components.testcontext.AppAemContext.CONTENT_ROOT; +import static io.wcm.wcm.core.components.testcontext.AppAemContext.DAM_ROOT; +import static io.wcm.wcm.core.components.testcontext.TestUtils.assertInvalidLink; +import static io.wcm.wcm.core.components.testcontext.TestUtils.assertInvalidMedia; +import static io.wcm.wcm.core.components.testcontext.TestUtils.assertValidLink; +import static io.wcm.wcm.core.components.testcontext.TestUtils.assertValidMedia; +import static io.wcm.wcm.core.components.testcontext.TestUtils.loadComponentDefinition; +import static org.apache.sling.api.resource.ResourceResolver.PROPERTY_RESOURCE_TYPE; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; + +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.Resource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import com.adobe.cq.wcm.core.components.models.Image; +import com.adobe.cq.wcm.core.components.models.datalayer.AssetData; +import com.adobe.cq.wcm.core.components.models.datalayer.ComponentData; +import com.adobe.cq.wcm.core.components.models.datalayer.ImageData; +import com.day.cq.dam.api.Asset; +import com.day.cq.wcm.api.Page; + +import io.wcm.handler.link.type.ExternalLinkType; +import io.wcm.sling.commons.adapter.AdaptTo; +import io.wcm.testing.mock.aem.junit5.AemContext; +import io.wcm.testing.mock.aem.junit5.AemContextExtension; +import io.wcm.wcm.commons.contenttype.ContentType; +import io.wcm.wcm.core.components.testcontext.AppAemContext; +import io.wcm.wcm.core.components.testcontext.ImageAreaTestData; +import io.wcm.wcm.core.components.testcontext.MediaFormats; +import io.wcm.wcm.core.components.testcontext.ResourceTypeForcingResourceWrapper; + +@ExtendWith(AemContextExtension.class) +class ImageV3ImplTest { + + private final AemContext context = AppAemContext.newAemContext(); + + private Page page; + private Asset asset; + + @BeforeEach + void setUp() { + loadComponentDefinition(context, RESOURCE_TYPE); + + page = context.create().page(CONTENT_ROOT + "/page1"); + asset = context.create().asset(DAM_ROOT + "/sample.jpg", 160, 90, ContentType.JPEG, + DC_TITLE, "Asset Title", + DC_DESCRIPTION, "Asset Description"); + // create web rendition to test auto-cropping + context.create().assetRenditionWebEnabled(asset); + } + + @Test + @SuppressWarnings("deprecation") + void testNoImage() { + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE)); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertNull(underTest.getSrc()); + assertNull(underTest.getTitle()); + assertNull(underTest.getAlt()); + assertNull(underTest.getUuid()); + assertNull(underTest.getLink()); + assertTrue(underTest.displayPopupTitle()); + assertNull(underTest.getFileReference()); + assertNull(underTest.getJson()); + assertArrayEquals(new int[0], underTest.getWidths()); + assertNull(underTest.getSrcUriTemplate()); + assertFalse(underTest.isLazyEnabled()); + assertEquals(0, underTest.getLazyThreshold()); + assertNull(underTest.getAreas()); + assertFalse(underTest.isDecorative()); + assertNotNull(underTest.getId()); + + assertInvalidMedia(underTest); + assertInvalidLink(underTest.getImageLink()); + assertNull(underTest.getData()); + + assertEquals(RESOURCE_TYPE, underTest.getExportedType()); + } + + @Test + void testInvalidAssetReference() { + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, "/content/dam/invalid")); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertInvalidMedia(underTest); + assertInvalidLink(underTest.getImageLink()); + } + + @Test + @SuppressWarnings("deprecation") + void testWithAssetImage() { + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath(), + JCR_TITLE, "Resource Title", + PN_ALT, "Resource Alt")); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + String expectedMediaUrl = DAM_ROOT + "/sample.jpg/_jcr_content/renditions/original./sample.jpg"; + + assertEquals(expectedMediaUrl, underTest.getSrc()); + assertEquals("Asset Title", underTest.getTitle()); + assertEquals("Asset Description", underTest.getAlt()); + assertEquals("", underTest.getUuid()); + assertNull(underTest.getLink()); + assertTrue(underTest.displayPopupTitle()); + assertEquals(asset.getPath(), underTest.getFileReference()); + assertArrayEquals(new int[] { 160 }, underTest.getWidths()); + assertEquals("/content/dam/sample/sample.jpg/_jcr_content/renditions/original.image_file.{.width}.0.file/sample.jpg", underTest.getSrcUriTemplate()); + assertFalse(underTest.isLazyEnabled()); + assertNull(underTest.getAreas()); + + assertValidMedia(underTest, expectedMediaUrl); + assertInvalidLink(underTest.getImageLink()); + } + + @Test + @SuppressWarnings("deprecation") + void testWithUploadedImage() { + Resource imageResource = context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + NN_MEDIA_INLINE_STANDARD + "Name", "file1.png"); + context.load().binaryFile("/files/test.png", imageResource.getPath() + "/" + NN_MEDIA_INLINE_STANDARD, ContentType.PNG); + context.currentResource(imageResource); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + String expectedMediaUrl = "/content/sample/en/page1/_jcr_content/image/file./file1.png"; + + assertEquals(expectedMediaUrl, underTest.getSrc()); + assertNull(underTest.getTitle()); + assertNull(underTest.getAlt()); + assertNull(underTest.getUuid()); + assertNull(underTest.getLink()); + assertTrue(underTest.displayPopupTitle()); + assertNull(underTest.getFileReference()); + assertArrayEquals(new int[] { 160 }, underTest.getWidths()); + assertEquals("/content/sample/en/page1/_jcr_content/image/file.image_file.{.width}.0.file/file1.png", underTest.getSrcUriTemplate()); + assertFalse(underTest.isLazyEnabled()); + assertNull(underTest.getAreas()); + + assertValidMedia(underTest, expectedMediaUrl); + assertInvalidLink(underTest.getImageLink()); + } + + @Test + @SuppressWarnings("deprecation") + void testWithImageAndLink() { + enableDataLayer(context, true); + + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath(), + PN_LINK_TITLE, ExternalLinkType.ID, + PN_LINK_EXTERNAL_REF, "http://myhost")); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertEquals("Asset Title", underTest.getTitle()); + assertEquals("Asset Description", underTest.getAlt()); + assertEquals("http://myhost", underTest.getLink()); + + assertValidLink(underTest.getImageLink(), "http://myhost"); + + ComponentData data = underTest.getData(); + assertNotNull(data); + assertEquals(RESOURCE_TYPE, data.getType()); + assertEquals("Asset Title", data.getTitle()); + assertEquals("http://myhost", data.getLinkUrl()); + + AssetData assetData = ((ImageData)data).getAssetData(); + assertEquals(asset.getPath(), assetData.getUrl()); + assertEquals(asset.getMimeType(), assetData.getFormat()); + } + + @Test + @SuppressWarnings("deprecation") + void testWithImageAndLink_Decorative() { + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath(), + PN_LINK_TITLE, ExternalLinkType.ID, + PN_LINK_EXTERNAL_REF, "http://myhost", + PN_IS_DECORATIVE, true)); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertEquals("Asset Title", underTest.getTitle()); + assertEquals("", underTest.getAlt()); + assertNull(underTest.getLink()); + assertTrue(underTest.isDecorative()); + + assertInvalidLink(underTest.getImageLink()); + } + + @Test + @SuppressWarnings("deprecation") + void testWithImageAndLink_Decorative_ContentPolicy() { + context.contentPolicyMapping(RESOURCE_TYPE, + PN_IS_DECORATIVE, true, + PN_DESIGN_LAZY_THRESHOLD, 10); + + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath(), + PN_LINK_TITLE, ExternalLinkType.ID, + PN_LINK_EXTERNAL_REF, "http://myhost")); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertEquals("Asset Title", underTest.getTitle()); + assertEquals("", underTest.getAlt()); + assertNull(underTest.getLink()); + assertEquals(10, underTest.getLazyThreshold()); + + assertInvalidLink(underTest.getImageLink()); + } + + @Test + void testWithImage_TitleAltNotFormAsset() { + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath(), + JCR_TITLE, "Resource Title", + PN_ALT, "Resource Alt", + PN_TITLE_VALUE_FROM_DAM, false, + PN_ALT_VALUE_FROM_DAM, false)); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertEquals("Resource Title", underTest.getTitle()); + assertEquals("Resource Alt", underTest.getAlt()); + } + + @Test + void testWithImage_TitleAltNotFormAsset_ContentPolicy() { + context.contentPolicyMapping(RESOURCE_TYPE, + PN_TITLE_VALUE_FROM_DAM, false, + PN_ALT_VALUE_FROM_DAM, false); + + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath(), + JCR_TITLE, "Resource Title", + PN_ALT, "Resource Alt")); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertEquals("Resource Title", underTest.getTitle()); + assertEquals("Resource Alt", underTest.getAlt()); + } + + @Test + void testWithNoImageAsset() { + Asset pdfAsset = context.create().asset(DAM_ROOT + "/file1.pdf", "/files/test.pdf", ContentType.PDF); + + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, pdfAsset.getPath())); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertInvalidMedia(underTest); + } + + @Test + void testDisplayPopupTitle() { + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath(), + PN_DISPLAY_POPUP_TITLE, false)); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertFalse(underTest.displayPopupTitle()); + } + + @Test + void testDisplayPopupTitle_ContentPolicy() { + context.contentPolicyMapping(RESOURCE_TYPE, + PN_DISPLAY_POPUP_TITLE, false); + + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath())); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertFalse(underTest.displayPopupTitle()); + } + + @Test + void testLazyEnabled_ContentPolicy() { + context.contentPolicyMapping(RESOURCE_TYPE, + PN_DESIGN_LAZY_LOADING_ENABLED, false); // property is internally named "disabled", value is inverted + + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath())); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertTrue(underTest.isLazyEnabled()); + } + + @Test + void testUUID() { + Resource resource = AdaptTo.notNull(asset, Resource.class); + ModifiableValueMap props = AdaptTo.notNull(resource, ModifiableValueMap.class); + props.put(JCR_UUID, "test-uuid"); + + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath())); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertEquals("test-uuid", underTest.getUuid()); + } + + @Test + void testUUID_Disabled() { + context.contentPolicyMapping(RESOURCE_TYPE, + PN_UUID_DISABLED, true); + + Resource resource = AdaptTo.notNull(asset, Resource.class); + ModifiableValueMap props = AdaptTo.notNull(resource, ModifiableValueMap.class); + props.put(JCR_UUID, "test-uuid"); + + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath())); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertNull(underTest.getUuid()); + } + + @Test + void testWidths() { + context.contentPolicyMapping(RESOURCE_TYPE, + PN_COMPONENT_MEDIA_FORMATS, new String[] { MediaFormats.LANDSCAPE.getName() }, + PN_COMPONENT_MEDIA_RESPONSIVE_TYPE, "imageSizes", + NN_COMPONENT_MEDIA_RESPONSIVEIMAGE_SIZES, Map.of("sizes", "100vw", "widths", "100,50"), + PN_COMPONENT_MEDIA_AUTOCROP, true); + + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath())); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertEquals("/content/dam/sample/sample.jpg/_jcr_content/renditions/original./sample.jpg", underTest.getSrc()); + assertEquals(asset.getPath(), underTest.getFileReference()); + assertArrayEquals(new int[] { 50, 100, 160 }, underTest.getWidths()); + assertEquals("/content/dam/sample/sample.jpg/_jcr_content/renditions/original.image_file.{.width}.0.file/sample.jpg", underTest.getSrcUriTemplate()); + + assertValidMedia(underTest, DAM_ROOT + "/sample.jpg/_jcr_content/renditions/original./sample.jpg"); + } + + @Test + void testAreas() { + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath(), + PN_MAP, ImageAreaTestData.MAP_STRING)); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertEquals(ImageAreaTestData.getExpectedAreasV1(context), underTest.getAreas()); + } + + @Test + void testWithImageAutoCropping_ContentPolicy() { + context.contentPolicyMapping(RESOURCE_TYPE, + PN_COMPONENT_MEDIA_FORMATS, new String[] { MediaFormats.SQUARE.getName() }, + PN_COMPONENT_MEDIA_AUTOCROP, true); + + context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath())); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertEquals("Asset Title", underTest.getTitle()); + assertEquals("/content/dam/sample/sample.jpg/_jcr_content/renditions/original.image_file.90.90.35,0,125,90.file/sample.jpg", underTest.getSrc()); + } + + @Test + void testWithImageAutoCropping_ContentPolicy_WrappedResource() { + // prepare dummy component that delegates to the component under test + final String DELEGATE_RESOURCE_TYPE = "myapp/components/delegate"; + context.create().resource("/apps/" + DELEGATE_RESOURCE_TYPE, + "sling:resourceSuperType", RESOURCE_TYPE); + + context.contentPolicyMapping(DELEGATE_RESOURCE_TYPE, + PN_COMPONENT_MEDIA_FORMATS, new String[] { MediaFormats.SQUARE.getName() }, + PN_COMPONENT_MEDIA_AUTOCROP, true); + + Resource resource = context.create().resource(page.getContentResource().getPath() + "/image", + PROPERTY_RESOURCE_TYPE, DELEGATE_RESOURCE_TYPE, + PN_MEDIA_REF_STANDARD, asset.getPath()); + + // set context resource to wrapped resource + context.currentResource(new ResourceTypeForcingResourceWrapper(resource, RESOURCE_TYPE)); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + assertEquals("Asset Title", underTest.getTitle()); + assertEquals("/content/dam/sample/sample.jpg/_jcr_content/renditions/original.image_file.90.90.35,0,125,90.file/sample.jpg", underTest.getSrc()); + } + +} diff --git a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/wcmio/v1/ResponsiveImageV1ImplTest.java b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/wcmio/v1/ResponsiveImageV1ImplTest.java index a9b22e15..b5826159 100644 --- a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/wcmio/v1/ResponsiveImageV1ImplTest.java +++ b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/wcmio/v1/ResponsiveImageV1ImplTest.java @@ -178,7 +178,6 @@ void testWithUploadedImage() { } @Test - @SuppressWarnings("null") void testWithImageAndLink() { enableDataLayer(context, true); From 666b2c76bea53013945fc218bb16e0c74aa3b548 Mon Sep 17 00:00:00 2001 From: Stefan Seifert Date: Mon, 4 Sep 2023 12:45:30 +0200 Subject: [PATCH 06/10] reduce duplication --- .../impl/models/v2/ImageV2Impl.java | 285 +++--------------- .../impl/models/v3/ImageV3Impl.java | 138 ++++----- 2 files changed, 106 insertions(+), 317 deletions(-) diff --git a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2Impl.java b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2Impl.java index 4055cd2b..ec7693bd 100644 --- a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2Impl.java +++ b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2Impl.java @@ -19,53 +19,29 @@ */ package io.wcm.wcm.core.components.impl.models.v2; -import static com.day.cq.commons.ImageResource.PN_ALT; -import static com.day.cq.commons.jcr.JcrConstants.JCR_TITLE; - import java.util.Arrays; -import java.util.Collections; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; -import javax.annotation.PostConstruct; - import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.sling.api.SlingHttpServletRequest; -import org.apache.sling.api.resource.ValueMap; import org.apache.sling.models.annotations.Exporter; import org.apache.sling.models.annotations.Model; -import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy; -import org.apache.sling.models.annotations.injectorspecific.Self; -import org.apache.sling.models.annotations.injectorspecific.ValueMapValue; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import com.adobe.cq.export.json.ComponentExporter; import com.adobe.cq.export.json.ExporterConstants; import com.adobe.cq.wcm.core.components.models.Image; import com.adobe.cq.wcm.core.components.models.ImageArea; -import com.adobe.cq.wcm.core.components.models.datalayer.ImageData; -import com.adobe.cq.wcm.core.components.models.datalayer.builder.AssetDataBuilder; -import com.adobe.cq.wcm.core.components.models.datalayer.builder.DataLayerBuilder; -import com.day.cq.wcm.api.designer.Style; -import com.fasterxml.jackson.annotation.JsonIgnore; import io.wcm.handler.link.Link; -import io.wcm.handler.link.LinkHandler; -import io.wcm.handler.media.Asset; import io.wcm.handler.media.Media; -import io.wcm.handler.media.MediaHandler; -import io.wcm.handler.media.Rendition; -import io.wcm.handler.url.UrlHandler; -import io.wcm.sling.models.annotations.AemObject; -import io.wcm.wcm.core.components.impl.models.helpers.AbstractComponentImpl; import io.wcm.wcm.core.components.impl.models.helpers.ImageAreaV1Impl; +import io.wcm.wcm.core.components.impl.models.v3.ImageV3Impl; import io.wcm.wcm.core.components.impl.servlets.ImageWidthProxyServlet; import io.wcm.wcm.core.components.impl.util.HandlerUnwrapper; import io.wcm.wcm.core.components.models.mixin.LinkMixin; -import io.wcm.wcm.core.components.models.mixin.MediaMixin; /** * wcm.io-based enhancements for {@link Image}: @@ -84,222 +60,36 @@ @Exporter( name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION) -public class ImageV2Impl extends AbstractComponentImpl implements Image, MediaMixin, LinkMixin { +public class ImageV2Impl extends ImageV3Impl implements LinkMixin { /** * Resource type */ public static final String RESOURCE_TYPE = "wcm-io/wcm/core/components/image/v2/image"; - private static final String WIDTH_PLACEHOLDER = "{.width}"; - - @AemObject - private Style currentStyle; - @Self - private LinkHandler linkHandler; - @Self - private MediaHandler mediaHandler; - @Self - private UrlHandler urlHandler; - - @ValueMapValue(name = PN_ALT, injectionStrategy = InjectionStrategy.OPTIONAL) - private @Nullable String alt; - @ValueMapValue(name = JCR_TITLE, injectionStrategy = InjectionStrategy.OPTIONAL) - private @Nullable String title; - - @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL) - private @Nullable String imageCrop; - @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL) - private @Nullable String imageRotate; - - private Link link; - private Media media; - private String uuid; - private String fileReference; - private boolean displayPopupTitle; - private boolean enableLazyLoading; - private int lazyThreshold; - private boolean isDecorative; - private List areas; - - private List widths = Collections.emptyList(); - private long noScriptWidth; - private String srcPattern; - - @PostConstruct - private void activate() { - ValueMap properties = resource.getValueMap(); - // read basic properties - displayPopupTitle = properties.get(PN_DISPLAY_POPUP_TITLE, currentStyle.get(PN_DISPLAY_POPUP_TITLE, true)); - enableLazyLoading = !currentStyle.get(PN_DESIGN_LAZY_LOADING_ENABLED, true); - lazyThreshold = currentStyle.get(PN_DESIGN_LAZY_THRESHOLD, 0); - isDecorative = properties.get(PN_IS_DECORATIVE, currentStyle.get(PN_IS_DECORATIVE, false)); - boolean altFromAsset = properties.get(PN_ALT_VALUE_FROM_DAM, currentStyle.get(PN_ALT_VALUE_FROM_DAM, true)); - - // resolve media and properties from DAM asset - media = HandlerUnwrapper.get(mediaHandler, resource) + @Override + protected Media buildMedia(boolean altFromAsset) { + return HandlerUnwrapper.get(mediaHandler, resource) // disable dynamic media support as it is not compatible with the "src-pattern" concept .dynamicMediaDisabled(true) .decorative(isDecorative) .forceAltValueFromAsset(altFromAsset) .build(); - if (media.isValid() && !media.getRendition().isImage()) { - // no image asset selected (cannot be rendered) - set to invalid - media = mediaHandler.invalid(); - } - if (media.isValid()) { - initPropertiesFromDamAsset(properties); - widths = buildRenditionWidths(media.getRendition()); - noScriptWidth = getNoScriptWidth(); - srcPattern = buildSrcPattern(media.getUrl()); - areas = ImageAreaV1Impl.convertMap(media.getMap()); - } - - // resolve link - decorative images have no link and no alt text by definition - if (isDecorative) { - link = linkHandler.invalid(); - } - else { - link = HandlerUnwrapper.get(linkHandler, resource).build(); - } - } - - /** - * Checks if the resolved media is a DAM asset, and initializes properties from it. - * @param properties Resource properties - */ - private void initPropertiesFromDamAsset(ValueMap properties) { - Asset asset = media.getAsset(); - if (asset != null) { - com.day.cq.dam.api.Asset damAsset = asset.adaptTo(com.day.cq.dam.api.Asset.class); - if (damAsset != null) { - boolean titleFromAsset = properties.get(PN_TITLE_VALUE_FROM_DAM, currentStyle.get(PN_TITLE_VALUE_FROM_DAM, true)); - boolean uuidDisabled = currentStyle.get(PN_UUID_DISABLED, false); - - fileReference = damAsset.getPath(); - alt = asset.getAltText(); - - if (!uuidDisabled) { - uuid = damAsset.getID(); - } - - if (titleFromAsset) { - String assetTitle = asset.getTitle(); - if (StringUtils.isNotEmpty(assetTitle)) { - title = assetTitle; - } - } - } - } - } - - @Override - @NotNull - public Link getLinkObject() { - return link; - } - - @Override - @NotNull - public Media getMediaObject() { - return media; - } - - @Override - public String getSrc() { - if (noScriptWidth > 0) { - return StringUtils.replace(srcPattern, WIDTH_PLACEHOLDER, "." + noScriptWidth); - } - else { - return media.getUrl(); - } - } - - @Override - public String getAlt() { - return alt; - } - - @Override - public String getTitle() { - return title; - } - - @Override - public String getUuid() { - return uuid; - } - - /** - * @deprecated Deprecated in API - */ - @Override - @Deprecated - public String getLink() { - return link.getUrl(); - } - - @Override - public boolean displayPopupTitle() { - return displayPopupTitle; } @Override - public String getFileReference() { - return fileReference; - } - - @Override - public boolean isLazyEnabled() { - return enableLazyLoading; - } - - @Override - public int getLazyThreshold() { - return lazyThreshold; - } - - @Override - public String getSrcUriTemplate() { - return srcPattern; - } - - @Override - public int @NotNull [] getWidths() { - return widths.stream() - .mapToInt(Long::intValue) - .toArray(); - } - - @Override - public List getAreas() { - return areas; - } - - @Override - public boolean isDecorative() { - return isDecorative; + protected List buildAreas() { + return ImageAreaV1Impl.convertMap(media.getMap()); } - /** - * @deprecated Deprecated in API - */ - @Override - @Deprecated - public String getJson() { - // not required for image v2 - return null; - } - - /** * Validates the configured list of widths, removes those that are bigger than the original rendition, * and returns them sorted by size. - * @param rendition Primary rendition * @return Widths */ - private List buildRenditionWidths(Rendition rendition) { - long maxWidth = rendition.getWidth(); + @Override + protected List buildRenditionWidths() { + long maxWidth = media.getRendition().getWidth(); String[] configuredWidths = currentStyle.get(PN_DESIGN_ALLOWED_RENDITION_WIDTHS, new String[0]); return Arrays.stream(configuredWidths) .map(NumberUtils::toLong) @@ -308,23 +98,13 @@ private List buildRenditionWidths(Rendition rendition) { .collect(Collectors.toList()); } - /** - * Picks a "medium" widths from the set of available widths. - * @return Picked width - */ - private long getNoScriptWidth() { - if (widths.isEmpty()) { - return 0; - } - return widths.get((int)Math.round(widths.size() / 2d - 0.5d)); - } - /** * Build image url pattern based in ImageWidthServlet for dynamic rendition selection. - * @param mediaUrl Media Url * @return Url pattern */ - private String buildSrcPattern(String mediaUrl) { + @Override + protected String buildSrcPattern() { + String mediaUrl = media.getUrl(); String extension = StringUtils.substringAfterLast(mediaUrl, "."); String fileName = StringUtils.substringAfterLast(mediaUrl, "/"); @@ -349,23 +129,32 @@ private String buildSrcPattern(String mediaUrl) { "." + ImageWidthProxyServlet.SELECTOR + WIDTH_PLACEHOLDER + "."); } - // --- data layer --- + @Override + @NotNull + public Link getLinkObject() { + return link.getLinkObject(); + } @Override - @JsonIgnore - @SuppressWarnings("null") - public @NotNull ImageData getComponentData() { - return DataLayerBuilder.extending(super.getComponentData()).asImageComponent() - .withTitle(this::getTitle) - .withLinkUrl(this::getLink) - .withAssetData(() -> Optional.of(media) - .filter(Media::isValid) - .map(Media::getAsset) - .map(asset -> asset.adaptTo(com.day.cq.dam.api.Asset.class)) - .map(DataLayerBuilder::forAsset) - .map(AssetDataBuilder::build) - .orElse(null)) - .build(); + public String getSrc() { + long noScriptWidth = getNoScriptWidth(); + if (noScriptWidth > 0) { + return StringUtils.replace(srcPattern, WIDTH_PLACEHOLDER, "." + noScriptWidth); + } + else { + return media.getUrl(); + } + } + + /** + * Picks a "medium" widths from the set of available widths. + * @return Picked width + */ + private long getNoScriptWidth() { + if (widths.isEmpty()) { + return 0; + } + return widths.get((int)Math.round(widths.size() / 2d - 0.5d)); } } diff --git a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java index fd8f4d05..d7615af2 100644 --- a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java +++ b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java @@ -56,7 +56,6 @@ import io.wcm.handler.media.Asset; import io.wcm.handler.media.Media; import io.wcm.handler.media.MediaHandler; -import io.wcm.handler.media.Rendition; import io.wcm.handler.media.UriTemplate; import io.wcm.handler.media.UriTemplateType; import io.wcm.handler.media.format.Ratio; @@ -87,40 +86,44 @@ public class ImageV3Impl extends AbstractComponentImpl implements Image, MediaMi /** * Resource type */ - public static final String RESOURCE_TYPE = "wcm-io/wcm/core/components/image/v3/image"; - private static final String WIDTH_PLACEHOLDER = "{.width}"; + static final String RESOURCE_TYPE = "wcm-io/wcm/core/components/image/v3/image"; + + /** + * Width placeholder for URI template + */ + public static final String WIDTH_PLACEHOLDER = "{.width}"; @AemObject - private Style currentStyle; + protected Style currentStyle; @Self - private LinkHandler linkHandler; + protected LinkHandler linkHandler; @Self - private MediaHandler mediaHandler; + protected MediaHandler mediaHandler; @Self - private UrlHandler urlHandler; + protected UrlHandler urlHandler; @ValueMapValue(name = PN_ALT, injectionStrategy = InjectionStrategy.OPTIONAL) - private @Nullable String alt; + protected @Nullable String alt; @ValueMapValue(name = JCR_TITLE, injectionStrategy = InjectionStrategy.OPTIONAL) - private @Nullable String title; + protected @Nullable String title; @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL) - private @Nullable String imageCrop; + protected @Nullable String imageCrop; @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL) - private @Nullable String imageRotate; + protected @Nullable String imageRotate; - private LinkWrapper link; - private Media media; - private String uuid; - private String fileReference; - private boolean displayPopupTitle; - private boolean enableLazyLoading; - private int lazyThreshold; - private boolean isDecorative; - private List areas; + protected LinkWrapper link; + protected Media media; + protected String uuid; + protected String fileReference; + protected boolean displayPopupTitle; + protected boolean enableLazyLoading; + protected int lazyThreshold; + protected boolean isDecorative; + protected List areas; - private List widths = Collections.emptyList(); - private String srcPattern; + protected List widths = Collections.emptyList(); + protected String srcPattern; @PostConstruct private void activate() { @@ -142,16 +145,7 @@ private void activate() { } // resolve media and properties from DAM asset - ComponentFeatureImageResolver imageResolver = new ComponentFeatureImageResolver(resource, getCurrentPage(), currentStyle, mediaHandler) - .targetPage(link.getLinkObject().getTargetPage()) - .altValueFromDam(altFromAsset) - .mediaHandlerProperty(PROP_CSS_CLASS, "cmp-image__image") - .mediaHandlerProperty("itemprop", "contentUrl"); - String imageTitle = title; - if (displayPopupTitle && imageTitle != null) { - imageResolver.mediaHandlerProperty("title", imageTitle); - } - media = imageResolver.buildMedia(); + media = buildMedia(altFromAsset); if (media.isValid() && !media.getRendition().isImage()) { // no image asset selected (cannot be rendered) - set to invalid @@ -160,10 +154,27 @@ private void activate() { if (media.isValid()) { initPropertiesFromDamAsset(properties); widths = buildRenditionWidths(); - srcPattern = buildSrcPattern(media.getRendition()); - areas = ImageAreaV2Impl.convertMap(media.getMap()); + srcPattern = buildSrcPattern(); + areas = buildAreas(); + } + + } + + protected Media buildMedia(boolean altFromAsset) { + ComponentFeatureImageResolver imageResolver = new ComponentFeatureImageResolver(resource, getCurrentPage(), currentStyle, mediaHandler) + .targetPage(link.getLinkObject().getTargetPage()) + .altValueFromDam(altFromAsset) + .mediaHandlerProperty(PROP_CSS_CLASS, "cmp-image__image") + .mediaHandlerProperty("itemprop", "contentUrl"); + String imageTitle = title; + if (displayPopupTitle && imageTitle != null) { + imageResolver.mediaHandlerProperty("title", imageTitle); } + return imageResolver.buildMedia(); + } + protected List buildAreas() { + return ImageAreaV2Impl.convertMap(media.getMap()); } /** @@ -195,6 +206,30 @@ private void initPropertiesFromDamAsset(ValueMap properties) { } } + /** + * Build lists of rendition widths based on the resolved media renditions + * (those that share the same ratio as the primary rendition) + * @return Widths + */ + protected List buildRenditionWidths() { + double primaryRatio = media.getRendition().getRatio(); + return media.getRenditions().stream() + .filter(rendition -> Ratio.matches(rendition.getRatio(), primaryRatio)) + .map(rendition -> rendition.getWidth()) + .distinct() + .sorted() + .collect(Collectors.toList()); + } + + /** + * Build image url pattern via Media Handler URI template. + * @return Url pattern + */ + protected String buildSrcPattern() { + UriTemplate uriTempalte = media.getRendition().getUriTemplate(UriTemplateType.SCALE_WIDTH); + return StringUtils.replace(uriTempalte.getUriTemplate(), URI_TEMPLATE_PLACEHOLDER_WIDTH, WIDTH_PLACEHOLDER); + } + @Override @NotNull public Media getMediaObject() { @@ -307,41 +342,6 @@ public boolean isDmImage() { return false; } - /** - * @deprecated Deprecated in API - */ - @Override - @Deprecated - public String getJson() { - // not required for image v2 - return null; - } - - - /** - * Build lists of rendition widths based on the resolved media renditions - * (those that share the same ratio as the primary rendition) - * @return Widths - */ - private List buildRenditionWidths() { - double primaryRatio = media.getRendition().getRatio(); - return media.getRenditions().stream() - .filter(rendition -> Ratio.matches(rendition.getRatio(), primaryRatio)) - .map(rendition -> rendition.getWidth()) - .distinct() - .sorted() - .collect(Collectors.toList()); - } - - /** - * Build image url pattern via Media Handler URI template. - * @param rendition Primary rendition - * @return Url pattern - */ - private String buildSrcPattern(Rendition rendition) { - UriTemplate uriTempalte = rendition.getUriTemplate(UriTemplateType.SCALE_WIDTH); - return StringUtils.replace(uriTempalte.getUriTemplate(), URI_TEMPLATE_PLACEHOLDER_WIDTH, WIDTH_PLACEHOLDER); - } // --- data layer --- From dad150707451ff233e9301291e33fd5efe1b08e9 Mon Sep 17 00:00:00 2001 From: Stefan Seifert Date: Mon, 4 Sep 2023 12:51:57 +0200 Subject: [PATCH 07/10] eliminate code smells --- .../wcm/core/components/impl/models/v3/ImageV3Impl.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java index d7615af2..3f930b53 100644 --- a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java +++ b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java @@ -56,6 +56,7 @@ import io.wcm.handler.media.Asset; import io.wcm.handler.media.Media; import io.wcm.handler.media.MediaHandler; +import io.wcm.handler.media.Rendition; import io.wcm.handler.media.UriTemplate; import io.wcm.handler.media.UriTemplateType; import io.wcm.handler.media.format.Ratio; @@ -215,7 +216,7 @@ protected List buildRenditionWidths() { double primaryRatio = media.getRendition().getRatio(); return media.getRenditions().stream() .filter(rendition -> Ratio.matches(rendition.getRatio(), primaryRatio)) - .map(rendition -> rendition.getWidth()) + .map(Rendition::getWidth) .distinct() .sorted() .collect(Collectors.toList()); @@ -265,7 +266,7 @@ public com.adobe.cq.wcm.core.components.commons.link.Link getImageLink() { * @deprecated Deprecated in API */ @Override - @Deprecated + @Deprecated(forRemoval = true) public String getLink() { return link.getURL(); } @@ -351,7 +352,7 @@ public boolean isDmImage() { public @NotNull ImageData getComponentData() { return DataLayerBuilder.extending(super.getComponentData()).asImageComponent() .withTitle(this::getTitle) - .withLinkUrl(this::getLink) + .withLinkUrl(() -> this.link.getLinkObject().getUrl()) .withAssetData(() -> Optional.of(media) .filter(Media::isValid) .map(Media::getAsset) From 5f0eadc801f87d0af862efff90d1594e581fc56d Mon Sep 17 00:00:00 2001 From: Stefan Seifert Date: Mon, 4 Sep 2023 13:13:47 +0200 Subject: [PATCH 08/10] add unit tests --- .../impl/models/v3/ImageV3Impl.java | 2 +- .../impl/models/v2/ImageV2ImplTest.java | 38 ++++----- .../impl/models/v3/ImageV3ImplTest.java | 80 ++++++++++++++----- 3 files changed, 81 insertions(+), 39 deletions(-) diff --git a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java index 3f930b53..845872aa 100644 --- a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java +++ b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java @@ -163,7 +163,7 @@ private void activate() { protected Media buildMedia(boolean altFromAsset) { ComponentFeatureImageResolver imageResolver = new ComponentFeatureImageResolver(resource, getCurrentPage(), currentStyle, mediaHandler) - .targetPage(link.getLinkObject().getTargetPage()) + .targetPage(getCurrentPage()) .altValueFromDam(altFromAsset) .mediaHandlerProperty(PROP_CSS_CLASS, "cmp-image__image") .mediaHandlerProperty("itemprop", "contentUrl"); diff --git a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2ImplTest.java b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2ImplTest.java index 36cc3ea3..f28b49e2 100644 --- a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2ImplTest.java +++ b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2ImplTest.java @@ -102,7 +102,7 @@ void setUp() { @Test @SuppressWarnings("deprecation") void testNoImage() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE)); Image underTest = AdaptTo.notNull(context.request(), Image.class); @@ -132,7 +132,7 @@ void testNoImage() { @Test void testInvalidAssetReference() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, "/content/dam/invalid")); @@ -145,7 +145,7 @@ void testInvalidAssetReference() { @Test @SuppressWarnings("deprecation") void testWithAssetImage() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), JCR_TITLE, "Resource Title", @@ -174,7 +174,7 @@ void testWithAssetImage() { @Test @SuppressWarnings("deprecation") void testWithUploadedImage() { - Resource imageResource = context.create().resource(page.getContentResource().getPath() + "/image", + Resource imageResource = context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, NN_MEDIA_INLINE_STANDARD + "Name", "file1.png"); context.load().binaryFile("/files/test.png", imageResource.getPath() + "/" + NN_MEDIA_INLINE_STANDARD, ContentType.PNG); @@ -205,7 +205,7 @@ void testWithUploadedImage() { void testWithImageAndLink() { enableDataLayer(context, true); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_LINK_TITLE, ExternalLinkType.ID, @@ -233,7 +233,7 @@ void testWithImageAndLink() { @Test @SuppressWarnings("deprecation") void testWithImageAndLink_Decorative() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_LINK_TITLE, ExternalLinkType.ID, @@ -257,7 +257,7 @@ void testWithImageAndLink_Decorative_ContentPolicy() { PN_IS_DECORATIVE, true, PN_DESIGN_LAZY_THRESHOLD, 10); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_LINK_TITLE, ExternalLinkType.ID, @@ -275,7 +275,7 @@ void testWithImageAndLink_Decorative_ContentPolicy() { @Test void testWithImage_TitleAltNotFormAsset() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), JCR_TITLE, "Resource Title", @@ -295,7 +295,7 @@ void testWithImage_TitleAltNotFormAsset_ContentPolicy() { PN_TITLE_VALUE_FROM_DAM, false, PN_ALT_VALUE_FROM_DAM, false); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), JCR_TITLE, "Resource Title", @@ -311,7 +311,7 @@ void testWithImage_TitleAltNotFormAsset_ContentPolicy() { void testWithNoImageAsset() { Asset pdfAsset = context.create().asset(DAM_ROOT + "/file1.pdf", "/files/test.pdf", ContentType.PDF); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, pdfAsset.getPath())); @@ -322,7 +322,7 @@ void testWithNoImageAsset() { @Test void testDisplayPopupTitle() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_DISPLAY_POPUP_TITLE, false)); @@ -337,7 +337,7 @@ void testDisplayPopupTitle_ContentPolicy() { context.contentPolicyMapping(RESOURCE_TYPE, PN_DISPLAY_POPUP_TITLE, false); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath())); @@ -351,7 +351,7 @@ void testLazyEnabled_ContentPolicy() { context.contentPolicyMapping(RESOURCE_TYPE, PN_DESIGN_LAZY_LOADING_ENABLED, false); // property is internally named "disabled", value is inverted - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath())); @@ -366,7 +366,7 @@ void testUUID() { ModifiableValueMap props = AdaptTo.notNull(resource, ModifiableValueMap.class); props.put(JCR_UUID, "test-uuid"); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath())); @@ -384,7 +384,7 @@ void testUUID_Disabled() { ModifiableValueMap props = AdaptTo.notNull(resource, ModifiableValueMap.class); props.put(JCR_UUID, "test-uuid"); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath())); @@ -398,7 +398,7 @@ void testWidths() { context.contentPolicyMapping(RESOURCE_TYPE, PN_DESIGN_ALLOWED_RENDITION_WIDTHS, new String[] { "100", "50", "200", "-123", "0", "junk" }); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath())); @@ -414,7 +414,7 @@ void testWidths() { @Test void testAreas() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_MAP, ImageAreaTestData.MAP_STRING)); @@ -430,7 +430,7 @@ void testWithImageAutoCropping_ContentPolicy() { PN_COMPONENT_MEDIA_FORMATS, new String[] { MediaFormats.SQUARE.getName() }, PN_COMPONENT_MEDIA_AUTOCROP, true); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath())); @@ -451,7 +451,7 @@ void testWithImageAutoCropping_ContentPolicy_WrappedResource() { PN_COMPONENT_MEDIA_FORMATS, new String[] { MediaFormats.SQUARE.getName() }, PN_COMPONENT_MEDIA_AUTOCROP, true); - Resource resource = context.create().resource(page.getContentResource().getPath() + "/image", + Resource resource = context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, DELEGATE_RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath()); diff --git a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java index 1c74151a..31b2e2d7 100644 --- a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java +++ b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java @@ -23,10 +23,12 @@ import static com.adobe.cq.wcm.core.components.models.Image.PN_DESIGN_LAZY_LOADING_ENABLED; import static com.adobe.cq.wcm.core.components.models.Image.PN_DESIGN_LAZY_THRESHOLD; import static com.adobe.cq.wcm.core.components.models.Image.PN_DISPLAY_POPUP_TITLE; +import static com.adobe.cq.wcm.core.components.models.Image.PN_IMAGE_FROM_PAGE_IMAGE; import static com.adobe.cq.wcm.core.components.models.Image.PN_IS_DECORATIVE; import static com.adobe.cq.wcm.core.components.models.Image.PN_MAP; import static com.adobe.cq.wcm.core.components.models.Image.PN_TITLE_VALUE_FROM_DAM; import static com.adobe.cq.wcm.core.components.models.Image.PN_UUID_DISABLED; +import static com.adobe.cq.wcm.core.components.models.Page.NN_PAGE_FEATURED_IMAGE; import static com.day.cq.commons.ImageResource.PN_ALT; import static com.day.cq.commons.jcr.JcrConstants.JCR_TITLE; import static com.day.cq.commons.jcr.JcrConstants.JCR_UUID; @@ -105,7 +107,7 @@ void setUp() { @Test @SuppressWarnings("deprecation") void testNoImage() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE)); Image underTest = AdaptTo.notNull(context.request(), Image.class); @@ -130,12 +132,19 @@ void testNoImage() { assertInvalidLink(underTest.getImageLink()); assertNull(underTest.getData()); + assertNull(underTest.getWidth()); + assertNull(underTest.getHeight()); + assertNull(underTest.getSizes()); + assertNull(underTest.getSrcset()); + assertNull(underTest.getSmartCropRendition()); + assertFalse(underTest.isDmImage()); + assertEquals(RESOURCE_TYPE, underTest.getExportedType()); } @Test void testInvalidAssetReference() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, "/content/dam/invalid")); @@ -148,7 +157,7 @@ void testInvalidAssetReference() { @Test @SuppressWarnings("deprecation") void testWithAssetImage() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), JCR_TITLE, "Resource Title", @@ -174,10 +183,43 @@ void testWithAssetImage() { assertInvalidLink(underTest.getImageLink()); } + @Test + @SuppressWarnings({ "deprecation", "null" }) + void testWithAssetImageFromPage() { + Page page1 = context.currentPage(context.create().page(page, "page1", null, + NN_PAGE_FEATURED_IMAGE, Map.of( + PN_MEDIA_REF_STANDARD, asset.getPath()))); + + context.currentResource(context.create().resource(page1, "image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, true, + JCR_TITLE, "Resource Title", + PN_ALT, "Resource Alt")); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + String expectedMediaUrl = DAM_ROOT + "/sample.jpg/_jcr_content/renditions/original./sample.jpg"; + + assertEquals(expectedMediaUrl, underTest.getSrc()); + assertEquals("Asset Title", underTest.getTitle()); + assertEquals("Asset Description", underTest.getAlt()); + assertEquals("", underTest.getUuid()); + assertNull(underTest.getLink()); + assertTrue(underTest.displayPopupTitle()); + assertEquals(asset.getPath(), underTest.getFileReference()); + assertArrayEquals(new int[] { 160 }, underTest.getWidths()); + assertEquals("/content/dam/sample/sample.jpg/_jcr_content/renditions/original.image_file.{.width}.0.file/sample.jpg", underTest.getSrcUriTemplate()); + assertFalse(underTest.isLazyEnabled()); + assertNull(underTest.getAreas()); + + assertValidMedia(underTest, expectedMediaUrl); + assertInvalidLink(underTest.getImageLink()); + } + @Test @SuppressWarnings("deprecation") void testWithUploadedImage() { - Resource imageResource = context.create().resource(page.getContentResource().getPath() + "/image", + Resource imageResource = context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, NN_MEDIA_INLINE_STANDARD + "Name", "file1.png"); context.load().binaryFile("/files/test.png", imageResource.getPath() + "/" + NN_MEDIA_INLINE_STANDARD, ContentType.PNG); @@ -208,7 +250,7 @@ void testWithUploadedImage() { void testWithImageAndLink() { enableDataLayer(context, true); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_LINK_TITLE, ExternalLinkType.ID, @@ -236,7 +278,7 @@ void testWithImageAndLink() { @Test @SuppressWarnings("deprecation") void testWithImageAndLink_Decorative() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_LINK_TITLE, ExternalLinkType.ID, @@ -260,7 +302,7 @@ void testWithImageAndLink_Decorative_ContentPolicy() { PN_IS_DECORATIVE, true, PN_DESIGN_LAZY_THRESHOLD, 10); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_LINK_TITLE, ExternalLinkType.ID, @@ -278,7 +320,7 @@ void testWithImageAndLink_Decorative_ContentPolicy() { @Test void testWithImage_TitleAltNotFormAsset() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), JCR_TITLE, "Resource Title", @@ -298,7 +340,7 @@ void testWithImage_TitleAltNotFormAsset_ContentPolicy() { PN_TITLE_VALUE_FROM_DAM, false, PN_ALT_VALUE_FROM_DAM, false); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), JCR_TITLE, "Resource Title", @@ -314,7 +356,7 @@ void testWithImage_TitleAltNotFormAsset_ContentPolicy() { void testWithNoImageAsset() { Asset pdfAsset = context.create().asset(DAM_ROOT + "/file1.pdf", "/files/test.pdf", ContentType.PDF); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, pdfAsset.getPath())); @@ -325,7 +367,7 @@ void testWithNoImageAsset() { @Test void testDisplayPopupTitle() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_DISPLAY_POPUP_TITLE, false)); @@ -340,7 +382,7 @@ void testDisplayPopupTitle_ContentPolicy() { context.contentPolicyMapping(RESOURCE_TYPE, PN_DISPLAY_POPUP_TITLE, false); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath())); @@ -354,7 +396,7 @@ void testLazyEnabled_ContentPolicy() { context.contentPolicyMapping(RESOURCE_TYPE, PN_DESIGN_LAZY_LOADING_ENABLED, false); // property is internally named "disabled", value is inverted - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath())); @@ -369,7 +411,7 @@ void testUUID() { ModifiableValueMap props = AdaptTo.notNull(resource, ModifiableValueMap.class); props.put(JCR_UUID, "test-uuid"); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath())); @@ -387,7 +429,7 @@ void testUUID_Disabled() { ModifiableValueMap props = AdaptTo.notNull(resource, ModifiableValueMap.class); props.put(JCR_UUID, "test-uuid"); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath())); @@ -404,7 +446,7 @@ void testWidths() { NN_COMPONENT_MEDIA_RESPONSIVEIMAGE_SIZES, Map.of("sizes", "100vw", "widths", "100,50"), PN_COMPONENT_MEDIA_AUTOCROP, true); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath())); @@ -420,7 +462,7 @@ void testWidths() { @Test void testAreas() { - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_MAP, ImageAreaTestData.MAP_STRING)); @@ -436,7 +478,7 @@ void testWithImageAutoCropping_ContentPolicy() { PN_COMPONENT_MEDIA_FORMATS, new String[] { MediaFormats.SQUARE.getName() }, PN_COMPONENT_MEDIA_AUTOCROP, true); - context.currentResource(context.create().resource(page.getContentResource().getPath() + "/image", + context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath())); @@ -457,7 +499,7 @@ void testWithImageAutoCropping_ContentPolicy_WrappedResource() { PN_COMPONENT_MEDIA_FORMATS, new String[] { MediaFormats.SQUARE.getName() }, PN_COMPONENT_MEDIA_AUTOCROP, true); - Resource resource = context.create().resource(page.getContentResource().getPath() + "/image", + Resource resource = context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, DELEGATE_RESOURCE_TYPE, PN_MEDIA_REF_STANDARD, asset.getPath()); From 2a032e98327e255a9ebe18b06a5f7f149631dca9 Mon Sep 17 00:00:00 2001 From: Stefan Seifert Date: Mon, 4 Sep 2023 13:45:05 +0200 Subject: [PATCH 09/10] update sample content, image from page --- .../impl/models/v2/ImageV2Impl.java | 2 +- .../impl/models/v3/ImageV3Impl.java | 6 ++- .../util/ComponentFeatureImageResolver.java | 11 ++++- .../impl/models/v3/ImageV3ImplTest.java | 18 ++++++- .../library/core-content/image/.content.xml | 47 ++++++++++++++++++- 5 files changed, 78 insertions(+), 6 deletions(-) diff --git a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2Impl.java b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2Impl.java index ec7693bd..c8f1c379 100644 --- a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2Impl.java +++ b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v2/ImageV2Impl.java @@ -68,7 +68,7 @@ public class ImageV2Impl extends ImageV3Impl implements LinkMixin { public static final String RESOURCE_TYPE = "wcm-io/wcm/core/components/image/v2/image"; @Override - protected Media buildMedia(boolean altFromAsset) { + protected Media buildMedia(boolean altFromAsset, boolean imageFromPageImage) { return HandlerUnwrapper.get(mediaHandler, resource) // disable dynamic media support as it is not compatible with the "src-pattern" concept .dynamicMediaDisabled(true) diff --git a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java index 845872aa..545c4968 100644 --- a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java +++ b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java @@ -136,6 +136,7 @@ private void activate() { lazyThreshold = currentStyle.get(PN_DESIGN_LAZY_THRESHOLD, 0); isDecorative = properties.get(PN_IS_DECORATIVE, currentStyle.get(PN_IS_DECORATIVE, false)); boolean altFromAsset = properties.get(PN_ALT_VALUE_FROM_DAM, currentStyle.get(PN_ALT_VALUE_FROM_DAM, true)); + boolean imageFromPageImage = properties.get(PN_IMAGE_FROM_PAGE_IMAGE, true); // resolve link - decorative images have no link and no alt text by definition if (isDecorative) { @@ -146,7 +147,7 @@ private void activate() { } // resolve media and properties from DAM asset - media = buildMedia(altFromAsset); + media = buildMedia(altFromAsset, imageFromPageImage); if (media.isValid() && !media.getRendition().isImage()) { // no image asset selected (cannot be rendered) - set to invalid @@ -161,10 +162,11 @@ private void activate() { } - protected Media buildMedia(boolean altFromAsset) { + protected Media buildMedia(boolean altFromAsset, boolean imageFromPageImage) { ComponentFeatureImageResolver imageResolver = new ComponentFeatureImageResolver(resource, getCurrentPage(), currentStyle, mediaHandler) .targetPage(getCurrentPage()) .altValueFromDam(altFromAsset) + .imageFromPageImage(imageFromPageImage) .mediaHandlerProperty(PROP_CSS_CLASS, "cmp-image__image") .mediaHandlerProperty("itemprop", "contentUrl"); String imageTitle = title; diff --git a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/util/ComponentFeatureImageResolver.java b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/util/ComponentFeatureImageResolver.java index 974b9dd1..9ed91ce4 100644 --- a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/util/ComponentFeatureImageResolver.java +++ b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/util/ComponentFeatureImageResolver.java @@ -55,7 +55,7 @@ public class ComponentFeatureImageResolver { private final MediaHandler mediaHandler; private final Map mediaHandlerProperties = new HashMap<>(); - private final boolean imageFromPageImage; + private boolean imageFromPageImage; private final boolean altValueFromPageImage; private boolean altValueFromDam; private final boolean isDecorative; @@ -105,6 +105,15 @@ public ComponentFeatureImageResolver mediaHandlerProperty(@NotNull String key, @ return this; } + /** + * @param value Image from page image + * @return self + */ + public ComponentFeatureImageResolver imageFromPageImage(boolean value) { + this.imageFromPageImage = value; + return this; + } + /** * @param value Alt Value from DAM * @return self diff --git a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java index 31b2e2d7..3800cde8 100644 --- a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java +++ b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java @@ -159,6 +159,7 @@ void testInvalidAssetReference() { void testWithAssetImage() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath(), JCR_TITLE, "Resource Title", PN_ALT, "Resource Alt")); @@ -192,7 +193,6 @@ void testWithAssetImageFromPage() { context.currentResource(context.create().resource(page1, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, - PN_IMAGE_FROM_PAGE_IMAGE, true, JCR_TITLE, "Resource Title", PN_ALT, "Resource Alt")); @@ -221,6 +221,7 @@ void testWithAssetImageFromPage() { void testWithUploadedImage() { Resource imageResource = context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, NN_MEDIA_INLINE_STANDARD + "Name", "file1.png"); context.load().binaryFile("/files/test.png", imageResource.getPath() + "/" + NN_MEDIA_INLINE_STANDARD, ContentType.PNG); context.currentResource(imageResource); @@ -252,6 +253,7 @@ void testWithImageAndLink() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_LINK_TITLE, ExternalLinkType.ID, PN_LINK_EXTERNAL_REF, "http://myhost")); @@ -280,6 +282,7 @@ void testWithImageAndLink() { void testWithImageAndLink_Decorative() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_LINK_TITLE, ExternalLinkType.ID, PN_LINK_EXTERNAL_REF, "http://myhost", @@ -304,6 +307,7 @@ void testWithImageAndLink_Decorative_ContentPolicy() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_LINK_TITLE, ExternalLinkType.ID, PN_LINK_EXTERNAL_REF, "http://myhost")); @@ -322,6 +326,7 @@ void testWithImageAndLink_Decorative_ContentPolicy() { void testWithImage_TitleAltNotFormAsset() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath(), JCR_TITLE, "Resource Title", PN_ALT, "Resource Alt", @@ -342,6 +347,7 @@ void testWithImage_TitleAltNotFormAsset_ContentPolicy() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath(), JCR_TITLE, "Resource Title", PN_ALT, "Resource Alt")); @@ -358,6 +364,7 @@ void testWithNoImageAsset() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, pdfAsset.getPath())); Image underTest = AdaptTo.notNull(context.request(), Image.class); @@ -369,6 +376,7 @@ void testWithNoImageAsset() { void testDisplayPopupTitle() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_DISPLAY_POPUP_TITLE, false)); @@ -384,6 +392,7 @@ void testDisplayPopupTitle_ContentPolicy() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath())); Image underTest = AdaptTo.notNull(context.request(), Image.class); @@ -398,6 +407,7 @@ void testLazyEnabled_ContentPolicy() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath())); Image underTest = AdaptTo.notNull(context.request(), Image.class); @@ -413,6 +423,7 @@ void testUUID() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath())); Image underTest = AdaptTo.notNull(context.request(), Image.class); @@ -431,6 +442,7 @@ void testUUID_Disabled() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath())); Image underTest = AdaptTo.notNull(context.request(), Image.class); @@ -448,6 +460,7 @@ void testWidths() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath())); Image underTest = AdaptTo.notNull(context.request(), Image.class); @@ -464,6 +477,7 @@ void testWidths() { void testAreas() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath(), PN_MAP, ImageAreaTestData.MAP_STRING)); @@ -480,6 +494,7 @@ void testWithImageAutoCropping_ContentPolicy() { context.currentResource(context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath())); Image underTest = AdaptTo.notNull(context.request(), Image.class); @@ -501,6 +516,7 @@ void testWithImageAutoCropping_ContentPolicy_WrappedResource() { Resource resource = context.create().resource(page, "image", PROPERTY_RESOURCE_TYPE, DELEGATE_RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, PN_MEDIA_REF_STANDARD, asset.getPath()); // set context resource to wrapped resource diff --git a/examples/content-packages/examples-sample-content/jcr_root/content/wcmio-core-components-examples/library/core-content/image/.content.xml b/examples/content-packages/examples-sample-content/jcr_root/content/wcmio-core-components-examples/library/core-content/image/.content.xml index f97d1631..62342e5a 100644 --- a/examples/content-packages/examples-sample-content/jcr_root/content/wcmio-core-components-examples/library/core-content/image/.content.xml +++ b/examples/content-packages/examples-sample-content/jcr_root/content/wcmio-core-components-examples/library/core-content/image/.content.xml @@ -1,5 +1,5 @@ - @@ -118,6 +119,7 @@ altValueFromDAM="true" displayPopupTitle="false" fileReference="/content/dam/core-components-examples/library/sample-assets/lava-into-ocean.jpg" + imageFromPageImage="false" isDecorative="false" titleValueFromDAM="true"/> @@ -164,6 +166,7 @@ altValueFromDAM="true" displayPopupTitle="true" fileReference="/content/dam/core-components-examples/library/sample-assets/snowy-mountain-glacier.jpg" + imageFromPageImage="false" isDecorative="false" linkURL="/content/wcmio-core-components-examples/library/core-content/image" titleValueFromDAM="true"/> @@ -211,6 +214,7 @@ altValueFromDAM="true" displayPopupTitle="true" fileReference="/content/dam/core-components-examples/library/components/component.svg" + imageFromPageImage="false" isDecorative="false" titleValueFromDAM="true"/> @@ -234,6 +238,47 @@ reference="../../component"/> + + + + + + + + + + + + Date: Mon, 4 Sep 2023 13:51:17 +0200 Subject: [PATCH 10/10] svg image --- .../components/impl/models/v3/ImageV3Impl.java | 6 +++++- .../impl/models/v3/ImageV3ImplTest.java | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java index 545c4968..b9069af5 100644 --- a/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java +++ b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java @@ -229,7 +229,11 @@ protected List buildRenditionWidths() { * @return Url pattern */ protected String buildSrcPattern() { - UriTemplate uriTempalte = media.getRendition().getUriTemplate(UriTemplateType.SCALE_WIDTH); + Rendition rendition = media.getRendition(); + if (!rendition.isImage() || rendition.isVectorImage()) { + return null; + } + UriTemplate uriTempalte = rendition.getUriTemplate(UriTemplateType.SCALE_WIDTH); return StringUtils.replace(uriTempalte.getUriTemplate(), URI_TEMPLATE_PLACEHOLDER_WIDTH, WIDTH_PLACEHOLDER); } diff --git a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java index 3800cde8..57522a9e 100644 --- a/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java +++ b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java @@ -184,6 +184,23 @@ void testWithAssetImage() { assertInvalidLink(underTest.getImageLink()); } + @Test + void testWithAssetImage_SVG() { + Asset svgAsset = context.create().asset(DAM_ROOT + "/sample.svg", 160, 90, ContentType.SVG); + + context.currentResource(context.create().resource(page, "image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, + PN_MEDIA_REF_STANDARD, svgAsset.getPath())); + + Image underTest = AdaptTo.notNull(context.request(), Image.class); + + String expectedMediaUrl = DAM_ROOT + "/sample.svg/_jcr_content/renditions/original./sample.svg"; + + assertEquals(expectedMediaUrl, underTest.getSrc()); + assertNull(underTest.getSrcUriTemplate()); + } + @Test @SuppressWarnings({ "deprecation", "null" }) void testWithAssetImageFromPage() {