This article covers a Bloomreach Experience Manager version 13. There's an updated version available that covers our most recent release.

Develop a Custom Page Tool Tutorial

A page tool is an external web application rendered aside of a page shown in the Channel Manager. It extends the base functionality of Bloomreach Experience Manager with third-party functionality.

In this tutorial we are going to add a page tool that renders the JSON data provided by the Channel Manager. The page tool will be named "Show JSON". In the end it will look like this:

Page tool

The show-json page tool will be a separate project based on Webpack. We'll assume NodeJS and NPM are already installed. First create a new project:

mkdir show-json && cd show-json
npm init -y
npm install --save-dev webpack webpack-cli webpack-dev-server

Add a file called index.html in the root folder:

<!doctype html>
<html>
  <head>
    <title>Show JSON</title>
  </head>
  <body>
    <script src="main.js"></script>
  </body>
</html>

Create a source folder for JavaScript code:

mkdir src

Create the JavaScript source file src/index.js. It will generate some test output for now:

document.write('Hi!');

Next, edit the package.json file and change the "scripts" entry in there to:

  "scripts": {
    "start": "webpack-dev-server --port 9000 --mode development --watch-content-base"
  },

Start the Webpack development server using the 'start' command we've just added:

npm start

The "show json" extension can now be viewed separately at http://localhost:9000.

Bloomreach Experience Manager Project

Follow the Get Started steps to create a new archetype project. Install some Essentials features to get a working site. For example, install the "News" feature.

Next we're going to integrate the "show json" extension. Open http://localhost:8080/cms/console and navigate to /hippo:configuration/hippo:frontend/cms/ui-extensions and add one node:

definitions:
  config:
    /hippo:configuration/hippo:frontend/cms/ui-extensions/showJson:
      jcr:primaryType: frontend:uiExtension
      frontend:displayName: Show JSON
      frontend:extensionPoint: channel.page.tools
      frontend:url: http://localhost:9000

Login to the CMS as admin or editor. Navigate to Channels > My Project > Page > Tools. The "show json" tab should now show "Hi!".

Show JSON data

Instead of "Hi" we want to see the JSON data provided by the Channel Manager. Let's go back to the code base of the "show JSON" extension. We'll create our extension using only plain HTML and JavaScript, but any framework may be used.

First install the BloomReach JavaScript client library:

npm install @bloomreach/ui-extension

Next, change the index.html file so it contains two <pre> tags that will show the data:

<!doctype html>
<html>
  <body>
    UI properties:
    <pre id="ui"></pre>

    Page properties:
    <pre id="page"></pre>

    <button id="refreshChannel">Refresh Channel</button>
    <button id="refreshPage">Refresh Page</button>

    <script src="main.js"></script>
  </body>
</html>

The Webpack dev server will pick up the changes automatically and reload the extension in the Channel Manager. Nice!

Change the src/index.js file to:

import UiExtension from "@bloomreach/ui-extension";

document.addEventListener('DOMContentLoaded', () => {
  UiExtension.register().then((ui) => {
    showUiProperties(ui);

    ui.channel.page.get().then(showPageProperties);
    ui.channel.page.on('navigate', showPageProperties);

    onClick('refreshChannel', () => ui.channel.refresh());
    onClick('refreshPage', () => ui.channel.page.refresh());
  });
});

function showUiProperties(ui) {
  const properties = pluck(ui, ['baseUrl', 'extension', 'locale', 'timeZone', 'user', 'version']);
  showJson(properties, 'ui');
}

function showPageProperties(page) {
  showJson(page, 'page');
}

function showJson(json, id) {
  document.getElementById(id).innerHTML = JSON.stringify(json, null, ' ');
}

function onClick(id, listener) {
  document.getElementById(id).addEventListener('click', listener);
}

function pluck(object, properties) {
  return Object.keys(object)
    .filter(key => properties.includes(key))
    .reduce((result, key) => {
      result[key] = object[key];
      return result;
    }, {});
}

The code waits for the DOMContentLoaded event to ensure the DOM is ready for changes. Next it calls UiExtension.register() to register the extension with the CMS. The register method returns a Promise that resolves with a ui object.

The ui object contains several properties, which are shown using the showUiProperties() method. It uses a custom pluck() to extract some properties, and displays those as JSON in one of the <pre> tags.

The extension.config property will be set to the string configured in the JCR property /hippo:configuration/hippo:frontend/cms/ui-extensions/showJson/frontend:config. When that JCR property is not set the JSON property will be null. It's up to the extension how to use/interpret the config property (e.g. parse it as JSON).

The timeZone property is only available when the time zone selector is enabled in the login screen.

The ui object also returns data about the current page via ui.channel.page.get() and shows it in the other <pre> tag.

Our page tool also registers a 'navigate' listener. To see this listener in action, navigate to another page by browsing the site in the Channel Manager. The 'navigate' listener will be called for every new page and make our page tool show the new page data.

Last, the extension shows two buttons that refresh the channel and page, respectively. Refreshing the page is probably noticeable. Refreshing the channel may not seem to do much at first, though. To see it in action, use an admin user that views the page tools and a concurrent editor user that changes the channel (e.g. adds a component). When the admin clicks Refresh Channel the yellow changes indicator will appear in the top-left corner. Also the site map in the left side panel will sync.

Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?