Package com.activecq.tools.quickimage.impl

Source Code of com.activecq.tools.quickimage.impl.QuickImageResourceDecorator

/*
* Copyright 2013 david gonzalez.
*
*  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.
*/
package com.activecq.tools.quickimage.impl;

import com.activecq.tools.quickimage.QuickImageResource;
import com.day.cq.commons.PathInfo;
import com.day.cq.commons.jcr.JcrConstants;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.*;
import org.apache.sling.api.request.RequestPathInfo;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceDecorator;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.osgi.framework.Constants;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* User: david
*
* Order or parameter application:
*
* Crop (x,y,width,height)
* Resize (width, height)
* Rotate (degrees)
*
*/
@Component(
        label = "ActiveCQ - QuickImage Resource Decorator",
        description = "QuickImage Resource Decorator that allows image resources to be rendered using the parameterization of the Adobe CQ Image Component (width, height, rotation and crop) via constructed URIs.",
        metatype = true,
        immediate = false,
        configurationFactory = true)
@Properties({
        @Property(
                label="Vendor",
                name= Constants.SERVICE_VENDOR,
                value="ActiveCQ",
                propertyPrivate=true
        )
})
@Service
public class QuickImageResourceDecorator implements ResourceDecorator {
    private final Logger log = LoggerFactory.getLogger(this.getClass());
    private static final int SYSTEM_MAX_DIMENSION = 10000;

    /**
     * OSGi Properties *
     */
    private static final String DEFAULT_RESOURCE_TYPE = "foundation/components/parbase";
    private String resourceType = DEFAULT_RESOURCE_TYPE;
    @Property(label = ".img Resource Type", description = "Resource type to apply to QuickImage sling:resourceType property. This resourceType must implement the .img selector script. [Default: foundation/components/parbase]", value = DEFAULT_RESOURCE_TYPE)
    private static final String PROP_RESOURCE_TYPE = "prop.resource-type";

    private static final int DEFAULT_MAX_WIDTH = 2048;
    private int maxWidth = DEFAULT_MAX_WIDTH;
    @Property(label = "Max Image Width", description = "Maximum width dimension in pixels allowable for resizing. [Default: 2048]", intValue = DEFAULT_MAX_WIDTH)
    private static final String PROP_MAX_WIDTH = "prop.image.max-width";

    private static final int DEFAULT_MAX_HEIGHT = 2048;
    private int maxHeight = DEFAULT_MAX_HEIGHT;
    @Property(label = "Max Image Height", description = "Maximum height dimension in pixels allowable for resizing. [Default: 2048]", intValue = DEFAULT_MAX_HEIGHT)
    private static final String PROP_MAX_HEIGHT = "prop.image.max-height";

    private static final String[] DEFAULT_EXTENSION_WHITELIST = new String[] {"png", "jpg", "gif", "jpeg", "bmp", "tif", "tiff", "pic", "pict"};
    private String[] extensionWhiteList = DEFAULT_EXTENSION_WHITELIST;
    @Property(label = "Extension White List", description = "List of resource type extensions this decorator can decorate. [Default: \"png\", \"jpg\", \"gif\", \"jpeg\", \"bmp\", \"tif\", \"tiff\", \"pic\", \"pict\"]", value = { "png", "jpg", "gif", "jpeg", "bmp", "tif", "tiff", "pic", "pict" })
    private static final String PROP_EXTENSION_WHITELIST = "prop.white-list.extensions";

    private static final String[] DEFAULT_PATH_WHITELIST = new String[] { "^/content/.*", "^/etc/designs/.*", "^/apps/.*", "^/libs/.*" };
    private String[] pathWhiteList = DEFAULT_PATH_WHITELIST;
    @Property(label = "Path White List", description = "Regex for paths this decorator can decorate. [Default: \"^/content/.*\", \"^/etc/designs/.*\"]", value = { "^/content/.*", "^/etc/designs/.*", "^/apps/.*", "^/libs/.*" })
    private static final String PROP_PATH_WHITELIST = "prop.white-list.paths";

    private static final String[] DEFAULT_WIDTH_WHITELIST = new String[] { };
    private String[] widthWhiteList = DEFAULT_WIDTH_WHITELIST;
    @Property(label = "Width White List", description = "List of widths in pixels that are allowable for re-sizing. Blank allows all. System maximum of 10000. [Default: All]", value = { })
    private static final String PROP_WIDTH_WHITELIST = "prop.white-list.widths";

    private static final String[] DEFAULT_HEIGHT_WHITELIST = new String[] { };
    private String[] heightWhiteList = DEFAULT_HEIGHT_WHITELIST;
    @Property(label = "Height White List", description = "List of heights in pixels that are allowable for re-sizing. Blank allows all. System maximum of 10000. [Default: All]", value = { })
    private static final String PROP_HEIGHT_WHITELIST = "prop.white-list.heights";

    private static final String[] DEFAULT_ROTATE_WHITELIST = new String[] { };
    private String[] rotateWhiteList = DEFAULT_ROTATE_WHITELIST;
    @Property(label = "Rotation White List", description = "List of widths in pixels that are allowable for rotation. Blank allows all.  [Default: All]", value = { })
    private static final String PROP_ROTATE_WHITELIST = "prop.white-list.rotations";


    /**
     * Default cstor, will return the usual Resource object as Request is null
     *
     * @param resource
     * @return
     */
    public Resource decorate(Resource resource) {
        return this.decorate(resource, null);
    }

    /**
     * Returns a QuickImage resource
     *
     * @param resource
     * @param request
     * @return
     */
    public Resource decorate(Resource resource, HttpServletRequest request) {
        // Basic condition checking
        if(!accepts(resource, request)) {
            return resource;
        }

        final String suffix = getSuffix(request.getRequestURI());

        // Get suffix values from the Request URI
        String width = parseSuffix(suffix, com.activecq.tools.quickimage.Constants.KEY_WIDTH);
        String height = parseSuffix(suffix, com.activecq.tools.quickimage.Constants.KEY_HEIGHT);
        String rotate = parseSuffix(suffix, com.activecq.tools.quickimage.Constants.KEY_ROTATE);
        String crop = parseSuffix(suffix, com.activecq.tools.quickimage.Constants.KEY_CROP, 4, ",");

        // Scrub suffix param data
        width = scrubDimension(width,  maxWidth, widthWhiteList);
        height = scrubDimension(height, maxHeight, heightWhiteList);
        rotate = scrubRotate(rotate, rotateWhiteList);

        // Create QuickImageResource
        final QuickImageResource imageResource = new QuickImageResource(resource, resourceType);
        imageResource.setCrop(crop);
        imageResource.setRotate(rotate);
        imageResource.setWidth(width);
        imageResource.setHeight(height);
        return imageResource;
    }

    /**
     * Method used to determine if the request can be handled by the QuickImage Resource Decorator
     *
     * @param resource
     * @param request
     * @com.activecq.tools.quickimage.Constants
     */
    private boolean accepts(Resource resource, HttpServletRequest request) {
        if(resource == null || request == null) {
            return false;
        }

        final RequestPathInfo pathInfo = new PathInfo(request.getRequestURI());
        final String suffix = getSuffix(request.getRequestURI());
        final String extension = pathInfo.getExtension();
        final String quick = parseSuffix(suffix, com.activecq.tools.quickimage.Constants.KEY_QUICK, 0, null);

        if(StringUtils.isBlank(extension) ||
                StringUtils.isBlank(suffix) ||
                !ResourceUtil.isA(resource, JcrConstants.NT_FILE) ||
                !isWhiteListedMatch(resource.getPath(), pathWhiteList) ||
                !isWhiteListed(extension, extensionWhiteList) ||
                !StringUtils.equals(quick, String.valueOf(true))) {
            return false;
        }

        return true;
    }

    /**
     * Wrapper method for parseSuffix(String suffix, String key, int size, String delimiter)
     *
     * Passes
     * size: 0
     * delimiter: null
     *
     * @param suffix
     * @param key
     * @return
     */
    private String parseSuffix(String suffix, String key) {
        return parseSuffix(suffix, key, 1, null);
    }

    /**
     * Get key/values from the suffix string
     *
     * @param suffix Suffix string (foo/bar/pets/cat/dog/nuts.bolts)
     * @param key Suffix segment to treat as the key
     * @param size Number of suffix value segments after the key segment to return
     * @param delimiter Delimiter used to join the value segments
     * @return "true" if key exists and size = 0, segment value is key exists and size = 1, joined segment values using delimiter if key exists and size > 1
     */
    private String parseSuffix(String suffix, String key, int size, String delimiter) {
        final String[] suffixes = StringUtils.split(suffix, "/");

        final int index = ArrayUtils.indexOf(suffixes, key);
        if(index < 0 || (index + size) >= suffixes.length) { return null; }

        if(size < 1) {
            return String.valueOf(true);
        }

        final List<String> result = new ArrayList<String>();
        for(int i = index + 1; i <= index + size; i++) {
            result.add(suffixes[i]);
        }

        delimiter = StringUtils.isBlank(delimiter) ? "" : delimiter;
        return StringUtils.join(result, delimiter);
    }

    /**
     * Returns the Suffix string from the RequestURI due to bug in CQ Common PathInfo parsing algorithm
     *
     * @param uri
     * @return
     */
    private String getSuffix(String uri) {
        if(!StringUtils.contains(uri, '.'))  {
            return null;
        }

        String suffix = StringUtils.substringAfter(uri, ".");
        suffix = StringUtils.substringAfter(suffix, "/");

        if(StringUtils.isNotBlank(suffix)) {
            return suffix;
        }

        return null;
    }

    /**
     * Scrub width and height image resize parameters based on QuickImage configurations
     *
     * @param dimension
     * @param max
     * @param whitelist
     * @return
     */
    private String scrubDimension(String dimension, int max, String[] whitelist) {
        final String DEFAULT_DIMENSION = "0";
        try {
            if(StringUtils.isBlank(dimension)) {
                return DEFAULT_DIMENSION;
            }

            final int d = Integer.parseInt(dimension);

            if(d < 0 || d > max || d > SYSTEM_MAX_DIMENSION) {
                // falls outside of normal bounds, return zero
                return DEFAULT_DIMENSION;
            }

            if(isWhiteListed(dimension, whitelist)) {
                return dimension;
            }

            // Dimension is not a white listed value
            return DEFAULT_DIMENSION;
        } catch (Exception ex) {
            // Error occurred parsing dimension value, use the DEFAULT_DIMENSION
            // which forces the image to render using native dimension
            return DEFAULT_DIMENSION;
        }
    }

    /**
     * Validates and normalizaed rotation parameter.
     *
     * This normalized values to exist between -360 and 360.
     *
     * @param rotate
     * @param whiteList
     * @return
     */
    private String scrubRotate(String rotate, String[] whiteList) {
        final String DEFAULT_ROTATE = "0";

        try {
            if(StringUtils.isBlank(rotate)) {
                return DEFAULT_ROTATE;
            }

            final long r = Long.parseLong(rotate) % 360;

            if(isWhiteListed(rotate, whiteList)) {
                return String.valueOf(r);
            }

            // Rotate is not a white listed value
            return DEFAULT_ROTATE;
        } catch (Exception ex) {
            // Error occurred parsing rotate value, use the DEFAULT_ROTATE
            // which forces the image to render using 0 rotation
            return DEFAULT_ROTATE;
        }
    }


    /**
     * Checks if the value exists in the parameter whitelist
     *
     * @param value
     * @param whitelist
     * @return true if the value is in the whitelist (case sensitive)
     */
    private boolean isWhiteListed(String value, String[] whitelist) {
        if(whitelist == null || whitelist.length == 0) {
            return true;
        }

        return ArrayUtils.contains(whitelist, value);
    }

    /**
     * Checks if the value matches an item in the parameter whiteList
     *
     * @param path
     * @param whiteList
     * @return true if the value matches a value in the whiteList (case sensitive)
     */
    private boolean isWhiteListedMatch(String path, String[] whiteList) {
        if(whiteList == null || whiteList.length == 0) {
            return true;
        } else if(StringUtils.isBlank(path)) {
            return false;
        }

        for(String regex : whiteList) {
            if(!StringUtils.isBlank(regex)) {
                if(path.matches(regex)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * OSGi Component Methods *
     */
    @Activate
    protected void activate(final ComponentContext componentContext) throws Exception {
        configure(componentContext);
    }

    @Deactivate
    protected void deactivate(ComponentContext ctx) {
    }

    private void configure(final ComponentContext componentContext) {
        final Map<String, String> properties = (Map<String, String>) componentContext.getProperties();

        this.resourceType = PropertiesUtil.toString(properties.get(PROP_RESOURCE_TYPE), DEFAULT_RESOURCE_TYPE);

        this.maxWidth = PropertiesUtil.toInteger(properties.get(PROP_MAX_WIDTH), DEFAULT_MAX_WIDTH);
        this.maxHeight = PropertiesUtil.toInteger(properties.get(PROP_MAX_HEIGHT), DEFAULT_MAX_HEIGHT);

        this.extensionWhiteList = PropertiesUtil.toStringArray(properties.get(PROP_EXTENSION_WHITELIST), DEFAULT_EXTENSION_WHITELIST);
        this.pathWhiteList = PropertiesUtil.toStringArray(properties.get(PROP_PATH_WHITELIST), DEFAULT_PATH_WHITELIST);

        this.widthWhiteList = PropertiesUtil.toStringArray(properties.get(PROP_WIDTH_WHITELIST), DEFAULT_WIDTH_WHITELIST);
        this.heightWhiteList = PropertiesUtil.toStringArray(properties.get(PROP_HEIGHT_WHITELIST), DEFAULT_HEIGHT_WHITELIST);
        this.rotateWhiteList = PropertiesUtil.toStringArray(properties.get(PROP_ROTATE_WHITELIST), DEFAULT_ROTATE_WHITELIST);
    }
}
TOP

Related Classes of com.activecq.tools.quickimage.impl.QuickImageResourceDecorator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.