- /*
- * #%L
- * wcm.io
- * %%
- * Copyright (C) 2019 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.handler.media.impl.ipeconfig;
- import static io.wcm.handler.media.impl.ipeconfig.CroppingRatios.MEDIAFORMAT_FREE_CROP;
- import static io.wcm.handler.media.impl.ipeconfig.PathParser.NN_ASPECT_RATIOS;
- import static io.wcm.handler.media.impl.ipeconfig.PathParser.NN_CONFIG;
- import static io.wcm.handler.media.impl.ipeconfig.PathParser.NN_MEDIA_FORMAT;
- import java.util.Iterator;
- import java.util.LinkedHashMap;
- import java.util.Map;
- import java.util.Set;
- import java.util.SortedSet;
- import java.util.TreeSet;
- import org.apache.commons.lang3.StringUtils;
- import org.apache.sling.api.resource.Resource;
- import org.apache.sling.api.resource.ResourceResolver;
- import org.apache.sling.api.resource.SyntheticResource;
- import org.apache.sling.spi.resource.provider.ResolveContext;
- import org.apache.sling.spi.resource.provider.ResourceContext;
- import org.apache.sling.spi.resource.provider.ResourceProvider;
- import org.jetbrains.annotations.NotNull;
- import org.jetbrains.annotations.Nullable;
- import org.osgi.service.component.annotations.Component;
- import com.day.cq.wcm.api.components.ComponentManager;
- import io.wcm.handler.media.format.MediaFormat;
- import io.wcm.handler.media.format.MediaFormatHandler;
- import io.wcm.sling.commons.adapter.AdaptTo;
- /**
- * Resource provider that overlays a IPE config resource with a dynamically generated
- * set of cropping aspect ratios derived from given set of media formats.
- *
- * <p>
- * URL pattern for resource access:<br>
- * <code>/wcmio:mediaHandler/ipeConfig/{componentContentPath}/wcmio:mediaFormat/{mf1}/{mf2}/.../wcmio:config/{relativeConfigPath}</code>
- * </p>
- */
- @Component(service = ResourceProvider.class, property = {
- ResourceProvider.PROPERTY_NAME + "=wcmioHandlerIPEConfig",
- ResourceProvider.PROPERTY_ROOT + "=" + IPEConfigResourceProvider.IPECONFIG_OVERLAY_ROOTPATH
- })
- public class IPEConfigResourceProvider extends ResourceProvider<Void> {
- /**
- * Root path for IPE config overlay resources.
- */
- @SuppressWarnings("java:S1075") // no file path
- public static final String IPECONFIG_OVERLAY_ROOTPATH = "/wcmio:mediaHandler/ipeConfig";
- @Override
- public @Nullable Resource getResource(@NotNull ResolveContext resolveContext, @NotNull String path,
- @NotNull ResourceContext resourceContext, @Nullable Resource parent) {
- PathParser parser = new PathParser(path);
- if (!parser.isValid()) {
- return null;
- }
- ResourceResolver resolver = resolveContext.getResourceResolver();
- if (parser.isAspectRatiosNode()) {
- // simulate 'aspectRatios' node
- return buildAspectRatiosResource(resolver, path);
- }
- else if (parser.isAspectRatioItem()) {
- // simulate 'aspectRatios/xxx' node
- String mediaFormatName = parser.getAspectRatioItemName();
- if (parser.getMediaFormatNames().contains(mediaFormatName)) {
- return buildAspectRatioItemResource(resolver, path, mediaFormatName, parser);
- }
- }
- else {
- // return wrapped overlaid resource
- String overlayResourcePath = getIpeConfigPath(resolver, parser);
- if (StringUtils.isNotEmpty(overlayResourcePath)) {
- Resource overlayResource = resolver.getResource(overlayResourcePath);
- if (overlayResource != null) {
- return new OverlayResource(overlayResource, path);
- }
- }
- }
- return null;
- }
- @Override
- public @Nullable Iterator<Resource> listChildren(@NotNull ResolveContext resolveContext, @NotNull Resource resource) {
- Map<String, Resource> childMap = getOverlayedResourceChilden(resource);
- String path = resource.getPath();
- PathParser parser = new PathParser(path);
- if (!parser.isValid()) {
- return null;
- }
- ResourceResolver resolver = resolveContext.getResourceResolver();
- if (parser.isPluginsCropNode()) {
- // add simulated 'aspectRatios' node
- childMap.put(NN_ASPECT_RATIOS, buildAspectRatiosResource(resolver, path + "/" + NN_ASPECT_RATIOS));
- }
- else if (parser.isAspectRatiosNode()) {
- // add simulated 'aspectRatios/xxx' nodes
- childMap.clear();
- for (String mediaFormatName : parser.getMediaFormatNames()) {
- Resource item = buildAspectRatioItemResource(resolver, path + "/" + mediaFormatName, mediaFormatName, parser);
- if (item != null) {
- childMap.put(mediaFormatName, item);
- }
- }
- }
- if (childMap.isEmpty()) {
- return null;
- }
- else {
- return childMap.values().iterator();
- }
- }
- /**
- * Gets children of overlaid resource and converts children to {@link OverlayResource}.
- * @param resource Requested resources
- * @return Map with all children
- */
- private Map<String, Resource> getOverlayedResourceChilden(Resource resource) {
- Map<String, Resource> childMap = new LinkedHashMap<>();
- if (resource instanceof OverlayResource) {
- Resource overlayResource = ((OverlayResource)resource).getOverlayedResource();
- Iterator<Resource> childrenIterator = overlayResource.listChildren();
- while (childrenIterator.hasNext()) {
- Resource child = childrenIterator.next();
- childMap.put(child.getName(), new OverlayResource(child,
- resource.getPath() + "/" + child.getName()));
- }
- }
- return childMap;
- }
- /**
- * Build resource for /aspectRatios node
- * @param resolver Resource resolver
- * @param path Path
- * @return Resource
- */
- private Resource buildAspectRatiosResource(ResourceResolver resolver, String path) {
- return new SyntheticResource(resolver, path, null);
- }
- /**
- * Build virtual resource with name and aspect ratio of given media format.
- * @param resolver Resource resolver
- * @param path Path
- * @param mediaFormatName Media format name
- * @param parser Path parser
- * @return Resource or null if media format not found or has no valid ratio
- */
- private Resource buildAspectRatioItemResource(ResourceResolver resolver, String path, String mediaFormatName,
- PathParser parser) {
- Resource componentContent = resolver.getResource(parser.getComponentContentPath());
- if (componentContent != null) {
- MediaFormatHandler mediaFormatHandler = AdaptTo.notNull(componentContent, MediaFormatHandler.class);
- MediaFormat mediaFormat = getMediaFormat(mediaFormatName, mediaFormatHandler);
- if (mediaFormat != null) {
- return new AspectRatioResource(resolver, mediaFormat, path);
- }
- }
- return null;
- }
- private MediaFormat getMediaFormat(String mediaFormatName, MediaFormatHandler mediaFormatHandler) {
- if (StringUtils.equals(mediaFormatName, MEDIAFORMAT_FREE_CROP.getName())) {
- return MEDIAFORMAT_FREE_CROP;
- }
- else {
- return mediaFormatHandler.getMediaFormat(mediaFormatName);
- }
- }
- /**
- * Get IPE config path from component associated with given resource and append the relative
- * config path from current resource request.
- * @param resolver Resource resolver
- * @param parser Path parser
- * @return Path or null
- */
- private String getIpeConfigPath(ResourceResolver resolver, PathParser parser) {
- Resource componentContent = resolver.getResource(parser.getComponentContentPath());
- if (componentContent != null) {
- ComponentManager componentManager = AdaptTo.notNull(resolver, ComponentManager.class);
- com.day.cq.wcm.api.components.Component component = componentManager.getComponentOfResource(componentContent);
- if (component != null
- && component.getEditConfig() != null
- && component.getEditConfig().getInplaceEditingConfig() != null) {
- String ipeConfigPath = component.getEditConfig().getInplaceEditingConfig().getConfigPath();
- if (StringUtils.isNotEmpty(ipeConfigPath)) {
- return ipeConfigPath + StringUtils.defaultString(parser.getRelativeConfigPath());
- }
- }
- }
- return null;
- }
- /**
- * Build path to overlaid IPE configuration services by this resource provider.
- * @param componentContentPath Content resource path containing reference component with image IPE enabled
- * @param mediaFormatNames Media format names
- * @return Path
- */
- public static String buildPath(String componentContentPath, Set<String> mediaFormatNames) {
- SortedSet<String> sortedMediaFormatNames = new TreeSet<>(mediaFormatNames);
- return IPECONFIG_OVERLAY_ROOTPATH + componentContentPath
- + "/" + NN_MEDIA_FORMAT + "/" + StringUtils.join(sortedMediaFormatNames, "/")
- + "/" + NN_CONFIG;
- }
- }