A note from 2026: This article was published in 2016 and covers the legacy hybris Cockpit Framework, HMC, and older cockpit-based tools. In modern SAP Commerce Cloud, these technologies have been deprecated or replaced by Backoffice, SmartEdit, and SAP Composable Storefront, so treat this as guidance for maintaining legacy installations.

The hybris Cockpit Framework was introduced many years ago and is now being decommissioned. However, for various reasons, new projects and almost all existing projects actively use this framework for extending hybris cockpits.

It is one of the most challenging areas in hybris because the framework is underdocumented and the code is very old. This article sheds some light on the topic and makes difficult things clearer. I hope this information will be a useful addition to the official documentation.

Cockpit core architecture

The hybris Cockpit Framework is based on ZK Framework 3.6, a rich internet application framework that enables desktop-like GUIs within a web browser. The starting page of all cockpits is index.zul. Let’s start our journey with this file.

<window xmlns="http://www.zkoss.org/2005/zul" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:zk="http://www.zkoss.org/2005/zk" xsi:schemaLocation="http://www.zkoss.org/2005/zul http://www.zkoss.org/2005/zul/zul.xsd " id="mainWin" mode="embedded" height="100%" sclass="hywin" use="de.hybris.platform.cockpit.components.impl.MainWindow" shadow="false">

</window>

The use parameter of the window tag defines the component implementation. For the main window, the implementation class is de.hybris.platform.cockpit.components.impl.MainWindow.

MainWindow and perspectives

“Perspectives” are something like “tabs” in the UI, but with a slightly different look. Each perspective is based on the same page structure if you use the built-in BasePerspectiveComponent.

MainWindow creates a UICockpitSession. It manages, for example, the available cockpit perspectives. You can define your own perspective by creating a bean in the following way:

<bean id="MyCockpitSession" class="de.hybris.platform.cockpit.session.impl.UISessionImpl" scope="session" init-method="registerAdditionalPerspectives">
...
<property name="availablePerspectives">
<list>
<ref bean="MyPerspective1"/>
<ref bean="MyPerspective2"/>
</list>
</property>
..
</bean>

registerAdditionalPerspectives

adds all the perspectives defined in the beans of

PerspectivePluginList.class

into a collection named

avPerspUnrestricted

(an attribute of UICockpitSession). Cockpit Framework applies restrictions to this list; the results are saved in

availablePerspectives.

<property name="cachePerspectivesEnabled" value="true" />
<property name="requestHandler" ref="CMSRequestHandler" />
<property name="dragOverPerspectivesEnabled" value="true" />
<property name="sendEventsImmediately" value="false" />
<property name="pushContainers">
<list value-type="de.hybris.platform.cockpit.session.impl.PushCreationContainer">
<ref bean="WorkflowPushContainer" />
<ref bean="CommentPushContainer" />
</list>
</property>
<bean id="CMSRequestHandler" class="de.hybris.platform.cockpit.session.impl.DefaultRequestHandler" scope="session">
<property name="requestEventHandlers">
<map>
<entry key="activation">
<bean class="de.hybris.platform.cockpit.session.impl.ActivationEventHandler">
<property name="prefix" value="act" />
</bean>
</entry>
<entry key="search">
<bean class="de.hybris.platform.cockpit.session.impl.SearchEventHandler">
<property name="prefix" value="srch" />
</bean>
</entry>
...
</bean>
<bean id="WorkflowPushContainer" class="de.hybris.platform.cockpit.session.impl.PushCreationContainer" scope="session">
<constructor-arg value="de.hybris.platform.importcockpit.session.impl.CronJobsPushController" index="0"/>
<constructor-arg index="1">
<map>
<entry key="updateInterval" value="500"/>
</map>

</constructor-arg>
</bean>
<bean id="ValidationPerspective" class=" de.hybris.platform.admincockpit.session.impl.AbstractConstraintPerspective" scope="session" parent="BasePerspective">
<property name="uid" value="admincockpit.perspective.validation" /> <!-- unique ID of the perspective -->
<property name="label" value="admincockpit.perspective.validation" /> <!-- localization key for the label of the perspective -->
<property name="customCsaURI" value="/admincockpit/validationCSA.zul" /> <!-- file with optional custom client side actions -->

customCsaURI commonly refers to the ZUL template with JavaScript specific to the perspective.

Note that this bean extends BasePerspective:

<bean id="BasePerspective" class="de.hybris.platform.cockpit.session.impl.BaseUICockpitPerspective" abstract="true" scope="session">
<property name="viewURI" value="/cockpit/basePerspective.zul"/>
<property name="activationEffectEnabled" value="false"/>
<property name="effectDuration" value="0.7"/>
<property name="moveTargetX" value="130"/>
<property name="moveTargetY" value="50"/>
<property name="effectBorderColor" value="#BCD2EF"/>
<property name="cockpitTypeService" ref="cockpitTypeService"/>
<property name="uiConfigurationService" ref="uiConfigurationService"/>
<property name="popupEditorArea">
<bean id="DefaultPopupEditor" parent="BasePopupEditor"/>
</property>
</bean>

As we can see, BasePerspective has a very simple template (/cockpit/basePerspective.zul):

<div width="100%" height="100%" use="de.hybris.platform.cockpit.components.BasePerspectiveComponent"></div>

There is one component defined in the perspective template: BasePerspectiveComponent.

BasePerspectiveComponent initializes the following subcomponents if they are defined:

Cockpit core architecture diagram

Below is an example from admincockpit that shows how new perspectives can be created.

</pre>
<bean id="ValidationPerspective" class=" de.hybris.platform.admincockpit.session.impl.AbstractConstraintPerspective" scope="session" parent="BasePerspective">
....
<property name="navigationArea">
<ref bean="ValidationNavigationArea" />
</property>
...
</bean>
<bean id="ValidationNavigationArea" class="de.hybris.platform.admincockpit.session.impl.AdmincockpitNavigationArea" scope="session" parent="BaseNavigationArea">
<property name="sectionModel">
<ref bean="ValidationNavigationAreaModel"/>
</property>
<property name="infoSlotRenderer">
<bean class="de.hybris.platform.cockpit.components.navigationarea.renderer.InfoBoxRenderer"/>
</property>
<property name="sections">
<list>
<ref bean="ValidationConstraintsSection" />
<ref bean="ValidationConstraintGroupsSection" />
<ref bean="AdmincockpitUndoSection" />
</list>
</property>
<property name="cockpitTypeService" ref="cockpitTypeService"/>
</bean>

Note that the bean ValidationNavigationArea extends BaseNavigationArea.

Cockpit navigation area

To customize the navigation area, you may:

Examples of navigation area customization from hybris built-in extensions. Cockpit’s default implementations are marked in black, and extension-specific implementations are in red:

Examples of navigation area customization in built-in extensions

For example, in Admin Cockpit, the custom renderer defines a render method that is used by NavigationPanelSection:

public void render(SectionPanel panel, Component parent, Component captionComponent, Section section) {
Vbox vbox = new Vbox();
Label constrainedTypeLabel = new Label(Labels.getLabel("na.constrainedSection.constrained_type_label") + ": ");
vbox.appendChild(constrainedTypeLabel);
....
parent.appendChild(vbox);
}

As a result, we see these three custom sections:

Three custom sections in Admin Cockpit navigation

ConstraintSectionRenderer:

ConstraintSectionRenderer screenshot

Browser Area

Cockpit browser area

To customize the Browser Area, you can:

Browser Area contains the following components:

Cockpit browser area components

DefaultExtendedSearchBrowserModel creates MainAreaListViewBrowserComponent by default. DefaultAdvancedContentBrowser creates MainAreaGridViewBrowserComponent by default.

The configuration of UI elements is stored in XML files, such as admingroup/contentElement_CategoryPage.xml. There is a service named UIConfigurationService that sets up a configuration for the components. For the mentioned ListView component,

Cockpit browser area UI configuration

Editor Area

Cockpit editor area

EditorAreaListener

is used to intercept the following events:

For example, valueChanged is triggered once you change the value of any item in the editor pane.

To replace or extend the default Editor Area Listener, you need to add a parameter to the XML configuration.

Renderers are responsible for creating HTML code. If you need to inject something before or after the list of rows, you can extend the default renderer. However, it is not easy to inject something in the middle of the supplied code other than by rewriting that code completely.

To change editors for different types, there is a separate configuration that tells Cockpit Framework what editor should be used for what type.

You can’t open another level of editor by default as you may have been used to doing with HMC. There are ways to allow nested popups in multiple levels. This feature can be activated for any cockpit with one of the following properties:

# activate only for specific cockpit
productcockpit.default.popUpEditor.allowOverlap=true
# activate for all cockpits
default.popUpEditor.allowOverlap=true

The result is that pencil icons will also appear for editor rows in the popup editor area. Clicking the pencil icon will simulate the next level, and a back button will appear at the top.

There is a big topic on how to operate wizards and popup windows created by ZK. This topic is for the next part.

To sum up, in this article I explained the very basics of Cockpit Framework: perspectives, areas and their components, their architecture, and the customization approach. This is just the tip of the iceberg. I’ll come back soon with other topics about Cockpit Framework and expert customization. Stay tuned!

© Rauf Aliev, November 2016