/* * #%L * wcm.io * %% * Copyright (C) 2014 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.parsys.controller; import static io.wcm.wcm.parsys.ParsysNameConstants.PN_PARSYS_GENERATE_DEAFULT_CSS; import static io.wcm.wcm.parsys.ParsysNameConstants.PN_PARSYS_NEWAREA_CSS; import static io.wcm.wcm.parsys.ParsysNameConstants.PN_PARSYS_PARAGRAPH_CSS; import static io.wcm.wcm.parsys.ParsysNameConstants.PN_PARSYS_PARAGRAPH_ELEMENT; import static io.wcm.wcm.parsys.ParsysNameConstants.PN_PARSYS_WRAPPER_CSS; import static io.wcm.wcm.parsys.ParsysNameConstants.PN_PARSYS_WRAPPER_ELEMENT; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.annotation.PostConstruct; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.models.annotations.Model; import org.apache.sling.models.annotations.injectorspecific.RequestAttribute; import org.apache.sling.models.annotations.injectorspecific.SlingObject; import org.osgi.annotation.versioning.ProviderType; import com.day.cq.wcm.api.NameConstants; import com.day.cq.wcm.api.WCMMode; import com.day.cq.wcm.api.components.Component; import com.day.cq.wcm.api.components.ComponentContext; import com.day.cq.wcm.api.components.ComponentManager; import com.google.common.collect.ImmutableMap; import io.wcm.sling.commons.adapter.AdaptTo; import io.wcm.sling.models.annotations.AemObject; /** * Controller for paragraph system. * Unlike the AEM-builtin paragraph systems this parsys does not support column controls or iparsys inheritance, * but is only a simple paragraph system which allows full control about the markup generated for the child resources * and the new area. */ @Model(adaptables = SlingHttpServletRequest.class) @ProviderType public final class Parsys { static final String RA_PARSYS_PARENT_RESOURCE = "parsysParentResource"; static final String SECTION_DEFAULT_CLASS_NAME = "section"; static final String NEWAREA_RESOURCE_PATH = "./*"; static final String NEWAREA_STYLE = "clear:both"; static final String NEWAREA_CSS_CLASS_NAME = "new"; static final String NEWAREA_CHILD_NAME = "newpar"; static final String FALLBACK_NEWAREA_RESOURCE_TYPE = "wcm-io/wcm/parsys/components/parsys/newpar"; static final String DEFAULT_ELEMENT_NAME = "div"; /** * Allows to override the resource which children are iterated to display the parsys. */ @RequestAttribute(name = RA_PARSYS_PARENT_RESOURCE, optional = true) private Resource parsysParentResource; @SlingObject private Resource currentResource; @SlingObject private ResourceResolver resolver; @AemObject private WCMMode wcmMode; @AemObject private ComponentContext componentContext; private ComponentManager componentManager; private boolean generateDefaultCss; private String paragraphCss; private String newAreaCss; private String paragraphElementName; private String wrapperElementName; private String wrapperCss; private List<Item> items; @PostConstruct private void activate() { // read customize properties from parsys component final ValueMap props = componentContext.getComponent().getProperties(); generateDefaultCss = props.get(PN_PARSYS_GENERATE_DEAFULT_CSS, true); paragraphCss = props.get(PN_PARSYS_PARAGRAPH_CSS, String.class); newAreaCss = props.get(PN_PARSYS_NEWAREA_CSS, String.class); paragraphElementName = props.get(PN_PARSYS_PARAGRAPH_ELEMENT, String.class); wrapperElementName = props.get(PN_PARSYS_WRAPPER_ELEMENT, String.class); wrapperCss = props.get(PN_PARSYS_WRAPPER_CSS, String.class); // prepare paragraph items items = new ArrayList<>(); if (parsysParentResource == null) { parsysParentResource = currentResource; } for (Resource childResource : parsysParentResource.getChildren()) { items.add(createResourceItem(childResource)); } if (wcmMode != WCMMode.DISABLED) { items.add(createNewAreaItem()); } } private Item createResourceItem(Resource resource) { CssBuilder css = new CssBuilder(); if (generateDefaultCss) { css.add(SECTION_DEFAULT_CLASS_NAME); } css.add(paragraphCss); Map<String,String> htmlTagAttrs = getComponentHtmlTagAttributes(resource.getResourceType()); // apply html tag attributes from component definition String itemElementName = paragraphElementName; if (StringUtils.isEmpty(itemElementName)) { itemElementName = StringUtils.defaultString(htmlTagAttrs.get(NameConstants.PN_TAG_NAME), DEFAULT_ELEMENT_NAME); } if (StringUtils.isEmpty(paragraphCss)) { css.add(htmlTagAttrs.get("class")); } return new Item(resource.getPath(), null, itemElementName, null, css.build(), false); } /** * Get HTML tag attributes from component. * @param resourceType Component path * @return Map (never null) */ private Map<String, String> getComponentHtmlTagAttributes(String resourceType) { if (StringUtils.isNotEmpty(resourceType)) { Component component = componentManager().getComponent(resourceType); if (component != null && component.getHtmlTagAttributes() != null) { return component.getHtmlTagAttributes(); } } return ImmutableMap.of(); } private ComponentManager componentManager() { if (componentManager == null) { componentManager = AdaptTo.notNull(this.resolver, ComponentManager.class); } return componentManager; } private Item createNewAreaItem() { String style = null; CssBuilder css = new CssBuilder(); css.add(NEWAREA_CSS_CLASS_NAME); if (generateDefaultCss) { style = NEWAREA_STYLE; css.add(SECTION_DEFAULT_CLASS_NAME); } css.add(newAreaCss); String newAreaElementName = StringUtils.defaultString(paragraphElementName, DEFAULT_ELEMENT_NAME); String newAreaResourceType = getNewAreaResourceType(componentContext.getComponent().getPath()); return new Item(NEWAREA_RESOURCE_PATH, newAreaResourceType, newAreaElementName, style, css.build(), true); } /** * Get resource type for new area - from current parsys component or from a supertype component. * @param componentPath Component path * @return Resource type (never null) */ private String getNewAreaResourceType(String componentPath) { Resource componentResource = resolver.getResource(componentPath); if (componentResource != null) { if (componentResource.getChild(NEWAREA_CHILD_NAME) != null) { return componentPath + "/" + NEWAREA_CHILD_NAME; } String resourceSuperType = componentResource.getResourceSuperType(); if (StringUtils.isNotEmpty(resourceSuperType)) { return getNewAreaResourceType(resourceSuperType); } } return FALLBACK_NEWAREA_RESOURCE_TYPE; } /** * @return Paragraph system items */ public List<Item> getItems() { return items; } /** * @return Element name for wrapper element */ public String getWrapperElementName() { return StringUtils.defaultString(wrapperElementName, DEFAULT_ELEMENT_NAME); } /** * @return Wrapper element CSS */ public String getWrapperCss() { return this.wrapperCss; } /** * @return True if the wrapper element should be rendered */ public boolean isWrapperElement() { return StringUtils.isNotBlank(wrapperElementName); } /** * Paragraph system item. */ public static final class Item { private final String resourcePath; private final String resourceType; private final String elementName; private final String style; private final String cssClassName; private final boolean newArea; Item(String resourcePath, String resourceType, String elementName, String style, String cssClassName, boolean newArea) { this.resourcePath = resourcePath; this.resourceType = resourceType; this.elementName = elementName; this.style = style; this.cssClassName = cssClassName; this.newArea = newArea; } /** * @return Resource path */ public String getResourcePath() { return resourcePath; } /** * @return Resource type */ public String getResourceType() { return resourceType; } /** * @return Name for item element */ public String getElementName() { return elementName; } /** * @return Style string */ public String getStyle() { return style; } /** * @return CSS classes */ public String getCssClassName() { return cssClassName; } /** * @return true if this is the new area */ public boolean isNewArea() { return newArea; } } }