package com.cognifide.slice.cq.taglib.include;
/*-
* #%L
* Slice - CQ Taglib
* $Id:$
* $HeadURL:$
* %%
* Copyright (C) 2012 Cognifide Limited
* %%
* 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%
*/
import java.util.Arrays;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;
import org.apache.commons.lang.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import com.cognifide.slice.cq.taglib.AbstractBodyTag;
import com.day.cq.wcm.api.WCMMode;
import com.day.cq.wcm.api.components.Component;
import com.day.cq.wcm.api.components.ComponentManager;
import com.day.cq.wcm.api.components.IncludeOptions;
/**
* Extended version of the cq:include tag. In addition to the original behaviour allows controlling the WCM
* mode inside the include, and disabling the decoration markup that's added by WCM by default. The control is
* done either at component level - using custom properties stored in a component's node, or at the include
* level - using tag's attributes. Tag's attributes have higher priority than component's meta-data.
*
* @author Jan Kuźniak
* @author Witold Szczerba
* @author maciej.majchrzak
*/
public class IncludeTag extends AbstractBodyTag {
private static final long serialVersionUID = -214258163075048064L;
static final DecorationMode DEFAULT_DECORATION_MODE = DecorationMode.ALL;
static final boolean DEFAULT_DISABLE_WCM = false;
private Boolean disableWcm;
private Boolean enableDecoration;
private String[] additionalCssClassNames;
/**
* The path to the resource object to include in the current request processing. If this path is relative
* it is appended to the path of the current resource whose script is including the given resource. Either
* resource or path must be specified. If both are specified, the resource takes precedences.
*/
private String path;
/**
* The resource type of a resource to include. If the resource to be included is specified with the path
* attribute, which cannot be resolved to a resource, the tag may create a synthetic resource object out
* of the path and this resource type. If the resource type is set the path must be the exact path to a
* resource object. That is, adding parameters, selectors and extensions to the path is not supported if
* the resource type is set.
*/
private String resourceType;
/** wrapping the original include tag to mimic it's behaviour */
private com.day.cq.wcm.tags.IncludeTag wcmIncludeTag = new com.day.cq.wcm.tags.IncludeTag();
/** {@inheritDoc} */
@Override
public int doStartTag() throws JspException {
return wcmIncludeTag.doStartTag();
}
/** @param disableWcm the disableWcm to set */
public void setDisableWcm(boolean disableWcm) {
this.disableWcm = disableWcm;
}
/** @param enableDecoration the enableDecoration to set */
public void setEnableDecoration(boolean enableDecoration) {
this.enableDecoration = enableDecoration;
}
/** @param flush the flush to set */
public void setFlush(Boolean flush) {
this.wcmIncludeTag.setFlush(flush);
}
/** @param path the path to set */
public void setPath(String path) {
this.path = path;
wcmIncludeTag.setPath(path);
}
/** @param resourceType the resourceType to set */
public void setResourceType(String resourceType) {
this.resourceType = resourceType;
wcmIncludeTag.setResourceType(resourceType);
}
/** @param script the script to set */
public void setScript(String script) {
wcmIncludeTag.setScript(script);
}
/** @param ignoreComponentHierarchy the ignoreComponentHierarchy to set */
public void setIgnoreComponentHierarchy(boolean ignoreComponentHierarchy) {
wcmIncludeTag.setIgnoreComponentHierarchy(ignoreComponentHierarchy);
}
public void setAdditionalCssClassNames(final String additionalCssClassNames) {
this.additionalCssClassNames = StringUtils.split(additionalCssClassNames, ',');
}
/** {@inheritDoc} */
@Override
public void setPageContext(PageContext pageContext) {
super.setPageContext(pageContext);
wcmIncludeTag.setPageContext(pageContext);
}
/** {@inheritDoc} */
@Override
public void setParent(Tag t) {
super.setParent(t);
wcmIncludeTag.setParent(t);
}
/** {@inheritDoc} */
@Override
public void setId(String id) {
super.setId(id);
wcmIncludeTag.setId(id);
}
/** {@inheritDoc} */
@Override
public void release() {
super.release();
wcmIncludeTag.release();
}
/** {@inheritDoc} */
@Override
public void removeValue(String k) {
super.removeValue(k);
wcmIncludeTag.removeValue(k);
}
/** {@inheritDoc} */
@Override
public void setValue(String k, Object o) {
super.setValue(k, o);
wcmIncludeTag.setValue(k, o);
}
/** {@inheritDoc} */
@Override
public int doEndTag() throws JspException {
// we consider removing decoration only if we're including a component
SlingHttpServletRequest request = getRequest();
WCMMode wcmMode = WCMMode.fromRequest(request);
ComponentConfiguration componentConfiguration = readComponentConfiguration(request);
boolean decorationEnabled;
if (this.enableDecoration == null) {
decorationEnabled = isDecorationEnabled(componentConfiguration.getDecorationModes(), wcmMode);
} else {
decorationEnabled = this.enableDecoration;
}
IncludeOptions options = IncludeOptions.getOptions(request, true);
if (!decorationEnabled) {
options.forceSameContext(true);
}
boolean wcmDisabled;
if (this.disableWcm == null) {
wcmDisabled = componentConfiguration.isDisableWcm(); // default is in componentConfiguration
} else {
wcmDisabled = this.disableWcm;
}
if (wcmDisabled) {
WCMMode.DISABLED.toRequest(request);
}
try {
String[] componentAdditionalCssClassNames = componentConfiguration.getAdditionalCssClassNames();
if ((null != componentAdditionalCssClassNames) && (componentAdditionalCssClassNames.length > 0)) {
options.getCssClassNames().addAll(Arrays.asList(componentAdditionalCssClassNames));
}
if ((null != additionalCssClassNames) && (additionalCssClassNames.length > 0)) {
options.getCssClassNames().addAll(Arrays.asList(additionalCssClassNames));
}
wcmIncludeTag.setResourceType(resourceType);
return wcmIncludeTag.doEndTag();
} finally {
if (wcmDisabled) {
wcmMode.toRequest(request);
}
}
}
private ComponentConfiguration readComponentConfiguration(SlingHttpServletRequest request) {
ComponentConfiguration componentConfiguration = new ComponentConfiguration(logger);
if ((resourceType != null) && (path != null)) {
ComponentManager componentManager = request.getResourceResolver().adaptTo(ComponentManager.class);
if (componentManager != null) {
Component component = componentManager.getComponent(resourceType);
if (component != null) {
componentConfiguration.readFromComponent(component);
} else {
logger.warn("Cannot read component configuration for '{}' at {}", resourceType, path);
}
} else {
logger.warn("Unable to obtain component manager for '{}' at {}", resourceType, path);
}
}
return componentConfiguration;
}
private boolean isDecorationEnabled(DecorationMode[] decorationModes, WCMMode wcmMode) {
boolean enabled = false;
for (int i = 0; (i < decorationModes.length) && !enabled; i++) {
DecorationMode decorationMode = decorationModes[i];
if (null != decorationMode) {
enabled = decorationMode.isEnabledInWcmMode(wcmMode);
}
}
return enabled;
}
// /////////////////////////////////////////////////////////////////////////
// DecorationMode enum
// ///////////////////////////////////////////////////////////////////////
/**
* Represents configuration options for the {{cog:enableDecorationInModes}} property.
*/
enum DecorationMode {
/** decoration is enabled in the WCM disabled mode */
DISABLED("disabled"), //
/** decoration is enabled in the WCM edit mode */
EDIT("edit"), //
/** decoration is enabled in the WCM read-only mode */
READ_ONLY("read-only"), //
/** decoration is enabled in the WCM preview-mode */
PREVIEW("preview"), //
/** decoration is enabled in the WCM design-mode */
DESIGN("design"), //
/** decoration is enabled in the authoring mode */
AUTHOR("author"), //
/** decoration is enabled in the publish mode */
PUBLISH("publish"), //
/** decoration is enabled in all WCM modes */
ALL("all"), //
/** decoration is disabled (not enabled in any mode) */
NONE("none");
private String name;
private DecorationMode(String name) {
this.name = name;
}
public static DecorationMode fromObject(Object o) {
if (o == null) {
return null;
} else {
return fromNotNullString(o.toString());
}
}
public static DecorationMode fromString(String name) {
if (name == null) {
return null;
} else {
return fromNotNullString(name);
}
}
private static DecorationMode fromNotNullString(String name) {
for (DecorationMode b : DecorationMode.values()) {
if (name.equals(b.name)) {
return b;
}
}
// default
return null;
}
/**
* Returns true if the decoration mode is enabled in the current WCM Mode
*
* @param wcmMode the WCM mode model representing current state of the WCM
* @return true if the decoration mode is enabled in given WCM mode, false otherwise
*/
public boolean isEnabledInWcmMode(WCMMode wcmMode) {
boolean enabled = (this == DISABLED) && (wcmMode == WCMMode.DISABLED);
enabled |= (this == EDIT) && (wcmMode == WCMMode.EDIT);
enabled |= (this == READ_ONLY) && (wcmMode == WCMMode.READ_ONLY);
enabled |= (this == PREVIEW) && (wcmMode == WCMMode.PREVIEW);
enabled |= (this == DESIGN) && (wcmMode == WCMMode.DESIGN);
enabled |= (this == AUTHOR) && ((wcmMode == WCMMode.EDIT) || (wcmMode == WCMMode.DESIGN));
enabled |= (this == PUBLISH) && ((wcmMode != WCMMode.EDIT) && (wcmMode != WCMMode.DESIGN));
enabled |= (this == ALL);
return enabled;
}
}
}