SmartEdit: A Deep Dive Into Technical Aspects Of the New CMS & Personalization Capabilities


Non-product content management has for many years been the weakest link of the SAP Commerce platform. Formally, the suite had a WCMS module from the very first version, but it was obvious to everybody that the solution was terribly old-fashioned and outdated. Already back in 2016, SAP Hybris Commerce was extended with a new solution, SmartEdit. On the one hand, SAP released it too early. Many commented that the product is underbaked to replace WCMS Cockpit. Even two years later, Smartedit had drawn much criticism from the users and developers. On the other hand, it was long overdue. We had been waiting for a replacement for years. In the search of the truth, in 2018, I was examining Smartedit in detail and shared the findings here on Hybrismart. There hasn’t been a lot of water under the bridge since 2018, but the situation with Smartedit has taken a definite turn for the better. However, being a “non-mandatory” component during two years, it was generally ignored by the community. After all, why should we use Smartedit if there is a good old WCMS Cockpit, time-tested and proven, albeit with known issues and poor customizability? This is why only a small percentage of developers are aware of Smartedit from the technical perspective. I hope this article will help them to move forward. It should be noted that starting from the version 1811, we’re currently out of options. The cockpit framework, and with it the WCMS Cockpit and BTG personalization, were completely removed from the platform. So, Smartedit is the only option for content management and personalization.

Content Management Systems

To better understand the concepts of Smartedit and its place in the market, let’s look back at how content management systems have evolved over time. The first CMSs were developed to support only websites. Mobile, desktop, and kiosk versions were considered as separate channels.  As a result, many of the solutions were designed as monolithic applications in which the user interface and data access code are combined into a single program to form a single platform. When it comes to innovation, most of the CMS solutions are constrained by their legacy architecture. This was one of the reasons why CMS Cockpit had not been evolved for years by SAP. With every single year of dealing with the deprecating software, the developer experience has been a key pain point which boils down to lack of vendor support, poor documentation, problematic troubleshooting and debugging. The traditional monolithic CMSes provide both content management and content rendering.  More specifically, they have been developed with the frontend and backend being designed into a single platform. In Hybris, we had Accelerator templates built in JSP, page controllers built in Java, and all this stuff must be aligned with the data model and architecture of the CMS system.  The WCMS was designed mainly for developers thereby reducing the leverage of these systems by non-technical users. Many concepts require an understanding of the underlining data model. Headless CMS has no default front-end system to determine how the content is presented to the end user. It provides all of the capabilities of the backend of a traditional CMS (i.e., the “body”) while giving the responsibility of content presentation to the delivery channels (i.e., the head). It acts as a content repository. The data can be requested or pulled from the CMS by any channel, such as mobile or kiosk app, by way of a RESTful API. Each individual channel takes advantage of their own unique presentation capabilities. Some would also name those content management systems “API driven CMS”. The problem with most of headless CMS is you need one more layer to be managed somehow, presentation layer. The possibility to have a page-oriented approach with layouts or grids is important, but generally not provided by headless CMS solutions.

Smartedit

Smartedit was designed to bring the best of both worlds. Its architecture combines the flexibility and adaptability of API-first approach and the user-friendliness of a traditional CMS. For the stable set of API interfaces, you can create more than one storefront and even more than one content management client.  For example, on top of the CMS API, you can develop a module that regularly checks the content and performs corrective actions if it doesn’t meet requirements. Smartedit is built as a single page app interacting with the CMS API. Like a storefront, it makes Smartedit headless too, so that theoretically you can have an alternative client app for CMS operations. Such architecture makes Smartedit clear and understandable in terms of architecture, customizability and data flows. However, Smartedit introduces a bunch of new technologies and tools. It imposes stricter requirements to the project team. It is very unlikely you will use old and obsolete AngularJS 1.6 for the storefront too. The reason for that is clear. Being a single page app, the essential part of Smartedit belongs to frontend development. Frontend world is crazy: it has changed fundamentally in the last 4-5 years. Multiple tools and libraries rose up to meet the challenge, and the best ones have slowly floated to the top. Years ago AngularJS was a promising toolset. As we can see from the code, roadmap and Smartedit releases and updates, the product has been making great headway in refactoring and redesign. The extendable components are now in Typescript, and the interfaces have been evolving to meet the market standards.

Key concepts of Smartedit

Smartedit maintains content in a “page-oriented” manner. Content is generally stuck to the components it is used in. You can’t browse a list of all banners in the system to manage their parameters in isolation from the presentation configuration. Instead, you need to open a page, and select a banner on the page, and change the configuration for the particular banner instance. All alternative approaches will require deep customization of the system. The page editor is integrated into the website UI. It makes Smartedit look like a transparent layer on top of the storefront.
Without Smartedit With Smartedit
This layer can be presented in different functional modes, from the basic one to advanced. Each mode provides a set of available content operations and can be restricted to a content management role. There are five modes out of the box: Preview, Basic Edit, Advanced Edit, Versioning, and Personalization. The content delivery layer is completely decoupled from the content management. Smartedit is responsible for the content management part only. Content delivery and rendering are based on the old good Spring MVC. For the pages, components, and templates, the data model is generally the same we had with WCMS.  Smartedit is designed to work with any storefront supporting the well-documented Smartedit storefront integration contract. Currently, only two storefronts support the contract, the built-in JSP-based SAP Commerce Accelerator, and a brand-new javascript storefront, Spartacus. The modular structure helps to extend almost everything. The extensibility capabilities are demonstrated with a personalization module, which can also be used as a reference implementation for your own custom modules.

Key components

There are three functional components, Navigation Management, and Page Management and Personalization. In terms of navigation, there is a fourth component, Versioning, but functionally it is part of page management. As a name of the CMS feature, “Versioning” was really confusing to me.  It is not an Undo/Redo functionality as I’d have thought. Functionally, Smartedit’s versioning is closer to Backup/Restore: you can save a current version before making changes, and revert to the saved version to discard the changes and restore the saved state. Currently, versioning in SmartEdit is only by page. Content slot and component versioning are not available yet but promised to be added in the future. Navigation management is based on similar concepts as found in good old CMS Cockpit. You can add, move, edit and delete navigation nodes, change the order of the navigation nodes using drag and drop feature. Smartedit’s UI is faster, sleeker, and generally more convenient than what we had in WCMS Cockpit. However, a few hiccups are still a struggle for newbies. For example, you can’t publish the changes in navigation directly from the page where these changes were done. You need to synchronize a navigation component which is in a completely different corner. The navigation data model makes it difficult for many people who are not familiar with complex data structure handling and not experts in this matter at all. How to explain the different types of navigation entry to non-specialist?   However, these cases are few. The Smartedit’s personalization module replaced the old similar-purpose component, Behaviour Target Groups, or Advanced Personalization. The legacy solution triggers actions or shows CMS content to the different group of customers based on their behavior, interests, and historical data. You could use customer-specific promotions, suggest the products, or adapt the content and products of the website to their interests. It looks great at the demonstrations, but due to significant impact on performance, this module was generally avoided. The new module, personalization module, is promising.

Architecture of SmartEdit Personalization

Personalization module is based on two pillars:
  • User/Segment assignments. The segment is used to group the users having the similar behavior or attributes.
    • For example, all users come from the e-mail ad can be grouped into the “Email ad” segment.
    • For example, all users with the age from 40 to 50 can be grouped into the “40-50 y.o.” segment.
  • Page or component actions, variations, and the triggers to activate a group of variations.
    • For example, for users assigned to the combination of the segments “Email ad” and “40-50 y.o”, the banner on the main page is different.
It is important to say that SAP Commerce can’t assign customers to segments automatically based on their behavior or attributes. This is so because of the separation of responsibilities: this is not part of the e-commerce platform purpose and tasks.  You need to integrate SAP Commerce with the segment data provider, such as SAP Marketing or Context-driven Services. There is an out-of-the-box integration package from SAP to make the process smooth and fast. Also, you can’t edit or remove the segments and users-to-segment assignments in Smartedit. These data are considered as external to the e-commerce platform. Smartedit allows you to create variations and connect them to variation triggers. A variation is a set of changes. This change, an action, can be applied to the CMS, Promo or Search configurations. For example, you can replace a CMS component with another version of it and name it as a variation. The trigger is used for enabling the variations for specific conditions. For example, a segment trigger is used when the customer belongs to a particular segment. The legacy BTG personalization engine was not popular mainly because of the performance impact it creates. That module was capable to assign users to segments dynamically, and activate the changes based on the combination of the rules in near real-time manner. These two operations are expensive in terms of resources. In the new implementation, the user segmentation is no longer the e-commerce platform’s responsibility, and the rules are checked only when particular events (such as User Login) occur. After a variation is calculated for the customer session, the system caches the findings in the session, and uses it for further requests. Such actions are used for Smartedit configuration (DefaultCxRecalculationService):
  • RECALCULATE  internally calculates the variations and caches the results in the session for the further calls,
    • code: calculateAndLoadInSession(user)
  • UPDATE pulls the user/segment assignment updates from the external system,
    • code: updateSegments(user, configData.getUpdateProviders())
  • ASYNC_RECALCULATE initiates the asynchronous variation recalculation process.
    • asyncRecalculate(user, configData.getAsyncProcessProviders())
  • LOAD loads the personalization results from the database and saves them serialized as a session var
    • loadResult(user)
These actions can be specified for a fixed (OOTB) number of events, such as user login or user gives consent: The key difference between SmartEdit personalization and legacy BTG personalization is a way how the calculation is implemented. For BTG, it was a real-time calculation while in the Smartedit the calculation is asynchronous. The results can come seconds later after the customer is logged in. Using the asynchronous calculation makes this approach fast and manageable in terms of performance. There are tiny delays, but they don’t affect the customer experience because they occur in the background. When the events are expected to be too frequent, there is a way to perform recalculation on schedule. There is  CxDefaultPersonalizationCalculationJob which executes CxService().calculateAndStoreDefaultPersonalization. 

Customizing Smartedit

There are two main techniques (used together) for customizing the business logic and interfaces:
  • Extending APIs
    • Configuring existing
    • Rewriting existing
    • Creating new APIs
  • Extending frontend business logic and presentation
    • Menu items, popups, buttons…
The following APIs are considered as Smartedit-related. Some of them are purely frontend mechanisms, such as Angular services, some of them are RESTful webservices, or a combination of the webservices and Angular services. Permission Service and Permission API. It is used to determine what functional elements are available for a user and what a user can access or access and edit. Permissionwebservices exposes the RESTful API for global, types, attribute, and catalog permission checks.  The typical requests for global and catalog permissions:
Global permissions Catalog permissions
https://localhost:9002/ permissionswebservices/ v1/ permissions/ principals/ admin/ global?permissionNames= smartedit.configurationcenter.read https://localhost:9002/ permissionswebservices/ v1/ permissions/ principals/ admin/ catalogs?catalogId= electronicsContentCatalog &catalogVersion=Staged
Response: Response:
Example code:
/* Copyright (c) 2017 SAP SE or an SAP affiliate company. All rights reserved. */
angular.module('personalizationsmarteditRulesAndPermissionsRegistrationModule', [
'permissionServiceModule',
'personalizationsmarteditServicesModule'
]).run(function($q, permissionService, personalizationsmarteditRestService, personalizationsmarteditContextService) {

var getCustomizationFilter = function() {
return {
currentPage: 0,
currentSize: 1
};
};

// Rules
permissionService.registerRule({
names: ['se.access.personalization'],
verify: function() {
return personalizationsmarteditContextService.refreshExperienceData().then(function() {
return personalizationsmarteditRestService.getCustomizations(getCustomizationFilter()).then(function() {
return $q.when(true);
}, function(errorResp) {
if (errorResp.status === 403) {
//Forbidden status on GET /customizations - user doesn't have permission to personalization perspective
return $q.when(false);
} else {
//other errors will be handled with personalization perspective turned on
return $q.when(true);
}
});
});
}
});

// Permissions
permissionService.registerPermission({
aliases: ['se.personalization.open'],
rules: ['se.read.page', 'se.access.personalization']
});
});
  • SmartEdit side:
    • Define a rule “se.access.personalization” => REST CALL to /customizations. If 403, rule result is NO ACCESS
    • Define a rule “se.read.page” => catalogVersionPermissionService.hasReadPermissionOnCurrent()
    • Define a permission
      • se.personalization.open” = ”se.read.page” and “se.access.personalization
    • perspectiveService creates a personalization perspective with a constraint: se.personalization.open
    • Combined view toolbar item is created with a constraint:
      • se.read.page
  • SAP Commerce side:
    • /customization access is driven by spring security (user roles) and oauth scopes 
Catalog API. Returns catalog + catalog Versions you have access to, namely: catalog name, catalog version name, thumbnail of the homepage, and page display condition details. CMS SmartEdit Structure API. Returns metadata about the specific CMS component type to determine which of the component’s attributes are editable, to handle editable and non-editable attributes properly. The response contains the field types as well. They are used for rendering the UI controls. It provides information about
  • CMS component types (code, name, exposed attributes)
  • CMS component type attributes (qualifier, type, localization info)
Configuration API. CRUD operations with the configuration attributes. Drag-and-drop service. Angular API for implementing drag-and-drop services. Translation API. Provides the functionality to retrieve a SAP Commerce resource bundle for specific locales. Gateway Factory. A gateway system between the iframe (where the storefront is injected) and a SmartEdit container (which contains the iframe). Establishes communication between the frames (outer/inner) using a pub/sub pattern HTTP Interceptor Service. This service is used in the preprocessing and the postprocessing of $http requests and responses in AngularJS. There are four predefined interceptors:
  • httpAuthInterceptor – adds an authentication token to all REST requests
  • httpErrorInterceptor – handles all 401 (unauthorized access) or OCC validation errors
  • i18nInterceptor – appends a locale to an URL for the request and postprocess the response
  • experienceInterceptor — adds a current catalog and version to the /cmswebservices/catalogs/
Toolbar Service. Adding actions to the SmartEdit header, experience, and perspective toolbar. This service will be explained in detail later.

Smartedit contract for the storefront

The SmartEdit framework is capable to work on any storefront that implements the SmartEdit Contract. The SmartEdit Contract consists of the following:
  • webApplicationInjector.js JavaScript file, to be included in each page that you want to edit with SmartEdit
<script src="some/location/webApplicationInjector.js" 
data-smartedit-allow-origin="domain1, domain2"></script>
  • An HTML markup contract for content slots and components
<div 
class="smartEditComponent" 
data-smartedit-component-
type="SimpleResponsiveBannerComponent" 
data-smartedit-component-id=
"SAPCommerceComponentName" 
data-smartedit-component-uuid="somecompositekeyserialization" 
data-smartedit-catalog-version-uuid="apparel-deContentCatalog/Staged"> 
  
<your-original-component/> 

</div>
Body:
<body 
class="smartedit-page-uid-mypageuid 
smartedit-page-uuid-mypageuuid 
smartedit-catalog-version-uuid-mycatalogversionuuid">
  • A preview ticket API mechanism
  • Optional: reprocessPage and renderComponent functions that perform frontend rendering after the addition or modification of content

Modules

The following diagram shows how Smartedit modules interact with the platform:
  • previewwebservices — Preview API. Preview ticket operations
  • cmswebservices – CMS API for retrieving
    • CRUD for CMS Items (Components, Pages, etc.)
    • R/O and RW operations on CMS data structures
  • cmssmarteditwebservices. Read products, categories, perform synchronization
  • smarteditwebservices. Configuration, languages
  • smartedit. /smartedit web module
  • smarteditaddon. An adapter to Accelerator. SmartEdit Design Contract implementation for the OOTB accelerator.
  • ysmarteditmodule. Template for your own smartedit extension.

CMS Components

Smartedit is compatible with SAP Commerce CMS Components. As usual, for a new component, you need to register a component type extending AbstractCMSComponent or its childs, create a JSP view (<yourcomponentname.jsp>). Additionally, you need to associate a component type group with the component type:
INSERT_UPDATE ComponentTypeGroups2ComponentType; source(code)[unique=true]; target(code)[unique=true]
; wide ; YourCustomComponent
The attributes and type details are exposed to Smartedut via Structure API. By default it provides converters for Collections, Maps, Primitive Wrapper classes, Dates, Products, Categories, User Groups, Media Containers, CMS Items, and all localized variations of these types. For blacklisting the specific attributes, list these in a defaultCmsStructureTypeBlacklistAttributeMap. If all slots have ValidComponents configured with the specific set of allowed components, if your new component type is outside this list (merged across the slots), it won’t be displayed in Smartedit.

Smartedit UI Customization

You can extend the services and features by adding or replacing the OOTB code with your own. As a starting point, create your extension using the ysmarteditmodule template. It is very likely that your code will leverage the services listed above and AngularJS built-in capabilities. So, understanding the details of both is crucial to success. The deprecated ycmssmartedit extension is not used anymore. There are three things you can customize easily:
  • Decorators. A decorator is a feature that adds rendering and behavior to components, which are elements in the storefront that perform different functions. For example, if some of your components are not rectangular, you need to override the default decorator to show a complex shape.  A decorator is attached to a component within a page and provides HTML-rendering, DOM-manipulation, or REST API calls around its component. As a module developer, you can create decorators and wire them to different components.
  • Contextual Menu Items. A contextual menu is a built-in SmartEdit decorator that contains a set of menu items, each of which performs a different function. In the following example, ICON and a submenu were added using a Contextual Menu Service.
  • Toolbar Actions. Buttons or links integrated to the Smartedit header.  Can be added to the header, experience, and perspective toolbars in theSmartEditUI. In the following example, A NEW BUTTON is added using the Toolbar Service.

Custom toolbar actions

In order to extend the toolbars, you need to use featureService.addToolbarItem, and register it in the perspectiveService (sorry for no indents):
import {IFeatureService, IPerspectiveService, SeModule} from 'smarteditcommons';
import {doImport} from './forcedImports';
doImport();
@SeModule({
imports: [
'smarteditServicesModule',
'trainingsmarteditPerspectiveToolbarItemModule',
'MyServiceModule'
],
initialize: (perspectiveService: IPerspectiveService, featureService: IFeatureService, myService: any) => {
'ngInject';
featureService.addToolbarItem({
toolbarId: 'smartEditPerspectiveToolbar',
key: 'trainingsmarteditPerspectiveToolbarItem',
type: 'HYBRID_ACTION',
nameI18nKey: 'A New Button',
priority: 2,
section: 'left',
iconClassName: 'hyicon hyicon-info se-toolbar-menu-ddlb--button__icon',
include: 'trainingsmarteditPerspectiveToolbarItemWrapperTemplate.html',
callback: () => {
myService.openAWizard();
},
});
perspectiveService.register({
key: 'se.cms.perspective.advanced',
nameI18nKey: 'se.cms.perspective.advanced',
descriptionI18nKey: 'se.hotkey.tooltip',
features: ['trainingsmarteditPerspectiveToolbarItem'],
perspectives: []
});
}
})
export class TrainingsmarteditContainer {}
This code creates “A NEW BUTTON” in the Perspective Toolbar: With this service, you can customize all three toolbars: The name of the toolbar, Header, Experience or Perspective, is specified in the addToolbarItem.toolbarId. If you decide to add a custom Wizard, add a callback function implementation into a toolbar definition (see above):
//...
callback: () => {
myService.openAWizard();
},
and your custom service (myService.ts):
(function() {
angular.module('MyServiceModule', [

])
.service('myService', function(modalWizard, catalogService, pageFacade) {
this.openAWizard = function openAWizard(pageData) {
var promise = pageData ? catalogService.retrieveUriContext() :
pageFacade.retrievePageUriContext();
return promise.then(function(uriContext) {
return modalWizard.open({
controller: 'myWizardController’,
controllerAs: '
myWizardCtrl’,
properties: {
uriContext: uriContext,
basePageUUID: pageData ? pageData.uuid : undefined
}
});
});
};
})
.controller('myWizardController', function() {
this.getWizardConfig = function() {
var wizardConfig = {
steps: [{
id: "step1", name: 'Step1’,
title: '
Step’, templateUrl: 'step1.html’
}]
};
return wizardConfig;
}.bind(this);
});
})();

Contextual Menu Items

A contextual menu is a decorator that wraps around a component. It can contain a set of icons, each of which performs a different action. You can add your own controls to the menu.
featureService.addContextualMenuButton({
key: "my.menu.item.key",
i18nKey: ‘My Menu Item',
nameI18nKey: '
my.menu.item',
regexpKeys: ['
MyTestComponent3’], //Regexp, here is a
condition: (config: any) => { //component uid
return true;
},
action: {
template: 'test – will be displayed below the icon'
},
displayClass: "movebutton",
iconIdle: '/trainingsmartedit/icons/icon_off.png',
iconNonIdle: '/trainingsmartedit/icons/icon_on.png',
smallicon: '/trainingsmartedit/icons/info.png',=
// permissions: ['se.read.page’], // for permissions
} as IContextualMenuButton);
featureService.enable(' my.menu.item.key ');

Homepage links

After analyzing the customization approach and available toolset, I decided to experiment with making changes in the UI outside the documented scope. Such as adding a link to a Smartedit homepage. For such operations, you need to clone the simple module from cmssmartedit and extend it:
  • …smarteditContainer.js: import catalogVersionDetailsModule
  • Clone catalogVersionDetailsModule from cmssmartedit
  • Add
this.addItems([{
include: 'myCustomCVDTemplate.html'
}], CATALOG_DETAILS_COLUMNS.LEFT);
  • Create myCustomCVDTemplate and add a link component
In my PoC, this link opens a custom page:

SmartEdit builder

SmartEdit uses Grunt Task Runner and its plugins to provide a single, unified set of commands for easier and faster development. Using the Grunt scripts as a black box works well if you don’t make mistakes. Without the understanding of what is behind the scripts, troubleshooting will be rather difficult. For example, all your template filenames must end with “Template”. You can read that in the documentation, but… If you forget about that, your template file won’t be converted into a javascript code during the build process, and Angular components will throw an exception. Almost all error messages are not informative in AngularJS/SmartEdit, and even in such a simple case, you can waste a day trying to find a root cause. The builder scans for the templates, and the folders and filename patterns are listed in the grunt scripts. It is very useful to read them over and understand the builder logic in detail. It will save a lot of your time later.

Conclusion

Summary

  • With the latest release, Smartedit has taken a definite turn for the better.
  • starting from the version 1811, we’re currently out of options. Smartedit it the only tool for content management and personalization from the Commerce suite. WCMS Cockpit and BTG were removed as well as other cockpits.
  • The majority of hybris developers have a lack of skills and experience. No training from SAP is provided. Documentation is good but partly outdated.
  • Asynchronous personalization calculation helps to keep performance under control.
  • You need an external tool to provide segments for the customers. SAP Marketing, Context-Driven Services. You can integrate a third-party or your own tool as well.

Things I liked in SmartEdit

In the last release, Smartedit seems to be a mature tool for content management. Despite some drawbacks, such as poor media management and no workflows, the product is good enough for the majority of e-commerce content management needs. It is great that all forms are built automatically based on the platform model definitions. Unlike the Cockpit framework, Smartedit’s code is extendable. Architecture is clear, the source code is available, the system components are decoupled. All this makes troubleshooting much easier in comparison with what we had in the legacy WCMS cockpit.

What pains me the most

The architecture of Smartedit is not settled yet. Despite the fact the product is relatively new, many components are marked as deprecated. Even the official SmartEdit Trail is not 100% relevant to what we have in the last release (1801). A deprecated tech stack. SmartEdit is built on top of AngularJS 1.6. AngularJS has been announced as deprecated, has 3 years of support for security. Better alternatives than AngularJS or even Angular 2 may become available as SAP goes. Poor error handling. The error messages are not informative or absent. For example, if you don’t install a SmartEdit addon or the preview URL is not correct, the system simply won’t start. Of course, after taking weeks of experience, you will learn how to see the signs of the typical root cause. However, in the beginning, that is tough. Smartedit introduces a bunch of new technologies and tools. It imposes stricter requirements to the project team. Some tools are too young. It creates risks of functional and performance issues and frequent changes in the core architecture.

Leave a Reply