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

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

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.