smartUI Goodies for Webreport Fans

One of the smartUI goodies for webreports

In the rich universe of Opentexts smartUI there are even some goodies for the webreport addicts. Besides the standard webreport widgets there are two controls witch are used happily and heavily by webreports designers, named Parameter.Prompt and Status.Screen. These are the true smartUI Goodies for Webreport fans.

smartUI support
Need smartUI support?

Especially two controls are of interest for the seasoned webreport developer.

For tests, I’ve used a senseless webreport

A senseless webreport just for demonstration

This is a dummy webreport with several parameters. At the top right corner you see the execution result. Just for demonstration purposes, this has been kept simple and stupid.

But what if…….. the webreport is supposed to be run under the new and amazing smartUI? In this case we have two controls to help.

Parameter.Prompt

This control senses, if the webreport does have one or several parameters and allows to alter the parameters before the webreport is started.

PArameter.Prompt, a special service for all the webreport addicts

Here, all parameters can be changed. A click on “Run WebReport” will start the webreport if activated.

The js code is easy

JS code for the parameter prompt control

Status.Screen

can be used to start the webreport and to display some End-Messages. A standard situation is displayed here

A webreport has finished and the End-Messages are displayed

For example, if a html file is defined as output under the folder “webreports”, then this infos can be displayed at the status screen.

The js code is easy

A smartUI goodie for webreport fans in JS: status.screen

Both controls can be combined to a widget to start and execute webreports.

If you need some more sophisticated, its easy to override those controls with custom controls.

That were the smartUI Goodies for Webreport Fans.

Happy playing with webreports, but dont forget: Webreports will make a machine not faster!

Asking your browser for help in smartUI developments

Browser Memory

When you think of smartgUI, you may always think of a lot of small js/css text files and a couple of resources to be loaded. All together act as smartUI Page like this:

A typical standard landing page

This is a typical landing page. With such a page you can control, how your perspective is working and also if certain commands, controls and widgets are correct. For example, the widget ot the top left is a modified Welcome Widget showing System Messages and other news channels.

What else can a browser do for you in your continuing quest to produce an awesome user experience for an enduser?

A lot! First, open the development tools in the browser. Here we are using Chrome, but this tools are available on all browsers.

Developer tools – a closer look

Developer Tools

Press Ctrl+Shift+I or the Develeloper tools entry in the menu.

The tabs in the developer tools

This will open up the develeloper tools. The functionality is subdivided in several tabs. The most important (from left to right) are

  • Elements. Gives you the possibility to see the actual DOM loaded, Here you can change css and other elements and you’ll see immediately the effect. The rightmost icon lets you identify any element on the screen (and display it in the DOM). Thaty a handy way to identify elements on the screen and to which js file they belong (Similar way as we had in legacy gui with oscript in these ancient times)
Identify Icon
  • Console. Gives you all messages from the browser. You can see all module messages, also missing modules are displayed in red. There are several levels of messages, which can be filtered. Here, since this is a develeopent VM, all levels are activated.
  • Sources. The main tab to work with.
The sources window

The complete smartUI is loaded in the browsers memory (except those data parts, which will be changed via REST/Ajax later on). So we can examine directly, whats happening in the browser.

Interesting is the treeview at left:

Loaded js parts

We see blue and orange folders, which can be opened. Lets open a blue folder, for example the “New Generation Desktop” ngd and the resubmission

Bundles and Sources

First, we see, the ngd has the name ngd/bundles. It contains the js and css bundles of ngd. Lets open another folder, the resubmission folder. Here you see a blue app/bundles folder and an orange folder named src. Here you have the standard SDK tree. If there sre orange folders, then you can browse into the source code, like this:

Load a souce file which is not in the SDK

Here we checked the AddReminder.js from the reminder package, which is one of these modules not part of the smartUI SDK.

Whats to do with that file(s)? Select the file (with js extension) in the tree and right klick. Here you can edit the file or save the file to your filesystem.

Save a loaded js file

Search a needle in a haystack

We have a lot of js files in the browser memory. How to search for a specific file?

First, display the search window by clicking on the to right 3 point menu. Goto More Tools and click on Search. Then a search bar opens at the lower left. Enter your search name (mostly part of the requirejs path) and you’ll see the results. Select the .js file (bundles are not very useful to read). The file is displayed in the source window.

Our requirejs paths in the browser

Sometimes its quite nice to inspect the loaded requirejs modules in the browsers memory.

At first, we have to switch to the debugger mode. So open the console tab and type “debugger” in the last line to put the browser in Debug mode.

Then our interestring things appear at the right of the developers tools.

We see, the scope is filled with the loaded objects. Go to the global scope. Scroll down, until you’ll find ther csui entry. Please note, this may change to nuc from 21.2!

There are several entries under csui. Open the first “require” entry (or “requirejs”). Then open s. Expand contexts. Open _ (underscore). Expand registry.

Then all loaded csui and extension parts are listed in the registry. Lets examine one entry (countryselector.view).

Under depmaps, we see the dependencies.

Under map we see all map related things, this is the place to override an original module with a custom one.

And we have access to the source code (if we click on the link at countryselector.view:13.)

Now you have the base for your own experiments. There are a lot of additional features in the developers tools, which could not be covered here. So: Happy experimenting.

The new smartUI Nuc. A closer look.

The new NUC package in smartUI

Disclaimer: The discussed topic is on Content Server 21.2 smartUI SDK and may change anytime without notice after today (I dont hope so, but….)

The new package nuc in the smartUI SDK is the

(quote) nucleus of the nucleus (endquote)

and something like “the Best of the best, Sir” (Men in Black I)

What is nuc?

Lets take a look at the lib/src folder in an initialized 21.2 project. To see the images larger, right click and open the image in a new tab.

Nuc, The Nucleus of the Nucleus

The nuc consists of these folders:

utils

This folder was located in the csui package in ancient time. Nowadays it has moved to nuc.

If you compare that to the csui/utils(21.1), you’ll discover a few differences

And also csui/utils(21.2)

If you use requirejs paths to elements, which are not in csui/utils(21.2), you should check these references and edit them to point to the proper element if needed.

models

This folder was located in the csui package in ancient time. Nowadays it has moved to nuc.

If you compare this with the csui/models(21.1), you’ll notice a few differences.

And also compared to the csui/models(21.2) there are differences:

A closer look reveals, that the csui/models(21.2) is not identical to csui/models(21.1) and therefore you are supposed to correct manually any requirejs paths in your js code, if you have to require those models moved from csui/models to nuc/models.

lib

This folder was located in the csui package in ancient time. Nowadays it has moved to nuc. The folder contains

If you compare that with lib at csui (21.1) and also with that lib folder existing in the csui (21.2), you’ll discover, that csui/lib(21.1) is the same as csui/lib(21.2), whereas nuc contains a subset of the entries of 21.1. At least on 21.2 the csui/lib references seem to work perfect, so that those in nuc/lib could be ignored.

grunt-tasks

This folder was located in the csui package in ancient time. Nowadays it has moved to nuc. It contains that:

If you compare that with the grunt-tasks at 21.1, you’ll discover a surprisingly large amount of additional documentation files. The old 21.1 csui/grunt-tasks had only the README.md, whereas there are a lot of additional md’s on 21.2 nuc/grunt-tasks available.

bundles

this is a new folder consisting of a couple of pre-defined bundles. These Defines([]) are quite handy to use. Nothing special here.

How to check if the smartUI SDK is working

Test the sdk

If you get new Content Server Versions, you also have to upgrade your existing smartUI projects to the new version.

In this post, I’ll give a short receipe how to check the functionality of the sdk.

The primary component of the sdk is the generator. It contains the compete sdk and all infrastructural things (grundfiles etc) to build a project folder. All other zip files (except the style override kit for webdesigners) are secondary.

The following receipe builds on a running SDK of any older version.

The Check

So to check, if a new generator is working, do this: (can be done in parallel to existing versions).

  • Unzip the new generator to a folder.
  • if you do have older generator installed and you want to keep it:
    • change the name of the new generator to reflect the test, for example generator-test21.2.
    • Also configure this name in the generator. Click here for details
  • go inside the unzipped generator folder and link it to your npm
Yo Generator Links
Yo Generators
  • init the new test project with the new generator (here yo csui-21.1-standard). Then wait and check, if there are any issues. You can ignore minor issues, as long as the folder is initialized.
  • install the demo widget from the generator by typing yo <your-generatorname>:widget. Do not try to install the demo widget from different sources, you want to test if the generator runs.
  • run grunt from the commandline for the new project
    • Should not indicate any issues.
    • in the out-module folder should be the module to install in the Content Server
    • in the out-release folder should be at least a json file and a bundles folder with content.
Output files

But if you get a result like this:

Non working SDK

then the generator is broken und the whole SDK wont work. You can try to grunt –force, but the results will be not defined. In this case, you should wait for a working version of the SDK.

If the thing is working, you should be able to install the module and see the hello widget in the perspective manager.

Happy Testing

Yeoman & generator-csui-extension secrets

Yeoman and smartUI

Have you ever wondered about the yeoman and the genererator-csui-extension? And whats going up in the somewhat bumpy installation and generation of a project dir? This are the Yeoman & generator-csui-extension secrets.

Yeoman & generator-csui-extension secrets: Lets unveil this secrets

First, recap the installation steps from scratch:

  1. Download and install node.js Version 11.15.0 (In you try a newer version, you will be rewarded by the famous primordials not defined error on starting yo, see this post )
  2. Set npm (part of node.js) to npm version 3 globally (npm install -g npm@3 (I am using 3.3.10)
  3. Install the grunt cli globally (npm install -g grunt-cli@1)
  4. Optional path: Unpacking the sdk and setting up the sampes)
    1. Unpack the SDK in a folder (p.ex. sdk)
    2. go inside this folder
    3. Use npm to install all requirements (npm install)
    4. Then you can start the internal webserver (Default under post 7777) by npm start and browse to sample-index.html inthis dir. Here you will get a listing of certain samples.
    5. The doc folder contains a very basic documentation of the smartUI SDK
  5. But for practical work, you need to gererate at least one project folder. This is a complete structure, containing the actual sdk and a place to build your own packages. So lets generate a project folder named project
    1. Preparation, only to do once
      1. unpack the gererator-csui-extension to a dedicated folder (p.ex. generator)
      2. While not(!) in this folder, install yeoman globally (npm install -g yo@1)
      3. go inside the folder generator
      4. link this as an extension to npm (npm link)
    2. Lets generate the project folder
      1. make the folder named project
      2. go inside the folder named project. Its empty
      3. Start yo to execute the csui-extension (yo csui-extension)
        1. you need to define the name of your oscrippt module and the requirejs base name. Then its doing a lot.
        2. Now your project is really full. You can optionally add a demo widget (yo csui-extension:widget)
      4. Now, build the project (grunt)

Ok, now the project folder project is operable. Lets take a close look on the generator.

(Remark: The versions mentioned are the working versions in my dev machines. Other, newer versions may work, but this is not guaranteed.)

Close Look at the Generator

When you do a npm link, a symbolic link is is build from the folder to [roaming dir]\npm\node_modules\generator-csui-extension.

Symlink Generator and Yeoman

So the extension folder is used as input for the yo execution.

Our Generator source folder looks like

Mann Content of the Generator

The app folder here is interesting

App folder of the Generator

First lets take a look on the rest of the templates folder

Some deeper folders in the hierarchie

Here we see, we have the same structure as lib…. in the sdk. SO the complete SDK is copied fron the generator folder to the project dir unter lib. There is no need to use the sdk folder (except for docs and samples)

A closer look at index.js (Excerpt). This file is executed during “yo csui-extension”.

Index.js

Here you see, the whole structure is copied directly from the templates folder to the destination project. All contents in this templates folder are later in the project folder.

A closer look at package.js

Package.js

The package.json contains as always the definition of this generator extension like, name, description and depedencies of this extension.

Areas for Customizations are at this areas (for example):

  • Custom gruntfiles, which shoulkd apprear in all new generated project folders
  • A different setup for the linters which should apprear in all new generated project folders
  • Added libraries
  • Added documentation structures

How to do this

Simply modify the generatot folder, add your customizations and modifications. You can save this folder as .zip to distribute internal standards.

Happy Customizing

New in 21.1: Multilingual Metadata Support

Multilingual Metadata in 21.1

Finally, Multilingual Metadata Support is implemented in Content Server 21.1. Before that version there was only Support available here.

The Users Point of Vue

Multilingual Metadata can be added/edited at the Property Panel of a document. The first screenshot gives the entry of the name, the second gives the entry of the Description field.

Multilingual Metadata in 21.1
Multilingual Metadata in 21.1

Only Names and Descriptions are supported for Multilingual Metadata. For more fields or even Category Attributes in multiple languages, refer to an OpenText Partner like CASSIA.

The Programmers Point of Vue

The Name and Description fields show at least two multilingual text pickers. These are found in the SDK in /lib/csui/controls/multilingual.text.picker and in /lib/csui/dialogs/multilingual.text.picker. Unfortunately there is no Documentation and no index.html. Lets take a look on that control:

The Controls

The left screenshot is from dialogs, the right is from controls.

Multilingual Text PIcker

This is embedded in the header.view.js from the dialogs/node.picker and will only be activated, if Metadata Languages are enabled. The mixin allows the form to be displayed as a Bootstrap Popover.

Mixin Start

Changes in that area require the changed multilingu<al text picker and also the changed paths to be followed for that changes. Therefore require a spawn of the whole nodestable widget to reflect that changes. This is acually not recommended.

Happy MLM

The new Treebrowse Component – Nearly a Windows Explorer

Treeview Control

The Treebrowse Component is a new in Content Server 21.1. What’s a Treebrowse?

Let’s take a look at the index.html at csui/controls/treebrowse/test

Treeview Control

This is an additional Window with the tree based display of all folders in a Content Server. It can be configured that a click on the top left icon will switch it on and off. This Treebrowse Control will then change the display of a Nodestable according to the folder pressed.

Additional Features

Lazy loading and prefetch options for performance. There are a couple of options to configure the loading of the nodes for performance. A “Show More” button can be configured

Tree Navigation is in sync with Table View in Nodestable view. The Nodestable is displays the children of the selected folder.

Multiple root nodes can be configured.

Enable

This Treebrowse is always there, but it must be enabled.

define(['module',
'csui/lib/backbone', "csui-ext!csui/controls/treebrowse/navigation.tree"
], function (module, Backbone, moduleConfigs) {
'use strict';
var config = module.config(),
enableSystemLevel = !!config.enable;
moduleConfigs = moduleConfigs || [];
var configModel = Backbone.Model.extend({
defaults: {
sequence: 100,
enabled: function (status, options) {
return false;
},
data: function (status, options) {
return {};
}
}
}),
configCollection = Backbone.Collection.extend({
model: configModel,
comparator: 'sequence'
});

The var enableSystemLevel is interesting.

var NavigationTree = {
enabled: function (status, options) {
  if (!!enableSystemLevel) {
    return true;
  }
  var enabled                = false,
      moduleConfigCollection = new configCollection(moduleConfigs);
  moduleConfigCollection.some(function (mConfig) {
    enabled = mConfig.get('enabled')(status, options);
    if (enabled) {
      status.originatingView.treeData = mConfig.get('data')(status, options);
    }
    enabled = enabled ? true : enabled;
    return enabled;
  });
  return enabled;
}
};
return NavigationTree;
});

As you can see, the Enablement on a system level can be done in OScript. Refer to this post, if you want so figure out how.

If you want to configure this control only for certain folders, you should do:

  • Implement methods enable and data in a separate file and return them in an enclosed object.
  • Register this file which returns object containing enable and data methods as an extension to the “csui/controls/treebrowse/navigation.tree” in the respective module.
"csui/controls/treebrowse/navigation.tree": {
    "extensions": {
      "RMExtensions": ["RMExtensions/controls/treebrowse/RMExtensions.treebrowse.tree"]
    }
  }
  • Enable method should return a condition that specifies when the Tree Navigation should be enabled.
  • The data method should return data that must be passed to tree navigation while instantiation. This data should include configurable settings. If there is no data to be passed default settings are applied.

Example

define([], function () {
  'use strict';
  var RMTreeView= {
    enabled: function (status, options) {
       var supportMaximizeWidget = $("body").hasClass("csui-support-maximize-widget");               return (supportMaximizeWidget && $("body").hasClass("csui-maximized-widget-mode") === false);
    },
    data: function () {
      return {
        "lazyTree": true;
      };
    },
    sequence: 50
  };
  return RMTreeView;
});

A more complex enabled function (like in the index.html) is

enabled: function (status, options) {
var parent_id = status && status.container && status.container.get('parent_id');
return !!parent_id;
}

Parameters

  • Parameters thats need to be included as part of module config:
    • sequence – number default: 100 Decides the priority of a module when multiple modules enabled treeview.
    • enable – function controls whether the treeview command should be visible or not. Need to return a logic whether tree view command is visible.
    • data – function Returns an object of data that are passed to treeview. The parameters that are returned by the data method are passed as options to the constructor of node.tree.view.
    • Parameters that can be returned by the data method are::
      • lazyTree – Boolean default: false Decides whether to prefetch items at one level ahead to display without any delay on clicking showmore button / to fetch the items after clicking show more button.
      • showItemsBlockSize – Number default: 10 Number of items that are shown on expansion of any node or clicking on showmore button.
      • rootNodes – Array of nodemodels default: top most ancestor node of the currently active node An array of nodes which are used as root nodes by the tree. If more than one root node is provided then multiples root nodes are displayed.

Happy Tree Browsing!

The magic of Extension Points: the csui-ext module

require js

Have you ever thought of the magic of extension points in smartUI?

What is an extension point?

This are the dynamic configuration points of smartUI. Calling the config URL in smartUI, you will see all extension points with all configured extension modules in the current smarUI. Normally you’ll add your modules in the file <your-base-requirepath>-extensions.json in your projectstructure build by yo.

All of these modules indicated by these json files are loaded at initialization and form the dynamic smartUI system.

This is an example of an actual configuration in a Content Server System :

Extension Point

As you can see in the configuration, an extension point allows the extension of the extension point with several definable extension modules. Ok,this sounds like a typical lawyer sentence.

In plain english:

This technique allows you to extend the capabilities of a base module (the extension point) with the extension modules listed.

How to add your own extension point?

Use cs-ext as requirejs extensions. This is part of the SDK (/lib/src/csui/utils/load-extensions). There is the module and a very brief short documentation.

If you want more infos on custom requirejs modules, refer to the example of a conditional module,

This is a require.js module like the one which I introduced for conditional loading in Febr 2020. cs-ext is part of the smartUI core. If you want to modify the cs-ext, then use the easy way of loading an requirejs extension described in the conditional loading article.

Overview of cs-ext (in the sdk)

Lets take a look at cs-ext. But if you are more interested in the usage of cs-ext, skip the overview and go to the next chapter “Usage of cs-ext” below.

define(['module', 'csui/lib/underscore'], function (module, _) {
'use strict';

The cs-ext requires only module and underscore.

var config = _.defaults({}, module.config(), {
ignoreRequireErrors: false,
modulePrefixesToRetry: [
'csui', 'classifications', 'esoc', 'wiki', 'workflow', 'webreports'
]
});

This is the standard entry to add sme vars into the configuration. Interesting is the list of modulePrefixesToRetry, includes all modules to retry a reload, if the extension point loading fails.

function handleSuccess(onLoad, parameters) {
onLoad(Array.prototype.slice.call(parameters));
}

The success handler, handles the callback

function handleError(error, onLoad) {
if (config.ignoreRequireErrors) {
console.error(error);
console.warn('Loading extensions of "' + name +
'" failed:', error.requireModules);
onLoad([]);
} else {
onLoad.error(error);
}
}

The error Handler

function retryLoading(require, name, modules, onLoad, firstError) {
var droppedModules = [],
selectedModules = .filter(modules, function (module) { var slash = module.indexOf('/'); if (slash < 0 || .contains(config.modulePrefixesToRetry,
module.substring(0, slash))) {
return true;
} else {
droppedModules.push(module);
}
});
if (selectedModules.length && droppedModules.length) {
console.error(firstError);
console.warn('Loading extensions of "' + name +
'" failed:', firstError.requireModules);
console.warn('Dropping extensions:', droppedModules);
console.warn('Retrying extensions:', selectedModules);
require(selectedModules,
function () {
handleSuccess(onLoad, arguments);
},
function (error) {
handleError(error, onLoad);
});
return true;
}
}

The retryLoading handler. Demonstrates how the reload a module, if there is an error condition. Only the modules with the prefixes listed in modulesPrefixesToRetry (see above) will be reloaded.

return {
load: function (name, require, onLoad, runtimeConfig) {
if (runtimeConfig.isBuild) {
onLoad();
} else {
var moduleConfig = runtimeConfig.config[name] || {},
modules = moduleConfig.extensions;
if (modules) {
if (!.isArray(modules)) { modules = Array.prototype.concat.apply([], .values(modules));
}
if (modules.length) {
require(modules,
function () {
handleSuccess(onLoad, arguments);
},
function (error) {
if (!retryLoading(require, name, modules, onLoad, error)) {
handleError(error, onLoad);
}
});
} else {
onLoad([]);
}
} else {
onLoad();
}
}
}
};

The main method is simply returned as js object.

All listed modules in the extension point list will be loaded and executed before the callback (the carrier module with the cs-ext entry) will be executed.

This may sound quite theoretical. Lets use cs-ext.

Usage of cs-ext

  • Select the module which you want to amend with an extension point. Lets use the csui/controls/table/cells/cell.factory (MUST exist) as an example.
  • Configure the proper extension in the project json like

  • Open the cell factory and examine the source code.

Notice the csui-ext! line at the end of the define-module list. This will search the require.config entries to get a list of the extension modules for this extension point. All listed modules (here hello.view) will be loaded and executed before the callback (stated in the line csui-ext) will be executed.

Back in the callback , the _.extend function extends the prototype of the cell factory by the methods “hasCellViewByOtherKey” and “getCellView”.

Summary

To make a custom extensible module (with extension point), add the csui-ext requirejs extension to your module and set the modules to be loaded in the appropriate json extension file.

Then point to your extensible module from your widget, and you are done. Then your module is configurable and extendable by using the csui-ext util module.

If you want to extend existing modules, you’ll have to spawn the widgets from the library to reflect your new module.

The famous “define is not defined” Error (or “Mismatched anonymous define modules)

Famous Image Problem

In the content server 20.4 you’ll encounter from time to time a requirejs error “define is not defined”. Normally on defining icons. This is very entertaining.

In 16.2.10 the same thing worked as a charm.

Dont panic, in the “Hitchhikers Guide to the Galaxy” you’ll find the solution.

42 (As always)

(Just kidding. Lets be serious.)

Serious

The case in 16.2.10:

Screenshot in 16.2.x

In 20.2 and above

The same perspective in 20.2 and later

Ups. Looking insane.

Cure

Examine the browser console output. At the debug level.

Browser Console
Browser Console

As a nice addition for the entertainment, the module is uglyfied, it makes no sense to search for this strings, as they are produced during the grunt tasks. But on the other hand, a couple of lines further down there are some complaints of the grid.view.js that the “getSpritePath” is missing. And the offending module is supposed to return the getSpritePath function.

So, a search on getSpritePath gave the offending module, the sprite.js, which was introduced in 20.2 and is obviously required, when a widget wants to display sprites.

The module shows

Original Sprite.js

Lets consult the requirejs error page mentioned in the browser console.

Requirejs Error page

A check of stackoverflow.com gave me the resolution “name collisions can produce this” so I changed the sprite.js to

Changed sprite.js
Changed Sprite.js

Changed the function argument from require to requireReiner. The name doesnt matter.

Result

Everything runs under 20.2 like it used to in previous releases. No more mismatched anonymous define() modules.

But be careful: You’ll get the old js file in all new SDK releases. You should override the old file with the corrected one in all js projects.

As always:

Disclaimer: This works for me. The usage requires proper testing and I will not be liable for any problems whatsoever on that.