
OpenText Content Server's SmartUI framework allows developers to add custom URL routes, making pages linkable and easy to share. A typical custom route might appear as:
https://myserver/otcs/cs.exe/app/my-custom-route/12345?arg1=abc&arg2=def
To make such a route work, several pieces must come together: handling direct URL entry from bookmarks or external sources, navigating within the application through code, extracting URL parameters and query strings, and feeding that information to the view that renders the page. Tools like Vue Router handle this seamlessly, but SmartUI leaves you to build it all yourself. Every new route demands the same repetitive setup of module extensions, event binding, and manual URL assembly.
A routing plugin removes this repetition by offering a straightforward configuration layer. You define a route in a few lines, navigate via a simple programming interface, and get URL parameters automatically delivered to your view.
First, register the plugin with an extension orphan in the usual SmartUI fashion:
{
"csui/pages/start/perspective.routing": {
"extensions": {
"geniusui": ["geniusui/perspective.routers/my.perspective.router"]
}
}
}
The router inherits from rhcore/routing/rhcore.router.register, which expands upon the core csui/pages/start/perspective.router. Routes go inside the RHCoreRoutes array:
define("geniusui/perspective.routers/my.perspective.router", [
"rhcore/routing/rhcore.router.register",
"geniusui/routes/my.route.view",
], function (RHCoreRouterRegister, MyGeniusView) {
return RHCoreRouterRegister.extend({
RHCoreRoutes: [
{ path: "my-custom-route", name: "myCustomRoute", view: MyGeniusView, meta: { somekey: "value1" } },
{ path: "my-custom-route/:id", name: "myCustomRouteId", view: MyGeniusView, meta: { somekey: "value2" } },
],
});
});
Every route entry contains: path (the URL pattern after the base URL), name (a distinct identifier for code-based navigation), view (a Marionette view responsible for rendering), and meta (custom information sent to the view, handy when sharing a view across multiple routes).
When the view is created, the router supplies contextual data through options.rhcore:
define("geniusui/routes/my.route.view", ["csui/lib/marionette"], function (Marionette) {
return Marionette.ItemView.extend({
constructor: function (options) {
Marionette.ItemView.prototype.constructor.apply(this, arguments);
const { router, sessionData, route } = options.rhcore;
},
});
});
This object holds three items:
router.navigateTo({ name: "myCustomRoute", params: { id: "67890" }, query: { hello: 1 } }) sends you to /app/my-custom-route/67890?hello=1, and router.openDataId(12345) opens a specific Content Server node.baseUrl, the support directory location, and an otcsticket for REST authentication (compatible with the @kweli/cs-rest package).id (the route name), params (URL segments like { "id": "12345" }), query (query parameters like { "arg1": "abc", "arg2": "def" }), and meta (the custom data from the route definition).Marionette is not your only option for the view layer. Package your React, Vue, or Angular app as an ES6 module and load it on demand:
Marionette.ItemView.extend({
onRender: function ({ options }) {
const { router, sessionData, route } = options.rhcore;
import("./myApp.mjs").then(({ default: mount }) => {
this.app = mount(this.el, { router, sessionData, route });
});
},
onBeforeDestroy: function () { this.app?.unmount(); },
});
This keeps the SmartUI portion minimal while your main interface lives inside whichever framework you prefer. An added benefit is portability — the same app can function inside both SmartUI and the classic Content Server interface.