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.

Java<->OScript Data Types

This is the thirth article on calling Java from OScript. If you want to refer the first two articles on how to call Java from Oscript, please take a look at the first article and the second article here.

The equivalent data types in java and oscript are:

a. Java to OScript

JavaOScript
booleanBoolean
byte
char, Character
short, Short
int, Integer
Integer
float, Float
double, Double
Real
long, LongLong
java.util.ListList
java.util.MapAssoc
com.opentext.util.RecArrayRecArray
com.opentext.util.RecordRecord
StringString
nullUndefined
Java -> Oscript

b. OScript to Java

OScriptJava
Assocjava.util.Map
Booleanboolean, Boolean
Integershort, Short
int, Integer
long, Long
JavaObjectPassed as wrapped value
Listjava.util.List
Longlong, Long
Object or Framecom.opentext.livelink.oml.OScriptObject
Realfloat, Float
double, Double
Recordcom.opentext.util.Record
RecArraycom.opentext.util.RecArray
StringString
Undefinednull
OScript->Java

Happy Java Programming in the Content Server!

Add a permanent Footer/Header etc to smartUI

Permanent Footer

Disclaimer: This requires a modification of app.html. Doing that will put you in the club of villains who intentionally change internal weblingo files.

OK, you’ve been warned.

Just kidding.

Permanent modifications

Lets take a look on the result. Maybe you need such a thing to comply with federal rules, to setup any “Impressum” or any links with GPDR of other infos. Then this example is for you:

A permant footer on the landing page
A permanent footer
A permanent footer on the nodelist widget
A permanent footer in the nodelist

This is not a thing you can do with perspectives. So the easiest way is to change the app.html which starts smartUI. This time, a serious disclaimer:

As always, the app.html can be changed by patches or with new versions of Content Server without a notice. You should test these things after applying patches/new versions to check, if the modifications are still inside this html file.

But this is normal. At least on 20.4 this is running.

So how to do this?

Lets take a look on app.html (hier its named app-demo.html, I can switch between footer version and normal version by simply copying the appropriate html files).

First half is quite standard. The page adds the theme stylesheet and, if the CSS-Override is switched on, loads the custom stylesheet at last.

Then the Oscript based configuration of dynamic JS module parameters is loaded and the csui modules on the client side are configurated with that parameters by sending JSON arrays to csui.config.

Then all bundles are required for csui.

As mentioned, straitforward and not very complex.

The first half of app.html

The second part is simply an empty body. csui is using JQuery to add the perspective contents in the main body.

So you can simply add permanent things in that main body.

Csui will append the content after your content.

The second half of the app.html

The content to make the red footer (see above) is

<body>
<div id="custhdr"
   style="position: fixed;
   left: 0;  bottom: 0;  width: 100%;  z-index:10;  
   background-color: darkred;  color: white;  
   text-align: center;font-size:15px; font-weight:bold">
       <p id="borderimg1" style="border: 10px solid transparent;   padding: 15px;  border-image:          url(img/border.png) 30 stretch;">
        Standard html links like <a href="www.spiegel.de">Spiegel Magazine</a>  or 
        normal content server legacy gui links like 
<a href="http://localhost/cs162/cs.exe?func=ll&objId=2000&objAction=properties&nexturl=%2Fcs162%2Fcs%2Eexe%3Ffunc%3Dllworkspace" target="_blank" >Enterprise Workspace</a> of custom widgets can be used
</p>

</div>
</body>

First, lets define a div with our custom extension. The positioning is done via an internal style and serves as an example. The position can be set to any direction, I put it down as footer.

Then inside a p element is the content. First, to make things more nicely, a border image is put around the p element. Then standard text, standard html links or links to the content server content can be used as content.

You can even add a widget here, but this follows no perspective conventions. It will be fixed on the screen.

The smartUI will follow later after the closing </div>.

Nice, isn’t it?

System Messages (in the smartUI Welcome Widget)

System Messages in smartUI

Remember the System Messages in the Content Server? This is an handy tool to display system related messages, like: “Next weekend we have maintenance” to inform users on issues with the system.

Normally, System Messages are added or deleted in the Admin pages. Here is an example:

How to add or delete system messages

A name of the Message, the message itself, an option URL for any explanatory pages and an effective date build together the System Message. Per default the System Message has a timeout period of 2 days after publishing, but this can be set in the opentext.ini file by setting a value to NewsDFTExpiration.

From this source the System Messages will be displayed in the legacy gui like this

System Messages are displayed in the legacy gui

But whats if somebody uses smartUI? Unfortunately, there is no possibility to display System Messages in smartUI.

Until the otherwise senseless Welcome Widget is amended. It can look like this:

System Messages in smartUI
System Messages – the new feature in the Welcome Widget.

(Disclaimer: This widget is NOT in the OpenText SDK. This is made by me. Contact me if you interested in using that)

The widget gets its system messages directly from an REST call which extracts this Messages and delivers them to the widget.

The news player is basically an unordered list with an <li> for each news entry. all news were rendered in the <ul> This is a screenshot of the handlebars template.

If any url is in the System Messages, the link opens up in a new tab or a new window.

The news entries scroll with a pure CSS3 animation, no additional Javascript is needed for that. Here is the animation part of the news

And the magic of animation is done with this:

And suddenly the Welcome Widget supports System Messages.

Nice, isn’t it.

Hidden Gems in the smartUI SDK: The side.panel

An overview of the side panel

One of the most interesting controls in the SDK is the side.panel. Reminds somehow to the good Documentum side panels, but is native in the new SDK for Content Server 20.4.

This can be used as a simple control to show things like search forms or oanything you like. Also, multiple views (slides) can be shown in a “Wizard Style”.

First lets take a look on the the sidepanel right out of the box. Quite astonishing, there is a index.html actually working under csui/controls/side.panel/test/index.html. If somebody starts this file, we’ll see side.panel in action:

Shows a nice panel sliding from left/right based on the configuration. This view can be used to show a single view or multiple views (as slides) as per configuration provided to it.

Quite interesting is the fact that if slides provided to the panel, panel’s footer include navigation buttons along with the button provided to the respective slide as part of configuration.

side.panel has modal (dialog) behaviour by default which optionally can pass using constructor param as well (options.modal).

There are several usage possibilities.

Simple Usage (on the right of the screenshot above):

var sidePanel = new SidePanelView({
title: 'Simple Usage Title',
content: new Backbone.View(),
buttons: [{
label: 'Button1'
}]
});
sidePanel.show();

If you want to have several buttons on the footer (in the middle of the screenshot above):

var sidePanel = new SidePanelView({
headerView: new Backbone.View(),
content: new Backbone.View(),
footer: {
leftButtons: [{
label: 'Button1'
}],
rightButtons: [{
label: 'Button2',
id: 'btn2'
}]
});
sidePanel.show();

An example for a “wizard style” sidepanel sliding from the left or the right into the screen

var sidePanel = new SidePanelView({
slides: [{
title: 'Step1',
content: new Backbone.View(),
buttons: [{
label: 'Reset Form',
className: 'binf-btn binf-btn-default'
},
{
label: 'Search',
disabled: true
}]
},
{
title: 'Step2',
content: new Backbone.View(),
buttons: [{
label: 'Finish',
close: true,
className: 'binf-btn binf-btn-primary'
}]
}]
});
sidePanel.show();

There is also a documentation available, at csui/controls/side.panel/doc/side.panel.view.md

Nice, isn’ it?

Happy sliding !

Remove Commands from Nodestable etc in the smartUI SDK

Command is Gone

From time to time you’ll need to remove commands from the widgets, like the nodeslist widget.

The magic spice is setting up a blacklist on the masks.js files during the initial phase:

csui.require.config({
  config: {
    'csui/widgets/search.results/toolbaritems.masks': {
      'mycustomer': {
        'otherToolbar': { blacklist: ['Properties', 'permissions'] },
        'inlineToolbar': { blacklist: ['Properties', 'permissions'] }
      }
    }
  }
})

There are several possibilities to do that.

1, The approved way

The approved way is to configure smartUI before its initialized from OScript.

This can be done by overriding the GetDynamicConfiguration in your base smartUI module (the one with the csuiextension orphan) like:

override function Assoc GetDynamicConfiguration(Object prgCtx, Record request)
  
    List blacklist = { "Copy", "Move" }
    return assoc{
      "csui/widgets/nodestable/toolbaritems.masks": assoc{
        "basecsui": assoc{
          "tableHeaderToolbar": assoc{ "blacklist": blacklist },
          "inlineToolbar": assoc{ "blacklist": blacklist },
        }
      },
      "csui/widgets/nodestable/headermenuitems.masks": assoc{ 
        "basecsui": assoc{ "blacklist": blacklist }     }    }
  end
end

Here in this example, the toolbaritem.masks and the headermenuitem.masks are set up with our magic spice (a blacklist containing all the signatures of the commands not desired). This will remove the “Copy” and “Move” commands from the list of commands permanently. And permanently means, this will survice a Page Reload.

2. The ugly way (but also surviving a Page Reload)

The ugly way is a way is the standard OpenText Way of overriding app.html and configuring the blacklists by yourself. Lets take a look at the app.html (found at ../core/module/csui/html).

Here is an example on a windows server:

Where to find app.html
app.html - the head

In the upper part you can see the csui.require calls. At the top you’ll notice a csui.require.config call, where you can put our magic spice (see above).

The disadvantage is, when you override app.html (directly or by using htmlmap) then you’ll have to check, if any future OpenText patches will override the app.html also.

BUT: If you override app.html, you can take advantage of this for example by implementing a permanent footer, which is always drawn at the bottom. You cannot do that with pure smartUI tools.

Here is an example of a permanent footer in the body part of the app.html.

Multiple usages of app.html - the body

3. The force way

There is also a brutal way to remove commands without that configuration of app.html. This requires

  • a widget or a command to do the removal
  • a couple of js lines

But on the other hand, this is the way which allows easy recovery of the command by doing a simple refresh of the page.

Here is a demo case (a wild version of the hello widget from my training installation). This is a widget with some demos and a new inserted button to delete a command.

a very chaotic demo

Behind the button are some js lines of code, which will remove the Copy-Command. Actually, the lines var b=commands.get(“Copy”); and commands.remove(b); do the job.

You will have to require /csui/utils/commands unter commands to use this two lines.

Two lines to decide the fate of the copy command.
Two Javascript Lines will decide the fate of the copy command

Lets see the initial command set. The Copy Command is here as usual.

Copy command lives

After the red button is pressed in the then the Copy Command is gone:

Copy Command is gone

We simply deleted the Copy command from the list of commands. This means, a simple Refresh will restore it.

Have much fun on playing around with commands!

Hidden Gems in smartUI: The Wizard Control

The wizard in Action

From time to time there are real gems waiting to be discovered in the deep abyss of smartUI. Today we’ll take a look on the Wizard control.

Example

Lets look at an example.

Hidden inside the permission explorer, there is an option to assign a new user with some permissions to a document.

When you select a document, you can select a user or a group to add to this document. This is the first page of our wizard. This control directly calls the MemberPickerWizard with this two steps.

Step 1: Select user or group

Wizard Example: Step 1

Step 2: Assign permissions

Wizard Exapmple: Step 2

Nice thing.

The code definition

Lets look at the member picker wizard:

var membersPickerDialog,
    dialogClass = "cs-permission-group-picker",
    dialogTitle = "Add users or groups",
    displayName = "Users and Groups",
    context = new PageContext(),
    connector = context.getObject(ConnectorFactory),
    node = new NodeModel({id: 37474}, {connector: connector}),
    startLocations = ['all.members', 'member.groups'];
membersPickerDialog = new MembersPickerDialog({
  command: 'adduserorgroup',
  context: context,
  connector: connector,
  dialogClass: dialogClass,
  displayName: displayName,
  dialogTitle: dialogTitle,
  startLocation: 'all.members',
  adduserorgroup: true,
  addButtonLabel: lang.AddButtonLabel,
  startLocations: startLocations,
  nodeModel: node,
});
membersPickerDialog.show();

Almost immediately the memberPickersDialog is instantiated and shown:

The membersPickerDialog

As we can see, here is our wizard.view.js used as reference.

The wizard itself is wired inside the prototyype:

Show the wizard

First, the steps are created in _createWizardSteps (see below).

All of these steps are used as argument on instantiating the wizard. Then the wizard listens to some events (“save:result” and “add:member”) and processes them.

Thats all. Easy.

The Step Defnitions

Steps are defined inside the _createWizardSteps method:

Define the steps

The object step1 defines the selection of the users, the object step2 (below) defines the permission level.

Example of a step: The Step 2

All steps have in common:

  • a title
  • headers (with the NLS language sting, a class and an id)
  • a next Button (with next button label)
  • and, of course, a view defining the content of the wizard step.

At the end, the defined steps are returned as array:

return [step1, step2, step3];

So you`ll find that under

csui/controls/wizard/wizard.view

Happy wizarding

smartUI in practice: SMART TOOLS(4) –Direct Access of Renditions – Technical

smart tools
Cell Renderer

Overview

Last week, we discussed a possible implementation of the direct access to renditions. This icon is drawn an a document, which das renditions at the newest version. Clicking on that icon opens a panel which lets the user select one of the renditions to download or delete.

Direct Access Rendition

Selecting one of the renditions will download this rendition or (if permissions allow it)( delete the rendition.

Technical

The whole thing is based on a cell renderer. This relies on the extension point node.state.icons which is (as always) declared in the extensions.json file:

"csui/controls/table/cells/node.state/node.state.icons": {
"extensions": {
    "csuia": [
       "csuia/cells/node.state/node.rendition/node.state.icons"
             ]
      }
},

This declaration points direct to the appropriate node.state.icons

define(['csuia/cells/node.state/node.rendition/rendition.view'
],
function (RenditionView) {
'use strict';
return [ { sequence: 60, iconView: RenditionView } ]; });

This basically declares the required view named “rendition.view.js” as responsible to draw the icon at sequence position 60.

This is the template used for the view.

Cell renderer Template

The image is drawn cia the css seen above.

Encapsulating this inside a link allows to make the svg clickable.

Enable the direct access

The first point is the decision, if the cell renderer is to be drawn or not. This is a static method at the view, returning true or false. We dont have ony clue if the nodeid does have a rendition at the newest version, so we have to implement a new REST command at the server to get the information. The REST must be called synchronously.

the static enable function

This is done via a simple XMLHTTPRequest. If the result is true, we return true and store the number of each rendition types at the node in the prototype for further processing.

Then lets take a look on the events:

events

This means, a click on the class of our link in the template will call onOpenView. Before we look at that, look at this in the templateHelpers:

Open the Panel after the click

Remember, we got the number of each rendition types from the server? We want tzo display these numbers as a tooltip over the icon, so this is the way to formet the output string. A note to the knights of the holy JS-grail: Referencing this.__proto__ instead of finding the prototype via object maybe depreciated, but at least its also in ECMA 6! So nowadays its a valid method.

When we click on the icon, we want to open a DialogView from the sdk toolkit with an embedded view (SelectView) in the method onOpenView:

OpenDialog

Before we open this view, we call the server to get the renditionlist of this node via XMLHTTPRequest. If we get the list (status=200) we intiantiate the view (selview1) and display the DialogView containing the instantiated view at the bin_modal default EL anchor.

Then lets show this region and store a event handler (string “submit”), which is listening on the view selview1 for that string. If it receives that string, the DialogView is closed.

Next week, we’ll examine the SelectView.

Missed something in the Posts? Here are the parts already posted:

smartUI in practice: SMART TOOLS(3) – Renditions

smart UI in practice: SMART TOOLS(2) – Multilingual Metadata

smart UI in practice: SMART TOOLS(1) – the beginning

smartUI in practice: SMART TOOLS(3) – Renditions

smart tools

In SMART TOOLS renditions are also build in the properties manager of smartUI. Renditions can be displayed by selecting renditions on the pull down menu, just like Multilingual Metatata in my last post.

The main menu

Rendition Main Panel

Next, the main panel of renditions is displayed. The renditions of the newest version or the latest 5 versions will be displayed.. A Rendition can be downloaded, deleted, replaced or (if configured) with “View as Webpage” be seen.

Rendition Commands

If there are a lot of versions, a starting version can be selected. Then this version and the last 5 versions with all renditions will be displayed.

Ascending or descending sorts on versions can be done by clicking on Versions.

Interesting is the “View as Wewbpage” function. This is a build in function in the Content Server, so it can also be used to see the contents of any renditions in the classic UI and in a separate window.

view as webpage display

A rendition can also be downloadeed by clicking on the down.arrow icon. This will normally download the file as setup by the users browser. If the rendition is a PDF and print.js (a standard extension in browsers) is activated, the the pdf can also be displayed directly.

A list of renditions can also be printed. This is a nice addition to help you to get an overview.

Rendition list

Direct Access Renditions

For a direct access to renditions, I also added a cell renderer to be displayed, if the newest version of the document has a rendition.

This is a little svg icon displayed directly in the list of documents.

Cell Renderer

The little icon can be clicked and then a list of renditions belopnging to this document is displayed.

Direct Access Rendition

From this panel, a rendition can be selected and downloaded. If the user has proper permissions, he can also delete the rendition selected. Also a printed list (see above) can be made at this point.

Sorting in ascending/descending versions can be done by clicking on the Arrow in the “Vers” columnn.

Because this direct access requires a lot of calls to the server, this feature can be switched off at the Admin Pages of smartUI.

Add Rendition Command

Additionally, there is also one command required, the “Add Rendition” command. The “Make Rendition” command is not implemented, because this requires local admin rights, something which should be rare.

This command can be issued at any document directly from the list of documents

Addrendition command

Commands are usually displayed in two locations, the Headertoolbar (above) and the Toolbar (below)

Command in Toolbar

Adding a rendition means, selecting one of system defined rendition types and a file beeing intended as the rendition. If the selected rendition type does not exist on that document, then the file is uploaded and used as a rendition.

Add Renditions - Panel

Next week, we dive more in the direct access of renditions and the Pros and Cons of that technique.

Missed something in the Posts? Here are the parts already posted:

smart UI in practice: SMART TOOLS(2) – Multilingual Metadata

smart UI in practice: SMART TOOLS(1) – the beginning

Using react components in smartUI environments

Optional Module

With the react-dom package this can be easier then expected. The idea is, add the react component directly to the DOM

Build Environment

When you have a project directory made by yo from the sdk, you’ll need a couple more node.js module:

npm install -save react
npm install -save react-dom

Then make the two files

react.js
react-dom.js

ready for use with require, either by directly requiring them or use the require in the src path of your development. Dont forget, you cant use the IMPORT statement, because its ES6. The exact procedure depends on your project build modifications .

First steps

The React component

define([
'basec/widgets/hello/impl/lib/react'
], function (React) {
'use strict';
class UserInfo extends React.Component {
render() {
var img = this.props.img, name = this.props.name, mail = this.props.title; return ( 
React.createElement("div", null, 
      React.createElement("img", {width: "100", src: img}),
      React.createElement("div", {className: "name"}, name),
     React.createElement("div", {className: "mail"}, mail)));
}
);

This component is a simple div containing an image link and two other divs with content (name and mail)

The sdk demo widget

Then use the sdk demo widget, which will be generated, if you tell yo to do so:

yo csui-extensions:widget

This will add a widget/hello tree in your source tree (if your name for the widget was hello), The first couple of lines in the hello.view.js are:

define([
'csui/lib/underscore', // Cross-browser utility belt
'csui/lib/jquery',
'csui/lib/marionette', // MVC application support
'basec/widgets/hello/impl/hello.model.factory', // Factory for the data model
'basec/widgets/hello/impl/lib/react',
'basec/widgets/hello/impl/lib/react-dom',
'basec/widgets/hello/impl/UserInfo',
'i18n!basec/widgets/hello/impl/nls/lang', // Use localizable texts
'hbs!basec/widgets/hello/impl/hello', // Template to render the HTML
'css!basec/widgets/hello/impl/hello' // Stylesheet needed for this view
], function (_, $, Marionette, HelloModelFactory, React, ReactDOM, UserInfo, lang, template) {
'use strict';
// An application widget is a view, because it should render a HTML fragment var HelloView = Marionette.ItemView.extend({

First we need react and react-dom as modules. They are added here via define. I put it in some weird place, but this is up to you where to put this two modules. There are also a couple of different possibilities to get react and react-dom inside a module.

Next, we need a place where we can add our react component. So lets add this in the hello.hbs file:

...
<div id="userreact">
And this is the react component
</div>

this is simply a div going to held out react component.

The react-dom plays its role

The react-dom can render the component to a designated element, something like the $el element. We added a div with the id “userreact” in the hbs file above.

Now we can render the handlebars template. In Marionette, there is an onRender event routine, which can be used to spice up any rendings of Marionette.

onRender: function() {
var name = this.model.get("last_name");
var mail = this.model.get("mail");
var img = this.model.get("photo_url");
ReactDOM.render(
React.createElement(UserInfo, { name: name, mail: mail, img: img }),
this.$("#userreact").get(0));
},

In the onRender function, all is rendered and we now have a DOM. Now we can render our component.

  1. We need to provide your component with some data. So we extract these from our userdata model we got from the content server. This is more or less the equivalent to our old friend template_helper
  2. The ReactDOM is supposed to render the element UserInfo with the parameters in our div with the id “userreact”

Housekeeping

When the Backbone view’s remove method is called (from the csui app from the content server), we need to remove also our react component.

So lets add our Remove in the Backbone remove()

  remove: function() {
    ReactDOM.unmountComponentAtNode(this.el);
    Marionette.View.prototype.remove.call(this);
  },

Btw: If you want to use jsx for react ui definitions, you should amend your build process with the babel transpiler. This is an example of jsx and quite different to javascipt.

const element = ( <h1 className="greeting"> Hello, world! </h1> );

The transpiler basically builds this structure

// Note: this structure is simplified 
const element = { 
type: 'h1', 
props: { className: 'greeting', 
        children: 'Hello, world!' } 
};

Summary

  • Install react and react-dom and make them requirable.
  • Build or get the react components. Make them requireable.
  • Require this components from your view.
  • Add an anchor to your hbs file.
  • Let Backbone-Marionette render.
  • In the OnRender callback, gather all data for your react component and let react-dom render the component to the anchon.
  • Use standard JQuery to get data from the react-component.

Some further infos (on pure Backbone/React things without smartUI reference)

Integrating React with Backbone

Using BackboneJS successfully with React (using MobX)

Thats it.

But nevertheless not often used in our OpenText smartUI practical live. 🙂