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.
    • sectionsThis parameters defines a list of prototype-type beans that implements NavigationPanelSectionFor 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.zulYou can use your own implementations of the components in your zul file to replace OOTB BrowserContainer and CenterAreaContainer .
    • supportedBrowserIdsThis parameters defines a list of prototype-type beans that implements WidgetDashboardBrowserModelFor each bean you can specify the custom renderer. For example, CSCockpit (deprecated now) uses three browsers: csTicketPoolBrowser, csBasketBrowser, csEndCallBrowser.
    • defaultBrowserIddefines 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 CatalogBrowserAreaextends 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!

5 comments

  1. 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

    Like

    1. One of the topics of forthcoming articles. New backoffice is better, of course

      Liked by 2 people

  2. Leonardo Zamariola · · Reply

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

    Like

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

    Like

  4. Vishal · · Reply

    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

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: