Introduction
Unlocking Custom Experiences in SAP Build Work Zone: Shell Plugin in Action
In today’s digital workplace, personalization is key. This article showcases how to enhance SAP Build Work Zone with a custom shell plugin that brings tailored notifications right to the user’s fingertips. Using a Multi-Target UI5/CAP application, we’ll demonstrate how to create, manage, and surface custom alerts—seamlessly integrated into your Work Zone experience.
To replicate this you should have a base knowledge of SAP BTP, JS, SAPUI5 and SAP CAP, I will not go deep down in to how to generate a project in BAS, or create an instance in SAP BTP.
***GITHUB URL REMOVED BY MODERATION***
Project Architecture
Backend/Frontend App Development:
Generate a regular SAP CAP application
Firstly I've created a primitave data structure under db/schema.cds
namespace my.Notification;
entity Notification {
key ID : UUID;
type: String(30);
title: String;
message: String;
} Than a service at srv/service.cds:
using { my.Notification as my } from '../db/schema';
service NotificationService {
entity Notification as projection on my.Notification;
}And the backend is almost done, now add an app router and a sapui5 freestyle base app to the project.
Now let's add a form to create and delete the notifications under app/<app name>/webapp/view/<view name>.view.xml:
<mvc:View
controllerName="com.sap.shellnotificationsender.controller.MyView"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:form="sap.ui.layout.form"
xmlns:core="sap.ui.core">
<Toolbar>
<Title text="{i18n>title}" />
</Toolbar>
<Shell id="mainShell">
<VBox class="sapUiSmallMargin">
<form:SimpleForm
editable="true"
layout="ResponsiveGridLayout"
labelSpanL="1" labelSpanM="2"
adjustLabelSpan="false"
columnsL="3" columnsM="3"
class="sapUiSmallMarginBottom">
<Label text="Type" />
<ComboBox id="newType" selectedKey="information">
<core:Item key="Warning" text="Warning" />
<core:Item key="Success" text="Success" />
<core:Item key="Information" text="Information" />
<core:Item key="Error" text="Error" />
<core:Item key="Alert" text="Alert" />
<core:Item key="Confirm" text="Confirm" />
</ComboBox>
<Label text="Title" />
<Input id="newTitle" />
<Label text="Message" />
<Input id="newMessage" />
<Label text="" />
<Button text="Add Notification"
press="onAddNotification"
type="Emphasized"/>
</form:SimpleForm>
<!-- Table Section -->
<Table id="shellNotificationsTable" items="{/Notification}">
<columns>
<Column>
<Text text="Delete" />
</Column>
<Column>
<Text text="Type" />
</Column>
<Column>
<Text text="Title" />
</Column>
<Column>
<Text text="Message" />
</Column>
</columns>
<items>
<ColumnListItem>
<cells>
<Button icon="sap-icon://delete" type="Negative" press="deleteRow" />
<Text text="{type}" />
<Text text="{title}" />
<Text text="{message}" />
</cells>
</ColumnListItem>
</items>
</Table>
</VBox>
</Shell>
</mvc:View>As next let's implement the controller that manages the view with all the necessary notification creation/deletion methods:
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/m/MessageBox",
"sap/m/MessageToast"
], (Controller, MessageBox, MessageToast) => {
"use strict";
return Controller.extend("com.sap.shellnotificationsender.controller.MyView", {
onInit() {
},
deleteRow: function (oEvent) {
var oContext = oEvent.getSource().getBindingContext().getObject();
console.log(oContext);
MessageBox.confirm("Are your sure you want to delete this message?", {
title: "Confirm",
onClose: function (sAction) {
if (sAction === "OK") {
this.onDeleteSpecificRecord(oContext)
}
}.bind(this),
actions: [
MessageBox.Action.OK,
MessageBox.Action.CANCEL
],
emphasizedAction: MessageBox.Action.OK
})
},
onDeleteSpecificRecord: function (oRecord) {
var oDataModel = this.getOwnerComponent().getModel();
var oBusyDialog = new sap.m.BusyDialog({
title: "Deleting Record",
text: "Please Wait ..."
})
oBusyDialog.open();
oDataModel.delete(`/Notification('${oRecord.ID}')`)
.then(() => {
console.log("Record deleted successfully");
})
.catch((error) => {
console.error("Deletion failed:", error);
})
.finally(() => {
this.getView().byId("shellNotificationsTable").getBinding("items").refresh();
oBusyDialog.close();
});
},
onAddNotification: function () {
const sType = this.byId("newType").getValue();
const sTitle = this.byId("newTitle").getValue();
const sMessage = this.byId("newMessage").getValue();
this.byId("shellNotificationsTable").getBinding("items").create({
"type": sType,
"title": sTitle,
"message": sMessage
}).created().then(function() {
MessageToast.show("Created")
})
}
});
});The last thing to do is to add all the features that you need to the application, like HANA DB, xsuaa in my case, to do so just run
cds add hana
cds add xsuaaAnd like this is the app ready, let's deploy it, firstly login to Cloud Foundry
cf loginthen right click on the mta.yaml file -> "Build MTA Project"
then go to mta_archives/<archive name>.tar right click and "Deploy MTA Project".
BTP Steps
To connect to the app from the shell plugin you need to create a SAP BTP Destination,
to do so go to <BTP Subaccount> -> Cloud Foundry -> <Your space> -> <Your app name>-srv and copy it's address, that's your destination URL
Now you need to get the authentication data for the Destination
Go to <Your CF Space> -> Instances -> <Your app name>-auth -> <Your app name>-auth-key
Copy these:
Client ID, Client secret and url (Access Token URL)
Now go back to your Subaccount and create a new destination with your data:
- URL: app-srv URL
- Authentication: OAuth2ClientCredentials
- ClientID: ClientID
- Client secret: Client secret
- Token service URL type: Dedicated
- Token Service URL: <Access token URL>/oauth/token
After this add these 2 parameters:
- HTML5.DynamicDestination: true
- WebIDEEnabled: true
With this step is the backend development concluded.
Shell Plugin Development:
Firstly a general explanation about shell plugins. Shell Plugins are UI5 Applications that allow to add new Elements to SAP Build Work Zone/Fiori Launchpad like Headers, Footers, menu items etc.
To create a shell plugin generate a plain UI5 Basic Freestyle application with the "Template Wizard" in Business Application Studio.
There are a couple of things to modify in the manifest.json to make a a Shell Plugin:
Inside sap.app change the type to component:
"type": "component"In sap.app>crossNavigation hide the launcher:
"crossNavigation": {
"inbounds": {
"Shell-plugin": {
"semanticObject": "Shell",
"action": "plugin",
"title": "{{Shell-plugin.flpTitle}}",
"hideLauncher": true,
"icon": "",
"signature": {
"parameters": {},
"additionalParameters": "allowed"
}
}
}
},Add this part under after sap.cloud:
"sap.flp": {
"type": "plugin"
}This way the Shell Plugin configuration is completed, now let's add our newly created Destination
in sap.app>dataSources add this:
"NotificationService": {
"uri": "/ShellNotifications/odata/v4/notification/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
}and in sap.ui5>models:
"Notification": {
"dataSource": "NotificationService",
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
"autoExpandSelect": true
},
"type": "sap.ui.model.odata.v4.ODataModel"
}and as last thing add this route to your xs-app.json file:
{
"source": "^/ShellNotifications/(.*)$",
"destination": "ShellNotifications",
"target": "$1",
"authenticationType": "xsuaa",
"csrfProtection": true
},Now finally you can add the code that displays the notifications, to do so open the Component.js file and add the following code to the init method:
const oModel = this.getModel("Notification");
oModel.bindList("/Notification").requestContexts().then((aContexts) => {
const aData = aContexts.map(oContext => oContext.getObject());
for (const el of aData) {
const nType = el["type"];
const nTitle = el["title"];
const nMessage = el["message"]
if (nType === "Information") {
MessageBox.information(nMessage, {
title: nTitle
});
} else if (nType === "Alert") {
MessageBox.alert(nMessage, {
title: nTitle
})
} else if (nType === "Confirm") {
MessageBox.confirm(nMessage, {
title: nTitle
})
} else if (nType === "Error") {
MessageBox.error(nMessage, {
title: nTitle
})
} else if (nType === "Success") {
MessageBox.success(nMessage, {
title: nTitle
})
} else if (nType === "Warning") {
MessageBox.warning(nMessage, {
title: nTitle
})
}
}
}).catch((err) => {
console.error("Failed to load notifications:", err);
});And like this is the development finished, the last step is to deploy it and add to WorkZone.
To build it do the same steps as with the MTA app:
- right click on the mta.yaml file -> "Build MTA Project"
- go to mta_archives/<archive name>.tar right click and "Deploy MTA Project".
SAP Build Work Zone Steps
Although the SAP Build Work Zone standard and advanced edition have some UI differences, the steps are the same for both of them.
All the steps performed below are done on SAP Build Work Zone advanced edition.
Start by going to the Administration Console -> External Integration -> Business Content -> Content Manager
Than open the content channel and update the "HTML5 Apps"
After that go Content Explorer and click Content Explorer (In the current UI it's a button in the header)
Select "HTML5 Apps" and add the new Shell Plugin that you just created
To be able to see the plugin, assign it to a role, in my case it's the "Everyone" role, you can find it in the Content Manager
After you've done that you should be able to see your new shell plugin the SAP Build Work Zone's launchpad
If you don't see it try to open the page in an "Anonymous" Tab and check the developer console
Conclusion
What we’ve built here is just a glimpse into what’s possible with shell plugins in SAP Build Work Zone. By integrating a custom notification system, we’ve shown how easily you can extend the platform to better fit your customers’ needs. But this is only the starting point.
There’s a wide range of enhancements you can explore next—such as targeting specific users with personalized notifications, adding actionable buttons to each alert, or even integrating with external systems to trigger real-time messages. The flexibility of shell extensions offers endless opportunities to enrich the Work Zone experience and tailor it to your organization’s workflows.
So go ahead—experiment, extend, and elevate your Work Zone.