Integrating non-ReactJS UI screens

November 14, 2023

Create a UI extension JSON file

Inside an existing or new Maven project source code, create a jahia.json file under src/main/resources/javascript/apps/jahia.json with the following content:

{
  "jahia": {
    "apps": {
      "jahia": "javascript/apps/register.js"
    }
  }
}

In the above example, the key here is the app you want to extend, and the value is the path to the file that will load/register your app. This file will be the entry point containing the JavaScript initialization part of your module.

Load/Register your app

You will need to create the JavaScript file you declared in the jahia.json file, in this example, register.js, and add the logic you need inside. The module can register different extensions in the application. This file should remain small, as the application won't start until fully loaded and executed.

You can find a simple example of such jahia.json and registration files in the Event module

Component registry registrations

The registrations need to be done in the register.js file (the one specified in the jahia.json file)

All extensions are added to the component registry by calling the registry.add function. The registry object can be found by importing the registry from @jahia/ui-extender. The add function task has at least three parameters: the type of extension to register, its unique id, and an object describing the extension.

To ease this process when doing plain Javascript, we expose a few things in the window.jahia object. As of today, in the jahia object, you can find:

  • uiExtender: which contains the registry

  • moonstone: which contains React components and utility functions from the moonstone design system

  • i18n: which contains all the necessary functions to handle translations

Another interesting object available in the browser is the window.contextJsParameters that contains lots of information about the current editing context. Here is an example:

{
    "workspace": "default",
    "uilang": "en",
    "currentUserPath": "/users/root",
    "langdisplayname": "English",
    "ckeCfg": "/modules/dx-base-demo-templates/javascript/ckeditor_config.js",
    "serverDisplayableTimeZone": "Coordinated Universal Time (GMT0:00)",
    "contextPath": "",
    "areaAutoActivated": "true",
    "dxVersion": "8.1.4.0",
    "currentUser": "root",
    "baseUrl": "/cms/render/default/en",
    "editUrl": "/cms/edit/default/en",
    "servletPath": "/appshell",
    "studioMaxDisplayableFileSize": "1048576",
    "developmentMode": "true",
    "modulesSourcesDiskPath": "/var/jahia/sources",
    "uilangdisplayname": "English",
    "lang": "en",
    "serviceEntryPoint": "/gwt/",
    "pathInfo": "/jahia/administration/sampleAdmin",
    "siteKey": "digitall",
    "siteUuid": "90788f31-5cb5-4388-a941-00515a51187b",
    "version": "8.1.4.0",
    "user": {
        "username": "root",
        "fullname": "root",
        "firstName": "",
        "lastName": "",
        "email": "",
        "path": "/users/root"
    },
    "valid": true,
    "config": {
        "environment": "",
        "displayWorkflowCounter": "true",
        "operatingMode": "development",
        "maxUploadSize": 100,
        "links": {
            "whatsNew": "https://academy.jahia.com/whats-new?dxversion=$dx-version&site=$site-servername",
            "documentation": "https://academy.jahia.com/documentation/enduser/jahia/8",
            "sql2CheatSheet": "https://academy.jahia.com/documentation/techwiki/search/jcrsql2-query-cheat-sheet",
            "importAcademy": "https://academy.jahia.com/documentation/digital-experience-manager/7.3/modules/content-and-media-manager#exporting_importing_contents"
        },
        "wip": "https://academy.jahia.com/documentation/enduser/jahia/8/authoring-content-in-jahia/using-content-editor/understanding-work-in-progress-content",
        "maxNameSize": 32,
        "defaultSynchronizeNameWithTitle": true,
        "logoutUrl": "/cms/logout?redirect=/start",
        "jcontent": {
            "showPageComposer": "false"
        }
    },
    "site": "digitall",
    "language": "en",
    "urlbase": "/jahia"
}

Routes

React routes can be added to the application as extensions. For example, the following can be used to declare a new route/linkchecker (path) that will display a linkchecker (render) in the main area (target) :

window.jahia.uiExtender.registry.add('route', 'route-jcontent', {
      targets: ['main:2'],
      path: `/linkchecker`, // Catch /linkchecker urls
      render: function () {           // The render function for my route in this example we want to display an iframe which contains our legacy site settings, to do that we pass the URL to the `getIframeRenderer` function
        return window.jahia.uiExtender.getIframeRenderer(window.contextJsParameters.contextPath + '/cms/editframe/default/sites/$site-key.linkChecker.html');
      }
  });

Primary nav items

Items in the primary nav bar can be added as primary-nav-item. Here, add a link to /linkchecker (path) into the main primary nav (target) with a label and an icon.

// Call the add method from the registry which is in uIExtender to add a menu entry to point to my module
// `primary-nav-item` is the type I want to register and `linkcheckerRoute` is the key (must be unique), the last parameter is an object with the necessary options
window.jahia.uiExtender.registry.add('primary-nav-item', 'linkcheckerRoute', {
    targets: ['nav-root-top:21'],       // Which menu I want to extend, it can take multiple values, each value can be ordered `target:position`
    path: '/linkchecker',               // Path to call when clicking on my link
    label: 'linkchecker:label.title',   // RB to use to display the name of my link `namespace:key`
    icon: window.jahia.moonstone.toIconComponent('Feather')  // Icon to use with my link, we must use the `toIconComponent` function to make sure we return an Icon Component
});

Admin panels

Administration panels can be registered with a single entry, that will add an item in the tree and the appropriate route at the same time.

window.jahia.uiExtender.registry.add('adminRoute', 'linkchecker', {
    targets: ['jcontent:10'],
    label: 'linkchecker:label.title',
    isSelectable: true,
    requiredPermission: 'siteAdminLinkChecker',
    requireModuleInstalledOnSite: 'linkchecker',
    iframeUrl: window.contextJsParameters.contextPath + '/cms/editframe/default/$lang/sites/$site-key.linkChecker.html'
});

See more in settings documentation.

Actions

You can add actions in the application anywhere that buttons or menu entries display.

window.jahia.uiExtender.registry.add('action', 'downloadFile', {
    buttonIcon: window.jahia.moonstone.toIconComponent('CloudDownload'),
    buttonLabel: 'jcontent:label.contentManager.contentPreview.download',
    targets: ['contentActions:10'],
    onClick: context => {
        window.open(context.path);
    }
});

Translation namespaces

If you want to use translated label in your module, you need to declare your module namespace, which must be your module name :

// Declare the namespace to find the translations for my module, put the name of your module and reuse it in your labels
window.jahia.i18n.loadNamespaces('linkchecker');

This will download the translations from JSON files under the following path src/main/resources/javascript/locales/<lang>.json:

{
  "label" : {
    "title": "Link Checker"
  }
}

Such label can be used in your code with: {moduleId}:{label.path}

For instance, to retrieve the title label for the linkchecker module, you will need to use:

linkchecker:label.title