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/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..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 @@ -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, boolean imageFromPageImage) { + 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 new file mode 100644 index 00000000..b9069af5 --- /dev/null +++ b/bundles/core/src/main/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3Impl.java @@ -0,0 +1,372 @@ +/* + * #%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 + */ + 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 + protected Style currentStyle; + @Self + protected LinkHandler linkHandler; + @Self + protected MediaHandler mediaHandler; + @Self + protected UrlHandler urlHandler; + + @ValueMapValue(name = PN_ALT, injectionStrategy = InjectionStrategy.OPTIONAL) + protected @Nullable String alt; + @ValueMapValue(name = JCR_TITLE, injectionStrategy = InjectionStrategy.OPTIONAL) + protected @Nullable String title; + + @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL) + protected @Nullable String imageCrop; + @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL) + protected @Nullable String imageRotate; + + 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; + + protected List widths = Collections.emptyList(); + protected 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)); + 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) { + link = new LinkWrapper(linkHandler.invalid()); + } + else { + link = new LinkWrapper(HandlerUnwrapper.get(linkHandler, resource).build()); + } + + // resolve media and properties from DAM asset + media = buildMedia(altFromAsset, imageFromPageImage); + + 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(); + areas = buildAreas(); + } + + } + + 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; + if (displayPopupTitle && imageTitle != null) { + imageResolver.mediaHandlerProperty("title", imageTitle); + } + return imageResolver.buildMedia(); + } + + protected List buildAreas() { + return 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; + } + } + } + } + } + + /** + * 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::getWidth) + .distinct() + .sorted() + .collect(Collectors.toList()); + } + + /** + * Build image url pattern via Media Handler URI template. + * @return Url pattern + */ + protected String buildSrcPattern() { + 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); + } + + @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(forRemoval = true) + 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; + } + + + // --- data layer --- + + @Override + @JsonIgnore + @SuppressWarnings("null") + public @NotNull ImageData getComponentData() { + return DataLayerBuilder.extending(super.getComponentData()).asImageComponent() + .withTitle(this::getTitle) + .withLinkUrl(() -> this.link.getLinkObject().getUrl()) + .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/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..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,9 +55,9 @@ 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 final boolean altValueFromDam; + private boolean altValueFromDam; private final boolean isDecorative; private final String componentAltText; @@ -105,6 +105,24 @@ 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 + */ + public ComponentFeatureImageResolver altValueFromDam(boolean value) { + this.altValueFromDam = value; + return this; + } + /** * Build media after resolving feature images and alt. texts. * @return Media @@ -167,6 +185,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/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..dbc64c00 --- /dev/null +++ b/bundles/core/src/main/webapp/app-root/components/image/v3/README.md @@ -0,0 +1,25 @@ +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 +* 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..174824b8 --- /dev/null +++ b/bundles/core/src/main/webapp/app-root/components/image/v3/image.json @@ -0,0 +1,196 @@ +{ + "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", + + "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 + } + } + } + } + } + + } + } + } + } + } + +} 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/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..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); @@ -201,11 +201,11 @@ void testWithUploadedImage() { } @Test - @SuppressWarnings({ "null", "deprecation" }) + @SuppressWarnings("deprecation") 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 new file mode 100644 index 00000000..57522a9e --- /dev/null +++ b/bundles/core/src/test/java/io/wcm/wcm/core/components/impl/models/v3/ImageV3ImplTest.java @@ -0,0 +1,548 @@ +/* + * #%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_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; +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, "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()); + + 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, "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, "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")); + + 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 + 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() { + 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, + 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, "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); + + 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, "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")); + + 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, "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", + 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, "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")); + + 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, "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", + 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, "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")); + + 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, "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); + + assertInvalidMedia(underTest); + } + + @Test + 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)); + + 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, "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); + + 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, "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); + + 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, "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); + + 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, "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); + + 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, "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); + + 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, "image", + PROPERTY_RESOURCE_TYPE, RESOURCE_TYPE, + PN_IMAGE_FROM_PAGE_IMAGE, false, + 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, "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); + + 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, "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 + 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); diff --git a/changes.xml b/changes.xml index e53f3a9f..ad39c420 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/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/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..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 @@ - + linkURL="https://github.com/wcm-io/wcm-io-wcm-core-components/tree/develop/bundles/core/src/main/webapp/app-root/components/image/v3"/> @@ -114,10 +115,11 @@ sling:resourceType="core-components-examples/components/demo/component"> @@ -160,10 +162,11 @@ sling:resourceType="core-components-examples/components/demo/component"> @@ -207,10 +210,11 @@ sling:resourceType="core-components-examples/components/demo/component"> @@ -234,6 +238,47 @@ reference="../../component"/> + + + + + + + + + + + + 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 48ca58b2..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 @@ -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 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}/ diff --git a/src/site/markdown/components.md b/src/site/markdown/components.md index fc69bda1..2ba09312 100644 --- a/src/site/markdown/components.md +++ b/src/site/markdown/components.md @@ -17,7 +17,7 @@ _Enhanced with_ 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)