hybris Cockpit Framework Expert Customization Explained. Part I.


hybris Cockpit framework was introduced many years ago and now it is being decommissioned. However, due to various reasons, new projects and almost all of the existing are actively using this framework for extending hybris cockpits. It is one of the most challenging areas in hybris, because the framework is underdocumented, the code is very old. This article will shed some light on the topic and make difficult things clearer. I hope that this information will become a good 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 parameter “use” of the tag “window” defines an implementation of the component. For the main window the implementation class is de.hybris.platform.cockpit.components.impl.MainWindow.

MainWindow and perspectives

“Perspectives” are something like “tabs” in UI, but with a bit different look. Each perspective is based on the same page structure, if you use the built-in BasePerspectiveComponent. MainWindow creates a UICockpitSession. It manages e.g. 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
(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>
  • dragOverPerspectivesEnabled – you can drag the object over perspective name. Hybris will change the perspective to allow you to drop the object to the other perspective’s area. by default, dragOverPerspEnabled is false. Did you know that you can drag a product from the Product perspective to Catalog and link it to one of the categories? This parameters enables it.
  • requestHandler defines the request event handlers for jump-in URLs. By default, the following events are defined: “activation”, “search”, “celum”, “msg”. The product cockpit extension extends this list with “wf” (workflow).   CMSCockpit extends them with “cmsnavigation“, “pageviewnavigation“, “liveedit“, “liveeditpagenavigation” and “wf“. Example of jump-in URL: https://localhost:9002/admincockpit/index.zul?persp=admincockpit.perspective.validation&events=msg&msg-title=Test%20Message
<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>
  • sendEventsImmendiately defines the event processing mode. If true, global events are pushed into perspective event handlers immediately. Otherwise, the event cache is used and there is a delay.
  • pushContainers defines  PushComponents. Push component enables server to push the messages in the cockpit (reverse AJAX). Hybris injects them into MainWindow. For each bean hybris creates a controller. PushComponent operates like a cronjob inside the cockpit app.  One of the parameters of the push component is update interval. For example, WorkflowPushContainer
<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 for 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 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):
  • baseNavigationComponent  in the navigationArea
  • BasePopupEditorComponent in popupEditorArea
  • BrowserContainer in browserArea
  • BaseEditorAreaComponent in editorArea
  • CustomCsaIncludeHelper in customCsaURI (*CSA.zul).
cockpit-core-architecture.png 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.

NavigationArea

cockpit-navigation-area.png To customize navigation area you may :
  • Create a new navigationArea bean with the OOTB implementation (BaseUICockpitNavigationArea).  However, you can use you custom values of beans parameters or custom implementations for the beans referenced in the navigationArea parameters, such as:
    • viewURI
      and
      headerURI
      . You can introduce your own zul files instead of OOTB
      baseNavigation.zul
      and
      left_section_header.zul
      . You can use your own implementations of components in your own zul files to replace OOTB
      NavigationAreaComponent
       and
      LeftSectionHeaderComponent
       respectively.
    • sections
      This parameters defines a list of prototype-type beans that implements
      NavigationPanelSection
      For each bean you can specify the custom renderer.
    • contentSlotRenderer
      .
    • infoSlotRenderer
      .
  • Create a new navigationArea bean with the custom implementation. See above for the example from admincockpit.
  • Redefine a Navigation Area component by extending NavigationAreaComponent.
Examples of navigation area customization from hybris built-in extensions. Cockpit’s default implementations are marked as black, the specific extension’s implementations are in red: 2016-11-26_12h37_23.png 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: 2016-11-25_14h19_25.png ConstraintSectionRenderer: 2016-11-25_14h17_18.png

Browser Area

cockpit-browser-area.png In order to customize the Browser Area you can:
  • Create a new browserArea bean with the OOTB implementation (DefaultConfigurableBrowserArea or DefaultSearchBrowserArea).  You can use you custom values of beans parameters or custom implementations for the beans referenced in the navigationArea parameters, such as:
    • viewURI
      . You can introduce your own zul template to replace OOTB
      baseSearchBrowserArea.zul
      You can use your own implementations of the components in your zul file to replace OOTB
      BrowserContainer
       and
      CenterAreaContainer
       .
    • supportedBrowserIds
      This parameters defines a list of prototype-type beans that implements
      WidgetDashboardBrowserModel
      For each bean you can specify the custom renderer. For example, CSCockpit (deprecated now) uses three browsers: csTicketPoolBrowser, csBasketBrowser, csEndCallBrowser.
    • defaultBrowserId
      defines a default browser from the list above. The value is a reference to a bean, not a real id. For example, for the example above, .
    • showCreateDefaultBrowserButton
      .
    • rootSearchTypeCode
      . You can specify a type to browse by default. “Product” means that the browserarea will display a list of products. It is defined only in DefaultSearchBrowserArea.
    • extendedSearchBrowser
       is a boolean parameter. Defines what class should be used for the area, DefaultExtendedSearchBrowserModel or DefaultSearchBrowserModel.
  • Create a new browserArea bean with the custom implementation. For example, CMS Cockpit goes this way. Their custom implementation 
    CatalogBrowserArea
    extends cockpit’s  
    DefaultSearchBrowserArea
  • Redefine a Navigation Area component by extending
    BaseContainer
     or
    CenterAreaContainer
    .
Browser Area contains the following components:
  • Browsers. These are basically tabs at the top of the area. You can create/remove tabs via browser.
  • Mainarea component.
  • Caption component. According to QueryCaptionBrowserComponent, it contains query text box, search and advanced search buttons, save query button, advanced search related stuff,  a split button.
  • Toolbar component. By default, DefaultSearchContentBrowser defines toolbar component.
cockpit-browser-area1.png DefaultExtendedSearchBrowserModel creates MainAreaListViewBrowserComponent by default. DefaultAdvancedContentBrowser creates MainAreaGridViewBrowserComponent by default. The configuration of UI elements is stored in XML files (like 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-area2.png

Editor Area

cockpit-browser-area4.png
EditorAreaListener
 is used to intercept the following events:
  • currentObjectChanged
  • nextItemClicked
  • previousItemClicked
  • valuesStored
  • valuesUpdated
  • browseItemClicked
  • currentObjectUpdated
  • valueChanged.
For example, valueChanged is triggered once you change the value of any item in the editor pane. To replace/extend the default Editor Area Listener, you need to add a parameter into 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 to rewrite 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.

Popup Editor Area

You can’t open another level of editor by default as you possibly used to do with HMC. There are ways to allow nested popups in multiple levels. This feature can be activated for any cockpit with one of following properties:
# activate only for specific cockpit
productcockpit.default.popUpEditor.allowOverlap=true
# activate for all cockpits
default.popUpEditor.allowOverlap=true
The result will be that pencil icons will appear also for editor rows in popup editor area. Clicking the pencil icon will simulate next level and back button will appear on top. There are 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 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

5 Responses

  1. Wojtek (@jestemwojtek)

    Wojtek (@jestemwojtek)

    Reply

    28 November 2016 at 02:52

    Hi,

    have you had a chance to work with the new cockpit framework and backoffice? I would be very curious to see a comparison of the old framework against the new.

    Regards,
    Wojtek

  2. Leonardo Zamariola

    Leonardo Zamariola

    Reply

    29 November 2016 at 20:53

    Great article, Rauf! This kind of cockpit has a lot of customization options and unfortunately a not that good documentation (as you said).

  3. Leonardo

    Leonardo

    Reply

    29 November 2016 at 20:54

    Great article Aliev! This kind of cockpit has a lot of customization options and unfortunately a not that good documentation (as you said).

  4. Vishal

    Vishal

    Reply

    14 September 2017 at 06:08

    Nice article Alive!! I am facing some problem in cockpit customization like
    When I was clicked on Listview in NavigationArea that time BrowserArea want to updated
    I am written some code for updating the browser area is given below but not updating browser UI
    CODE :
    BrowserModel focusBrowserModel = UISessionUtils.getCurrentSession().getCurrentPerspective().getBrowserArea().getFocusedBrowser();
    UISessionUtils.getCurrentSession().getCurrentPerspective().getBrowserArea().addVisibleBrowser(focusBrowserModel, true);
    UISessionUtils.getCurrentSession().getCurrentPerspective().getBrowserArea().update();
    focusBrowserModel.focus();
    focusBrowserModel.updateItems();

    Please help me regarding the same

Leave a Reply