Adding Custom CMS component in Hybris

In this requirement, we are going to create a completely new component type and add it to the page.

Let’s say the requirement is to display the Offers on the Home page, where Offers component should include the attributes like images,header text of the offer,footer text of the offer.


So Our aim is to create a new CMS component type to add these attributes into it and display it in the page


Lets see the steps below to achieve the same.

1) Create a new CMS component type by defining in the *items.xml
hybris\bin\custom\training\trainingcore\resources\trainingcore-items.xml

2)Write an impex statements for adding the new component item type instance and to add the newly created CMS component to a home page by assigning it to the right content slot

3)Create the controller and JSP page for the new component

4)Add this new component into the Home page JSP using CMS tags

5)Open the Home page and Verify to check new component


Step 1

Create a new CMSComponent in *items.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<itemtype code="CustomOffersComponent" extends="SimpleCMSComponent" generate="true" autocreate="true"
            jaloclass="org.training.core.jalo.CustomOffersComponent">
            <description>Used to display offers on homepage</description>
                <attributes>
                    <attribute type="localized:java.lang.String" qualifier="headrerText">
                        <modifiers read="true" write="true" optional="false" />
                        <persistence type="property" />
                    </attribute>
                    <attribute type="localized:java.lang.String" qualifier="footerText">
                        <modifiers read="true" write="true" optional="false" />
                        <persistence type="property" />
                    </attribute>
                    <attribute type="Media" qualifier="offerImage">
                        <modifiers read="true" write="true" optional="false" />
                        <persistence type="property" />
                    </attribute>
                    <attribute type="CustomOfferLinksList" qualifier="offerImageLink">
                        <persistence type="property" />
                        <description>List of CMS Link Component</description>
                        <modifiers read="true" write="true" />
                    </attribute>
                </attributes>
            </itemtype>
<itemtype code="CustomOffersComponent" extends="SimpleCMSComponent" generate="true" autocreate="true"
			jaloclass="org.training.core.jalo.CustomOffersComponent">
			<description>Used to display offers on homepage</description>
				<attributes>
					<attribute type="localized:java.lang.String" qualifier="headrerText">
						<modifiers read="true" write="true" optional="false" />
						<persistence type="property" />
					</attribute>
					<attribute type="localized:java.lang.String" qualifier="footerText">
						<modifiers read="true" write="true" optional="false" />
						<persistence type="property" />
					</attribute>
					<attribute type="Media" qualifier="offerImage">
						<modifiers read="true" write="true" optional="false" />
						<persistence type="property" />
					</attribute>
					<attribute type="CustomOfferLinksList" qualifier="offerImageLink">
						<persistence type="property" />
						<description>List of CMS Link Component</description>
						<modifiers read="true" write="true" />
					</attribute>
				</attributes>
			</itemtype>

Add below collection type for CMSLinkComponent Collection type attribute in the same items.xml file under collectiontypes tag

1
<collectiontype code="CustomOfferLinksList" elementtype="CMSLinkComponent" autocreate="true" generate="true" type="list" />
<collectiontype code="CustomOfferLinksList" elementtype="CMSLinkComponent" autocreate="true" generate="true" type="list" />

Do ant all and refresh the platform after build success.

We can now see that CustomOffersComponentModel.java class created with all the attributes as we defined in *items.xml file

Now only java class is generated but table for this new component is not yet created.

So do the update system, so that table will be created for this new item type


Step 2

We need to write an impex to add the data for this new component type.

Since new component item type has some of the composed types(like CMSLinkComponent,Media),
lets define the impex for those composed types first.

Note: Write all these impexes in the file specified below
hybris\bin\ext-data\apparelstore\resources\apparelstore\import\sampledata\contentCatalogs\apparel-ukContentCatalog\cms-content.impex

Impex for CMSLinkComponent

1
2
3
4
5
6
7
8
#Custom offers components
INSERT_UPDATE CMSLinkComponent;$contentCV[unique=true];uid[unique=true];name;url;&linkRef;&componentRef;target(code)[default='sameWindow'];$category;
 
;;MenOfferLink;Men Offer Link;/MenOfferLink;MenOfferLink;MenOfferLink;;;;
 
;;WomenOfferLink;Women Offer Link;/WomenOfferLink;WomenOfferLink;WomenOfferLink;;;;
 
;;KidsOfferLink;Kids Offer Link;/KidsOfferLink;KidsOfferLink;KidsOfferLink;;;;
#Custom offers components
INSERT_UPDATE CMSLinkComponent;$contentCV[unique=true];uid[unique=true];name;url;&linkRef;&componentRef;target(code)[default='sameWindow'];$category;

;;MenOfferLink;Men Offer Link;/MenOfferLink;MenOfferLink;MenOfferLink;;;;

;;WomenOfferLink;Women Offer Link;/WomenOfferLink;WomenOfferLink;WomenOfferLink;;;;

;;KidsOfferLink;Kids Offer Link;/KidsOfferLink;KidsOfferLink;KidsOfferLink;;;;

Impex for Media

1
2
INSERT_UPDATE Media;$contentCV[unique=true];code[unique=true];@media[translator=de.hybris.platform.impex.jalo.media.MediaDataTranslator];mime[default='image/jpeg'];&imageRef;folder(qualifier)[default='images'];altText
;;customOffersMedia;$siteResource/images/banners/homepage/offers.jpeg;;offers.jpeg;;
INSERT_UPDATE Media;$contentCV[unique=true];code[unique=true];@media[translator=de.hybris.platform.impex.jalo.media.MediaDataTranslator];mime[default='image/jpeg'];&imageRef;folder(qualifier)[default='images'];altText
;;customOffersMedia;$siteResource/images/banners/homepage/offers.jpeg;;offers.jpeg;;

Impex for CustomOffersComponent

1
2
INSERT_UPDATE CustomOffersComponent;$contentCV[unique=true];uid[unique=true];name;headrerText[lang=$lang];footerText[lang=$lang];offerImage(code);&componentRef;offerImageLink(&linkRef);
;;summerOfferComponent;Summer offer Component;"up to 70 % off";"Grab this offer on all summer<br>Collections";customOffersMedia;summerOfferComponent;MenOfferLink,WomenOfferLink,KidsOfferLink
INSERT_UPDATE CustomOffersComponent;$contentCV[unique=true];uid[unique=true];name;headrerText[lang=$lang];footerText[lang=$lang];offerImage(code);&componentRef;offerImageLink(&linkRef);
;;summerOfferComponent;Summer offer Component;"up to 70 % off";"Grab this offer on all summer<br>Collections";customOffersMedia;summerOfferComponent;MenOfferLink,WomenOfferLink,KidsOfferLink

Note: Copy any image to the below path to have media reference, the same image appears on the Home page after completing the requirement
Make sure the name of the image should be offers.jpeg, else change the impex accordingly.

hybris\bin\ext-data\apparelstore\resources\apparelstore\import\sampledata\contentCatalogs\apparel-ukContentCatalog\images\banners\homepage


Now define the content slot and link the component to it


Impex for defining content slot name

1
2
INSERT_UPDATE ContentSlotName;name[unique=true];template(uid,$contentCV)[unique=true][default='LandingPage2Template'];validComponentTypes(code);compTypeGroup(code)
;SummerOffersSlotName;;CustomOffersComponent
INSERT_UPDATE ContentSlotName;name[unique=true];template(uid,$contentCV)[unique=true][default='LandingPage2Template'];validComponentTypes(code);compTypeGroup(code)
;SummerOffersSlotName;;CustomOffersComponent

Impex for defining content slot

1
2
INSERT_UPDATE ContentSlot;$contentCV[unique=true];uid[unique=true];name;active;cmsComponents(&componentRef)
;;summerOfferContentSlot;Summer offer slot;true;summerOfferComponent
INSERT_UPDATE ContentSlot;$contentCV[unique=true];uid[unique=true];name;active;cmsComponents(&componentRef)
;;summerOfferContentSlot;Summer offer slot;true;summerOfferComponent

Impex for defining content slot for page

1
2
INSERT_UPDATE ContentSlotForPage;$contentCV[unique=true];uid[unique=true];position[unique=true];page(uid,$contentCV)[unique=true][default='homepage'];contentSlot(uid,$contentCV)[unique=true]
;;summerOfferslotforpage-Homepage;SummerOffersSlotName;;summerOfferContentSlot
INSERT_UPDATE ContentSlotForPage;$contentCV[unique=true];uid[unique=true];position[unique=true];page(uid,$contentCV)[unique=true][default='homepage'];contentSlot(uid,$contentCV)[unique=true]
;;summerOfferslotforpage-Homepage;SummerOffersSlotName;;summerOfferContentSlot

Make sure below lines are added at the beginning of the cms-content.impex to avoid any errors

1
2
3
4
5
6
7
8
9
10
11
12
$contentCatalog=apparel-ukContentCatalog 
$contentCV=catalogVersion(CatalogVersion.catalog(Catalog.id[default=$contentCatalog]),CatalogVersion.version[default=Staged])[default=$contentCatalog:Staged] 
$productCatalog=apparelProductCatalog 
$productCatalogName=Apparel Product Catalog 
$productCV=catalogVersion(catalog(id[default=$productCatalog]),version[default='Staged'])[unique=true,default=$productCatalog:Staged] 
$picture=media(code, $contentCV) ;
$siteResource=jar:de.hybris.platform.apparelstore.constants.ApparelstoreConstants&/apparelstore/import/sampledata/contentCatalogs/$contentCatalog
$jarResourceCms=jar:de.hybris.platform.apparelstore.constants.ApparelstoreConstants&/apparelstore/import/sampledata/cockpits/cmscockpit
# Load the storefront context root config param
$storefrontContextRoot=$config-storefrontContextRoot
$lang=en
$category=category(code, $productCV)
$contentCatalog=apparel-ukContentCatalog 
$contentCV=catalogVersion(CatalogVersion.catalog(Catalog.id[default=$contentCatalog]),CatalogVersion.version[default=Staged])[default=$contentCatalog:Staged] 
$productCatalog=apparelProductCatalog 
$productCatalogName=Apparel Product Catalog 
$productCV=catalogVersion(catalog(id[default=$productCatalog]),version[default='Staged'])[unique=true,default=$productCatalog:Staged] 
$picture=media(code, $contentCV) ;
$siteResource=jar:de.hybris.platform.apparelstore.constants.ApparelstoreConstants&/apparelstore/import/sampledata/contentCatalogs/$contentCatalog
$jarResourceCms=jar:de.hybris.platform.apparelstore.constants.ApparelstoreConstants&/apparelstore/import/sampledata/cockpits/cmscockpit
# Load the storefront context root config param
$storefrontContextRoot=$config-storefrontContextRoot
$lang=en
$category=category(code, $productCV)

Note:
We are defining the content slot directly for page rather than template, doing so will just add this slot only to the page not to the template


Step 3


Create the custom controller and JSP for the new component

We can either extend the AbstractCMSComponentController or DefaultCMSComponentController for our custom controller.

AbstractCMSComponentController has no implementation for fillModel() method whereas DefaultCMSComponentController has overridden it and provided the definition for fillModel() method.

Note:If we dont provide our own controller, then above controllers will be called by Hybris automatically but its not a good practice to use like that for new component

Let’s extend our class with AbstractCMSComponentController as we are going to write our own implementation for fillModel() method as below

Create the below class in the same package where AbstractCMSComponentController is available

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package org.training.storefront.controllers.cms;
 
import javax.servlet.http.HttpServletRequest;
 
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.training.core.model.CustomOffersComponentModel;
import org.training.storefront.controllers.ControllerConstants;
@Controller("CustomOffersComponentController")
@Scope("tenant")
@RequestMapping(value = ControllerConstants.Actions.Cms.CustomOffersComponent)
public class CustomOffersComponentController extends AbstractCMSComponentController<CustomOffersComponentModel>
{
    @Override
    protected void fillModel(final HttpServletRequest request, final Model model, final CustomOffersComponentModel component)
    {
        model.addAttribute("offerImageLinks", component.getOfferImageLink());
 
        model.addAttribute("offerImage", component.getOfferImage());
 
        model.addAttribute("headerText", component.getHeadrerText());
 
        model.addAttribute("footerText", component.getFooterText());
    }
}
package org.training.storefront.controllers.cms;

import javax.servlet.http.HttpServletRequest;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.training.core.model.CustomOffersComponentModel;
import org.training.storefront.controllers.ControllerConstants;
@Controller("CustomOffersComponentController")
@Scope("tenant")
@RequestMapping(value = ControllerConstants.Actions.Cms.CustomOffersComponent)
public class CustomOffersComponentController extends AbstractCMSComponentController<CustomOffersComponentModel>
{
	@Override
	protected void fillModel(final HttpServletRequest request, final Model model, final CustomOffersComponentModel component)
	{
		model.addAttribute("offerImageLinks", component.getOfferImageLink());

		model.addAttribute("offerImage", component.getOfferImage());

		model.addAttribute("headerText", component.getHeadrerText());

		model.addAttribute("footerText", component.getFooterText());
	}
}

Also make sure the RequestMapping value is defined in the ControllerConstants.java file(\hybris\bin\custom\training\trainingstorefront\web\src\org\training\storefront\controllers\ControllerConstants.java)

add new constant value inside Actions Interface and inside CMS interface,it should be something like as below

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface ControllerConstants
{
interface Actions
    {
        interface Cms
        {
            //All the existing lines goes here
 
            String CustomOffersComponent = _Prefix + CustomOffersComponentModel._TYPECODE + _Suffix;
        }
    }
//All the existing lines goes here
}
public interface ControllerConstants
{
interface Actions
	{
		interface Cms
		{
			//All the existing lines goes here

			String CustomOffersComponent = _Prefix + CustomOffersComponentModel._TYPECODE + _Suffix;
		}
	}
//All the existing lines goes here
}

Create a new jsp page called customOffersComponent.jsp as below
(/trainingstorefront/web/webroot/WEB-INF/views/desktop/cms/customOffersComponent.jsp)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="template" tagdir="/WEB-INF/tags/desktop/template" %>
<%@ taglib prefix="theme" tagdir="/WEB-INF/tags/shared/theme" %>
<%@ taglib prefix="nav" tagdir="/WEB-INF/tags/desktop/nav" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="cms" uri="http://hybris.com/tld/cmstags" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="ycommerce" uri="http://hybris.com/tld/ycommercetags" %>
<%@ taglib prefix="common" tagdir="/WEB-INF/tags/desktop/common" %>
<%@ taglib prefix="breadcrumb" tagdir="/WEB-INF/tags/desktop/nav/breadcrumb" %>
<div class="customOffersComponent">
<p class="customOffersComponent_header_text">${headerText}</p>
<img alt="Offer Image" src="${offerImage.url}" class="offers_image">
<ul class="offers_list">
<c:forEach items="${offerImageLinks}" var="offerImageLink">
<li class="offers_list_element"><a href="${offerImageLink.url}">${offerImageLink.name}</a></li>
</c:forEach>
</ul>
<p class="customOffersComponent_header_text">${footerText}</p>
</div>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="template" tagdir="/WEB-INF/tags/desktop/template" %>
<%@ taglib prefix="theme" tagdir="/WEB-INF/tags/shared/theme" %>
<%@ taglib prefix="nav" tagdir="/WEB-INF/tags/desktop/nav" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="cms" uri="http://hybris.com/tld/cmstags" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="ycommerce" uri="http://hybris.com/tld/ycommercetags" %>
<%@ taglib prefix="common" tagdir="/WEB-INF/tags/desktop/common" %>
<%@ taglib prefix="breadcrumb" tagdir="/WEB-INF/tags/desktop/nav/breadcrumb" %>
<div class="customOffersComponent">
<p class="customOffersComponent_header_text">${headerText}</p>
<img alt="Offer Image" src="${offerImage.url}" class="offers_image">
<ul class="offers_list">
<c:forEach items="${offerImageLinks}" var="offerImageLink">
<li class="offers_list_element"><a href="${offerImageLink.url}">${offerImageLink.name}</a></li>
</c:forEach>
</ul>
<p class="customOffersComponent_header_text">${footerText}</p>
</div>

In the above JSP file, we have just added the new component attributes for display.

Add the CSS properties in CSS file for UI change

Add the below lines at the end to landingLayout2Page.css file for the same as below
(hybris\bin\custom\training\trainingstorefront\web\webroot\_ui\desktop\common\css\landingLayout2Page.css)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
.customOffersComponent{
    border:1px dotted #a5a5a5;
    border-width:1px 0 ;
    position:relative;
}
 
.customOffersComponent_header_text .customOffersComponent_header_text{
    border-bottom:1px solid #a5a5a5;
    height:30px;
    line-height:30px;
    text-transform:uppercase;
    font-weight:bold;
    font-size:1em;
    white-space: nowrap;
    display:block;
    margin:0 0 10px 0;
}
.offers_list{
    margin: 20px 0;
    padding: 5px;
    border: 1px dotted #000;    
}
.offers_list_element{
    vertical-align:top;
    border-bottom:1px dotted #a5a5a5;
    padding:20px 0 0 0;
}
 
.offers_image{
    float:left;
    text-align:center;
    
}
.customOffersComponent{
	border:1px dotted #a5a5a5;
	border-width:1px 0 ;
	position:relative;
}

.customOffersComponent_header_text .customOffersComponent_header_text{
	border-bottom:1px solid #a5a5a5;
	height:30px;
	line-height:30px;
	text-transform:uppercase;
	font-weight:bold;
	font-size:1em;
	white-space: nowrap;
	display:block;
	margin:0 0 10px 0;
}
.offers_list{
	margin: 20px 0;
	padding: 5px;
	border: 1px dotted #000;	
}
.offers_list_element{
	vertical-align:top;
	border-bottom:1px dotted #a5a5a5;
	padding:20px 0 0 0;
}

.offers_image{
	float:left;
	text-align:center;
	
}


Step 4

Add this component into the Home page jsp using CMS tags

Open landingLayout2Page.jsp and add the below code(for now , add it below Section5 pageSlot)

1
2
3
<cms:pageSlot position="SummerOffersSlotName" var="feature" element="div" class="span-24 section5 cms_disp-img_slot">
<cms:component component="${feature}" />
</cms:pageSlot>
<cms:pageSlot position="SummerOffersSlotName" var="feature" element="div" class="span-24 section5 cms_disp-img_slot">
<cms:component component="${feature}" />
</cms:pageSlot>

Note: position that we have added here should be same as content slot name in the impex.


Step 5

Do ant all, Update system and make sure the content catalog is copied from Staged to Online(if not done then synchronize it)

Open the Home page in the site and check the output

Homepage after adding new offer component

In the Home Page, we are getting our custom component displayed and highlighted the 4 attributes of the component rendered as we defined.

About the Author

Karibasappa G C (KB)
Founder of javainsimpleway.com
I love Java and open source technologies and very much passionate about software development.
I like to share my knowledge with others especially on technology 🙂
I have given all the examples as simple as possible to understand for the beginners.
All the code posted on my blog is developed,compiled and tested in my development environment.
If you find any mistakes or bugs, Please drop an email to kb.knowledge.sharing@gmail.com

Connect with me on Facebook for more updates

Share this article on