Introduction
A hybris catalog, as its name suggests, is similar to the concept of a printed catalog. It keeps a collection of your items together in one version.
Unlike a printed catalog, however, hybris catalogs are dynamic, powerful and provide a high level of flexibilty.
Catalogs allow you to update versions easily and permit to synchronize items from one catalog version to another, either by copying the entire catalog or by specifying attributes to be moved to another version.
One popular way to use catalogs is to have a staging and an online catalog containing identical products
. Products are kept in the staging catalog until considered ready for general release and are then synchronized into the online catalog version.
Products in the staging catalog are still available in the live system, but are only visible to administrators or to authorized users
with appropriate personalization settings. As soon as products are considered ready for release,
the catalog version can be updated and (all or selected) items are synchronized to the online catalog version.
Restrictions can be set up, so customers can only view Products in the online catalog whereas users may only edit products which are part of
the staging catalog. This way a staging or testing environment can be set up allowing us to view Products,
add them to shopping carts etc. before they are finally released to customers.
Motivation
In our example, we are going to demonstrate the use of Catalogs as a versioning system by making the News item
in our Cuppy application catalog aware. The administrator adds News items in a staging mode first,
so the news item is only released after having been proof-read and checked for consistency.
To implement a News item that can be staged and later released, we need to:
- Make the News Type catalog aware in the items.xml of the CuppyTrail, so that we can set a catalog version on it
- Create a News item with the catalog version (and id) and set the version to be Default:Staged initially
- Synchronize the News Item so that we have an online catalog version of the News Item
- View the read / write permissions of the News Item in the cuppy admin panel and notice that the News items in the online catalog are read-only
- Login to the cuppy frontend and you will notice that no restrictions are in place meaning that both versions of the News item are visible
- Add a Personalisation Rule for type News and user group cuppyplayers so that only News items of the online catalog version are visible
Tasks/Discussion
Make the News type catalog aware
We need to make the News type "catalog aware", so that we can associate it with a catalog and a catalog version. We do this by appending the two attributes catalogItemType and catalogVersionAttributeQualifier to the original definition of News. Note that we specify generate="false" because News is declared in the Cuppy extension already. We need to specify optional="false" since we need to specify the two attributes when we are creating a catalog aware News item.
Add the following XML fragment to the cuppytrail-items.xml in the items.xml
<itemtype
code="News"
autocreate="false"
generate="false">
<custom-properties>
<property name="catalogItemType"><value>java.lang.Boolean.TRUE</value></property>
<property name="catalogVersionAttributeQualifier"><value>"catalogVersion"</value></property>
<property name="uniqueKeyAttributeQualifier"><value>"id"</value></property>
</custom-properties>
<attributes>
<attribute qualifier="id" type="java.lang.String">
<modifiers initial="true" optional="false" write="true"/>
<persistence type="property"/>
</attribute>
<attribute qualifier="catalogVersion" type="CatalogVersion">
<modifiers initial="true" optional="false" write="true"/>
<persistence type="property"/>
</attribute>
</attributes>
</itemtype>
cuppytrail/resources/localization/cuppytrail-locales_en.properties
type.news.catalogVersion.name=Catalog Version
type.news.catalogVersion.description=Catalog Version of the News Item
type.news.id.name=id
type.news.id.description=unique identifier used by catalogs
Rebuild your project (
ant all
). Since the data model and localization has changed,
we also need update the system (Update running system and Localize types).
Create a News item
Login to cuppy at http://localhost:9001/cuppy, using the login pp and password 1234. Change to the admin perspective.
Now select News type from the console on the left and create a new News Item. You will notice that you get a dialog box displaying all mandatory fields you need to fill in. These are the Catalog ID and Catalog version.
---
Choose the existing Catalog version of "Staged" and add a unique identifier (e.g. "111"). Add all essential details and then click
on the done button to add a new News Item. Use the tab on the right to click on the "message" field and edit the Message field.
We had added the text "The women's world cup is over"
----
Logout of cuppy and log back in as a CuppY player using the login demo_cuppy and password 1234,
and notice that you can now see the news Item. However, this is not the behavior we want and we
still need to set up staging/online visibiltiy and access rights.
---
Synchronize the News Item
Before we add personalization rules to filter the players' views, we need to synchronize the News item to have an online and a staging version.
This will make it easier to see the personalization rules in effect when we create them. In the real world, your personalization rules
would need to be set up initially, and you would perform synchronization at regular intervals according to your requirements.
In our trail, we want to emphasize the different behavior of Items which reside in different version, that is why we are performing a synchronization first.
To synchronize the News Item, we first need to add it to the Synchronization rule. A Synchronization rule will only synchronize types
that it is told about:
Open the hMC console at http://localhost:9001/hmc/hybris and log in with login admin, password nimda.
Open the Catalog tree element on the left. Choose catalogs, open the default catalog Item and the default catalog type.
Choose the Default Staged Catalog version and click the synchronize catalog version on the top right to open the Synchronization wizard.Add News to the list of Root Types in the Type Setting list at the bottom of the page,
by right clicking on the list and using the +Add Composed Type.
---
In the “identifier” field at the top, type “News” and do a search. Select the News Item from the result and click “use”.
Now save and then click the small green start button on the bottom right of the Synchronization wizard pane.
You should get a pop-up success message. Now login to cuppy as the pp user and select the Admin page.
Choose News from the types on the left and you will see that you have two News items in the system.
If you double click on them alternatively and compare them by looking at the information on the right,
you will see that there is a staging and an online catalog version. Click on the message
field and you will see that the online version cannot be modified. This is because we are using
the default Catalog which has a staging and online version already. By default these are editable and
non editable respectively which is what you usually want.
Log into the cuppy cockpit and observe that no restrictions are in place yet
If you login login demo_cuppy and password 1234, you will see that there are two News Items visible to the user.
To prevent the user from seeing the staged news item, we need to set up a personalization rule.
--
Add a Personalisation Rule for the News type and user group cuppyplayers
We'll add the rule with impex
Open the hMC, choose System from the tree on the left, select Personalization and then specify the fields as follows: fill in the Identifier: Cuppy_Frontend_News, Name: News, Filter: {item:catalogVersion} IN (?session.catalogversions), Restricted Type: News - News,
Apply on: cuppyplayers
---This rule (or restriction) checks the catalogVersion variable against the session to make sure that only version values
that match those in the session will be returned.
For making it persistent include the equivalent impex file:
INSERT_UPDATE SearchRestriction;code[unique=true]; name[lang=en];query;restrictedType(code);principal(uid);generate[default=true];active[default=true]
;Cuppy_Frontend_News;News;"{item:catalogVersion} IN (?session.catalogversions)";News;cuppyplayers
The second part of getting this filter to work properly is to set the value of the catalogVersion.
This is done by extending the DefaultMatchService class with the CuppytrailMatchService class and overriding the getLatestNews() method.
You will see that we use an anonymous inner class SessionExecutionBody. The details of this are outside of the scope of this trail.
If we didn't do this, the catalog version filtering would be applied to all of the items in the system and not just News
Add the following piece of xml to the cuppytrail-spring.xml file:
<bean id="matchService" class="de.hybris.platform.cuppytrail.CuppytrailMatchService"/>
Rebuild your system (
ant all
) and run an update in the hybris Administration Console (
essential data
) to load the search restrictions
we created in the impex file above. Once complete, log back in to the Cuppy application using the login demo_cuppy and password 1234,
and you will see a single News item being displayed. This is the online version as we intended.
News items created in the staged catalog version will not be visible until they are synchronized to the catalog's online version.
Compatibility issues
Since we have changed News type as Catalog aware type we need to refactor all places in the code where News items are automatically
created (e.g. Trail ~ Events), since id
and catalogVersion
are mandatory. For that reason we add a PrepareInterceptor
which generates the id and sets the catalog Version before a News item is saved.
Create an additional Interceptor class NewsCatalogAwareInterceptor
NewsCatalogAwareInterceptor creates NewsModel with predefined catalog version and generated keys for newsId.
package
de.hybris.platform.cuppytrail.interceptors;
import
de.hybris.platform.catalog.CatalogVersionService;
import
de.hybris.platform.catalog.model.CatalogVersionModel;
import
de.hybris.platform.cuppy.model.NewsModel;
import
de.hybris.platform.servicelayer.interceptor.InterceptorContext;
import
de.hybris.platform.servicelayer.interceptor.InterceptorException;
import
de.hybris.platform.servicelayer.interceptor.PrepareInterceptor;
import
de.hybris.platform.servicelayer.keygenerator.KeyGenerator;
import
org.apache.commons.lang.StringUtils;
import
org.springframework.beans.factory.annotation.Required;
/**
* Adds default catalog version and default ID in case no catalog version or no ID is set.
*/
public
class
NewsCatalogAwareInterceptor
implements
PrepareInterceptor
{
private
String defaultCatalog;
private
String defaultCatalogVersion;
private
CatalogVersionService catalogVersionService;
private
KeyGenerator keyGenerator;
@Override
public
void
onPrepare(
final
Object model,
final
InterceptorContext ctx)
throws
InterceptorException
{
if
(model
instanceof
NewsModel)
{
final
NewsModel news = (NewsModel) model;
if
(StringUtils.isBlank(news.getId()))
{
news.setId(createNewsId());
}
if
(news.getCatalogVersion() ==
null
)
{
news.setCatalogVersion(getDefaultCatalogVersion());
}
}
}
private
String createNewsId()
{
return
this
.keyGenerator.generate().toString();
}
private
CatalogVersionModel getDefaultCatalogVersion()
{
return
this
.catalogVersionService.getCatalogVersion(
this
.defaultCatalog,
this
.defaultCatalogVersion);
}
@Required
public
void
setDefaultCatalog(
final
String defaultCatalog)
{
this
.defaultCatalog = defaultCatalog;
}
@Required
public
void
setDefaultCatalogVersion(
final
String defaultCatalogVersion)
{
this
.defaultCatalogVersion = defaultCatalogVersion;
}
@Required
public
void
setKeyGenerator(
final
KeyGenerator keyGenerator)
{
this
.keyGenerator = keyGenerator;
}
@Required
public
void
setCatalogVersionService(
final
CatalogVersionService catalogVersionService)
{
this
.catalogVersionService = catalogVersionService;
}
}
package de.hybris.platform.cuppytrail.interceptors;
import de.hybris.platform.catalog.CatalogVersionService;
import de.hybris.platform.catalog.model.CatalogVersionModel;
import de.hybris.platform.cuppy.model.NewsModel;
import de.hybris.platform.servicelayer.interceptor.InterceptorContext;
import de.hybris.platform.servicelayer.interceptor.InterceptorException;
import de.hybris.platform.servicelayer.interceptor.PrepareInterceptor;
import de.hybris.platform.servicelayer.keygenerator.KeyGenerator;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Required;
/**
* Adds default catalog version and default ID in case no catalog version or no ID is set.
*/
public class NewsCatalogAwareInterceptor implements PrepareInterceptor
{
private String defaultCatalog;
private String defaultCatalogVersion;
private CatalogVersionService catalogVersionService;
private KeyGenerator keyGenerator;
@Override
public void onPrepare(final Object model, final InterceptorContext ctx) throws InterceptorException
{
if (model instanceof NewsModel)
{
final NewsModel news = (NewsModel) model;
if (StringUtils.isBlank(news.getId()))
{
news.setId(createNewsId());
}
if (news.getCatalogVersion() == null)
{
news.setCatalogVersion(getDefaultCatalogVersion());
}
}
}
private String createNewsId()
{
return this.keyGenerator.generate().toString();
}
private CatalogVersionModel getDefaultCatalogVersion()
{
return this.catalogVersionService.getCatalogVersion(this.defaultCatalog, this.defaultCatalogVersion);
}
@Required
public void setDefaultCatalog(final String defaultCatalog)
{
this.defaultCatalog = defaultCatalog;
}
@Required
public void setDefaultCatalogVersion(final String defaultCatalogVersion)
{
this.defaultCatalogVersion = defaultCatalogVersion;
}
@Required
public void setKeyGenerator(final KeyGenerator keyGenerator)
{
this.keyGenerator = keyGenerator;
}
@Required
public void setCatalogVersionService(final CatalogVersionService catalogVersionService)
{
this.catalogVersionService = catalogVersionService;
}
}
Spring
Open the file cuppytrail/resources/cuppytrail-spring.xml. Add the following beans definition to it:
<bean id="newsCatalogAwareInterceptor" class="de.hybris.platform.cuppytrail.interceptors.NewsCatalogAwareInterceptor" autowire="byName">
<property name="keyGenerator" ref="newsIdGenerator" />
<property name="defaultCatalog" value="Default" />
<property name="defaultCatalogVersion" value="Online" />
<property name="catalogVersionService" ref="catalogVersionService" />
</bean>
<bean id="NewsValidateInterceptorMapping" class="de.hybris.platform.servicelayer.interceptor.impl.InterceptorMapping">
<property name="interceptor" ref="newsCatalogAwareInterceptor" />
<property name="typeCode" value="News" />
</bean>
<bean id="newsIdGenerator" class="de.hybris.platform.servicelayer.keygenerator.impl.PersistentKeyGenerator" init-method="init">
<property name="key" value="news_id" />
<property name="digits" value="8" />
<property name="start" value="00000000" />
<property name="numeric" value="true" />
</bean>