/*
* #%L
* ACS AEM Commons Bundle
* %%
* Copyright (C) 2013 Adobe
* %%
* 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 com.adobe.acs.commons.wcm.impl;
import com.adobe.acs.commons.wcm.ComponentEditType;
import com.adobe.acs.commons.wcm.ComponentHelper;
import com.day.cq.wcm.api.WCMMode;
import com.day.cq.wcm.api.components.ComponentEditConfig;
import com.day.cq.wcm.api.components.DropTarget;
import com.day.cq.wcm.commons.WCMUtils;
import com.day.cq.wcm.foundation.Placeholder;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletRequest;
/**
* Component Helper is an OSGi Service used in the context of CQ Components
* for encapsulating common tasks and performing common checks.
*
* Get using @Reference annotation or via SlingScriptHelper's .getService(..) method
*/
@Component(label = "ACS AEM Commons - Component Helper",
description = "Component Helper is a service used in the context of CQ Components for "
+ "encapsulating common tasks and performing common checks.",
immediate = false,
metatype = true)
@Service
public final class ComponentHelperImpl implements ComponentHelper {
private static final String CSS_EDIT_MODE = "wcm-helper-edit-mode";
public String generateClassicUIPlaceholder(String classNames, String title) {
StringBuilder html = new StringBuilder();
// Create the HTML img tag used for the edit icon
html.append("<img src=\"/libs/cq/ui/resources/0.gif\" ");
html.append(" class=\"").append(classNames);
html.append("\" ");
if (StringUtils.isNotBlank(title)) {
html.append("alt=\"").append(title).append("\" ");
html.append("title=\"").append(title).append("\"");
}
html.append("/>");
return html.toString();
}
public boolean isDesignMode(SlingHttpServletRequest request) {
return WCMMode.DESIGN.equals(WCMMode.fromRequest(request));
}
public boolean isDisabledMode(SlingHttpServletRequest request) {
return WCMMode.DISABLED.equals(WCMMode.fromRequest(request));
}
public boolean isEditMode(SlingHttpServletRequest request) {
return WCMMode.EDIT.equals(WCMMode.fromRequest(request));
}
public boolean isPreviewMode(SlingHttpServletRequest request) {
return WCMMode.PREVIEW.equals(WCMMode.fromRequest(request));
}
public boolean isReadOnlyMode(SlingHttpServletRequest request) {
return WCMMode.READ_ONLY.equals(WCMMode.fromRequest(request));
}
public boolean isAuthoringMode(SlingHttpServletRequest request) {
return (isEditMode(request) || isDesignMode(request));
}
public boolean isTouchAuthoringMode(ServletRequest request) {
return Placeholder.isAuthoringUIModeTouch(request);
}
public boolean printEditBlock(SlingHttpServletRequest request,
SlingHttpServletResponse response,
ComponentEditType.Type editType,
boolean... isConfigured) {
final String html = getEditBlock(request, editType, isConfigured);
if (html == null) {
return false;
}
try {
response.getWriter().print(html);
response.getWriter().flush();
} catch (IOException e) {
return false;
}
return true;
}
public boolean printEditBlockOrNothing(SlingHttpServletRequest request,
SlingHttpServletResponse response,
ComponentEditType.Type editType,
boolean... isConfigured) {
if (isAuthoringMode(request)) {
return printEditBlock(request, response, editType, isConfigured);
} else {
// If NOT authoring mode (Preview or Publish)
return !conditionAndCheck(isConfigured);
}
}
public boolean printDDEditBlock(SlingHttpServletRequest request,
SlingHttpServletResponse response,
String name,
boolean... isConfigured) {
return printDDEditBlock(request, response, name, null, isConfigured);
}
public boolean printDDEditBlock(SlingHttpServletRequest request,
SlingHttpServletResponse response,
String name,
ComponentEditType.Type editType,
boolean... isConfigured) {
final String html = getDDEditBlock(request, name, editType, isConfigured);
if (html == null) {
return false;
}
try {
response.getWriter().print(html);
response.getWriter().flush();
} catch (IOException e) {
return false;
}
return true;
}
public String getEditBlock(SlingHttpServletRequest request,
ComponentEditType.Type editType,
boolean... isConfigured) {
final Resource resource = request.getResource();
final com.day.cq.wcm.api.components.Component component = WCMUtils.getComponent(resource);
if (!isAuthoringMode(request)
|| conditionAndCheck(isConfigured)) {
return null;
} else if (ComponentEditType.NONE.equals(editType)) {
return "<!-- Edit Mode Placeholder is specified as: " + editType.getName() + " -->";
}
StringBuilder html = new StringBuilder("<div class=\"wcm-edit-mode " + CSS_EDIT_MODE + "\">");
if (component == null) {
html.append(getCssStyle());
html.append("Could not resolve CQ Component type.");
} else if (ComponentEditType.NOICON.equals(editType) || ComponentEditType.NONE.equals(editType)) {
final String title = StringUtils.capitalize(component.getTitle());
html.append(getCssStyle());
html.append("<dl>");
html.append("<dt>" + title + " Component</dt>");
if (component.isEditable()) {
html.append("<dd>Double click or Right click to Edit</dd>");
}
if (component.isDesignable()) {
html.append("<dd>Switch to Design mode and click the Edit button</dd>");
}
if (!component.isEditable() && !component.isDesignable()) {
html.append("<dd>The component cannot be directly authored</dd>");
}
html.append("</dl>");
} else if (ComponentEditType.DROPTARGETS.equals(editType)) {
// Use DropTargets
ComponentEditConfig editConfig = component.getEditConfig();
Map<String, DropTarget> dropTargets = (editConfig != null) ? editConfig.getDropTargets() : null;
if (dropTargets != null && !dropTargets.isEmpty()) {
// Auto generate images with drop-targets
for (final Map.Entry<String, DropTarget> entry : dropTargets.entrySet()) {
final DropTarget dropTarget = entry.getValue();
html.append("<img src=\"/libs/cq/ui/resources/0.gif\" ");
html.append("class=\"").append(dropTarget.getId());
html.append(" ").append(getWCMEditType(dropTarget).getCssClass()).append("\" ");
html.append("alt=\"Drop Target: ").append(dropTarget.getName()).append("\" ");
html.append("title=\"Drop Target: ").append(dropTarget.getName()).append("\"/>");
}
}
} else {
final String title = StringUtils.capitalize(component.getTitle());
// Use specified EditType
html.append("<img src=\"/libs/cq/ui/resources/0.gif\" ");
html.append("class=\"").append(editType.getCssClass()).append("\" alt=\"");
html.append(title).append("\" ");
html.append("title=\"").append(title).append("\"/>");
}
html.append("</div>");
return html.toString();
}
public String getDDEditBlock(SlingHttpServletRequest request, String name, boolean... isConfigured) {
return getDDEditBlock(request, name, null, isConfigured);
}
public String getDDEditBlock(SlingHttpServletRequest request, String name,
ComponentEditType.Type editType, boolean... isConfigured) {
if (!isAuthoringMode(request) || conditionAndCheck(isConfigured)) {
return null;
}
final Resource resource = request.getResource();
final com.day.cq.wcm.api.components.Component component = WCMUtils.getComponent(resource);
StringBuilder html = new StringBuilder();
ComponentEditConfig editConfig = component.getEditConfig();
Map<String, DropTarget> dropTargets = (editConfig != null) ? editConfig.getDropTargets() : null;
if (dropTargets != null && !dropTargets.isEmpty()) {
DropTarget dropTarget = null;
// Find the named Drop Target
for (final Map.Entry<String, DropTarget> entry : dropTargets.entrySet()) {
dropTarget = entry.getValue();
if (StringUtils.equals(name, dropTarget.getName())) {
break;
} else {
dropTarget = null;
}
}
if (dropTarget != null) {
// If editType has not been specified then intelligently determine the best match
editType = (editType == null) ? getWCMEditType(dropTarget) : editType;
String classNames = dropTarget.getId() + " " + editType.getCssClass();
String placeholderTitle = "Drop Target: " + dropTarget.getName();
html.append(generateClassicUIPlaceholder(classNames, placeholderTitle));
}
}
return html.toString();
}
public String getEditIconImgTag(ComponentEditType.Type editType) {
final String title = StringUtils.capitalize(editType.getName());
return "<img src=\"/libs/cq/ui/resources/0.gif\"" + " "
+ "class=\"" + editType.getCssClass() + "\""
+ " " + "alt=\"" + title + "\" "
+ "title=\"" + title + "\" />";
}
/**
* "Intelligently" determines the WCMEditType to use based on the
* DropTarget.
* <p/>
* Inspects the DropTarget's Groups and Accepts to make this determination.
* <p/>
* If no match can be found, defaults to TEXT
*
* @param dropTarget the drop target
* @return the component edit type
*/
private ComponentEditType.Type getWCMEditType(DropTarget dropTarget) {
if (dropTarget == null) {
return ComponentEditType.NONE;
}
List<String> groups = Arrays.asList(dropTarget.getGroups());
List<String> accepts = Arrays.asList(dropTarget.getAccept());
if (groups.isEmpty() && accepts.isEmpty()) {
return ComponentEditType.NONE;
}
if (groups.contains("media")) {
if (matches(accepts, "image")) {
return ComponentEditType.IMAGE;
} else if (matches(accepts, "video")) {
return ComponentEditType.VIDEO;
} else if (matches(accepts, "flash")) {
return ComponentEditType.FLASH;
} else if (accepts.size() == 1 && ".*".equals(accepts.get(0))) {
return ComponentEditType.FILE;
}
} else if (groups.contains("page")) {
return ComponentEditType.REFERENCE;
} else if (groups.contains("paragraph")) {
return ComponentEditType.REFERENCE;
}
return ComponentEditType.TEXT;
}
private boolean matches(List<String> list, String pattern) {
if (StringUtils.isBlank(pattern)) {
return false;
}
for (String item : list) {
if (item == null) {
continue;
} else if (item.contains(pattern)) {
return true;
}
}
return false;
}
private boolean conditionAndCheck(boolean... conditions) {
if (conditions == null) {
return false;
}
for (int i = 0; i < conditions.length; i++) {
if (!conditions[i]) {
return false;
}
}
return true;
}
/**
* Inline CSS style used for edit blocks. Ideally this would be a external
* CSS however for portability this exist in this Java class.
*
* @return inline CSS style declaration
*/
private String getCssStyle() {
String css = "";
css += "<style>";
css += "." + CSS_EDIT_MODE + " { border:dashed 2px #ccc; color:#ccc; padding:1em; }";
css += "." + CSS_EDIT_MODE + " dt { font-weight: bold; }";
css += "." + CSS_EDIT_MODE + " dd { display:list-item; list-style-type:disc; margin-left:1.5em; }";
css += "</style>";
return css;
}
}