meta data for this page
Synchronization of Parent-Child CMS Components: A working example
When a CMS Component contains other components, it is a requirement that any changes of the childrens trigger the synchronization of the parent. It is difficult to get this right, as there are limitations on the identification of changes on the WCMS. This is what has worked for me.
Introduction
The requirement is to show tabs in the pages accessible by the registered users. The order and other properties like the title may be changed by the customer on the WCMS. Currently each page has only one group of tabs but this may change in the future.
Data model
hybris/bin/custom/customcms/resources/customcms-items.xml <relation code="TabGroup2AbstractTabCMSComponent" generate="true" localized="false" autocreate="true"> <deployment table="tabgroup2tab" typecode="30099" /> <sourceElement qualifier="tabGroups" type="TabGroupCMSComponent" cardinality="many"/> <targetElement qualifier="tabs" type="AbstractTabCMSComponent" cardinality="many" collectiontype="list" ordered="true"/> </relation> <itemtype code="TabGroupCMSComponent" autocreate="true" generate="true" extends="AbstractCMSComponentContainer" jaloclass="customcms.jalo.components.TabGroupCMSComponent"> <description>It holds a group of tabs. The CMS cockpit sees the changes on the childs because it inherites from AbstractCMSComponent </description> </itemtype> <itemtype code="AbstractTabCMSComponent" generate="true" extends="AbstractCMSComponent" autocreate="true" abstract="false" jaloclass="customcms.cms.jalo.components.AbstractTabCMSComponent"> <description>Represents a tab component hold by a tab group.</description> <attributes> <attribute qualifier="tag" type="java.lang.String"> <persistence type="property"/> <description>A tag used for identifying the component within its parent component.</description> </attribute> <attribute qualifier="title" type="localized:java.lang.String"> <persistence type="property"/> </attribute> <attribute qualifier="tabbodyCaption" type="localized:java.lang.String"> <persistence type="property"/> </attribute> <attribute qualifier="tabbodyInfo" type="localized:java.lang.String"> <persistence type="property"/> </attribute> </attributes> </itemtype>
The important points to consider are:
- The parent component inherits from AbstractCMSComponentContainer.
- The child component isn't abstract
- The child component inherits from AbstractCMSComponent.
Here is the definition of the childs which doesn't have anything special:
hybris/bin/custom/customcms/resources/customcms-items.xml <itemtype code="ReportsCMSComponent" generate="true" extends="AbstractTabCMSComponent" autocreate="true" jaloclass="customcms.jalo.components.ReportsCMSComponent"> <description>Component for requesting reports for customer</description> <attributes/> </itemtype> <itemtype code="EnableOrdersCMSComponent" generate="true" extends="AbstractTabCMSComponent" autocreate="true" jaloclass="customcms.jalo.components.EnableOrdersCMSComponent"> <description> Orders - 'Enable orders' Component</description> <attributes/> </itemtype> <itemtype code="OrderHistoryCMSComponent" generate="true" extends="AbstractTabCMSComponent" autocreate="true" jaloclass="customcms.jalo.components.OrderHistoryCMSComponent"> <description> Orders - 'All orders' Component</description> <attributes/> </itemtype>
Synchronization Service
In the custom CMS cockpit extension the new attributes must be configured.
hybris/bin/custom/customcmscockpit/resources/customcmscockpit/customcmscockpit-spring-services.xml <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" default-autowire="byName"> <alias alias="synchronizationService" name="customSynchronizationService" /> <bean id="customSynchronizationService" class="de.hybris.platform.cmscockpit.sync.CMSSynchronizationService" scope="tenant" autowire="byName"> <property name="relatedReferencesTypesMap"> <map> <entry key="AbstractPage"> <list> <value>AbstractPage.restrictions</value> <value>AbstractPage.contentSlots</value> <value>ContentSlotForPage.contentSlot</value> <value>ContentSlot.cmsComponents</value> <value>AbstractCMSComponentContainer.simpleCMSComponents</value> <value>AbstractCMSComponentContainer.currentCMSComponents</value> <value>RotatingImagesComponent.banners</value> <value>AbstractCMSComponent.restrictions</value> <value>abstractMediaContainerComponent.media</value> <value>TabGroupCMSComponent.tabs</value> </list> </entry> <entry key="AbstractCMSComponent"> <list> <value>AbstractCMSComponentContainer.simpleCMSComponents</value> <value>AbstractCMSComponentContainer.currentCMSComponents</value> <value>RotatingImagesComponent.banners</value> <value>CMSProductCarouselComponent.carouselElements</value> <value>ContentSlot.cmsComponents</value> <value>AbstractCMSComponentContainer.simpleCMSComponents</value> <value>AbstractCMSComponentContainer.currentCMSComponents</value> <value>RotatingImagesComponent.banners</value> (...) <value>MediaContainer.medias</value> <value>NavigationBarCollectionComponent.components</value> <value>NavigationBarComponent.navigationNode</value> <value>NavigationBarComponent.link</value> <value>TabGroupCMSComponent.tabs</value> </list> </entry> <entry key="CMSNavigationNode"> <list> <value>CMSNavigationNode.children</value> <value>CMSNavigationNode.entries</value> <value>CMSNavigationNode.links</value> <value>CMSNavigationEntry.item</value> <value>CMSLinkComponent</value> </list> </entry> <entry key="ContentSlot"> <list> <value>ContentSlot.cmsComponents</value> <value>abstractMediaContainerComponent.media</value> <value>NavigationBarCollectionComponent.components</value> <value>NavigationBarComponent.navigationNode</value> <value>NavigationBarComponent.link</value> </list> </entry> <entry key="MediaContainer"> <list> <value>MediaContainer.medias</value> </list> </entry> <entry key="TabGroupCMSComponent"> <list> <value>TabGroupCMSComponent.tabs</value> </list> </entry> <entry key="AbstractTabCMSComponent"> <list> <value>AbstractTabCMSComponent.title</value> <value>AbstractTabCMSComponent.tabGroups</value> </list> </entry> </map> </property> <property name="modelService" ref="modelService"/> <property name="cockpitTypeService" ref="cockpitTypeService"/> <property name="searchRestrictionsDisabled" value="true"/> </bean> (...) </beans>
Some of the configuration is probably duplicated but it works.
Localization (optional)
The new components must be localized as any other Hybris type.
hybris/bin/custom/customcms/resources/localization/customcms-locales_en.properties ### Localization for type TabGroupCMSComponent type.tabgroupcmscomponent.name=Tab group component type.tabgroupcmscomponent.tabs.name=Tabs ### Localization for type AbstractTabCMSComponent type.abstracttabcmscomponent.name=Abstract Tab component type.abstracttabcmscomponent.tabgroups.name=Tab groups type.abstracttabcmscomponent.tabbodycaption.name=Section caption type.abstracttabcmscomponent.tabbodyinfo.name=Section text type.abstracttabcmscomponent.tag.name=Tag type.abstracttabcmscomponent.title.name=Title.
Jalo Classes
Two methods must be implemented:
package customcms.jalo.components; import de.hybris.platform.cms2.jalo.contents.components.SimpleCMSComponent; import de.hybris.platform.jalo.SessionContext; import java.util.List; public class TabGroupCMSComponent extends GeneratedTabGroupCMSComponent { @Override public Boolean isContainer(SessionContext sessionContext) { return Boolean.TRUE; } @Override public List<SimpleCMSComponent> getCurrentCMSComponents(SessionContext sessionContext) { return this.getSimpleCMSComponents(sessionContext); } } package customcms.jalo.components; import de.hybris.platform.jalo.SessionContext; public class AbstractTabCMSComponent extends GeneratedAbstractTabCMSComponent { @Override public Boolean isContainer(SessionContext sessionContext) { return Boolean.TRUE; } }
CMS Cockpit Configuration
The following section refers to the WCMS Cockpit. This extension will be replaced by SmartEdit and is deprecated since SAP Hybris 6.7. SmartEdit will support the old features and new ones.
The changes on the child are identified when the tag property is used in the cockpit configuration of the parent is used:
hybris/bin/customcmscockpit/resources/customcmscockpit-config/cmsmanagergroup/contentEditor_TabGroupCMSComponent.xml <?xml version="1.0" encoding="UTF-8"?> <content-editor hideEmpty="false" hideReadOnly="false" groupCollections="false"> <template> <![CDATA[ <div class="contentEditorWrapper"> <div class="contentEditorHeader"> <b>$label</b> </div> <div class="contentEditorLine"> <cockpit code="property" value="TabGroupCMSComponent.visible"/> </div> <div class="contentEditorLine"> <cockpit code="property" value="TabGroupCMSComponent.tabs"/> </div> </div> ]]> </template> </content-editor>
The Cockpit configuration of the childs doesn't contain anything special:
hybris/bin/customcmscockpit/resources/customcmscockpit-config/cmsmanagergroup/contentEditor_AbstractTabCMSComponent.xml <?xml version="1.0" encoding="UTF-8"?> <content-editor hideEmpty="true" hideReadOnly="true" groupCollections="true"> <template> <![CDATA[ <div class="contentEditorWrapper"> <div class="contentEditorHeader"> <b>$label</b> </div> <div class="contentEditorLine"> <cockpit code="property" value="AbstractTabCMSComponent.tag"/> </div> <div class="contentEditorLine"> <cockpit code="property" value="AbstractTabCMSComponent.title"/> </div> <div class="contentEditorLine"> <cockpit code="property" value="AbstractTabCMSComponent.tabbodyCaption"/> </div> <div class="contentEditorLine"> <cockpit code="property" value="AbstractTabCMSComponent.tabbodyInfo"/> </div> </div> ]]> </template> </content-editor> hybris/bin/customcmscockpit/resources/customcmscockpit-config/cmsmanagergroup/editorArea_AbstractTabCMSComponent.xml <?xml version="1.0" encoding="UTF-8"?> <editor> <group qualifier="general" visible="true" initially-opened="true"> <label lang="de">Stammdaten</label> <label lang="en">General</label> <property qualifier="CMSItem.name"/> <property qualifier="CMSItem.catalogVersion" editor="shortListEditor"/> <property qualifier="AbstractTabCMSComponent.title"/> <property qualifier="AbstractTabCMSComponent.tabbodyCaption"/> <property qualifier="AbstractTabCMSComponent.tabbodyInfo"/> </group> <group qualifier="visibility" visible="true" initially-opened="false"> <label lang="de">Context Visibility</label> <label lang="en">Context Visibility</label> <property qualifier="AbstractCMSComponent.restrictions"> <parameter> <name>allowCreate</name> <value>true</value> </parameter> </property> <property qualifier="AbstractCMSComponent.onlyOneRestrictionMustApply"/> </group> <group qualifier="admin" visible="false" initially-opened="false"> <label key="config.general.administration" /> <property qualifier="Item.pk" /> <property qualifier="Item.creationTime" /> <property qualifier="Item.modifiedtime" /> </group> <custom-group class="de.hybris.platform.cockpit.services.config.impl.UnassignedEditorSectionConfiguration" qualifier="unassigned" initially-opened="false" visible="false"> <label lang="de">Andere</label> <label lang="en">Other</label> </custom-group> </editor>
Views
The JSP file must include CMS tags which render the childs as any other CMS component container:
hybris/bin/custom/customstorefront/web/webroot/WEB-INF/views/cms/tabgroupcmscomponent.jsp <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="links" uri="http://www.springframework.org/tags/form" %> (...) <c:forEach var="tabcomponent" items="${tabcomponents}" begin="0"> <cms:component component="${tabcomponent}"/> </c:forEach> (...)
–Based on Hybris 5.5.1
Discussion