CMS Integration Points Case Studies
CMS Integration Points Case Studies
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.
Requirements
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
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.
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.
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.
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
Widgets
Requirements
Data Storage
Velocity markup is stored in Velocity files directly on the Commerce Cloud WebDAV in the dynamic
location.
• 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 ...)
• 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
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.
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".
var file;
var folderPath;
var pathToFile;
folderPath = [File.DYNAMIC,
Site.getCurrent().ID,
request.locale,
templateModel.context.type,
templateModel.context.pageID].join(File.SEPARATOR);
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.
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;
});
module.exports = TemplateModel;
CategoryModel
'use strict';
* @constructor module:model/CategoryTemplateModel~CategoryTemplateModel
*/
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);
checkDefault
var checkDefault = function (templateModel){
while (!defaultFound){
if ((empty(file)) || (!file.exists())){
parentID =
dw.catalog.CatalogMgr.getCategory(currentPositionID).getParent().getID();
return file;
}
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).
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.