Understanding and customizing the XUI in OpenAM 12.x

About the XUI

Overview

The XUI is a single page web application that interacts with OpenAM using REST. The majority of the REST interactions are provided by backbone.js. The application is themeable, using its own theming engine and themes consisting of less variables and other declarations. It styles page content and layout using the less preprocessor; styles are rendered on page load.

The XUI uses template files to define page structure, based on the handlebars.js templating library. JQueryℱ is used to simplify rendering of some page elements.

The XUI is only responsible for user interaction with OpenAM. It is not used in the administrative user interfaces.

Note
Despite the name, the XUI has no relation to the cut down JQuery library for mobile devices, which is also known as XUI.

JavaScript Libraries

The XUI relies on several open source JavaScriptÂź libraries.

  • Require.js - JavaScript has no out of the box functionality for loading files or modules of reusable code. Require.js adds this functionality to JavaScript. The XUI relies heavily on Require.js and uses it to execute all JavaScript code in the application.
  • Backbone.js - Provides a lightweight MV* (model view) framework
  • Handlebars.js - Provides a client side HTML templating system for JavaScript web applications.
  • I18next - Provides a JavaScript framework for translating user facing web page elements into multiple languages based on the language configured in the web browser.
  • LESS - LESS is a CSS pre-processor. It adds extra functionality such as variables and functions to standard CSS3.
  • JS2form & Form2JS - Automatically converts HTML forms and all containing fields to JavaScript objects and vice versa.

The following other libraries are used in parts of the XUI: JQuery, JQuery-ui, jquery.ba-dotimeout, jquery.jqGrid, Lodash and moment.

Roadmap

In OpenAM 13 and OpenIDM 4, the XUI will be part of the ForgeRock commons project. The commons project shares common components between the different products in the stack.

The XUI will be themeable using bootstrap themes and uses a simple standards based implementation, allowing easier adoption and customization. The reduction in JavaScript code and flexible responsive layouts provided by Bootstrap result in a faster and more rich environment for mobile devices.

XUI development environment

The following are required for XUI customization and development:

  • A suitable machine, container and JVM setup with OpenAM deployed as per the OpenAM installation instructions.

  • A great text editor such as Atom, Sublime Text, VIM or Notepad++ or an IDE such as intelliJ idea. Where possible, setup linting or validation plugins for JSON, to prevent mistakes being made in the XUI theme and localization files.

  • A copy of the OpenAM source (available from Bitbucket); see FAQ: Source code in AM for information on cloning source via Git.

  • If you plan on editing or creating backbone views, either of the following:

    • Main.js copied from openam-ui-ria/src/main/js in OpenAM source to the /XUI directory on the live server (otherwise OpenAM will use the minified main.js which contains all the XUI JavaScript code and ignores changes to JavaScript files).
    • A deployment system such as grunt setup to copy changes made in your source directory to your live development server. Note a gruntfile is supplied in the source, this will need customizing for your environment.
  • The following property set in the advanced options in the OpenAM server and the server instance restarted:

    org.forgerock.openam.core.resource.lookup.cache.enabled = false
    
  • Appropriate plugins in your browser to clear the cache on page refresh, such as “Cache Killer” in Chromeℱ.

XUI Themes

All XUI theme configuration is carried out in themeconfig.json, which is located in the root of the XUI directory. It allows simple branding to be performed by altering certain parts of the standard XUI layout.

The properties that can be altered include standard logo, favicon, footer text and less variables set against various different CSS selectors in the XUI CSS files.

For more information, read OpenAM Installation Guide â€ș Customizing the OpenAM End User Pages â€ș Configuring the XUI.

Themes are realm specific; additional themes can be applied per realm and realms can inherit theme properties from parent realms in the themeconfig.json. A theme defined for a subrealm must have copies of the default XUI CSS and Templates directories in its own theme directory, OR it must set the path variable in the theme to match that of its parent realm.

Creating an XUI Theme in a subrealm

The following exercise creates a basic theme for a subrealm that inherits some properties from the theme in the default realm.

  1. Create a subrealm in OpenAM. In this example the subrealm is called “acme”.
  2. Create a folder in the XUI folder on the web server. In this example this folder is named “acme”.
  3. Copy the CSS and Templates folders from the XUI folder to the folder you created.
  4. Create a folder called images in the folder you created and copy in a logo image and a background image. In this example these are called acme.png and cloud.png respectively.
  5. Add the following realm definition to the ThemeConfig.json file in the XUI directory. Note that the path for the “background-image” and the path for the logo are different, despite these files being in the same location. This is because the path in the lessVars collection is relative to the location of the less CSS files.
{
		"name": "acme",
		"path": "acme/",
		"realms": ["acme.*"],
		"regex": true,
		"settings": {
			"logo": {
				"src": "acme/images/acme.png?v=12.0.2",
				"title": "Acme",
				"alt": "Acme",
				"height": "80px",
				"width": "120px"
			},
			"lessVars": {
				"background-color": "#99ccff",
				"background-image": "url('../images/cloud.png?v=12.0.2')",
				"column-padding": "111px",

				"login-container-width": "572px",
				"medium-container-width": "692px",
				"site-width": "960px",
        				"label-short-length": "150px",
				"font-size": "12px",
				"font-family": "Comic Sans MS, cursive, sans-serif",
				"line-height": "18px"

			},
			"footer": {
				"mailto": "wileyCoyote@example.com",
				"phone": "+1 555 555 5555"
			}
		}
	}

  1. Use a JSON validator in your IDE or text editor to verify the JSON syntax, or use http://jsonlint.com
  2. Reset your web browser cache and refresh the page either manually or using a tool such as cache killer for Chrome.
  3. Navigate to the acme realm in your browser (e.g. http://openam.example.com/openam:8080/XUI/#login/acme)

The following screenshot illustrates changes that could have been applied with the above procedure:

Localization and Internationalization

The XUI is set for a particular locale in two different ways simultaneously:

  • The server sets a locale based on the language set in the browser. The text used for certain UI elements is returned to the XUI in callbacks (e.g. the /json/authenticate endpoint)
  • The I18next library loads a locale from a translation.json file, based on the language set in the browser. This typically applies localization to the XUI templates.

Localization is set globally in OpenAM, not per realm.

Note
I18n is an abbreviation for “Internationalization”.

Setting up a locale in the XUI

The following exercise builds on the previous exercise, to create a locale file for another language in the XUI.

  1. Change the browser language to a language of your choosing. In this example we are using German – Deutsch.
  2. Refresh the browser (and clear your browser cache) and observe the changes to the login dialog in the realm you created earlier. Some of the elements on the page have changed to German, but not all. These are the elements that are generated by callbacks from the OpenAM server.
  3. Create a folder that matches the I18n code for the language set in your browser, within XUI/locales. I18next expects either the two letter language abbreviation (for example, en) or the language and the country (for example en-US). Create a copy of the default translation.json file in that folder and edit the values to set up your language translations. In this example we use “de”.
  4. Verify your translation.json file using a JSON lint tool or validator.
  5. Refresh and clear the cache of your browser to examine the changes.

The following screenshot shows the login dialog without the locale file. The elements of the OpenAM UI that use server callbacks have been translated into German. The rest are still in English:

image

The following screenshot shows the German (de) translation.json file in place:

image

LESS Stylesheets

The XUI uses the LESS stylesheet pre-processor. LESS compiles LESS syntax into CSS either on the fly during page load or in pre-production. LESS files, consisting of LESS syntax allow extra capabilities to standard CSS, including variables and functions. Valid CSS is valid LESS.

In the XUI, LESS stylesheets are processed on page load. Any LESS variables can be defined in the themeconfig.json and all LESS is realm specific in the XUI, whether defined in the themeconfig.json or in the LESS files themselves. The path parameter in themeconfig.json determines a location where the XUI expects to find a folder called CSS, containing LESS files for the realm.

Fixing the XUI CSS layout on the login page

In the previous exercise, the browser language was set to German and a locale file was created for the XUI. Unfortunately, this resulted in part of the German translation for username appearing behind the username field.

In this exercise a change will be made to fix the layout for German language visitors.

  1. Use your browser developer tools to inspect the label for the username field. The label uses a style called “label.short”.
  2. On the XUI development server, edit the forms.less stylesheet in the CSS folder of the theme folder you created earlier. In our previous example, this folder was acme/CSS.
  3. Search for label.short and change the value 100px to @label-short-length. Save your work.
  4. Edit config.less in both the default CSS folder and your realm specific folder and add @label-short-length: 100px; to the bottom of each file. Save your work.
  5. Edit themeconfig.json and add “label-short-length”: “150px” to your theme.

The following screenshot shows that the layout has been corrected when viewed in German. A less variable was defined in config.less, set with a default of 100px and then added to the form.less stylesheet. This variable was then added to the themeconfig.json and set with 150px, overriding the default value of 100px and therefore fixing the layout:

image

Handlebars Templates

Handlebars integrates with backbone to provide a templating framework around backbone views. In the XUI, views are often made up of multiple nested handlebars templates. For example, the default login page with a DataStore authentication module uses the DataStore1.html, NavigationTemplate.html, LoginBaseTemplate.html and FooterTemplate.html template files.

Handlebars templates can provide some simple conditional logic which the XUI makes use of. For example, the DataStore1.html template contains conditional logic to decide whether to display links for the forgotten password and registration services.

Templates may also contain statements like {{t “common.user.Login” }}, which instruct the I18next library to replace that statement with text that is specific to the locale (in translation.json).

Template files can be set per realm, and are either located in the XUI/templates folder for the default realm, or in the templates folder in the realm path.

Authentication Module Templates

Authentication module templates allow customizations specifically for the type of authentication occurring. An authentication module template is specific to the type of module and the state that the module is in. The combination of the module type and state it is in is referred to as the stage. For example, the DataStore module at state 1 has the stage name of DataStore1.

An authentication module template called DataStore1.html exists by default. This is a useful template to use when altering the standard OpenAM login screen.

Adding buttons to the login screen

In this exercise, buttons will be added to the login screen which link to the default and acme realms.

  1. Locate the DataStore1.html template in your realm and add two html buttons above the {{callbackrender}} at line 20. Surround the buttons in an {{if isSubmit}} and {{/if}} and save your work. The entire code block starting at line 19 should look like the following:
<div class="group-field-block{{#if isSubmit}} float-right{{/if}}">

    {{#if isSubmit}}
       <input type="button" 
       onclick="document.location='#login';return false;" 
       name="topRealm" class="button float-right" 
       value="{{t "common.form.gotoDefaultRealm" }}" />
       <input type="button" 
       onclick="document.location='#login/realm=acme';return false;" 
       name="acmeRealm" class="button float-right" 
       value="{{t "common.form.gotoAcmeRealm" }}" />
     {{/if}}

        {{callbackRender}}
</div>

  1. Edit the locales files to have entries for common.form.gotoDefaultRealm and common.form.gotoAcmeRealm. Verify the files have valid JSON and save your work.
  2. Clear the cache in your browser and refresh the login page for your realm.

Two buttons were added to the DataStore1 template. The buttons were put in some handlebars {{if
}} logic, because the whole template works through a for loop rendering different elements, some of which require callbacks from the OpenAM server. We want the buttons to only be rendered once in the loop next to the form submit button hence {{if isSubmit}}.

Each button has a value containing a {{t declaration, which instructs the XUI to retrieve that value from the appropriate locale file. Once the entries are defined in the locale files, the buttons are rendered correctly by the XUI.

Altering the profile template to have additional user attributes

Most LDAP implementations apply the attribute “postalCode” by default to users. In this example, that attribute will be added to the profile template and OpenAM will be configured to provide that attribute to the user and accept changes to that attribute from the user. The server side configuration is sufficiently covered in the OpenAM 12 developer guide.

  1. Edit the UserProfileTemplate.html file to have an additional postalCode input box by copying the telephone number block. Use common.user.postalCode for the I18next code. Make sure the input id is postalCode.
  2. Configure the DataStore in your realm to have the postalCode attribute in “LDAP User Attributes” and save your work. At this point the XUI will now render the postal code in a new field in the profile, but won’t be able to save it.
  3. Follow OpenAM Developer’s Guide â€ș Customizing Profile Attributes â€șTo Update the AMUser Service For the New Attribute, i.e. Add the postalCode attribute to amUser.xml and reinstall the service.
  4. Follow OpenAM Developer’s Guide â€ș Customizing Profile Attributes â€șTo Allow Users To Update the New Attribute

The profile page should now resemble the following screenshot and alterations to the postal code field should work successfully:

As the profile data in this view is sent and retrieved using the binding built into backbone.js, adding another field to the template is simply a matter of defining an input box with an id that matches the element in the REST response.

Backbone models and views

Backbone is an in browser MV (model view) JavaScript framework with a RESTful JSON interface that is designed for use in single page web applications. Backbone has two dependencies, JQuery and underscore.js. In the XUI, the underscore.js dependency is satisfied by lodash.

Unlike MVC (model view controller) frameworks such as Django, Rails and ASP.NET MVC, there is no controller code as such. Instead controller code lives in the view. Just like MVC frameworks, routes are used to match URL patterns to views.

The operations that the XUI executes using backbone are global to OpenAM, they are not realm specific.

Modifying the Forgotten Password View

The user self service feature in OpenAM allows a user to provide their username and start a password reset procedure by receiving an email. However, the REST endpoint in OpenAM that starts this procedure can also start the process by looking up the user based on email address. In this exercise, the code in the FogottenPasswordView will be changed to provide the email address.

Note
This exercise involves making a change to the JavaScript files in the XUI. It therefore requires a copy of the root main.js file from the OpenAM source code, in place of the minified main.js file on your testing server.

This exercise also requires that your realm has been configured with the User Self Service service enabled and that password reset is enabled.

  1. Overwrite the ForgotPassWordView.js file on your test server with the equivalent file from the OpenAM source code. On a standard OpenAM 12 server, this file is located in XUI/org/forgerock/openam/ui/user/profile/
  2. Locate the postData around line 72 and change the line username: $(‘#username’).val(), to email: $(‘#username’).val(), Save your work. This will alter the REST call to supply a search on an email address instead of a username.
  3. Edit line 10 of the ForgotPasswordTemplate.html and set the I18n code for the label to be common.user.emailAddress. Save your work. This will change the label in the form to say “email address” instead of “username”.

The forgot password screen now shows email address instead of username. When a user starts a forgotten password flow, the REST call submits and email address instead of a username.

See Also

How do I modify the text on the Login page for one or more realms in AM (All versions)?

FAQ: Customizing, branding and localizing end user pages in AM
https://backstage.forgerock.com/knowledge/kb/article/a18529200

1 Like