0% found this document useful (0 votes)
44 views

CMS Integration Points Case Studies

This document discusses two case studies for integrating a CMS into Commerce Cloud. Scenario A involves only managing content pages, with the CMS controlling the page body but providing a skeleton structure. Scenario B requires managing complete page layouts, including dynamic widgets. Both scenarios store Velocity markup in content assets or files on the Commerce Cloud to separate page structure from content and allow reuse. The solutions implement approaches like decorator templates to merge structure and body markup.

Uploaded by

raju
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
44 views

CMS Integration Points Case Studies

This document discusses two case studies for integrating a CMS into Commerce Cloud. Scenario A involves only managing content pages, with the CMS controlling the page body but providing a skeleton structure. Scenario B requires managing complete page layouts, including dynamic widgets. Both scenarios store Velocity markup in content assets or files on the Commerce Cloud to separate page structure from content and allow reuse. The solutions implement approaches like decorator templates to merge structure and body markup.

Uploaded by

raju
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

CMS Integration

into Commerce
Cloud
Case Studies
Case Studies

We will conclude this paper with a brief overview of two case studies. These integrations scenarios
are meant to serve as a demonstration. When selecting your integration approach, consider
functional requirements and identify the optimal solution that both meets project requirements as
well as follows best practices for manageability, performance and security.

Version 1.1 Salesforce | 1


Scenario A

Requirements

• Only content pages covered.


• Dynamic elements (widgets) to be used inside the markup.
• CMS is largely controlling the main body, however, it still provides the skeleton.

Data Storage
The Velocity markup will be stored in content assets, however there could be Velocity rendering
template(s) stored in the dynamic location and referenced in the assets, as rendering template(s).
Please, see Solution section for more details.
Localization requirements are satisfied by the ability to localize the body of a content asset.

Dataflow

Version 1.1 Salesforce | 2


• CSS and images are moved to the Library location (shared or site specific) via WebDAV
• Pages are segregated in the following manner:

The page structure itself will be stored as Velocity template file in the Dynamic location and
the structure offers the HTML skeleton. The approach is similar to the ISML decorator templates.

rendering.vs
<!doctype html>
<!--[if lt IE 7]> <html class="ie6 oldie" lang="en">
<![endif]-->
<!--[if IE 7]> <html class="ie7 oldie" lang="en">
<![endif]-->
<!--[if IE 8]> <html class="ie8 oldie" lang="en">
<![endif]-->
<!--[if gt IE 8]><!--> <html lang="en"> <!--<![endif]-->
<head>
$include.htmlhead($pdict)
</head>
<body>
<div id="wrapper" class="pt_content">
$include.header($pdict)
<h1>VELOCITY RENDERING TEMPLATE</h1>
<div id="main" role="main" class="full-width clearfix">
<div id="primary" class="primary-content">
$html
</div>
</div>
$include.footer($pdict)
</div>
</body>

The body and additional metadata for the pages are sent through the OCAPI Data API. This type of
data will be stored in content assets. The metadata includes, for instance, the assignment to the
right rendering template and additional SEO attributes.

Solution

Requirements for this implementation scenario are simple and only address content pages, not
leveraging any other commerce content, such as categories or products. Additionally, all of the
required data is contained inside the content assets.

Version 1.1 Salesforce | 3


Decoupling page structure and content

To meet the requirements, CMS will provide both the content itself as well as the page skeleton. As
the skeleton is shared between multiple pages (possibly between all pages), it is unnecessary to
store the markup, which repeats in each asset. Common elements in this case are html-head, the
footer, the header element and possibly the navigation menu.

To avoid duplicating the content and to promote re-usability, a concept similar to the ISML
decorator templates is applied here as well, allowing to make changes to the overall page structure
from a single location.

A rendering template (or multiple) will be created as Velocity template file(s). A sample snippet can
be seen in the last code block. It is functioning as a decorator providing HTML skeleton including
html-head, header and footer. Additionally, there is wrapper div container and a placeholder -
"$html "- where later on the body will be rendered into.

General Lookup

The rendering is implemented in the Page controller/pipeline; a content asset will be looked up
based on the page ID. Next, the rendering template, configured in respective field (see below) will
determine if this content asset will be rendered as Velocity or not.

This field can be parsed via a RegEx.

hasVelocityTemplate check
exports.isVelocityAsset = function(obj){
return obj && obj.template && /\.vs$|\.vm$/.test(obj.template);
};

If you are not that familiar with using regular expressions, you can also use another custom attribute
acting as a flag (obj.custom.velocity) returning true/false.
Basically, configuration will determine how content assets need to be rendered. If Velocity template
is used, then myModule.renderAsset(content) method will take care of rendering.

Version 1.1 Salesforce | 4


Since Velocity is plain HTML with some additional syntax, it is also possible to render pure HTML with
the new Velocity script class. You will need to make sure that it doesn't include special markup, such
as the Commerce Cloud markup syntax $..$.

rendering in Page controller


if(myModule.isVelocityAsset(content)){
response.setExpires(new Date().getTime() + 24*60*60*1000); // now + 24h cache
time
myModule.renderAsset(content);
}else{
app.getView({
Content: content,
ContentSearchResult: contentSearchModel
}).render(content.template || 'content/content/contentpage');
}

In this example, the content asset will be rendered with Velocity, however there is still the matter of
de-coupled markup, which needs to be tackled. Basically there are two attributes - the rendering
template and the body - where the rendering template includes a placeholder to mark the exact
place for the body to be added. The separate markup of these attributes will need to be merged.

This is achieved by the rendering method in the custom script module. Rendering is executed in two
steps.

1. The markup inside the body attribute will be rendered with the Velocity class and written into
a StringWriter, so that it can be used further and won't be written into the response directly.
2. The rendering template will be rendered with Velocity as well and a global object will be
inputted. Note, that the previously rendered body String will be added to this object
"global.html" to enable it for usage inside the rendering template. This is where the
rendering into the response will take place.

This two-step rendering outline demonstrates how the $html placeholder is working.

myModule: renderAsset()
var File = require("dw/io/File");
var object = require('app_storefront_controllers/cartridge/scripts/object');
var Site = require("dw/system/Site");
/**
* Render an asset using the assigned velocity template
*
* @param content the asset to render

Version 1.1 Salesforce | 5


* @param params additional keys that will be made available to the velocity
template
*/
exports.renderAsset = function(content, params){
//create the template file object
var templateFileName = [File.DYNAMIC,
Site.getCurrent().ID,
content.template].join(File.SEPARATOR);
var template = new File(templateFileName);
if(!template.exists()){
require('dw/system/Logger').error('Specified template {0} does not exist.',
templateFileName);
return;
}
var velocity = require('dw/template/Velocity');
//create the global scope that can be accessed via $xyz in the template
var global = {
asset : content,
include : require('./include')
};
object.extend(global, params);
//add a self reference to the global scope so it can be accessed via $pdict
global.pdict = global;
//finally treat the HTML of the body as velocity template as well
if(content.custom.body){
var html = new dw.io.StringWriter();
velocity.render(content.custom.body, global, html)
global.html = html.toString();
}
//...and render it
velocity.renderTemplate(template, global);
};

Widgets

See below scenario B, as both case studies have this in common.

Version 1.1 Salesforce | 6


Scenario B

Requirements

• Complete page layout must be managed in the CMS


• Dynamic elements (widgets) to be used inside the markup
• Impacted areas are category landing pages, customer service pages and the homepage
• Implement template fallback mechanism (a default version to be used in case there is no
template available)
o A default template can be set at any level in the category tree
o For content, there will be a single default
o There must be a default template at root level

Data Storage

Velocity markup is stored in Velocity files directly on the Commerce Cloud WebDAV in the dynamic
location.

To meet the requirements, the following folder structure is applied.


-> DYNAMIC / {locale} / {type} / {filename}

• locale: will be the locale of the site (default, en, de, ...)
• type: page type according the context (content, category) -> homepage will be added directly
under the locale as "special" page (DYNAMIC / {locale} / homepage.vs)
• filename: filename matching the Commerce Cloud IDs (mens.vs, womens.vs, faq.vs, about-us ...)

Version 1.1 Salesforce | 7


Data Flow

• Data is sent from the CMS to Commerce Cloud through WebDAV interface.
• CSS and images are moved to the Library location (shared or site-specific)
• Pages are moved to the new dynamic location

Solution

Full Markup Management

To meet requirements in this scenario, CMS system will be sending the complete markup, including
HTML head and body. All re-usable components, such as header, footer, header menu, etc., should
be provided as widgets. See Section 5.3 for more details on widgets.

Version 1.1 Salesforce | 8


General Lookup

Correct Velocity template file can be looked up either by an explicit assignment of the template to
the respective object, or by a custom lookup based on naming convention.

• Explicit assignment:
o For content assets and categories, you can utilize the rendering template fields to specify
the relevant Velocity file name. The right Velocity template will be retrieved and applied
when rendering a category through your controller/pipeline.
o To set a default template, you may need a second attribute field (for content you would
use the root library folder, as there is just one global fallback)
o For the homepage you create a fallback content asset and treat it as special content page
(no default required, as the homepage is always needed)

• Custom lookup:
o As the elements are added to different folders based on type, the check for content
pages or category pages is trivial
o ID can be used as a filename and file search will rely on this naming convention: ({id}.vs)
o If template file is not found, the fallback strategy would apply:
§ for content: the global default will be used: "../content/default.vs".
§ for categories: search should go up the category structure and check if there is a
default template in the tree following the convention
"{currentParentID_default}" & when root level is reached, a global default will be
used: "../category/default.vs".

getPage : function (templateModel){

var file;
var folderPath;
var pathToFile;

folderPath = [File.DYNAMIC,
Site.getCurrent().ID,
request.locale,
templateModel.context.type,
templateModel.context.pageID].join(File.SEPARATOR);

file = new File(pathToFile);

Version 1.1 Salesforce | 9


if ((empty(file)) || (!file.exists())){
file = checkDefault(templateModel);
}

return file;
}

The template model is also carries context information (e.g. category, content, homepage) and
allows adding necessary objects relevant for the current context. You can build an inheritance chain
with basic information and context specific information.

Default Template Model:

TemplateModel
/** @module model/TemplateModel */
var Class = require('~/cartridge/scripts/util/Class').Class;
var object = require('~/cartridge/scripts/object');
/**
* Base class for all modules following the <strong>Model Concept</strong>.
*
* @see
*/
var TemplateModel = Class.extend(
/** @lends module:model/TemplateModel~TemplateModel.prototype */
{
/**
* (Default) TemplateModel constructor
* Just loops the parameters through to the template
*
* @constructs
* @param {Object} params The parameters to pass
*/
init : function (params) {
//copy all properties of params to the view
object.extend(this, params);

this.currentHttpParameterMap = request.httpParameters;
this.url = require('dw/web/URLUtils');
//utility module to help creating Widget urls more easily inside the template
this.include = require('./utils/include');
this.context = params.context;

Version 1.1 Salesforce | 10


this.context.type = 'default';
return this;
}

});
module.exports = TemplateModel;

Category Template Model:

CategoryModel
'use strict';

/** @module model/CategoryTemplateModel */

var TemplateModel = require('./TemplateModel');


/**
* Template model for category page context
*/

var CategoryTemplateModel = TemplateModel.extend(


{
/**

* @constructor module:model/CategoryTemplateModel~CategoryTemplateModel

*/

init : function (params) {

this._super(params);
this.context.type = 'category';
this.Category = dw.catalog.CatalogMgr.getCategory(params.cgid);
return this;

}
});
module.exports = CategoryTemplateModel;

Rendering:

Rendering
//adding the template model into the rendering
velocity.renderTemplate(template, templateModel);

Version 1.1 Salesforce | 11


Fallback Lookup

Category default template look up can be implemented as follows: (simplified)

checkDefault
var checkDefault = function (templateModel){

var currentPositionID = templateModel.context.pageID;


var defaultFound = false;
folderPath = [File.DYNAMIC,
Site.getCurrent().ID,
request.locale,
templateModel.context.type].join(File.SEPARATOR);

while (!defaultFound){

pathToFile = [folderPath, currentPositionID,


'_default'].join(File.SEPARATOR);
file = new File(pathToFile);

if ((empty(file)) || (!file.exists())){
parentID =
dw.catalog.CatalogMgr.getCategory(currentPositionID).getParent().getID();

if(parentID === 'root'){


pathToFile = [folderPath, 'default'].join(File.SEPARATOR);
file = File(pathToFile);
defaultFound = true;
}
else{
currentPositionID = parentID;
}
}
else{
defaultFound = true;
}
}

return file;
}

Version 1.1 Salesforce | 12


Widgets

The purpose of widgets is to allow for dynamic elements to be used inside the Velocity markup.
Widgets can also be elements which are shared between ISML and Velocity storefront pages. In
general, you would use widgets as remote includes, so any public controller/pipeline endpoint can
be used as a widget. The template language a widget is implemented in, is not relevant. For instance,
you could have a Velocity page triggering a remotely included widget, which is ending up in an ISML
template (e.g. producttile).

Widgets could be:

Type Elements Usage

Header menu Navigation and search $include.headermenu()

Header information Login, login info, und locale $include.header()


switcher, mini cart

Header HTML head $include.htmlhead()

Footer Footermenu, CSS and JS $include.footer()


includes, other meta data

ProductTile Product tile $include.product(pid)


[params: pid – product ID]

Granularity can be increased or decreased as required by the project. For example, the full header
can be a single widget or a set of widgets, in case it makes sense to recombine the elements into
various header experiences on the CMS side.

Version 1.1 Salesforce | 13

You might also like