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(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. 🙂

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

smart tools

This is the second part of a multipart post of the new SMART TOOLS – a product supporting mlm and renditions in smartui.

You can get the first part smart UI in practice: SMART TOOLS(1) – the beginning

Today lets look on the functionality of the multilingual metadata part of smartUI.

Its directly integrated in the properties manager of the nodelist widget.

The multilingual metadata panel

Use the pulldown menu in the properties manager.

If you go to the “Multilingual Metadata” entry, you’ll see a list of the two attributes, which make up the multilingual metadata in all supported languages.

Here, just for demo purposes, metadata languages are English, German, French, Italien, Japan, Spanish and simplified Chinese.

Automatic translation with Microsoft Azure services

At right, there is a globe icon visible. If there is a globe icon, then this language is configured to do an automatic translate using Microsofts Translate service. Its to translate from the users default metadata language (here English, also displayed with a different background) to this target language. Simplified chinese is not configured to use automatic translation, therefore there is no globe set at chinese. To configure that is the job the Content Server administrator.

Clicking on the globe will replace any text with the translation of the attribute in the default metadata language (here “a special cover letter”).

Although this translating machines are quite good, its always a good idea to have the opportunity to manual correct the automatic entries. Its done simply by clicking on an entry.

By pressing <RETURN> in a inpput field or by clicking on any language name, the edit mode is switched off and the green button “save mlm data” is activated. By pressing this button, mlm data is saved on the server.

Print a list of actual mlm data

If you have a lot of these language entries, its unlikely for one person to check all language values. It would be nice to have a list (printed of pdf) with the actual values. This can be done by simply clicking on the blue Print-Button.

This will open the browsers print window and give you a preview of the list.

Ok, thats all for today. Next week we’ll take a look at the SMART TOOLS Rendition support.

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

smart tools

Using OpenText Content Server as as developer, you have to face it sooner or later: Write an application in smartUI. This will give you a lot of experience for smartUI projects, especially on the Cost/Times/Materials base.

I did chose to build a product called SMART TOOLS with Renditions and Multilingual Metadata Support. Product means, it can be sold by other OT partners as well.

This is a multipart post. Today we talk about the basics of the application running in Content Server 20.2. Additional posts will explain the usage and some technical aspects

Features

SMART TOOLS includes these features:

  • Adds support for Multilingual Metadata to smartUI
    • Automatic Translations for the multilingual data using Microsoft Azure REST service (as nobody speaks all languages on this planet)
    • Add print lists (to get a list of the data entered for editing)
    • Add/edit mlm data directly
    • Integrated in the standard nodelist widget using the property manager
  • Adds support for Renditions
    • List all Versions and Renditions
    • Use “view as webpage” as a base viewer
    • Download renditions
    • Delete renditions
    • Replace renditions
    • Add Renditon as command
    • A cell icon marks all documents in the document list having renditions at the newest version. Clicking on the icon will open a window to show all renditions. Download or delete a rendition.
  • Adds configuration support for the javascript client software from the Admin pages. Critical things like MS Rest API key can be protected by sending this things at the page creation time to the client.

The SMART Tools are build for Content Server 20.2. A a considerable amount of new REST services also to be implemented in the Content Server.

The Integration

Integration is simply direct in the nodelist widget using the property manager.

Open the property manager

By clicking in the first icon in the list, the property manager opens.

Renditions and Multilingual Metadata

The panels adressed by this menu are the multilingual metadata panel (with the two fields Name and Description) and the rendition panel. Here you see the mlm panel with the autotranslate Icon (the globe at the right).

mlm main panel

This is the rendition panel which shows all renditions attached to all versions.

Renditions main panel

Language support

The SMART TOOLS support a couple of UI languages like

  • English
  • German
  • French
  • Spanish
  • Italian

This is one example on the multilingual metadata dialog using German, French and English.

The nls feature of smartUI is nice, isn’t it? I even tried it (experimental) for Arabic, and it worked!

Lets go more in detail in the next posts, as this here is intended to be a simple overview.

Next week I will describe the multilingual metadata support of smartUI

Config Settings send to a smartUI Module

There are several possibilities to set the configs from the base OScript Module. The most interesting is the method of setting the configs via Oscript. The complete list of the possibilities is_

  • When you visit a mode or get a children
    • Implement “fields” and/or “expand” in the appropriate CSNode.
    • Use the date from data.(fields.name)
  • When the Widget is created
    • Implement an REST service which will give you the data
    • Load the data on the first widget creation and cache it with a permanent model factory in the context
  • When the page is loaded
    • The CSUI::Extension::GetDynamicConfiguration method in OScript has normally this entry
function Assoc GetDynamicConfiguration( Object prgCtx, Record request )      
return Undefined
  end

Overwrite it with your desired configuration (here only the enableAppleSupport is displayed for clarity):

 function Assoc GetDynamicConfiguration(

        Object      prgCtx,

        Record      request )      
Assoc       basicAppData
Assoc       config 
(snip)

        Boolean     enableAppleSupport

    (snip)

        // changes in admin page should be reflected immediately by every thread

        settings = $WebAdmin.AdminUtils.GetSmartUISettings( prgCtx )
     (snip)
    if ( IsFeature( settings, "enableAppleSupport" ) && IsDefined( settings.enableAppleSupport ) )

        enableAppleSupport = settings.enableAppleSupport

    end

    

    config = Assoc{ 
    (snip)
     "csui/utils/commands/email.link": Assoc{ "enableAppleSupport": enableAppleSupport },
                  

    return config

end 

The returned assoc consists of a requirejs module name containing an assoc with the config values.

In your js module (here csui/utils/commands/email.link) add this line

var config = _.extend({
rewriteApplicationURL: false,
enableAppleSupport: false,
appleNodeLinkBase: 'x-otm-as-cs16://?launchUrl=nodes/'
}, module.config());

Then the config is avaliable in your js module and can be used like

var iOSEnabled = config.enableAppleSupport

Nice, isn’t it?

Dealing with Distributed Agents

Distributed Agent Dashboard

From time to time you’ll have to deal with distributed agents inside content server. This can be a little confusing, so maybe this post is helpful there

In the Admin Pages, you’ll find a dashboard to control the default setup. Either add a func=distributedAgent.AgentStatus to your servers URL or search inside the admin pages for distributed Agent things like this

Distribute Agent Entry

Click on Distributed Agent Dashboard.

Then you’ll see the distributed Agent dasshboard

Distributed Agent Dashboard

Here you’ll find all information about the current state of the system and the configuration of the distributed agent system

Per default a content server has one distributed agent with three workers. You can change the number of workers used by the agent by changing the number of workers in the opentext.ini file in the [distributedagent] section.

You can configure the whole distributed Agent system by clicking on the link indicated with the red arrow above. Then the configuration page opens:

Here you can set up values for the the treatment of low priority tasks (Enable Fairness). The agent can spent between 5% to 50% on low priority tasks, which will be processed aftera minimal age set by “Task Age”.

When you have more than 1 agent in the system, you can switch the priority Agent here also. OpenText recommends that you select the Distributed Agent with the greatest capacity to be your Primary Distributed Agent. Typically, the Distributed Agent with the greatest capacity resides on your most powerful or your least busy computer.

If you want a defined outage to be set, you can do this for the Agent by clicking on “Add new Outage” at the upper right. You can do the same for specific workers, we’ll discuss this later.

Agent System Outage

Back on the DashBoard, you cal also modify single workers. SImply click on the name of a worker, then the “Configure Worker” page opens

Worker Configuration

If you want to set a more specific name as that automatically generated, fill the Description field.

Much more important are the three columns “Never Run”, “Run” and “Run First”. The Run column contains all tasks this worker is supposed to do.

You can exclude tasks from this worker, then this worker will never execute this task. You can also prioritize a task, which means this worker will do this task at first.

To configure this, select either all of the tasklist under “Run” or one or more tasks from the tasklist and move the entries either to “Never Run” on the left (worker will never execute the tasks) or to “Run First” on the right (worker will execute this tasks at first).

This allows you an effective task prioritizing for a single worker.

You can also define an outage for this worker by clicking on the Button “Add new Outage” on the lower right.

Worker Outage

Easy, isn’t it?

Practical smartUI: Reading complex categories on the Content Server Side

Normally, when building REST Services for smartUI, you’ll have to read complex attributes from categories to send them for display to a widget.

Lets use this example

A complex Category with 4 Sets and Table Key Lookups
A complex category with multible sets and table lookups (click to enlarge)

In your REST handler, you r task would be to get the values of the category attached to a node, read them and send them to the client using REST.

In this writing, we’ll focus on the “Read them” part. Our trick is

object  LLNODE = $LLIAPI.LLNodeSubsystem.GetItem(node.pSubtype)
result = LLNODE.NodeCategoriesGet(node)

Thos gives us all Categories connected to a node.

When we receive the REST call in ther server handler, we should do

  1. find the node id of the the object to examine.
  2. find the categories attached to this node (Red Arrow in the Screenshot)
  3. iterate over all categories and get the attributes we want. This is based on
    1. the attribute definitions (Green Arrow on the Screenshot)
    2. the attribute values (Yellow Arrow on the Screenshot)
  4. if you encounter a set (-18 as attribute type), you also will have to iterate over the set (Red Rectangle)
Get the Values of a complex category
The REST Handler to get the Values of a complex category

Lets take a look on the Values of a single Category. They are inside an assoc with one entry per attribute id.

i

The Value list in CSIDE
The value list in CSIDE

Now you are almost done. Put a switch construct to get all values of the attributes on which you are intersted and get them out of the fdata assoc. The Value is found in the fdata assoc of the n-th Category unter the content of the first Value entry. Then we have to pick the attribute id where we find the content of the attribute in the first Value entry.

The Str,ValueTostring typecasts the output to be a string. So when we finished the job, we’ll see in the entry event.eventcycle the value of the attribute with the name “EventCycle”.

Get the attributes of interest
The switch construct to get the attributes of interest

The only thing to do is put the values in assocs and send them to the smartUI widget under the main clause “events” in the JSON array

Sent the values back
Send the values back. Support 200 (ok) and 500 (error) as status code.

So you see, Categories are no Monsters. They are quite user friendly. Using the same mechanism, you can even update complex Categories with one REST call.

Happy smartUI programming.

New: We are offering custom widget development. Interested? Send an e-mail to merz at ebit-company.de stating the purpose of the widget and requesting a qoute

Practical Series II: Building an EVENT Management System in the Content Server using a graphical smartUI Dashboard and Connected Workspaces.

3-5 days, depending on the skills of the attendees.

Here, an EVENT Management system in the Content Server is built from the scratch. Events can be anything, from a Music Concert to Keysessions or anything else. To manage Events, all necessary Notes, Documents, Feedbacks can be stored in the Content Server.

Each event is mapped to a Business Workspace and has several folders to hold all documentation and feedback. Events can be related to other events. Each Event is containing Documents according to the template and has a Team of Coworkers. We also implement a Dashboard to see all Events in smartUI. If an Event is clicked in the Dashboard then the corresponding Connected WorkSpace Business Object is opened. Both parts, the Dashboard and the Business Workspace correspond via REST Service, which is also to be implemented during this workshop.

Uses the Content Server 16.2.10, the smartUI SDK and the D3 library, which is part of the smartUi SDK.

New: We are offering custom widget development. Interested? Send an e-mail to merz at ebit-company.de stating the purpose of the widget and requesting a qoute

Practical Series I: Strategies to “widgedize” an existing Content Server module and to add functionality in an existing widget.

1 day. Here, some practical parts of the SDK usage are discussed. First, we discuss, what’s to do, if somebody wants to use the Barcode Command (Enterprise Scan) as a widget in smartUI. We discuss what’s to investigate using CSIDE and implement the widget. Then the appropriate REST Services are implemented on the Content Server side. Then we add an email functionality to the TEAM widget by implementing this as a view and interconnecting this with the original widget using a command.

Practical Experience in the SDK or the Base Workshop is required.

New: We are offering custom widget development. Interested? Send an e-mail to merz at ebit-company.de stating the purpose of the widget and requesting a qoute