0% found this document useful (0 votes)
11 views201 pages

En Us - Dev Programming - 99 Sample Scripts

The document provides a collection of 99 script examples for automating tasks in ELOprofessional and ELOenterprise, aimed at assisting ELO consultants. It covers various topics such as workflow initiation, email notifications, and JavaScript integration, assuming prior knowledge of scripting. The examples serve as starting points for developers rather than comprehensive solutions, with updates included for different software versions over time.

Uploaded by

amelina
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views201 pages

En Us - Dev Programming - 99 Sample Scripts

The document provides a collection of 99 script examples for automating tasks in ELOprofessional and ELOenterprise, aimed at assisting ELO consultants. It covers various topics such as workflow initiation, email notifications, and JavaScript integration, assuming prior knowledge of scripting. The examples serve as starting points for developers rather than comprehensive solutions, with updates included for different software versions over time.

Uploaded by

amelina
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Programming for ELO

99 script examples
2 Programming for ELO

Table of contents

99 script examples 3

Preface 3
Workflow 6
General Java Client 22
ELO Dropzone and ELO Java Client 68
Repository information 82
Toolbar and navigation in the ELO Java Client 96
Add metadata in the ELO Java Client 110
Microsoft Office integration 134
ELO Automation Services 149
General remarks on JavaScript 164
Indexserver functions 169
Forms 179
Indexserver events 195
Web apps 198
3 Programming for ELO

99 script examples
Preface

Purpose of the document

This book presents a collection of sample scripts for automating various areas of ELOprofessional
and ELOenterprise (referred to in the following simply as ELO). Most of these examples were
developed in-house, and in some cases have already been published on the forum. Some of the
examples, however, were crafted specifically for this work.

The goal is to provide ELO consultants solving specific tasks for customers with a handy pool of
ideas and suggestions to help in developing a solution. In many cases, the example will not cover
all of the customer's requirements. It does, however, provide a starting point that you can build off
of.

Sometimes you may come across an interface that is described perfectly adequately, but its use in
practice remains unclear. This collection should also help in these cases, providing complete
examples of use instead of descriptions for individual functions.

The individual examples are divided into various categories. Longer scripts can be especially
difficult to categorize, as they accomplish several different tasks. This is why it's worth looking for
examples in related areas, even if you want to solve a specific problem.

Who should read this book

This book assumes knowledge of script programming in JavaScript. An experienced VB script


developer will understand many examples – but it is a still good idea to read up on the JavaScript
before diving into ELO interfaces. This book is not intended as a tool for learning to program
scripts, since the examples are not prepared as teaching examples, do not build off of each other,
and the individual functions are generally not explained.

What's next?

In this book, I have attempted to illuminate the broad use of the various scripting interfaces in an
ELO environment. Nevertheless, this means that it is not possible to go into great detail in every
explanation. For help in this, look into the JavaDoc API descriptions for the ELO Indexserver, ELO
Java Client, or ELOas.

The ELO Indexserver interface in particular is the be all and end all in the world of ELO. Regardless
of where you are working – in the ELO Java Client, ELOas, ELO Web Client, workflow forms, ELO web
apps – sooner or later, you will come upon the ELO Indexserver interface and the objects within it.
A good knowledge of this API will help you out in any programming tasks you may encounter.
4 Programming for ELO

Why aren't there exactly 99 examples?

This title is simply a working title that will quickly become obsolete, as new examples are being
added all the time. Plus, 99 is quite the catchy number for a title.

Why can't I download the examples?

This book does not provide any finished sample solutions. It is simply designed to explain different
possibilities. The examples have been kept short and often restrict themselves to a partial function.
Some of them cannot even be executed without additional programming.

Information

If you attempt to copy and paste examples from the PDF document, the formatting may
likely be corrupted. There are automatic formatting tools such as the JSTool plugin for
Notepad ++. There are other stand-alone tools as well.

Fig.: 'JSTool' extension

[10] Second edition

For version 10, I have released a new edition with additional examples for ELOprofessional/
ELOenterprise 10. To make these added examples easier to recognize, they have been designated
as such with a [10] prefix in their title.

The new examples highlight additional functions and their use. Under normal circumstances, they
will not work with older versions of ELO.
5 Programming for ELO

[10.1] Third edition

Version 10.1 includes new functions, which are described using examples in this book. These also
carry the prefix [10.1] in their title to make them easier to identify.

[10.2] Fourth edition

This document was once again updated for the release of Java Client version 10.2. The latest
examples will again carry a prefix [10.2] in their title to make them easier to identify.

[11.0] Fifth edition

This document has yet again been updated for release version 11.0. As versions 10.2 and 11.0 are
very similar, it only contains a handful of new examples.

[12.0] Sixth edition

A few more examples for the new version.

[20.0] Seventh edition

There are some examples of new functions as well as old functions that previous versions only
briefly described.

[21.0] Eighth edition

This edition includes a few examples that show how to automatically edit child folders.

Matthias Thiele

Management ELO Digital Office GmbH

February 16, 2021


6 Programming for ELO

Workflow

Start workflows with a single click

Some users have certain workflows that need to be started over and over again. It is a lot to ask of
users to go to the Tasks tab, click "Start workflow", look through and select the correct workflow
template from a list that can be quite long, and finally enter a name.

It would be much easier for them to start the specific workflow with a single click. This can be done
with a short script.

const WORKFLOWTEMPLATE = 631;

function getScriptButton100Name() {
return "Project application";
}

function getScriptButtonPositions() {
return "100,home,navigation";
}

function eloScriptButton100Start() {
var view = [Link];
if (![Link]()) {
var title = "Workflow start";
var message = "<html><h3>No entry selected</h3>Please first select an entry in order to start a wor
[Link](title, message);
return;
}

var items = [Link];


while ([Link]()) {
var item = [Link]();
var flowName = [Link];
[Link](WORKFLOWTEMPLATE, flowName, [Link]);
}

var fbMessage = ([Link] == 1) ?


"The workflow was started" :
[Link] + " Workflows were started"
[Link](fbMessage);
}
7 Programming for ELO

This script creates a new button on the ribbon named "Project application". When the user clicks it,
the script starts the workflow configured in the WORKFLOWTEMPLATE constant. The name of the
document is used as the name of the workflow.

This example also shows how easy it is for scripts to support multiple selection in many cases.
Instead of simply retrieving one entry with firstSelected, the complete list is read with
allSelected, then processed in a loop. If you want to make the user interface even cleaner, you can
also adjust the message text for the number.

Automatic notification for workflow tasks

Users who don't work with ELO on a regular basis may not realize that they have received a new
workflow task. For this reason, we have created an ELOas library that is able to send automated
notifications. To use it, you only have to create a simple rule that performs a check and sends the
e-mails. With the rule interval, you can define how quickly the user should be notified.

In the rule, you essentially only need to configure which e-mail server is used to send the
notification, as well as the sender address. The recipient address is read from the ELO user's e-mail
address field.
8 Programming for ELO

<ruleset>
<base>
<name>NotifyWf</name>
<search>
<name>"DIRECT"</name>
<value>"1"</value>
<mask>0</mask>
<max>200</max>
</search>
<interval>6:00</interval>
</base>
<rule>
<name>Rule1</name>
<condition></condition>
<script>[Link]("MyMailServer");

[Link]("Support@[Link]", "Notification", true, true, true);


</script>
</rule>
<rule>
<name>Global Error Rule</name>
<condition>OnError</condition>
<script></script>
</rule>
</ruleset>

The Misc child folder of the ELOas Base folder contains an HTML template for the text of your e-
mail.

Fig.: HTML template

<html>
9 Programming for ELO

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Workflow overview</title>
<style type="text/css">
body {
font-family: Verdana, Arial;
font-size: 14px;
}

table {
margin-top: 20px;
margin-bottom: 20px;
border: 1px silver solid;
border-collapse: collapse;
}

td {
border-bottom: 1px silver dotted;
padding: 5px;
}

.header {
background-color: #f0f2ff;
}

.urgent {
background-color: #ffd0d0;
}

.group {
background-color: #d0ffd0;
}

</style>
</head>
<body>
<h1>Workflow overview</h1>
You have active workflow tasks:
<table border="0">
<tr><td class="header">Name</td><td class="header">User/ Group</td>
<td class="header">Start date</td><td
class="header">Supplier</td>></tr>
<!--ListStart-->
<tr><td class="$$className$$">$$nodeName$$</td><td
class="$$className$$">$$userName$$</td><td
class="$$className$$">$$activateDate$$</td>
10 Programming for ELO

<td class="$$className$$">$$ixkey_0$$</td></tr>
<!--ListEnd-->
</table>
You can process these tasks in the ELO client.
</body>
</html>

Users can decide whether or not they want to receive this notification. The relevant settings are
configured in the profileopts for the user. You can read and write these using the ToggleMailReport
Java Client script. A dialog box shows the current status and presents the possible settings to the
user.

function getScriptButton513Name() {
return getText("ScriptButtonName");
}

function getScriptButtonPositions() {
return "513,home,view";
}

function eloScriptButton513Start(){
var actOpt = [Link]("[Link]", "");
actOpt = selectOptions(actOpt);
if (actOpt >= 0) {
[Link]("[Link]", actOpt);
}
}

function selectOptions(actOptions) {
var dlg = [Link](getText("Title"), 1, 8);
var panel = [Link];

actOptions = Number(actOptions);

var ckMail = [Link](1, 1, 1, getText("Activate"), (actOptions & 1) != 0);

var labelText = '<html><div style="width:400px; padding:5px; background-color: #f0f0f0">'


getText("OptionSeparator") +
'</div></html>';

var label = [Link](1,3,1, labelText);

var ckAlways = [Link](1, 4, 1, getText("SendAlways"), (actOptions & 2) != 0);


11 Programming for ELO

var ckGroup = [Link](1, 5, 1, getText("SendGroup"), (actOptions & 4) != 0);


var ckDeputy = [Link](1, 6, 1, getText("SendSubstitute"), (actOptions & 8) !=
var ckWeekend = [Link](1, 7, 1, getText("WithWeekend"), (actOptions & 16) != 0
var ckOnlyOnce = [Link](1, 8, 1, getText("SendOnce"), (actOptions & 32) != 0);

var result = -1;


if ([Link]()) {
result = 0;

if ([Link]()) {
result |= 1;
}

if ([Link]()) {
result |= 2;
}

if ([Link]()) {
result |= 4;
}

if ([Link]()) {
result |= 8;
}

if ([Link]()) {
result |= 16;
}

if ([Link]()) {
result |= 32;
}
}

return result;
}

function getText(name) {
return [Link]("ToggleMailReport", name);
}

This script also requires an external text file named "text_ToggleMailReport_EN" for the user
interface translation.
12 Programming for ELO

ScriptButtonName=E-mail notification
Title=Settings - E-mail notification
Activate=Enable e-mail notification.
OptionSeparator=Workflow notification settings
SendAlways=Always notify, even if no tasks are active.
SendGroup=Also notify for group tasks.
SendSubstitute=Also notify for substitution tasks.
WithWeekend=Also notify on weekend.
SendOnce=Only notify once per workflow.

Send an e-mail from a workflow

The notify ELOas module has the ability to check a workflow node to see if an e-mail needs to be
sent. For this check to run regularly, a rule must be created with a short interval (every 1 to 60
minutes), which calls the [Link] method.

<ruleset>
<base>
<name>SendWfMail</name>
<search>
<name>"WORKFLOW"</name>
<value>"1"</value>
<mask>0</mask>
<max>200</max>
</search>
<interval>1M</interval>
<onstart>
EM_ALLOWALLMASKS = true;
</onstart>
</base>
<rule>
<name>Send</name>
<condition></condition>
<script>
[Link]("MyMailServer");
[Link]();
[Link]();
</script>
</rule>
<rule>
<name>Global Error Rule</name>
<condition>OnError</condition>
13 Programming for ELO

<script></script>
</rule>
</ruleset>

If you want to resolve sending an e-mail from within a workflow, you must insert an ELOas node
there that contains the required information for the e-mail.

This data is entered into the comment field of the node. For the first line, enter the text
#wfsendmail. Next, enter additional parameters, such as for the recipient (which can be read from
an index field), the relevant e-mail template (which must be stored in the ELOas Base\Misc folder),
or a subject. You can also specify that the current document will be sent as an attachment.

See the description of the notify project for the complete list of available parameters.

Fig.: Definition of a "send e-mail" node

A workflow can also generate a feed post using the same mechanism. This does not require a
script. Instead, the comment field must start with #wfaddfeed or #wfmailandfeed. The
feedtemplate parameter can be used to define the template name. If the parameter is not
configured, the default template wffeed is used.

Fig.: wffeed template


14 Programming for ELO

The variables from the template are then replaced with the current values, then the generated text
is written to the feed of the current document.

You will find a complete description of the templates and variables in the notify documentation.

Forward a workflow in the ELO Java Client

If you want to run a script action on a workflow task that performs various activities and then
forwards a workflow without user interaction, the following code should help:

var task = [Link];


if ([Link]()) {
var nextNodeId = 1;
[Link]([nextNodeId]);
}

The parameter in confirmFlow contains a list of node IDs (not workflow IDs – they are not needed
here since it is only possible to forward a workflow from within a workflow).

Please note the confirmFlow method is only available for workflow entries, not for reminders.

Notification of a workflow task via e-mail

This Indexserver script sends an e-mail when a user receives a new task from another user.
Normally, the ELOas notify function is better suited for this, as it combines all existing tasks for a
user into a single e-mail message. Sometimes, though, it is necessary to send the notification
immediately for each individual task.

To enable this functionality, the e-mail server must be configured on the Tomcat running the
Indexserver (see the Indexserver Programming Guide by W. Imig) and the following script must be
entered into the start event of the person node in the workflow. The e-mail is only generated when
the task comes from another person. Tasks that users create themselves do not trigger an e-mail.
Of course, the e-mails are only generated by Indexserver clients, not by the Windows Client.

function onEnterNode( ci, userId, workflow, nodeId ){


try {
var node = getNodeById(workflow, nodeId);
if (node && (userId != [Link])) {
var mail = getUserAddress(ci, [Link]);
var subject = "Workflow task: " + [Link];
var body = '<h1>' + [Link] + '</h1><h4>' + [Link] + '</h4><a href = "elodms://wf/'
sendMail(mail, subject, body);
}
} catch(ex) {
15 Programming for ELO

[Link]("Error processing Workflow Info: " + ex);


}
}

function onAfterCheckinReminder(ec, reminders, sord, sordz, lockz) {


for (var i = 0; i < [Link]; i++) {
var reminder = reminders[i];
[Link]( "Reminder: " + [Link] );
var receiverId = [Link];
if ([Link] != receiverId) {
try {
var mail = getUserAddress([Link], receiverId);
[Link]("UserName: " + [Link] + ", Mail: " + mail);
if (mail) {
var subject = "Reminder: " + [Link];
var body = "<h1>" + [Link] + '</h1><a href="elodms://' + [Link] + '">You have recei
sendMail(mail, subject, body);
}
} catch(ex) {
[Link]("Error processing Reminder Info: " + ex);
}
}
}
}

function sendMail(to, subject, body) {


var sendMail = new [Link]("mail/SRVPEMAIL02vm");
[Link] = subject;
[Link] = "eloservice@[Link]";
[Link] = to;
[Link] = body;
[Link]();
}

function getUserAddress(ci, userId) {


var userData = [Link](ci, [userId], CheckoutUsersC.BY_IDS_RAW, [Link]);
var mail = userData[0].userProps[UserInfoC.PROP_NAME_EMAIL];
[Link]("Mail address of user " + userId + " is " + mail);

return mail;
}

function getNodeById(workflow, nodeId) {


var nodes = [Link];
for (var i = 0; i < [Link]; i++) {
var node = nodes[i];
16 Programming for ELO

if ([Link] == nodeId) {
return node;
}
}

return null;
}

Change the workflow status with a script

The workflow status is stored in the Condition field of the start node. Starting with version 9.3, the
WorkflowElement contains a method that can change the status in a single step.

[Link] = "from the script";

Remove a workflow node assignment

When a parallel approval workflow task is sent to several users, it makes sense to cancel
processing for all users involved as soon as one of these users rejects it.

To achieve this, create a collection node for the "Not approved" branch, which forwards the
workflow after only one of its predecessor nodes is complete. In the end script, all approval user
nodes are reset.

This script is not listed here as it is no longer needed. The list of nodes to reset is entered into the
collection node. The script function is built into the Indexserver.
17 Programming for ELO

Fig.: Collection node with a reset list

Action buttons to forward a workflow

You can enter up to five action buttons in the workflow definition for each person node. They are
shown in the "Forward workflow" dialog box and call a predefined script function.

Fig.: Defining action buttons

A script function must be assigned to each action button. The function's name consists of a prefix
followed by the button name: cfb<name>Start()

function cfbBesttigenStart() {
[Link]("Workflow action", "The user clicked the 'Confirm' button");
18 Programming for ELO

When this button is clicked during the "Forward workflow" process, the ELO Java Client calls the
corresponding script function.

Fig.: Active script function

Information

Since the action name is incorporated into the JavaScript function name, these names may
only consist of the letters A to Z, numbers, and underscores. All other characters result in a
syntax error in version 9. Starting with version 10.0, other characters are allowed. They are
then filtered out of the function name. When naming the function, you need to remove these
characters, as otherwise the corresponding event function will not be found. This is a little
less relevant to English-speaking audiences, but an example for German would be:
"Bestätigen" -> "Besttigen".

[10] Script event when refreshing the task list

Starting with version 10, it is possible to respond to new tasks in the tasks list with a script event.
Please note that all tasks are "new tasks" when the program starts, which is why this event is not
activated at this point in time. The event is only activated when existing data is updated.
19 Programming for ELO

The parameters of the event include the Indexserver UserTask object (workflow/reminder/activity),
as well as whether the user has already seen the task or not.

function eloNewTaskAvailable(task, isSeen) {


[Link](isSeen, [Link]());
}

[10] Edit a workflow diagram with a script

It is easy to load the complete workflow diagram for a WorkflowElement in the tasks list. To do this,
use the [Link]() command. This reads the workflow diagram with a lock. This
has two important consequences:

1. If the diagram has already been locked by another user or process, the call fails with an
exception. The script must take this into account and react accordingly. For example, the
diagram is temporarily locked when workflows are in the process of being forwarded. If you
do not know about this, it can lead to problems that are difficult to identify.

2. After completion, the lock must also be released. Otherwise, the workflow will be locked
forever and cannot be forwarded.

If the workflow only needs to be read, it can be viewed without a lock by using the
[Link]() command. This command will not fail due to another lock
and will not lock out other processes. However, if you use this function, you should never edit and
save the workflow afterwards. If other changes are made in the meantime, this would revert them
in a hard-to-predict manner, causing an inconsistent result.

[10] Determine the parent workflow of a subworkflow

Within a subworkflow, if you want to access the parent workflow that started it, you can use the
parentFlowId property from either the WFDiagram or in the WFCollectNode.

function eloHomeStart() {
var item = [Link];

if ( [Link]() ) {
var flowNode = [Link];
var parentFlow = [Link];

[Link]( "SubFlow", [Link] + " : " + parentFlow );

if (parentFlow > 0) {
var parentWorkflow = [Link]( parentFlow, [Link],
20 Programming for ELO

[Link], LockC

[Link]( "ParentFlow", [Link] );


}
}

return -1
}

Transferring data between these workflows is simple. Since both workflows run on the same object,
they share the same Sord object and Sord map object with the metadata. Access to the WF map
data is a bit more complicated. In this case, a checkoutMap must use the parent workflow ID instead
of the actual ID.

[10.1] Prevent the user from accepting a workflow (Indexserver)

If you want a group to be able to see all running instances of a workflow template, you can attach
an additional node for this group to the start node. This ensures that the group has an active node
for the course of the workflow. Each member of this group can view all workflows.

Fig.: Preventing the user from accepting a workflow

The problem with this method is that one of the members of the group could easily accidentally
accept this node. This would cause the user to lose the group membership and the other members
would no longer have access to the node.

You can use an ELO Indexserver script to ensure that only a specific person, such as the department
head, is able to accept this node. If there is an end node in your workflow, there is no need to
accept the node and pass it forward. However, if there is no end node, the department head must
accept the node and forward it on completion of all the necessary steps in order to be able to close
the workflow.
21 Programming for ELO

function onBeforeTakeWorkFlowNode(ec, workflow, node, user, flags, lock) {


if (([Link] == "ReleaseRequest") && ([Link] == 30)) {
if ([Link] != "HeadOfQsDepartment") {
[Link]("User tried to take QS node: " + [Link]);
throw "This node can only be accepted by head of QA."
}
}
}
22 Programming for ELO

General Java Client

[20.0] Enable script debugger

The Java Client comes with its own script debugger, specifically the Rhino engine. User with
sufficient permissions can enable the debugger with <STRG><ALT>-D. Starting with version 20, scripts
are compiled and stored in a local cache to enhance performance and make the program start
faster. However, this means that the debugger can no longer access the scripts and therefore
cannot be enabled.

You can easily change this setting in the configuration by unchecking the option "Compile and save
scripts" under Configuration > Technical presets.

Fig.: Compile and save scripts

Scripts in multiple languages

Scripts often contain display text that needs to be translated into various languages when the script
is used in several countries. In theory, one could copy the script several times and swap out the
texts. However, this is error-prone for translation: how should the translator know for sure what
needs to be translated and what doesn't? – as well as being difficult to maintain and update. If
there is an error in the script, all versions need to be corrected and none may be forgotten.

To avoid these problems, the ELO Java Client contains a mechanism that separates display texts in
a script from functional program code. The texts are placed in a separate text file. The name of the
file is the same as the script file name, with the prefix text_ and ending with a two-letter country
code (_DE, _EN, etc.).
23 Programming for ELO

This means that the ToggleMailReport script has an additional text file named
"text_ToggleMailReport_EN" for the English-language text.

ScriptButtonName=E-mail notification
Title=Settings - E-mail notification

A utils function is used in the script to access the text.

var title = getText("Title");


var buttonName = getText("ScriptButtonName");


function getText(name) {
return [Link]("ToggleMailReport", name);
}

Include external JAR files in scripts

The ELO Java Client is able to include external JAR files in scripts. This is useful when a desired
function is already available as Java source code or as a complete JAR module, and it needs to be
called from a script.

Simply place the JAR file in the Java Client Scripting Base folder. It is then deployed automatically
with the client and is available for use in scripts. A simple demo program from the Internet
([Link]) is used here as an example.

function getScriptButton100Name() {
return "Robo Miner";
}

function getScriptButtonPositions() {
return "100,home,view";
}

function eloScriptButton100Start() {
[Link]( [] );
}
24 Programming for ELO

Fig.: RoboMiner test script and demo JAR archive in the script folder

Information

This is also a good example of the associated risks: When a user closes the RoboMiner
window, the program executes a [Link]() and therefore closes not only itself, but the
entire Java Client. All errors in the included Java program strike back hard on the client.

Call external programs

You can easily call an external program or a browser display from a Java Client script. This occurs
through the Java Runtime in its Desktop classes.

Call a program

function execute(command) {
[Link]("Execute command: " + command);
var p = [Link]().exec(command);
[Link]();
[Link]("Done.");
}

Alternative for a call similar to ShellExecute

[Link](file);

Open the browser

function searchCustomer() {
var name = [Link](0);
var codec = new URLCodec();
var encodedName = [Link](name);
var uri = new URI("[Link] + encodedName)
[Link](uri);
}
25 Programming for ELO

In some cases, you may need to wait for a running program to finish. You can use the waitFor()
method to do this:

function eloScriptButton100Start(){
var command = [ "C:\\Program Files (x86)\\Notepad++\\Notepad++.exe", "d:\\temp\\[Link]"
var runtime = new [Link]();
var process = [Link](command);
[Link]();
[Link]("Test", "Notepad++ was closed");
}

Insert annotations in the Intray

If you want to allow users to insert annotations in the Intray (margin notes or otherwise) using a
script, you must take care of a few potential trouble spots caused by the viewer for the current
document using a cache that is written to frequently. This can easily lead to changes being undone
if you are not careful.

var item = [Link];


if (item != null) {
// Delete the view so the new notes aren't overwritten
[Link]();

// Create marker
var note = ixc.createNote2(0, NoteC.TYPE_ANNOTATION_MARKER, "");
[Link] = 100;
[Link] = 50;
[Link] = 200;
[Link] = 50;
[Link] = 0xc1ffc2;

[Link](note);

[Link](item);
}

The "trick" in this approach is to use clearSelection() on the current display before insertion,
which publishes all changes the user has made to the ELO Indexserver. This means there is no
longer a local cache that could use older data to overwrite the new input.
26 Programming for ELO

If you call selectDocument() at the end, the display is restored and the new annotation is shown
right away.

CommandLink dialog box

If a user needs to decide between a series of possible options, ELO can provide the user a
command link dialog box similar to a dialog in Windows. This selection is easier to use and quicker
than an OK/Cancel dialog box with a selection list. The dialog can only be used meaningfully,
however, when the list of options is fairly short.

It is fairly easy to use. You only have to provide a list of the options, along with an additional list
with descriptions of the options, then call the dialog box.

function eloScriptButton100Start(){
var result = simpleSelection();
[Link]("Selected option: " + result);
}

function simpleSelection() {
var choices =
["Complete list",
"Only the first column",
"And a third option"];

var descriptions =
["This happens when you choose the 'Complete list'",
"And this occurs for 'Only the first column'",
"This occurs when you choose the third option"];

var result = [Link]("Create range list",


"Choose the kind of range list you want to create",
"", CONSTANTS.DIALOG_ICON.QUESTION,
choices, descriptions, []);

return result;
}
27 Programming for ELO

Fig.: CommandLink dialog box with three options

CommandLink dialog box from a folder selection

A fairly frequent scenario is that users need to choose between different work areas, which present
themselves as an ELO folder list. This short utility function provides an easy way to generate a
CommandLink dialog box from a list of folders. You only need to enter the parent entry, the title,
and an editing note, with the utility function showing the corresponding selection. When the user
selects a repository entry, it is returned as the result.

function eloScriptButton100Start(){
var title = "Select the desired work area"
var message = "You can edit various areas. You can choose the appropriate area here.";
var parent = [Link];
var result = childSelection(title, message, parent);
[Link]("Selected option: " + result);
}
28 Programming for ELO

Fig.: CommandLink dialog box with folder selection

The required utility function is kept equally simple:

function childSelection(title, message, parent) {


var optionNames = new Array();
var optionTexts = new Array();
var optionElements = new Array();

var items = [Link];


while ([Link]()) {
var item = [Link]();

[Link]([Link]);

var desc = [Link];


if (desc && [Link]() && [Link]("<html>")) {
[Link]([Link]);
} else {
[Link]("");
}

[Link](item);

var result = [Link]( title, message, "",


CONSTANTS.DIALOG_ICON.QUESTION, optionNames, optionTexts, []);
29 Programming for ELO

if (result > 0) {
return optionElements[result - 1];
} else {
return null;
}
}

Link annotations

The ELO Java Client can jump from an annotation to another linked annotation. This allows you to
create hyperlinks between different documents or even within a document. You can create a link by
first selecting the target annotation, then calling "Remember position" from the context menu.

Fig.: Remember position - the target of the link

Afterwards, you can go to the starting point of the link and paste the remembered position to a text
note there.
30 Programming for ELO

Fig.: Paste position - define starting point

By default, the document name is entered into the text note. However, you can change the text via
the script as well.

The user can then jump to the linked target note via the context menu of the text note at any time.
31 Programming for ELO

Fig.: Go to linked target

Two script events are available for editing notes. First there is eloPrepareNoteLink — this event is
called when the user has selected the target before the actual link is generated. You can also
change the text that is entered into the text note later on here.

function eloPrepareNoteLink(linkPosition, sord, note) {


var page = "Page " + [Link];
[Link] = page + "\n" + [Link];
}

Fig.: Expanded text for the link

The eloInsertNoteLink event is also available. This event is called when the text is entered into the
text note. You can also make the displayed text dependent on the current starting position.

function eloInsertNoteLink( linkPosition, sordId, note ) {


var page = "From page " + [Link] + " to ";
[Link] = page + [Link];
}
32 Programming for ELO

Fig.: Adjusting the start and target points

Use the http automation interface

The ELO Java Client has an interface that can be addressed using http calls. To achieve this, a small
web server is integrated into the ELO Java Client that accepts these calls and passes them on to the
scripting interface.

Preparation:

The http interface is normally disabled. It must be enabled using profileopts entries. Two entries
are required to do this: a Boolean value that turns it on, and an integer value with the port number
at which the client waits for commands.

The two following SQL statements turn on the scripting interface for user 0 (Administrator). Of
course, you could use any other user or option group number, as well as the global (everyone) ID.

insert into profileopts (userid, optkey, optvalue) values (0, '[Link]', 1


insert into profileopts (userid, optkey, optvalue) values (0, '[Link]', 8990

After making this change, restart the ELO Java Client, as the options are only read when the
program opens.

Defining a script

To prevent the http interface from allowing arbitrary scripts to run on it, it is restricted to only allow
scripts to be activated that start with the name http. Our example uses the httpDoSomething
function. A script is created with this method in the ELO Java Client.

The method expects two parameters - which are then simply output again in a feedback message.
The return value is returned to the caller:
33 Programming for ELO

function httpDoSomething(param1, param2) {


[Link](param1 + " : " + param2);
return "done";
}

Calling the script

The caller must now send a simple GET call to localhost and send it over the configured port
number. This is possible in many applications. This book uses the browser for simplicity:

<a href="[Link]

The call triggers the start of the httpDoSomething script in the client, then writes "Something :
More" on the screen. It shows the return value afterwards.

OK|done

The OK| signals that the call was successful, and done is the return value of the script. If you only
want to call a command and do not want to leave the page, you can also send the command using
an AJAX call. To do so, define this function in the browser form:

function callScript(param1, param2) {


var url = "[Link] + param1 + "¶m2=" + param2
var xmlhttp=new XMLHttpRequest();
[Link]("GET", url, true);
[Link]();
}

The call now looks like this:

<a onclick="callScript('Something', 'More')"> `

If you want to do more than send a command, and instead require a result, you must fill the
[Link] function accordingly.
34 Programming for ELO

The major advantage of the http call over the COM interface is that this call does not require local
installation. For this reason, it also works on a WebStart call or when started through the batch file
(which does not instantiate a COM object). Additionally, this call also works on Mac OS X and Linux,
not just on Windows.

Send messages between windows

A fairly rare requirement, but one that comes up every now and then: When a user has two ELO
windows open, each window has its own set of scripts. A management script in window 1 doesn't
know anything about the status of window 2.

The ELO Java Client offers a function for sending information between these windows. Simply call
the [Link] method. In the first parameter, define the window you want to send
the message to (or all). Enter the message name to the second parameter, then the actual
message to the third.

If a script needs to receive these broadcast messages, it has to implement the eloBroadcast
function.

function eloScriptButton103Start() {
[Link](CONSTANTS.DESTINATION_WORKSPACE.ALL, "Neu", "My Message");
}

function eloBroadcast(from, tag, message) {


var msg = [Link] + " from " + from + " [" + tag + "] : " + message
[Link](msg);
}

Call a script using the COM interface

Many programs are available for Windows that can remote control other applications using the
COM interface. The ELO Java Client also provides a simple COM interface with a number of
functions. In practice, though, it is best to restrict it to a single function: RunScriptFunction.

Create a script in the ELO Java Client that implements the desired function. The function name
must start with eloCom. For security purposes, other scripts cannot be called from the external COM
interface.

function eloComMyScript(param1, param2, param3) {


var content = [
"<h3>MyScript</h3>",
"Param1 = " + param1,
"Param2 = " + param2,
35 Programming for ELO

"Param3 = " + param3


];

[Link]("ELO", [Link]("<br>"));
return 17
}

The script is called using the COM interface, such as with this small VBS program:

Set ELO = CreateObject("[Link]")


Result = [Link]("eloComMyScript", "Test1¶Test2¶Test3")
MsgBox Result

The RunScriptFunctionEx function is available if you want to return a string instead of an integer.

function eloComMyScript(param1, param2, param3) {


var content = [
"<h3>MyScript</h3>",
"Param1 = " + param1,
"Param2 = " + param2,
"Param3 = " + param3
];

[Link]("ELO", [Link]("<br>"));
return "My Return Value"
}

Set ELO = CreateObject("[Link]")


Result = [Link]("eloComMyScript", "Test1¶Test2¶Test3")
MsgBox Result

[10.2] OLE automation emulation

The ELO Java Client has an emulation interface for the ELO Window Client COM interface. This
emulation covers approximately 70% of the functions. Due to the client server architecture, some
parts cannot be emulated – e.g. the events when forwarding workflows. Since these run on the ELO
36 Programming for ELO

Indexserver, and not in the ELO Java Client, this results in an unavoidable difference to the ELO
Windows Client.

Some functions are available, but differ from the original in the specifics. You will notice differences
here when using undocumented "secondary effects" in particular.

Other functions have a different timing. The order of events may deviate significantly due to the
entirely different processing of commands. One example is selecting an entry. In the Windows
Client, the content of the OLE object is updated every time you click an entry. This is not possible in
the Java Client since many actions run asynchronously. In some cases, this update would come too
late, or it would reflect a state that is no longer active. An additional command is available for this
reason: UpdateSelection(). You have to call this command when you want to read the properties of
the currently selected entry.

Copy a file in the Intray

One customer with the Windows Client had gotten used to keeping a sort of "template file" in the
Intray, which they then copied as needed with ALT + drag-and-drop. Besides the fact that this is
rather questionable and could be better covered by the "Document from template" function –
creating a copy of the currently selected Intray documents is quite simple with scripting:

function getScriptButton100Name() {
return "Copy";
}

function getScriptButtonPositions() {
return "100,intray,insert";
}

function eloScriptButton100Start(){
var count = 0;
var items = [Link];
while ([Link]()) {
var item = [Link]();
var source = [Link];
[Link](source);
count++;
}
[Link](count + " Files copied.");
}
37 Programming for ELO

Version-dependent script functions

New features are added to the Java Client scripting interface regularly. If you are unsure of whether
a customer is using the newest version of the client and a function is not absolutely necessary, you
can make its use dependent on the client version number.

var version = [Link];


if (version >= "9.03.000") {
[Link]("New function", "The new function is running");
}

Integrate external viewers

The ELO Java Client does not allow you to integrate external viewer programs directly. This was
avoided to prevent unstable viewers from crashing the client. If you have a document format that
the Java Client doesn't support, you have two possibilities:

1. The easy way:

If your browser has a plug-in available for this format, install it and select "Browser display" in the
ELO Java Client.

1. The not-so-easy way:

Version 9.2 and higher allow external viewers to be integrated. They run as EXE files in their own
process, and if they crash, they have no effect on the ELO client. At the moment, the configuration
must be performed manually, but we plan on making this easier in the future. Regardless, it works.

A) Create an entry in the profileopts table with the key "[Link]. <my extension>". In the
optvalue field, enter the call for your EXE file with the conversion program from <my extension> to
PDF. The program receives two parameters that you must enter as placeholders, with %1 for the
original file, and %2 for the target PDF file name.

[Link] "C:\Program Files (x86)\Any DWG to PDF Converter Pro\[Link]" /InFile "%1" /OutFile
"%2" /hide

B) Change the setting for <my extension> in the Java Client to "PDF preview". Now, whenever a file
is loaded with <my extension>, the Java Client calls the configured EXE file before showing the
preview. This converts the file to a PDF document that the Java Client then displays.

File selection dialog box

Sometimes in scripts, you need a local file that the user has to select. To do this, the workspace
object contains a method for accessing the file selection dialog box. It is fairly easy to use. For
parameters, you enter the window title, whether the dialog box is used to open/read or save/write
files, whether folders and files, or only files can be selected, and the default path.
38 Programming for ELO

The script calls the dialog box and saves the path of the first file as the default path for subsequent
calls. All file names are then listed and shown.

var rememberPath = null;

function eloScriptButton100Start(){
var files = [Link]("Select file", false, true, rememberPath);
if (files && ([Link] > 0)) {
rememberPath = files[0].path;

var msg = "Selected files:<br>";


for (var i = 0; i < [Link]; i++) {
msg += (i + 1) + ": " + files[i].path + "<br>";
}

[Link]("ELO Files", msg);


}
}

Fig.: Notification dialog box with file name

Evaluate EML or MSG e-mails

In the Java Client, it is easy to access an e-mail in EML or MSG format via scripts. Analyzing the
data of these files, however, is relatively time-consuming due to their internal structure. For this
reason, the utils contain a method named readMail(file). This returns a MailItem object with a
range of information from the file.

function eloScriptButton100Start(){
var item = [Link];
39 Programming for ELO

var file = [Link];


var mailItem = [Link](file);
[Link]("Subject", [Link]);
}

This object is also suitable for reading attachments, body text, sender and recipient, and the e-mail
date.

Interval-controlled actions

If you want to perform an action regularly, you can start a thread in Java and let it sleep for a
certain time between each activation. However, a separate thread is problematic when the actions
cause changes to the GUI. Swing requires these changes to only be executed from the AWT thread –
even if every other call causes an error.

The Swing timer is available for simple interval-controlled actions. It regularly calls an event
routine from the AWT thread, but only if nothing is currently happening (which is the normal course
of events in a properly programmed GUI). The event function may only run for a short time, as it
would otherwise block the GUI. If this requirement is fulfilled, you can cover this case easily:

function eloWorkspaceStarted() {
globalTimer = new [Link](2000, function (evt) {
[Link](new Date());
} );

[Link]();
}

function eloWorkspaceClosing() {
[Link]("Stop timer");
[Link]();
}

This simple example only uses the event function to set the FeedbackMessage. The timer is started
when the workspace is shown (eloWorkspaceStarted), and stops when the workspace closes
(eloWorkspaceClosing).

Start a background process

Sometimes, you may have scripted operations that block the client until they complete. This is
pesky for the user, especially because the ELO Java Client places many other long-running actions
in the background. By using the [Link] method, you can run your
40 Programming for ELO

scripts in the background. The process is also entered into the process list in the Java Client,
allowing the user to recognize when it is complete.

var process;

function eloScriptButton100Start(){
process = [Link]("sinus", "longRunningTask");
}

function longRunningTask() {
try {
var sum = 0.0;
for (var i = 0; i < 100; i++) {
for (var j = 0; j < 1000000; j++) {
sum += [Link](i * j);
}

[Link](i + " percent complete");


}
} finally {
[Link]();
}
}

Fig.: Status display for background processes

Please remember that the process needs to remove itself from the list of active process when it is
complete. To ensure this, the script function contains a try – finally that calls [Link]()
in the finally block. This part is also executed in the case of an exception, ensuring that no finished
processes remain in the list.
41 Programming for ELO

With the [Link] method, you can regularly update the status display, notifying the user
of the progress. Make sure that the status does not update too often, as the client would be more
occupied with the display than with moving the process forward. For this reason, the function
should not be called more frequently than every five minutes.

Information

Warning: This function is designed for background processes. You can more or less only use
the direct Indexserver interface. All commands that have a direct effect on the user
interface are not permitted and can cause great confusion for the user.

Stop a background process before it completes

If a process takes a very long time, you will want to give the user the ability to cancel it. Since you
cannot simply cancel a running thread externally, the script must take care of it itself. The script
regularly checks whether the "stopped" flag is set and, if so, it cancels itself.

var process;

function eloScriptButton100Start(){
process = [Link]("sinus", "longRunningTask");
}

function longRunningTask() {
try {
var sum = 0.0;
for (var i = 0; i < 100; i++) {
for (var j = 0; j < 1000000; j++) {
sum += [Link](i * j);
}
if ([Link]()) {
break;
}

[Link]( i + " percent complete")


}
} finally {
[Link]();
}
}
42 Programming for ELO

Fig.: Canceling a process before completion

Process with log entries

A process can keep a log in order for the user to more easily recognize what occurred during a
background process. New entries in the log can be added by using the [Link]
method.

var process;

function eloScriptButton100Start() {
process = [Link]("sinus", "longRunningTask");
}

function longRunningTask() {
try {
var sum = 0.0;
for (var i = 1; i <= 100; i++) {
for (var j = 0; j < 1000000; j++) {
sum += [Link](i * j);
}
if ([Link]()) {
break;
}
var protMsg = "Sum of Sinus at " + i + "% is " + sum;
[Link]( CONSTANTS.PROTOCOL_LEVEL.INFO, protMsg);
[Link]( i + " percent complete");
}
} finally {
[Link]();
}
43 Programming for ELO

Logging levels of INFO, WARN, and ERROR are available. The user can filter the log to show each
level in order to find specific entries more quickly.

Fig.: Log from script execution

Background process with progress bar

A background process can also show and update a progress bar in a scripted dialog box. The
GridPanel provides the addProgressBar method for this. The background process can regularly call
the [Link] method to update the display.

This example generates a dialog box with an input field for the number of calculation loops and a
button to start calculation. The "startLongRunningTask" action routine for the button sets the dialog
box status to "Calculation active", shows the ProgressBar, and starts the background process.

The background process regularly updates the ProgressBar and the results display. Once the
calculation is complete, the ProgressBar is hidden again and the dialog status set to neutral.

var process;
var dialog;
var progressBar;
var cntField;
var resultField;

function eloScriptButton100Start(){
dlgWithProgressBar();
}
44 Programming for ELO

function dlgWithProgressBar() {
dialog = [Link]( "Sinus III", 4, 7 );
var panel = [Link];

[Link]( 1, 1, 1, "Loops" );
cntField = [Link]( 2, 1, 2 );
[Link]( 4, 1, 1, "Start calculation", "startLongRunningTask" );

progressBar = [Link]( 2, 2, 2 );
[Link] = false;

[Link]( 1, 4, 1, "Result" );
resultField = [Link]( 2, 4, 2);

[Link]( "Start calculation" );


[Link]( "", "stopLongRunningTask" );
}

function startLongRunningTask() {
[Link]( "Calculation active" );
[Link] = true;
process = [Link]("Sinus III", "longRunningTask");
}

function stopLongRunningTask() {
if (process) {
[Link]();
}
}

function longRunningTask() {
try {
var loopCount = parseInt([Link], 10);

var sum = 0.0;


for (var i = 1; i <= 100; i++) {
for (var j = 0; j < loopCount; j++) {
sum += [Link]( i * j );
}
if ([Link]()) {
break;
}

[Link] = sum;
[Link]( i );
45 Programming for ELO

[Link]( i + " percent complete");


}
} finally {
[Link]();
}

[Link] = false;
[Link]( "" );
}

Fig.: Active calculation with progress display

Configuration for scripts

Larger scripts often require configuration data, usually about metadata forms, repository paths, or
workflows used. At worst, the data is spread throughout the script – wherever it is currently needed.
It is better to enter the data as constants at the beginning of the script. It's even better to place a
single configuration object at the beginning of the script so there are fewer entries in the global
namespace (which, with large scripts from various sources, can lead to name conflicts). All
methods have the same problem – that configuration data related to the current installation is
mixed in with general script commands in a single source. Whenever there is a script update, you
must make sure to update the configuration data.

For this reason, the ELO Java Client has a mechanism that allows configuration data to be stored
completely externally (such as in its own folder or document:
¶Administration¶Config¶MyScriptConfig). The configuration data in the MyScriptConfig document is
stored in JSON format and read into the script at runtime by using the @config command.

// JavaScript functions
46 Programming for ELO

//@config myObject /Administration/Config/Test Config



var objId = myObject.prop1;

MyScriptConfig contains the following text:

{
"prop1" : 4711,
"prop2" : "Ein Test"
}

The script looks as follows at runtime in the debugger:

var myObject = [Link](' { \"prop1\" : 4711, \"prop2\" : \"A test\" } ');

The @config command contains the name of the configuration variable in the first parameter, and
the path to the JSON document in the repository as the second parameter. The goal is to enable all
ELO script-capable programs to understand and embed the configuration (ELOas, ELO Indexserver,
ELO Java Client, etc.).

Copy HTML text to the Windows Clipboard

It is relatively easy to copy normal text to the clipboard. It is a little more complicated if you want
to insert HTML text. Unfortunately, there are differences here between Windows and the rest of the
world.

The Windows HTML clipboard format is relatively complicated. However, a utility function is
available in the ELO Java Client interface that can help with this. All you have to do is generate the
HTML code and a normal text (if the target doesn't understand HTML) and call the addToClipboard
function.

function eloScriptButton100Start(){
var sord = [Link];
var guid = [Link];
var name = [Link]([Link]);
var htmlMsg = '<a href="elodms://' + guid + '">' + name + '</a>';
var textMsg = 'elodms://' + guid;
[Link](htmlMsg, textMsg);
47 Programming for ELO

Fig.: E-mail with elodms link

Determine the URL encoding

If you want to embed text in a URL, there are many special characters that are not permitted in the
URL and must be specially encoded (examples include ?, &, /, and spaces).

The following script queries text and shows the URL-encoded form, as well as entering the text into
the Windows clipboard. In this way, you can insert the text into a link in an e-mail message or a
Microsoft Word document.

function eloScriptButton100Start() {
var msg = "Enter the text to be embedded in the URL";
var url = [Link]("URL Encode", msg, "", -1, -1, false, -1);
if (url) {
var encoded = new [Link](url, "UTF-8");

[Link]("Encoded", encoded);
var clipboard = [Link]().getSystemClipboard()
[Link](new [Link](encoded), null
}
}
48 Programming for ELO

Fig.: Entering the source text

Fig.: Output in encoded format

[10] Display the second page of a document automatically

Sometimes you will have multi-page documents with a cover page. The cover page contains
important information, but usually the document contents are more interesting to the user. You can
use a short script to automatically switch to the second page when displaying the document.

Since the display runs asynchronously, the task is not really a simple matter. However, the client
has a cache that contains information about which page of a document was most recently viewed.
This information is there so that a user who is currently in the middle of a document can briefly
switch to another document, then return to the same page in the original document. You can also
use the cache to perform this task.
49 Programming for ELO

The script is inserted into the event when a document is displayed, and checks if a specific
document type is being displayed – based on the metadata form in this example. If so, the second
page is automatically entered as the document ID in the cache. When the document is actually
displayed later on, the viewer sees this information and switches to the desired page.

function eloPreviewAvailable(mode, item) {


if (item && ([Link] == "ZInvoice")) {
[Link]([Link], 3, true);
}
}

Information

The third parameter in setPageCachePage determines whether the entry is always applied
(false) or if it is only applied if another entry does not exist ( true). In its current form,
therefore, a document that the user has already looked through will not be returned to the
second page. If you always want to switch to the second page, set the parameter to false.

[10] Use a script to generate sticky notes in a document

Applying a sticky note to a document using the Indexserver API is really quite simple, and you can
find examples of such in the IX documentation. The main problem in the Java Client script is that
the client viewer is not automatically made aware of the new sticky note. A refresh() is also not
enough, since this action generally retrieves information from the client cache, which is not aware
of direct IX commands. Also, you must ensure that any sticky notes the user hasn't saved yet are
saved first. This occurs with the [Link]() call, which saves the changes from the
current display and closes the document.

Starting with version 10, [Link] additionally deletes the current entry from the cache
before restoring the display. This ensures that the document is reloaded from the Indexserver,
thereby also containing the newly created sticky note. The version in the cache would not show the
new note.

function eloHomeStart() {
var item = [Link];

var note = ixc.createNote2([Link], NoteC.TYPE_ANNOTATION_NOTE_WITHFONT, "");


[Link] = "My note\n\nNote message with font";
[Link] = 100;
[Link] = 100;
[Link] = 800;
[Link] = 300;
50 Programming for ELO

[Link] = 0xffa040;
[Link] = "Arial";

//[Link]();
[Link]();

[Link]([Link], [note], [Link], [Link]);

//[Link]();
//[Link]();
[Link]();

return -1;
}

For older versions of ELO, you can remove the lines for [Link]() and
[Link]() and instead insert the lines for [Link](),
[Link](), and [Link](), which are commented out. This
approach works as well, but it causes a lot more reorganization of the display.

[10] Display Wang annotations

Before ELO added a proprietary function to add annotations to a document, we supported Wang
annotations (around the year 2000). After Microsoft ended its support for them, we also stopped
supporting them. However, there are still users who saved documents to ELO with Wang
annotations. For these users to continue to view the documents, we have added the Windows
Client TIFF viewer to the Java Client setup as a command line program. The viewer is stored in the
Java Client directory and can be started with a simple system call.

This sample script requires a TIFF document containing Wang annotations to be selected. When the
user then clicks the script button, the (external) viewer is started.

function getScriptButton100Name() {
return "ELOmmView";
}

function getScriptButtonPositions() {
return "100,home,view";
}

function eloScriptButton100Start() {
try {
51 Programming for ELO

var fileName = [Link];


var cmd = ["C:\\Program Files (x86)\\ELO Java Client\\[Link]", fileName];
var runtime = new [Link]();
[Link](cmd);
} catch (e) {
[Link]("Display error " + e);
}
}

To inform the user that the currently displayed TIFF document contains Wang annotations, the
warning message "Document contains unsupported TIFF annotations" is shown for these types of
documents.

Fig.: Warning message for a document with Wang annotations

[10] Generate file codes

You can find a complete script on the ELO forum with a description of how to implement a file code
generator in the ELO Java Client.

The script gives you wide-ranging abilities to configure the file code using index field information,
special input and selection fields, fixed text segments, and automatically incrementing counters.

parts : [
{ name : "OrgUnit", type : "edit", width : 6, regex : "^[1-3]+$", errmsg : "Only numbers from 1 to
{ name : "Country", type : "combo", width : 10, data : ["Germany", "England", "France"]
{ name : "Supervision", type : "check", width : 9, textTrue : "V/", textFalse : "" }
],
52 Programming for ELO

Fig.: metadata form before generating the file code

When the user has entered the required information to generate the file code and clicked
"Generate", the input fields are hidden and show the file code instead. This index field should be
declared in the metadata form definition as a read-only field, since it is generally undesirable to
change an existing file code after it has been created.

Fig.: metadata display with file code

[10.1] Show all EXIF tags for a TIFF/JPEG file

You can automatically import EXIF tag data from TIFF or JPEG files to an index field using the
metadata form definition "External data". You need to know which tag names are available to be
able to do this. The following script shows you which tags (including the name and value) are
available for a selected document in the Intray.

importPackage([Link]);
53 Programming for ELO

importPackage([Link]);

function getScriptButton727Name() {
return "Exif";
}

function getScriptButtonPositions() {
return "727,intray,indexing";
}

function eloScriptButton727Start(){
var item = [Link];
var file = new File([Link]);

var keys = [Link](file);

var buf = [];


[Link]("<html><table>");

while ([Link]()) {
var item = [Link]();
var value = [Link](file, item);
[Link]("<tr><td>" + item + "</td><td>" + value + "</td>");
}

[Link]("</table></html>")
[Link]("Exif", [Link]("\r\n") );
}

Besides the normal EXIF tags, you can also choose from a few pseudo-tags:

FILENAME File name without extension


FILENAMEEXT File name with extension
FILEDATE File date
FILEDATEISO File date in ELO ISO format
FILESIZE File size in bytes
FILESIZEKB File size in kbytes (/1024), rounded up
FILESIZEMB File size in megabytes (/(1024*1024), rounded up

[10.1] Prevent the user from accepting a workflow (Java Client)

When you set up a substitution, the substitute can see all the tasks of the person they are covering
for and accept and process these tasks if required. This is not applicable for every type of workflow.
54 Programming for ELO

For example, one customer used workflows to manage training courses. It makes no sense for a
substitute to accept tasks like these on behalf of another user. It is possible to query the
AcceptWorkflow function in the Java Client and check if this type of workflow is included in the list of
selected entries. If so, you can cancel the function and issue a message.

function eloAcceptWorkflowStart() {
var items = [Link];
while ([Link]()) {
var item = [Link]();
if ([Link]()) {
var wfNode = [Link];
if ([Link] == "Training (personal)") {
[Link]("You have selected a workflow that you cannot accept: "
return -1;
}
}
}
}

[10.1] Handheld barcode scanners

There are a variety of inexpensive handheld barcode scanners on the market. These are easy to
install and do not require additional drivers due to the fact that they simulate a keyboard. The
barcode is transmitted as a sequence of keystrokes, so it is easy to install and implement.

However, the big disadvantage of this method is that it is impossible to differentiate keystrokes
entered from the barcode scanner or from the keyboard. In addition, the data appears in the
window that happens to be open.

If you can live with these drawbacks, the Java Client does offer a tool that assists you in barcode
recognition: You can register a script event, which is triggered when the barcode reader has
transmitted new data. But how can the Java Client differentiate between scanned data or typed
input? There is in fact a way: Most barcode readers transmit data much faster than a person would
be able to type. The Java Client monitors the keyboard interface so that if data is transmitted at
high speed, it is forwarded to the script event for evaluation.

function eloBarcodeRecognized(barcode) {
[Link](barcode);
}
55 Programming for ELO

However, it is important to remember that this only works if the Java Client is the active window
(otherwise the data cannot be transmitted to the Java Client). If the window does not expect a
keyboard input, it will be ignored. If the focus is on an input field, the barcode will be entered there.

As this cannot be guaranteed to work every time, you need to enable this option in the
configuration.

Fig.: Handheld barcode scanner setting

This function can be used to directly perform a search, for example. If you have a barcode printed
on a delivery note, for example, and there are documents related to this barcode in ELO (e.g. the
purchase order), then you can trigger a search in ELO using the handheld scanner. Similar to the
Click&Find example, this triggers an automatic (full text) search, only this time with the barcode
data transmitted in the script event. This returns all the documents related to the delivery note.

function eloBarcodeRecognized(barcode) {
[Link](barcode);

var view = [Link]("Barcode");


if (!view) {
view = [Link]("Barcode");
}

var fbf = [Link](barcode);


56 Programming for ELO

var fi = [Link]("");
[Link] = fbf;
[Link](fi, 1000, true);
}

[10.1] Automatic check-in via the script interface

The Java Client has had a function for automatically checking in documents generated by the ELO
template management feature since version 10.0. As of version 10.1, this function can also be
called from the script interface. If you have a CheckoutDocument object, you can use the method
markForAutoCheckin to mark it for automatic check-in. Once the file is no longer locked, since the
associated application has been closed, the document is automatically checked in.

[10.1] Set the zoom level with a script

If you are working with different types of documents and require different zoom levels, it can be
annoying to have to constantly change the zoom settings when switching between documents. You
now have the option to modify the zoom level using a script.

The following script assumes that all documents that start with an A are to be fit to the width of the
page (FitToWidth). All other documents are to be shown in full (FitToPage).

function eloPreviewAvailable() {
var item = [Link];
if (item) {
[Link]([Link]);
if ([Link](0, 1) == "A") {
[Link]();
} else {
[Link]();
}
}
}

[10.1] Example of a simple file import

This script was requested by a customer who had a Windows directory containing drawings that the
customer wanted to be able to import into the ELO directory with a single click. If there was already
a drawing in this directory with the same name as the file being imported, the customer wanted it
to be moved to another directory called "invalid". As the script contains a number of basic
programming features (handling external files, searching for, creating, or moving entries in ELO,
creating your own button), I have included it in this guide.
57 Programming for ELO

function getScriptButton100Name() {
return "Import starten";
}

function eloScriptButton100Start() {
[Link]();
}

function getScriptButtonPositions() {
return "100,home,edit";
}

var dirImport = {
sourcePath: "D:\\temp\\import",
eloPath: "/Import folder/Drawings",
invalidPath: "/Import folder/Drawings-invalid",
documentMaskName: "Basic entry",

start: function () {
[Link]();
[Link]();
},

// Determines the folders in the repository for current


// and invalid documents.
getParents: function () {
[Link] = [Link]( [Link] );
[Link] = [Link]( [Link] );
},

// Runs through all files in the Windows directory


processAllFiles() {
var sourceDir = new [Link]( [Link] );

// Lists all files in the import directory


var files = [Link]();
for (var i = 0; i < [Link]; i++) {
var file = files[i];
// Ignore child directories
if ([Link]()) {
[Link]( file );
}
}
},
58 Programming for ELO

// edits a file, checks whether it needs to be moved


// and imports the new file..
processOneFile: function ( file ) {
var name = [Link];
[Link](name);

// importiert die Datei


var sord = [Link]( [Link] );
[Link] = name;
[Link]( sord, [Link] );

// if the import was successful, the file is deleted.


file["delete"]();
},

// Moves the old file - if it exists..


moveInvalidFile: function ( name ) {
var oldpath = [Link] + "/" + name;
try {
// checks whether there is an entry with that name
var oldDocument = [Link]( oldpath );
} catch (e) {
[Link]("no old document exists, nothing to move.");
return;
}

// if there is an entry, it will be moved


[Link]( [Link], true );
}
}

Information

The spelling convention for deleting the file might seem strange:

file["delete"]();

This is because the word delete is a reserved word in JavaScript and cannot be used for
method names. However, since this is the name used for the method in the Java object, you
can only access it indirectly using the spelling convention for the array property.
59 Programming for ELO

[10.2] Generate version numbers with a script

The Java Client generates new version numbers by incrementing the number of the old working
version. If the version number contains one or more dots, then the number after the last dot
increments, i.e. working version 10 becomes 11, whereas version 10.1 becomes 10.2. This is a
useful method, but some customers may want to increment versions in a different way. There is a
new script event that is called before the user sees the version dialog box: eloBuildVersionNumber.

The script event has one parameter: a VersionInfo object. This contains information about the type
of call, the version text of the working version and the automatically generated version text as well
the current metadata object — if it exists. The script can create the new version text according to
own rules and enter it to the Info object. This is then used as the value that is suggested in the
version dialog box.

function eloBuildVersionNumber( info ) {


[Link]([Link]);
[Link]([Link]);
[Link]([Link]);
if ([Link]) {
[Link]([Link]);
[Link] = [Link];
}
}

[10.2] Switch to the PDF viewer with a script

One advantage of the integrated PDF viewer is that it can show annotations right on the document.
In addition, it provides access to the document contents via click OCR. The browser-integrated
Acrobat Reader does not offer these options. However, it is often faster in displaying many
document contents. There are also documents that the integrated viewer is unable to display in
part or at all. Here, the user can switch between the two viewers.

Sometimes, a script can also be used to detect which viewer will return the best result for this
entry. A new function setPdfPreference is now available for this in the PreviewAdapter. Acrobat
Reader may be the better option for displaying complex "product catalog"-type documents, while
ELO viewer with click OCR may be better for invoices. The metadata form number for the shown
entry can be helpful here.
60 Programming for ELO

function eloPreviewAvailable(mode, item) {


var useBrowser = [Link] == 0;
[Link](useBrowser);
}

[10.2] Change the file name for drag-and-drop operations

Depending on the setting, the Java Client either uses the internal name from the cache or an
automatically generated name based on the short name for drag-and-drop operations.

However, it is also possible to generate a custom name using a script. One possibility would be to
add the ELO object ID to the name to be able to associate the external file with the entry at a later
point. Or, you could keep an account of external files by adding a counter value. The advantage of
this is that you can extract the same file to a directory multiple times.

function eloGenerateExternalFilename(sord, doc) {


var counterValue = [Link]("DragAndDropCounter", 1000);
return "ELO_" + counterValue + "_" + [Link];
}

Fig.: File names created with a script

[10.2] Change the checkout behavior

If you mark and check out many files, many program instances would open for the documents you
have checked out. Sometimes, you don't want to edit the files you have checked out, but just lock
them at first. This behavior is relatively easy to achieve using a script.

The script checks how many entries are marked. If more than 100 entries are selected, an error
message appears. If only one entry is selected, the normal checkout function is executed and the
application is called.

When between 2 and 100 entries are selected, the documents are checked out, but the application
is not called. The Java Client now switches to the checkout view and shows the new documents.
61 Programming for ELO

function eloCheckOutStart() {
var selectionCount = [Link];

if (selectionCount > 100) {


[Link](CONSTANTS.FEEDBACK_TYPE.WARNING, "A maximum of 100 documents can be ch
return -1;
}

if (selectionCount > 1) {
var items = [Link];
while ([Link]()) {
var item = [Link]();
if ([Link]()) {
[Link]();
}
}

[Link]();
[Link](selectionCount + " Entries checked out.");
return -1;
}
}

[11.0] Check membership in a group

The [Link]( group ) method presents a simple option for checking whether the
current user is a member of a specific group. You can perform the query via the group name or
group ID. Decide in a script whether a user should be shown a specific script button, for example.

This check is also successful if the user isn't a member of the group directly, but rather via "group
in group" membership.

[11.0] Load an http-GET call in a string

With the [Link]( url ) call, you can easily query a website and write the result to a
string for further processing. In the most simple case, you can enter a direct URL. The example
below loads the Company Information page from the ELO website and searches it for a VAT ID.

var elo = String([Link]("[Link]


var id = [Link](/(DE [0-9]{8,9})/i);
[Link]("ELO", id[0]);
62 Programming for ELO

This simple functionality can be achieved with the older [Link] function. The
new function offers a placeholder function for ELO internal data, e.g. for the current ticket, the
active user, or active repository. If you need the current user name in the call, you can write it as
follows: [Link]

The following placeholders are available:

{archive} Replaced with the current repository name on execution.


{server} Replaced with the current server name on execution.
{ticket} Replaced with the current session ticket on execution.
{userid} Replaced with the current user number on execution.
{username} Replaced with the current user name on execution.

Information

Be careful when forwarding a session ticket to other services. The ticket is like a password.
If the third party service cannot be trusted, it can execute any action it wants in your name.

[12.0] Report unsaved changes

If you are editing a form in the ELO Java Client and want to switch to another entry, a warning
appears if you have any unsaved changes, as this action reloads the form.

Fig.: Unsaved changes

If you want to show this warning in your own ELO apps, you can notify the ELO Java Client whether
or not a change should trigger a warning via the communication interface.
63 Programming for ELO

The sendDirty() and sendSaved() functions can be used:

if ([Link]()) {
[Link]();
}

if ([Link]()) {
[Link]();
}

[12.0] Event when changing a selection

There is an event pair eloTreeSelectStart/End that is called when changing the selection in the
repository tree. However, this event does not notice if the change is made in the folder list.

Starting with version 12, there is an additional event, eloSelectionChanged, which is also called
when clicking the folder list. The Java ListSelection object is transferred as the parameter. You can
use this to determine the number of selected entries, for example.

function eloSelectionChanged(event) {
var selCount = [Link];

var item = [Link];


if (item != null) {
[Link](selCount + " : " + [Link]);
}
}

[12.0] Event at the end of a copy action

If you copy directory structures in ELO using copy and paste and a script then performs
adjustments, there is a new script event, eloPasteDone, starting with ELO 12. The parameter
returned is the object ID from the base folder or document in the target area.

function eloPasteDone(objId) {
[Link]("New Item Root: " + objId);
}
64 Programming for ELO

[12.0] Determine the selected repository entry

The script interface in the ELO Java Client is extremely symmetrical when it comes to the different
view filters. In many places, you don't even have to know what view filter you are in, as many
operations are available in all the view filters.

However, there are differences when reading the active entry. The firstSelected/allSelected
function returns the folder/document objects in the repository view filter. In the task view filter,
however, these are task objects containing the repository object. Plus, these special list views
always provide the selection on the left side, even if an entry is selected on the right in the folder
list.

For cases where only the repository object is of interest, there is are new calls in ELO 12:
firstSelectedArchiveElement/allSelectedArchiveElements. These calls return the selected
repository object in all view filters, taking any selection on the right side into consideration. If you'd
like to see the difference for yourself, you can use this script:

function eloHomeStart() {
var fs = [Link];
var fsa = [Link];

[Link]("fs: " + (fs ? [Link] : "null") + ", fsa: " + (fsa ? [Link]
}

The Intray is the only exception: As it doesn't contain any repository entries, this function always
returns null.

[12.0] Script events in the Mobile Connector

Starting with version 12, there are two new script events when starting and ending synchronization
via the Mobile Connector. You can perform preparations or follow-up work here.

These two events are called eloSyncStarted and eloSyncDone. You have a type SyncParams
parameter object that contains the properties rootGuid (GUID of the start folder in the ELO
repository) and rootFile (start folder in the file system).

In the start event, you can optionally register a callback function, which is then called when
reading, writing, or deleting a file or folder. You can specify the name of the callback function via
[Link]( <name). This call has to be performed in the eloSyncStarted function,
as the callback is cleared before every start.

var list;

function eloSyncStarted(param) {
65 Programming for ELO

[Link]("ELO start", [Link] + " --> " + [Link]);


[Link]("eloSyncItem");
list = [];
}

function eloSyncDone(param) {
[Link]("ELO done", [Link]("<br>"));
}

function eloSyncItem(mode, sord, file) {


var msg = mode + " : " + ((sord) ? [Link] : "-") + " : " + ((file) ? [Link] : "-");
[Link](msg);
}

The callback function has three parameters. The first parameter contains the reason for the call,
while the second contains the current Sord object, and the third one contains the current file or
folder. The last two parameters are not always configured (or may contain null). The Sord object is
not always complete.

[20.0] Open to personal folder

When the user starts the client, it opens to the same folder they were in last before closing the
client. In some cases, however, the user would prefer the client to open to a specific folder. In
addition, if the user was in a large folder before closing the client, the folder has to be loaded
again, causing the program to take a lot longer to start.

To switch to a specific folder, it is possible to use the event when starting the client. However, this
occurs so late that the original target has already been loaded. It is therefore better to switch to the
desired target folder in the event before closing the client.

The following example uses each user's personal folder as the start folder:

function eloWorkspaceClosing() {
[Link]([Link]);
}

[20.0] Process extensive lists from the Intray

If you want to transfer large quantities of documents from the Intray to the repository with a script,
it is useful to first get the full selection list with [Link] and then delete the selection
with [Link](). This has the advantage that you don't need to refresh the display
with each step. This can speed up the process considerably if you have large documents and
extensive lists.
66 Programming for ELO

[20.0] Script actions after inserting a reference

Events are triggered in the Java Client when the user inserts a reference:
eloReferenceElementStart/End. However, the end event occurs after the target folder has been
selected and before the actual copy action, which is executed asynchronously. This prevents you
from being able to intervene in the copied branch.

An additional event pair eloExecuteReferenceStart/End was introduced in version 10.1 to work


around this issue. As this event comes without parameters, you have to proceed in two steps. In
the eloReferenceArchiveElementStart event, the parameters are added to the object, old parent and
new parent. You may have to cache these parameters so that you can use them in the
eloExecuteReferenceEnd event.

var globNewParentId;

function eloExecuteReferenceEnd() {
[Link](globNewParentId);
}

function eloReferenceArchiveElementStart(mode, refItem, oldParentId, newParentId) {


globNewParentId = newParentId;
}

[20.1] Delete a document version

You can logically delete document versions in the same way as folders or documents, or they can
be deleted permanently. The following example illustrates a function that performs these actions.

/**
* Deletes a document version based on the version number.
* Optionally, the version can also be removed permanently,
* otherwise, it is only logically deleted.
*
* id : ELO object ID for the entry the version should be deleted from.
* version: Version number of the document to be deleted.
* deleteFinally: Deletes all versions marked as deleted permanently.
**/
function deleteVersion(id, version, deleteFinally) {
try {
var itemData = [Link](id, "-1", [Link], [Link]);
var docs = [Link];
for (var i = 0; i < [Link]; i++) {
67 Programming for ELO

if (docs[i].version == version) {
docs[i].deleted = true;
[Link](null, null,[Link], [Link]);

if (deleteFinally) {
deleteDocumentFile(id);
}
return true;
}
}
} catch(ex) {
[Link](ex);
} finally {
var unlockSord = new Sord();
[Link] = id;
[Link](unlockSord, [Link], [Link] );
}

return false;
}

/**
* This function permanently deletes all document versions of the
* specified entry marked as deleted.
**/
function deleteDocumentFile(documentId) {
var delOptions = new DeleteOptions();
[Link] = true;
[Link] = true;
[Link](null, documentId, [Link], delOptions);
}

Information

Since there is no function that permanently deletes a single document version, this script
removes all versions of that document marked as deleted and not just the newly deleted
version.
68 Programming for ELO

ELO Dropzone and ELO Java Client

Script tile with OK/error animation

A Dropzone tile normally shows an OK or error animation on filing to notify the user whether the
action was successful. This animation can also be used by a script tile. To do so, the script simply
needs to return a 0 for OK or a -1 for an error.

function fromVBS_Animation(param) {
var result = [Link]("Dropzone", "yes or no");
return result ? 0 : -1;
}

Use trim on a detected expression

Starting with version 9.03.002, Dropzone contains an additional area for modifiers in a range search
for specific terms. This is separated from the position numbers by a pipe symbol. Therefore, if
there is a file named "Inv Thm [Link]", the following is now output: Example (old) – take the
file name starting with position 4 and read the following 5 characters:

{[Link](4,5)} results in "<space>Thm<space>"

Example (new) – the same range, but remove all spaces before and after.

{[Link](4,5|T)} results in "Thm"

Example (new) – the same range, but without spaces and uppercase.

{[Link](4,5|TU)} results in "THM"

The following three modifiers are currently available:

• T for trim – removes leading and trailing spaces.

• U for upper – converts to uppercase letters.

• L for lower – converts to lowercase letters.

• More modifiers may be added to this list later.

• All modifiers can be combined however you wish, even if it makes no sense to use U and L at
the same time.
69 Programming for ELO

Send e-mails with Dropzone

Dropzone is unable to send e-mails by itself, but it can pass on this task to the Java Client by using
a script. To do this, create a script tile in Dropzone named "Send e-mail". In addition to the icon and
color, enter the script name [Link].

Fig.: E-mail tile configuration

The fromVBS_SendMail script in the Java Client is also quite simple. Dropzone passes all detected
values in a string parameter, which is first resolved into a map using the fromString method. The
file name and path, as well as the file name without extension, are read from this map. These
values are then passed to the workspace sendMail method. The Java Client subsequently sends the
e-mail.

function fromVBS_SendMail(param) {
var data = fromString(param);
var fileName = data["[Link]"];
var name = data["[Link]"];
if (fileName) {
[Link](name, "", "", [fileName]);
return;
}
}
70 Programming for ELO

function fromString(param) {
var result = new Object();

var lines = [Link]("\n");


for (var i = 0; i < [Link]; i++) {
var line = lines[i];
var ipos = [Link]("\t");
if (ipos > 0) {
var key = [Link](0, ipos);
var value = [Link](ipos + 1);
result[key] = value;
}
}

return result;
}

Fig.: E-mail to be sent in Outlook

Title recognition for ambiguous entries

Dropzone can use regular expressions to gain information from available window titles and use it
for metadata. This is useful when the title contains uniquely identifiable information. For example,
an invoicing program could have the customer number for the customer currently being processed
in the title of a dialog box – in a format like "Customer 4711 Meier". If a customer number is then
required for metadata, you can create a regular expression for the title that checks for the fixed
71 Programming for ELO

text "Customer " (with space) and a subsequent number. This number is applied as the customer
number and all subsequent text is ignored.

Sometimes it's more complicated, though. What happens when there are several open customer
dialogs? In that case, one of the customer numbers will be selected at random. For cases like this,
you can use the input field at the top of Dropzone, where you can enter the customer number
manually. However, this input field also always imports the data from the currently active window
title when a corresponding regular expression exists.

The Notepad text editor will be used for this example. In the window title, it shows the current file
name, followed by the fixed text " – Notepad". You can use a regular expression to get the file name
from this: (.*) - Notepad

Fig.: Two instances of Notepad open with the names Test1 and Test2

On its own, the expression is not unique when multiple instances of Notepad are open. Now when
you click the Notepad instance with the "Test1" file in it, the value is applied to the input field. This
allows the user to exactly recognize which value is active. In addition, the user can influence the
value by selecting the corresponding window.

Fig.: [Link] file is active

You can retrieve this value by selecting "[Link]" from the list of values for index fields.
72 Programming for ELO

Fig.: [Link] contains the value for the input field

Use the PDF printer for automatic filing

Dropzone can use one or more tiles to monitor the PDF printer output directory and automatically
file new documents from there. In principle, it works similarly to a simplified version of ELO
Print&Archive.

To monitor the directory, the PDF output directory first needs to be configured in Dropzone. It
doesn't necessarily need to be the ELO PDF Printer – any source can be used.

Fig.: Enter the PDF printer directory

Each filing tile can now set up monitoring for this directory. For the tiles to recognize whether a new
document is available for them, a regular expression must be defined that searches the document
text for a characteristic string. When the expression finds a corresponding value, the tile recognizes
it is responsible for the document and files it to the repository. If the expression is not detected, the
tile ignores the entry.
73 Programming for ELO

Tables in regular expressions

Here's something important to know when processing Word tables with regular expressions: Every
cell contains an invisible \r at the end in the full text contents. You won't see this either in the Word
form or in the full text – but you need to take it into account when a regular expression stretches
over multiple cells.

Fig.: Selection in the tile

You can optionally use the "To printer" check box to perform a task on a real printer. The "Send as
e-mail" check box generates an e-mail message with the PDF file as an attachment.

[10] Dropzone import via URL

You can download and import a file from an e-mail message or a website into the ELO repository.
All you have to do is enter a properly formatted elodms URL. In this case, the elodms link always
starts with "elodms://dz/<tile name>|".

The URL can either contain a file name or an HTTP address. When a file is used, it must be visible
to the client computer locally. The file name is then added to the elodms link with URL encoding.

Normally, an HTTP download address is specified. In this case, the identifier "http" must be added
to the link, followed by a pipe symbol and the file extension (e.g. PDF). This is followed by another
pipe symbol and then the download address. Please note that this part must be URL encoded for a
valid link to result.
74 Programming for ELO

elodms://dz/Report|http|pdf|[Link]%2Fscript%[Link]`

Fig.: Insert elodms link to a Microsoft Outlook e-mail

When the recipient of this e-mail clicks the link, the file is downloaded and transferred to the
specified tile – as if you had dragged it to the tile.

Fig.: Imported file

Information

Refer to the "Determining the URL encoding" section for information on how to create a URL
in encoded format.
75 Programming for ELO

[10.2] Range search within the current year or month

If you'd like to restrict a search tile to documents from the current year, you can perform a range
search. For this, you enter the following value into the date field in the tile definition:

{[Link]}0101... {[Link]}1231

Before performing the search, replace [Link] with the current year. The client runs a search
from January 1 to December 31 of the current year.

The same applies for restricting a search to the current month:

{[Link]}{[Link]}01...{[Link]}{[Link]}31

However, this scenario is a bit more complex: Not every month has exactly 31 days. A search
within April 2017 generates a range search 20170401…20170431. There is no April 31, so this
search would be impossible and should trigger an error message. The standard Java date routines
are more tolerant and April 31 becomes May 1 (February 31, 2017 becomes March 3). The search
runs, but sometimes returns too many results in the concerned months. In many situations, this
will not cause issues – it is relatively rare that there are documents dated in the future.

Fig.: Search expression for a date range


76 Programming for ELO

[11.0] Call an ELO Java Client tile via ELO Dropzone

Sometimes, it would be useful to be able to click an ELO Dropzone tile and jump to a work area in
the client. This is possible starting with version 11 by creating a script tile and entering the pseudo
script name TILE.<Java Client tile name>.

Fig.: Enter a script name

Fig.: Tile that takes you to an ELO work area

If you'd like to call a search favorite tile, you have to explicitly indicate this, as not only the view is
switched. For favorites, the view has to be created, and the favorites settings are loaded and
shown after switching. The call then looks as follows:

[Link].<MyFavoriteName>

[11.0] Favorites search via ELO Dropzone

Using ELO Dropzone, you can not only show a favorite view (via TILE.<Favorite_name> in the script
name), but also run a script that can extract the search term from ELO Dropzone data. Simply
create a script in the ELO Java Client that activates a search and then defines this script as a tile in
ELO Dropzone.

function fromVBS_Reports(param) {
[Link](param);
77 Programming for ELO

var map = [Link](param);


var search = [Link]("[Link]");
if (!search) {
search = "";
}

var view = [Link]("favoriteSearch");


if (!view) {
view = [Link]("favoriteSearch");
}

[Link]("New reports", search, search != "");


return -1;
}

Information

The [Link] function splits the string ELO Dropzone sends with all recognized
values into a map. From here, you can conveniently access any single value.

[11.0] Edit existing folder structures

If you generate the folder structure automatically on filing, you have little effect on the properties
of the folder created. However, you can make changes later as needed using a script.

Folders are often sorted alphabetically, which also makes sense in many cases. But if you have a
multi-level structure and only want to sort the last level with the inverse document date, you are
unable to achieve this with a form definition. Here, you can use a script configured for the filing tile.

Fig.: Sorting the folders with a script

function fromVBS_Postprocessor( param ) {


var map = [Link](param);
var id = [Link]("LastStoredObjectId");

var doc = [Link](id);


var parentFolder = [Link];
78 Programming for ELO

var parentSord = [Link];


if ([Link] != [Link]) {
[Link] = [Link];
[Link] = parentSord;
}
}

You can access the saved document from script parameter LastStoredObjectId. Next, the parent
folder is read and a check verifies whether the sort order is set appropriately. If not, it is changed
and the folder entry is saved again.

[12.0] Script tile feedback

If filing a document via a normal filing tile, this process is saved in the Dropzone overview. The user
can check whether it was successful later on. They can also click the new entry to view it.

Starting with version 12, this option is also available for script tiles. The script has to return the
display text and the new object ID via a [Link] cookie.

function fromVBS_Test(param) {
var result = [Link]("Dropzone", "yes or no");
[Link]("[Link]", "86706|ScriptTile|IDs für die Tabs|/Test1/IDs für die Tabs"
return result ? 0 : -1;
}

The cookie text contains the object ID, the tile name, the title, and the path for the display text,
separated by pipe symbols.

The return value of the script (0 or -1) determines whether "OK" is shown or an error is output.

Fig.: Checking stored documents


79 Programming for ELO

[20.0] Use parts of file names for metadata

If you want to transfer the entire file name to an index field, you can use the standard Dropzone
variable [Link]. It gets a bit more complicated if you want to split the name into several
parts. In this case, you need to define a regular expression for each part of the name.

In the following example, the file name consists of four parts, each separated by an underscore:

AF80808_JUMA ALLY_TANZANIA_08112019.docx

The first part contains an ID, the second the name, the third the country, and the fourth a date. The
regular expressions could look like this:

ID ^(.+\\)*(?<DZRESULT>[^_]*)_ AF80808
Name _([^_]*)_ JUMA ALLY
Country _([^_]*)_(?<DZRESULT>[^_]*)_ TANZANIA
Date _(([^_]*)_){2}(?<DZRESULT>[\d]*) 08112019

Information

With the default settings, Dropzone uses the result of the first group (first pair of brackets).
Sometimes, however, you may need groups to organize data that should not be read. In this
case, you can select the desired group with the name DZRESULT.

[20.0] Multiline property in regular expressions

In older Dropzone versions, the pattern matcher is "Multiline", i.e. a ^ symbol at the beginning of
the regular expression refers to the start of every line, not just to the start of the first line. This is
somewhat unfortunate because you can enable the multiline property with a modifier (?m) if
needed, but you can't remove the existing setting.

This has been changed in the current version. If you want read out the second text line, this
expression is used:
80 Programming for ELO

Fig.: Regular expression for reading the start of a specific line

If you want to restore the old state, you just have to add the prefix (?m):

Fig.: Regular expression for reading the beginning of all lines

[20.0] Different global tiles for different users

If you want different users to see different global tiles, version 20 provides the option to store a
different name for the global folders in the extra text of the user folder.

Fig.: Setting different tiles

Fig.: Corresponding folder structure


81 Programming for ELO

In this case, the tiles from the Global Dev folder are displayed instead of the tiles from the Global
folder. This allows you to create different tile combinations for different user groups. If you want
different users to see the same tile, you do not necessarily have to duplicate it. You can simply
reference it in the corresponding Global XYZ folders. The tiles have position data, which is a minor
issue. It is not possible for tiles to overlap, as the next position is automatically selected, but it can
lead to unwanted gaps.

[20.0] Filing paths with a pipe symbol

Dropzone uses the pipe symbol in the filing path definition to separate the additional reference
paths: "Main path|reference path1|reference path2". This leads to problems if the text of the main
path contains pipe symbols. These are interpreted as reference path separators, causing the object
to be filed at an unwanted location.

Starting with version 20, the reference paths can be separated with the Unicode character ‼
(U+203C). In this case, pipe symbols are permitted. If the ‼ character is found in the path definition,
the entries are separated as follows: "Main path with | symbol‼reference path1‼reference path2".

If you have pipe symbols in the main path and do not want to create reference paths, you can also
use this option by specifying an empty reference path: : "Main path with | symbol‼"
82 Programming for ELO

Repository information

Determine the repository path of an entry

If you are accessing a folder or a document, it's often important to find out where the entry is
located in the repository and if it has additional reference paths. This information is provided by
the Sord object in the refPaths property.

function getScriptButton100Name() {
return "Repository path";
}

function getScriptButtonPositions() {
return "100,home,navigation";
}

function eloScriptButton100Start(){
var firstSelElem = [Link];
var elemPathArray = [Link];
var elemPathString = "";
for ( var i = 0; i < [Link]; i++ ) {
elemPathString = elemPathString + elemPathArray[i].pathAsString + "<br>";
}

[Link]( "Repository paths:", elemPathString );


}

This property returns an array of ArcPath objects. The first entry is the filing location in the
repository. All other (optional) entries are further references elsewhere in the repository.

Information

If you only need the whole path, the pathAsString method in the ArcPath object provides
direct access. If you need the names or even the IDs of individual levels, the ArcPath object
provides you with the path property. It returns an array of IdName objects that contain an
entry for each level – each with its object ID and name.

Open a specific repository path

Some ELO environments frequently work with references to represent a structure in various places.
If you use gotoId to jump to a document, the main path is always used.
83 Programming for ELO

Sometimes, though, it would be advantageous for the process to open a folder structure in the
client using a reference.

You can use the gotoPath method in the ArchiveView to do this. You need to provide an array of
IdName objects as parameters. Here, enter the desired ELO object ID for each level that needs to be
opened up. This list can be taken from the Sord object, for example, which contains all paths under
refPaths, even those of references.

The path can also be taken from the current work area. From here, you can call the
getFirstSelectedPath method. It returns the complete path of the selected entry. This path can be
used to return to this exact location later.

var rememberPath = null;

function eloScriptButton100Start(){
if (!rememberPath) {
rememberPath = [Link];
} else {
[Link](rememberPath);
}
}

This simple script remembers the current position the first the button is clicked (even if it is within
a dynamic folder), then returns to exactly this location every time it is clicked in the future.

Delete empty folders

The Sord object provides you with a childCount property for use. This is not, however, suitable for
deciding whether a folder is empty and can be deleted. This method has two flaws:

1. According to the documentation, the childCount property only contains an "estimate" value.
This is because the property in question is a global value. However, an individual user may
see more or fewer documents depending on permissions.

2. Even if the value was identified from the database at the moment it is requested, it would
become questionable only milliseconds later. In a million nanoseconds, a number of new
documents could have been added to the folder.

For this reason, the Indexserver provides the folderMustBeEmpty option for use when deleting.
84 Programming for ELO

var deleteOptions = new DeleteOptions();


[Link] = true;
[Link](123, 456, [Link], deleteOptions);

Go to a certain page in a document

By using the [Link] command, you can do more than jump to a specific entry in the
repository – you can even go straight to a specific page. In this case, add the page information to
the GUID you are passing on. Enter @P and the page number to the end of the number.

[Link]( "(714466C2-7B76-715A-FD76-9D27C703EE7E)@P3" );

Alternatively, you can also jump to a specific annotation. In this case, use an @A and the ELO note
ID.

[Link]( "(714466C2-7B76-715A-FD76-9D27C703EE7E)@A12345" );

Find a list of online users

Technically speaking, this is not a repository task, but rather a task of the Access Manager. As
many ELO environments only use one repository, these terms are often used as synonyms.

You can get a list of all active users by calling the checkoutUsers Indexserver function. With the
SESSION_USERS_RAW parameter, you can specify that you only want the list of users who are currently
logged on, and not all users registered on the system.

getLoginCount: function[]([Link]) {
var values = [Link]().checkoutUsers( null, CheckoutUsersC.SESSION_USERS_RAW, LockC.

if ([Link]()) {
for (var i = 0; i < [Link]; i++) {
[Link]("- " + i + " : " + values[i].id + " : " + values[i].name);
}
}

return [Link];
85 Programming for ELO

Check whether a document exists

If you want to use a script to check whether a document with a unique metadata term exists in the
repository, you do not necessarily need to run a resource-intensive search. In this case, it is easier
to use a checkoutSord and the seldom-used OKEY: syntax.

From the Indexserver documentation: Use "OKEY:<index-group-name>=<index-value>" to select an


object by an index value, example: "OKEY:SAPPATH=123124109824123/data". The wildcards "%" or "*"
can be used in <index-group-name> or <index-value> to return all or any data that matches.

However, this approach requires the term to only occur once in the repository. Otherwise, the call
will return the first entry that is found at random.

Generate a custom report entry

All important events regarding a document or folder are stored in the report table. You can also
enter custom events there with scripting. These have action IDs in the range of 3000 to 3999.

function eloScriptButton100Start() {
var item = [Link];
var id = [Link];
var actionNo = 3000;
var extra1 = 100;
var extra2 = 999;
var message = "Generated with a script";
[Link](id, actionNo, extra1, extra2, message);
}

These entries are then listed with the others in the report overview.
86 Programming for ELO

Fig.: Listed entries in the report overview

In the current version, unfortunately, neither the Extra1 and Extra2 fields are output, nor is it
possible to assign the action ID a meaningful name. This remains a task for future versions of ELO.

Save user settings

When a user needs to perform similar inputs several times in a row, it makes sense to save the
inputs and pre-enter them upon the next call. In the simplest scenario, this is possible with a global
variable – if there are multiple values, you can use an object in a global variable.

It is even more convenient for the user, however, when the data remains stored beyond the current
session. You can use the user settings in the profileopts table to do this.

Since the user settings are stored on a repository basis, the access functions are saved in the
archive object: getUserOption and setUserOption.

Essentially, options consist of two parts: a name and a value. The name should begin with an
abbreviated version of the company name in order to avoid conflicts with names in other scripts.
For example: "[Link]". The S indicates a string option (there are other options
internal to the program). All option entries that start with ELO, Client, Mso, or WebClient are
reserved for ELO.

var mode = 1;
var lastId = [Link]("[Link]", "");

var result = [Link](lastId, mode);


if (!result) {
return;
}
87 Programming for ELO

[Link]("[Link]", result);

Import external files into the repository

In migration projects, it is sometimes the case that large data pools exist on a WORM device or a
disk appliance with a retention period. When you import these documents into ELO, the old storage
space is not freed up and you have double the storage requirements at the end.

The Document Manager provides the ability to use external document paths for this situation. In
this case, a file is not imported and filed again. Instead, an existing file reference is saved and the
user is given access to the original file.

To use this feature, ELOdm needs access to the files, of course, and the file reference must be
provided from the perspective of ELOdm. First, a path to the root directory of the storage medium
is created in the Administration Console.

Fig.: Create path for storage medium


88 Programming for ELO

The path can then be used to file existing documents to ELO. This can be done using an ELOas rule
or an ELO Java Client script, for example, which reads the migration database, determines the
metadata and file path from it, and then transfers the data to the ELO Indexserver.

var parentId = [Link];

var ed = [Link](parentId, "", "", [Link]);


var sord = [Link];
[Link] = "RelPath Document ZUGFeRD invoice";
var dv = new DocVersion();
[Link] = 8;
[Link] = "folder1\\[Link]";

var doc = [Link];


[Link] = [dv];

[Link](sord, [Link], doc, [Link]);

You can find the path ID used ([Link] = 8) in the ELO Administration Console as the "Internal
ID". The entire file path results from the root directory of the path definition ( e:\RelPath) and the
relativeFilePath from the document folder (folder1\[Link]) -> e:
\RelPath\folder1\[Link]

[10] Edit lists before display

With the eloCollectListStart event, you can edit metadata objects before displaying them. The
parent object ID and the list of Sord objects are returned as parameters. The parent ID is greater
than 0 if it is a child list in the repository tree. If the list is generated as the results of a search, the
parent ID is < 0.

The following script highlights all entries containing the text "ELO" in red. This is only applied
temporarily and locally, not saved to the repository.

function eloCollectListStart(objid, sords, items) {


for (var i = 0; i < [Link]; i++) {
if (sords[i].[Link]("ELO")) {
sords[i].kind = 1;
}
}
}
89 Programming for ELO

If you only want to highlight the search results but not the child lists in the tree view, you can
evaluate the objid parameter:

function eloCollectListStart(objid, sords, items) {


if (objid < 0) {
for (var i = 0; i < [Link]; i++) {
if (sords[i].[Link]("ELO")) {
sords[i].kind = 1;
}
}
}
}

Up to version 9, this event is only called for lists in the tree and in the metadata search. Starting
with version 10, it is also enabled for the full text search. Since further information is available
here, a third items parameter is provided. Similar to the sords array, this contains a list of
FindByFulltextResultItem entries. Each of them contain the sord entry with the metadata, as well
as a relevance entry or the summaryFulltext with the result environment.

With a metadata search, the items parameter is undefined, just like when paging through the tree. It
is only completed in case of a full text search.

The following example increases the relevance of all results beginning with the text "RE" by 100
points.

function eloCollectListStart(objid, sords, items) {


if (items) {
for (var i = 0; i < [Link]; i++) {
var item = items[i];
if ([Link]("AW")) {
[Link] += 100;
}
}
}
}

[10] Check whether a file exists in the repository

Starting with version 10 of ELO, you can call script functions from the command line. The
[Link] program provides a special parameter for this: rsf.
90 Programming for ELO

When the program is called as follows: [Link] rsf <script name> <parameter>, it
executes the runScriptFunction call over the COM interface. The <script name> script function is
activated with the <parameter> string parameter. This enables you to even run scripts in
environments that do not allow for COM programming.

The following example shows how to create a context menu entry in Windows Explorer that checks
whether individual files already exist in the repository. Context menu entries can be created in
Windows Explorer via a registry entry. Create a key in the registry editor under
HKEY_CLASSES_ROOT\*\shell with the name of the context menu entry – "Check ELO" in the example.
Under it, create another key named Command with the program call as default value that should be
run when the menu entry is selected.

Fig.: Menu entry in the registry

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\*\shell\Check ELO]

[HKEY_CLASSES_ROOT\*\shell\Check ELO\Command]
@="D:\\Transfer\\ELOactivateJC\\[Link] rsf eloComMD5Check \"%1\""

When you open the context menu for a file in Explorer, you will now see another menu entry
named Check ELO. Clicking the entry runs the eloComMD5Check script in the ELO Java Client. The file
name is passed as the parameter.
91 Programming for ELO

Fig.: Check ELO context menu entry

Next, you have to create the eloComMD5Check script in the ELO Java Client. This script reads the file
name from the parameter, then runs an MD5 search for the file contents in the repository.
Fortunately, there is a utils function that helps us do this: utils.findByMD5. If the file already exists
in the repository, the script immediately performs a gotoId on this document.

function eloComMD5Check(param) {
var sord = utils.findByMd5(new File(param));
if (sord) {
[Link]([Link]);
} else {
[Link]("Not found in repository");
}
}

Information

Please note that, for security purposes, script functions can only be called over the COM
interface if they begin with the prefix eloCom. This is intended to make sure external
programs are unable to run arbitrary script functions in the Java Client.

[10] Find the "Personal area" user folder

The user folder for an ELO user is automatically referenced in the list of top-level ELO folders when
the program starts. If you want to access it from a script, you can call the folder's ELO object ID by
using the [Link] script function. The "Personal area" folder is a child folder that you
can access with [Link].
92 Programming for ELO

var id = [Link];
var dataItem = [Link](id, "¶Personal area ");

[10] Duplicate check in the repository

If you want to store a file via a script, it is sometimes necessary to check whether the document
already exists in the repository. To do this, the UtilsAdapter gives you a tool that makes the task
extremely easy to accomplish: utils.findByMd5(file).

This call determines the file's MD5 checksum and performs a search for this checksum in the
repository. If the document does not yet exist there, the call returns null. If it does exist, the call
returns the Sord object of the first hit.

Information

A null return value does not necessarily mean that the file is not in the repository. It simply
indicates that there is no version of the file in the repository that is visible to the user.

[10.2] Update the full text information

Sometimes, you may add documents to the full text database from which no text information can
be extracted. This can be due to a configuration error, or the Textreader may not be able to process
this document format. Once the problem has been eliminated, you will want to add the relevant
documents to the full text database again for processing in the hope that they will now be
processed correctly and the full text information can be saved.

The following script runs through all child entries in the selected folder or document and checks
whether they have been registered for full text processing. If so, the size of the full text file is
checked
in the next step. If the size falls below a default value of 30 in the script (e.g. because only notConv
was entered), this document is removed from the full text database and imported again. As a
result, the document file is sent to the Textreader for processing again.

Please note that if your folders are very extensive, this script will take a very long time to run. In
addition, you can generate a large number of files in the Textreader area that can be forwarded for
processing all at the same time. This may generate a very high load on the server that could
interrupt normal operation.

With more extensive repository structures, you should divide the check into multiple steps. You
should also restrict this script using permissions so that only administrators or trained employees
can run it.
93 Programming for ELO

Fig.: Displaying the runtime of the script

function getScriptButton844Name() {
return "Update fulltext";
}

function getScriptButtonPositions() {
return "844,archive,information";
}

function eloScriptButton844Start(){
startUpdate();
}

var refreshCount;
var documentCount;

function startUpdate() {
refreshCount = 0;
documentCount = 0;
var root = [Link];
var msg = "<html><p>Checking the fulltext information may need a very long time and might generate a
if ([Link]("ELO Fulltext Updater", msg)) {
walk(10, root);
msg = "<html><p><b>Update fulltext completed</b></p>&nbsp;<p>Documents refreshed: " + refreshCount
[Link]("ELO Fulltext Updater", msg);
}
}
94 Programming for ELO

function walk(level, root) {


if (level < 0) {
return;
}

if ([Link]()) {
processDocument(root);
} else {
var items = [Link]();
for (var i = 0; i < [Link](); i++) {
var item = [Link](i);
walk(level - 1, item);
}
}
}

function processDocument(root) {
documentCount++;
if ([Link]) {
var editInfo = [Link]([Link], null, [Link], [Link]);
if (([Link] > 0) && [Link][0]) {
var size = [Link][0].[Link];
if (size < 30) {
[Link]("refresh fulltext (" + size + ") of: " + [Link]);
refreshCount++;
[Link] = false;
[Link]();
[Link] = true;
[Link]();
}
}
}
}

[20.1] List of all duplicates in the repository

Unfortunately, the Indexserver interface does not provide a command that lists all duplicates in the
repository. The only effective way to do this is through the database. Here is a solution from a
Community post:

select [Link], [Link], ed.md5, [Link]


from objects o
95 Programming for ELO

join dochistory dh on [Link] = [Link]


join elodmdocs ed on [Link] = [Link]
where ed.md5 in (select md5 from (select md5, count(*) count from elodmdocs group by md5 having
order by ed.md5

The list contains duplicates as versions within one document as well as duplicates between
different documents.

Fig.: Displaying duplicates

If you look at the consecutive lines with an identical GUID, there are repetitions with the same
object ID (green), which are identical versions within a document. If they are different object IDs
(blue), they are different documents with identical content.
96 Programming for ELO

Toolbar and navigation in the ELO Java Client


In addition to the standard work areas in the ELO Java Client such as the repository or Intray, you
can provide users with task-specific work areas.

Show an Intranet page in the ELO Java Client

The ELO Java Client provides the ability to add further task-related entries in the list of work areas.
Many companies have an Intranet that contains important information and activities. You can also
integrate this page into a work area in the Java Client. You don't even need a script in version 9.3 to
do this – it works through a configuration entry.

In the Administration folder, there is a child folder named "ELOapps" and a folder within that
named "ClientInfos". You can save the configuration for the Intranet page here in the form of a JSON
object.

Fig.: Path to the configuration area for the work areas

To do this, create a folder under ClientInfos and enter the JSON text to the "Extra text" field:

{
"selector": {
"ids": []
},
"view": {
"type": "REGION"
},

"web": {
"eloSession": false,
97 Programming for ELO

"url": "[Link]
"name": "Wikipedia"
},

"id": "wikipedia"
}

This definition indicates that a new work area needs to be created (view – type = REGION) named
"wikipedia" (id = wikipedia), and in this area, a browser window (web) needs to be shown using the
Wikipedia URL (url = [Link]

Fig.: Client with a WIKIPEDIA work area

In older versions of the client, you can achieve this with a script that performs the necessary steps
(create area, generate browser window, load URL). Starting with version 9.3, however, you should
no longer use this script, and instead use the configuration described above.

const baseUrl = " [Link]


98 Programming for ELO

const viewName = "ELO Wiki";


const viewIconGuid = "(1CF19062-B91F-7D9E-4DE6-2C46883FE45C)";

function eloWorkspaceStarted() {
[Link](baseUrl);
}

function eloScriptsReloaded() {
[Link](baseUrl);
}

function eloRefreshViewStart() {
[Link]();
}

var extraViewBrowser = {

browserToolbar : null,

refreshUrl: function() {
var view = [Link];
if ([Link] == viewName) {
[Link]("");
[Link](baseUrl);
}
},

showBrowser: function(url) {

if (![Link]) {
[Link] = [Link]();
var controlPanel = [Link](0);
[Link](viewName);
var view = [Link](viewName, false, [Link], controlPanel);
[Link](false);

try {
var icon = [Link](viewIconGuid);
[Link](icon);
} catch(e) {
[Link]("Cannot find view icon: " + e);
}
}
[Link](url);
}
99 Programming for ELO

Displaying documents with the Windows Protocol Handler

In Windows, an application can register itself as a protocol handler and perform certain actions
when a link is activated using that protocol. This is used for "[Link] URLs, for example.

The ELOactivate application registers itself as the protocol handler for the elodms prefix. If you
create a URL in elodms://... format, the browser calls ELOactivate when a user clicks it. This allows
you to open the ELO client from websites or e-mail messages in order to display a document.

The following options are available:

elodms://([guid])
elodms://wf/[flowId]/[nodeId]

The [guid] is an ELO object GUID and in this case, the client jumps to the entry.

The [flowId] and [nodeId] are a workflow ID and node ID, respectively. If the user has this entry in
their task list, the client jumps to this task.

Starting with the 9.2 version of the client, there are two additional options for the GUID variant:

elodms://([guid])@P[page number]
elodms://([guid])@A[annotation ID]

This allows you to go straight to a specific page in a document. The second variant with an
annotation ID can be used, for example, to jump to various bookmarks.

Create a button with a tooltip

Creating a button in the Java Client is easy. Basically, you have to implement three functions:
getScriptButtonXYZName(), eloScriptButtonXYZStart(), and getScriptButtonPositions().

function getScriptButton100Name() {
return "Project application";
}

function getScriptButtonPositions() {
return "100,home,navigation";
}
100 Programming for ELO

function eloScriptButton100Start(){
// .. my Code
[Link]("Button pressed”);
}

If you also want to enter a tooltip, you can do so by adding the function
getScriptButtonXYZTooltip(). This function returns the desired text as the return value. You can
split the text into several paragraphs by using </p><p>. Unfortunately, no other HTML formatting
options can be used.

function getScriptButton100Tooltip(){
return "A paragraph with explanations</p><p>A second paragraph";
}

[10] Metadata-dependent display

By using the AppManager, you can set the preferred view mode depending on the metadata form.
This only works, of course, if the desired tab is available in the current view. For a document type
that shows all important information in a form, you can switch the view mode to the form – all
other metadata forms show the document view instead.

Fig.: AppManager - switching the view mode


101 Programming for ELO

If you want to select the view mode depending on the current metadata, such as depending on the
status or a value in the entry's short name, you need a script. This script checks the condition and
selects the appropriate display tab.

importPackage([Link]);

function eloPreviewAvailable(mode, actElement){


var maskAssignments = {};
maskAssignments["To do item"] = CONSTANTS.PREVIEW_CONTENT.FORMULAR;
maskAssignments["EMail"] = CONSTANTS.PREVIEW_CONTENT.FEED;
maskAssignments["Ordner"] = CONSTANTS.PREVIEW_CONTENT.INDEX_PREVIEW;

try{
if(String(mode) == "SINGLE_SELECTION"){
var actMaskName = String([Link]);
var viewMode = maskAssignments[actMaskName];

if ([Link]("2")) {
viewMode = CONSTANTS.PREVIEW_CONTENT.FULLTEXT;
}

if (!viewMode) {
viewMode = CONSTANTS.PREVIEW_CONTENT.DOCUMENT_PREVIEW;
}

[Link](viewMode);
}
}catch(e){
[Link]("error changing viewMode");
}
}

[10] Names of work areas

Each work area has a standard name – Search, Task, Clipboard, Repository... With the search and
task work areas, though, multiple view filters may exist. For this reason, in addition to the
[Link] property, there is a [Link] that contains the view filter's specific name. This is
assigned by the script or the user when the view is created.

var views = [Link];


for (var i = 0; i < [Link](); i++) {
var view = [Link](i);
102 Programming for ELO

[Link]([Link]);
}

[10] Automatically transfer a search term to the results list

Since version 9.3, the ELO Java Client has had its own search field in the TIFF and PDF display. When
the user enters text to the field, the corresponding results are highlighted in the document. Since a
separate field is now available, the search term is not automatically used for display. You can either
double-click to manually apply a term to the text field, or configure a script to enter it
automatically.

function eloSearchResultAvailable(view, findResult) {


try {
var term = [Link];
[Link] = term;
[Link](term);
} catch(e) {
}
}

In contrast to older versions, the current client version is able to contain the current view and the
results list as parameters to the script call. This means that you can use the script to see which
view the call belongs to as well as the search parameters and their results.

[10.1] Call functions using a hotkey (Click&Find)

As of version 10.1, you can now call script functions using Windows hotkeys. Simply add the
[Link] function calls to the eloWorkspaceStarted event in the script file. The first
parameter represents the name of the key, whereas the second parameter is the name of the
function you want to call.

function eloWorkspaceStarted() {
[Link]("alt T", "hello1");
[Link]("alt Z", "hello2");
}

function hello1() {
var result = [Link](true, true);
[Link]("ELO", "HELLO T: " + result);
}
103 Programming for ELO

function hello2() {
var result = [Link](false, true);
[Link]("ELO", "HELLO Z: " + result);
}

Pressing ALT-T then calls the hello1 function. This contains yet another practical function:
[Link]. This function simulates a mouse-click if the first parameter is true. A
CTRL-C (copy) call is then simulated if the second parameter is true. A double-click selects the
word under the mouse cursor in most applications. CTRL-C copies the selected value to the
Windows Clipboard. Finally, the function reads the Clipboard and returns the selected value to the
script. This value can then be used for a search, for example.

You can use these functions to directly execute the Click&Find functions in the ELO Java Client
without any additional utilities. Here is an example of a script for reading the version number from
the ReleaseRequest form with a hotkey:

function hello1() {
var result = [Link](true, true);
[Link]("Search Version: " + result);
var view = [Link]("RelReq");
if (!view) {
view = [Link]("RelReq");
}

var key = new ObjKey();


[Link] = "RR_VERSION";
[Link] = [result + "*"];

var fbi = [Link]();


[Link] = 52;
[Link] = [key];

var fi = [Link]("");
[Link] = fbi;
[Link](fi, 1000, true);
}

[11.0] Show workflow tasks

The workspace object now has a method gotoWorkflow ( flowId, nodeId ) that allows you to jump to a
specific workflow in the task view filter via script command. The parameter for nodeId is optional
104 Programming for ELO

and can be set to 0. In this case, the first workflow node from the task view filter belonging to the
specified workflow is activated.

[20.0] Work with the RibbonAdapter

Originally, ribbon tabs, groups, and buttons were created using callback functions with predefined
names, e.g. eloButton100Start() was the callback function for button 100. This method causes
problems in complex and dynamic systems. Firstly, this means having to create a large number of
callback functions, and secondly, you need to keep a careful record of which button numbers have
already been assigned. This is especially a problem if the scripts come fron different sources.

The Java Client includes the RibbonAdapter option for this purpose. It allows users to create tabs,
ribbon groups, and buttons with a script. The old functions can still be used, or you can use a
mixture of both.

Since the ribbon interface must be fully configured when it is displayed, there is a separate event
for this: eloExpandRibbon. This method is where you have to make the ribbon settings.

Information

Keep in mind that changes you make at this point will not take effect until you restart the
client. Simply reloading the scripts does not work.

In its simplest form, if you add a button to an existing group on a tab, the code looks like this:

function eloExpandRibbon() {
var button = [Link]("home", "new", "my1stButton");
[Link]("My first button");
[Link](function() {
[Link]("My first button pressed.");
}, this);
}

If the action button contains more than just a basic function, you should outsource this part to a
separate function so that the eloExpandRibbon function remains simple and doesn't end up with
hundreds of lines if there are multiple buttons. To prevent conflicts with other scripts that happen to
have the same names, these should not be in the global namespace. Instead, you should create a
separate namespace that contains the callback functions, in this case within the ribbonActions
variables. As the button is supplied as a parameter within the call, you can use a callback function
to handle multiple buttons of the same type.

function eloExpandRibbon() {
var button = [Link]("home", "new", "my1stButton");
105 Programming for ELO

[Link]("My first button");


[Link](function() {[Link](button)}, this);
}

var ribbonActions = {
myButtonAction : function(source) {
[Link]("Some button pressed: " + [Link]);
},

anotherButtonAction : function(source) {
// do something
}
}

If you want the button to have a separate icon, you need to store an image file in the folder that
contains the script. Ideally, this is an icon file with 3232 and 6464 pixel images. The icon file is
registered with setIconName().

function eloExpandRibbon() {
var button = [Link]("home", "new", "my1stButton");
[Link]("My first button");
[Link]("MyButtonIcon");
[Link](function() {[Link](button)}, this);
}

You can also create your own groups for the script buttons. This also happens in the
eloExpandRibbon event. The parameters of the addBand call are the ID of the ribbon tab, a position
number, and the new group ID. The position number determines where the new group is inserted
within the existing groups.
106 Programming for ELO

function eloExpandRibbon() {
var group = [Link]("home", "25", "My1stGroup");
[Link]("My first Group");

var button = [Link]("home", [Link], "my1stButton");


[Link]("My first button");
[Link]("MyButtonIcon");
[Link](function() {[Link](button)}, this);
}

Fig.: Button function

If the button function is only available in some cases, you can also lock and unlock it with a script.
The function setEnabledCallback allows you to specify a function that enables or disables the
button.

Information

It is important to note that this call will be executed every time the client status changes. If
the callback function takes a long time, this slows down performance significantly, and the
user may get the impression that the client has crashed.

function eloExpandRibbon() {
var button = [Link]("home", "new", "my1stButton");
[Link]("My first button");
[Link]("MyButtonIcon");
[Link](function() {[Link](button)}, this);
[Link]([Link], this);
}

var ribbonActions = {
107 Programming for ELO

myButtonAction : function(source) {
[Link]("Some button pressed: " + [Link]);
},

isOnlyDocuments(selection) {
for (var i = 0; i < [Link](); i++) {
if ([Link](i).isStructure()) {
return false;
}
}

return true;
}
}

You can also create your own tabs. The addTab function is provided for this purpose. This option
should be used sparingly as it reduces usability if there are too many tabs on the ribbon.

function eloExpandRibbon() {
var tab = [Link](11, null, "My1stTab");
[Link]("My Tab");

var group = [Link]([Link], "25", "My1stGroup");


[Link]("My first Group");

var button = [Link]([Link], [Link], "my1stButton");


[Link]("My first button");
[Link]("MyButtonIcon");
[Link](function() {[Link](button)}, this);

var anotherButton = [Link]([Link], [Link], "anotherButton");


[Link]("Another Button");
}
108 Programming for ELO

Fig.: Displayed tabs

If you only want the tab to be displayed under certain circumstances (e.g. the Intray tab), you can
control this with a callback function: setVisibleCallback.

Information

In this case as well, whether the tab is shown or not is queried every time the client status
changes. For this reason it is extremely important that this function can be executed quickly.
In particular, calls that take a long time should not occur here as the user would think the
client has crashed.

function eloExpandRibbon() {
var tab = [Link](11, null, "My1stTab");
[Link]("My Tab");
[Link]([Link], this);

var group = [Link]([Link], "25", "My1stGroup");


[Link]("My first Group");

var button = [Link]([Link], [Link], "my1stButton");


[Link]("My first button");
[Link]("MyButtonIcon");
[Link](function() {[Link](button)}, this);

var anotherButton = [Link]([Link], [Link], "anotherButton");


[Link]("Another Button");
[Link](function() {[Link](anotherButton)}, this)
}

var ribbonActions = {
myButtonAction : function(source) {
[Link]("Some button pressed: " + [Link]);
109 Programming for ELO

},

isOnlyDocuments(selection) {
for (var i = 0; i < [Link](); i++) {
if ([Link](i).isStructure()) {
return false;
}
}

return true;
}
}
110 Programming for ELO

Add metadata in the ELO Java Client


This chapter provides some examples that make it easier to add metadata to documents in the ELO
Java Client.

Make an input field writeable

The form editor allows you to hide input fields (such as for technical values that are not interesting
for users) or to write-protect them, preventing accidental changes to the fields.

If a process makes it necessary to change an entry in a write-protected field, you don't need to give
up write protection completely. You can also remove it for specific entries through scripting.

function eloIndexDialogSetDocMask() {

var maskName = "SupportEvent";


var objKeyName = "ReadOnlyFeld";

if( [Link]().equals(maskName)) {
// Unlock the field
[Link]( objKeyName ).setEnabled(true);
}

Information

Please note that the "hidden" and "write-protected" field properties are pure utility features,
not security measure, as with a little work, users can bypass both of these settings.

Automatic counter field

The ELO Indexserver offers automatic counters that provide a unique incrementing value. This is
even the case when several clients request a value at the same time. You can use this counter to
assign invoice numbers, for example. In the ELO Java Client, you can request a counter value with
the [Link] method. You need to specify the counter name and the initial value (for
counters that do not yet exist) as parameters.

To ensure you have incrementing numbers, you have to make sure the numbers cannot be
discarded. For example, if the invoice number field is completed when the metadata is opened, and
the user closes the form by canceling, you will have an unused number. This can cause major tax-
related issues with invoices in particular. The safer approach is to enter the number when the user
saves the form with "OK". The user will not be able to cancel afterwards.
111 Programming for ELO

function eloIndexDialogOkStart() {
if ([Link] == 2) {
var counterValue = [Link]("ELOOUTL1");
if (counterValue == "") {
var cnt = [Link]("cntELOOUTL1", 100);
[Link]("ELOOUTL1", cnt);
[Link]("Counter value: " + cnt);
}
}
}

Show a button behind an index field

In order to show a button behind an index field, the field must be shortened somewhat so it does
not overlap the button. Afterwards, the new button must be created and placed at the end of the
index field.

There is a simple call to use this function – addButton – which both shortens the index field and
inserts the button. The first parameter specifies the button text, the second parameter indicates the
name of the callback function when the user clicks the button, and the third parameter states the
width of the button in logical units.

function eloIndexDialogSetDocMask() {
if ([Link] == "Delivery note") {
var delnote = [Link]( 4 );
[Link]( "DoIt", "doButtonAction", 4 );
}
}

function doButtonAction(){
[Link]( "Test", "Button pressed" );
}

Hide a tab (1)

In the metadata dialog box, the Java Client shows one or more tabs for index fields, as well as
other tabs for the extra text, options, permissions, change history, and additional information.
Users shouldn't always be able to see all tabs. This is why the setTabVisible function is in the Java
Client API for the metadata dialog box.
112 Programming for ELO

Fig.: Normal metadata dialog box view

This simple example hides the "Options" tab for a specific user when showing the e-mail metadata
form.

const SPECIAL_MASK = 2;
const SPECIAL_USER = 3;
const OPTION_TAB = 11;

function eloIndexDialogSetDocMask() {
if ([Link] == SPECIAL_MASK) {
if ([Link] == SPECIAL_USER) {
[Link](OPTION_TAB, false);
}
}
}

Fig.: metadata without the 'Options' tab

Hide a tab (2)

A dynamic folder contains the definition of an SQL query in the extra text. At best, this information
is confusing to most users In the worst case, a user might try to make changes. You can prevent
this by hiding the "Extra text" tab in dynamic folders.
113 Programming for ELO

Fig.: metadata without extra text

const MEMO_TAB = 10;

function eloIndexDialogSetDocMask() {
if (![Link]()) {
var desc = [Link];
if (desc && ([Link]() > 0) && ([Link](0) == 33)) {
[Link](MEMO_TAB, false);
}
}
}

Information

In JavaScript, unfortunately, there are two different kinds of strings: First, the normal
JavaScript string, which is used in all script-internal text operations. The other is the Java
string from the ELO Java API in the ELO Java Client, ELOas, or the ELO Indexserver. It isn't
always easy to see what type you receive, and in most cases both types appear identical.
But the length query does differ, which is accomplished in JavaScript with
[Link] and in Java with [Link]().

Automatic check-in on saving

Strictly speaking, this is not a scripting task, as no script is required. With the ELO Java Client, you
can configure documents to be checked in automatically in the metadata of a template document.
This only works for documents that keep the file open in the editor application during editing.
114 Programming for ELO

In this case, the Java Client regularly checks whether the file is exclusively available in the
checkout area. If not, it is still open in the original application. As soon as the file is available
exclusively, it is automatically checked in again.

Fig.: Entering the auto check-in marker

As you cannot or do not want to implement this for all document types, each auto check-in
document must be designated as such individually. To do this, go to the "Additional information"
tab in the template's metadata and create an elotemplate line. Enter autocheckin as the value –
this enables the option.

It is also possible to enter mask. In this case, the new document is assigned the metadata form and
metadata from the template document, instead of the default metadata form for new documents.

How was the metadata dialog box closed

Depending on the call type, opening the metadata with [Link] in a script returns
whether the metadata was changed (call with three parameters), or whether the dialog box was
closed by clicking OK (older call with two parameters). You may, however, want to distinguish
between three states:

1. Changed and closed with OK

2. No changes and closed with OK

3.
115 Programming for ELO

Closed with Cancel

This can be done by using the editSord variant with three parameters. The call returns whether the
entry was changed. This allows you to distinguish cases 2 and 3 from case 1. Additionally, you can
call the [Link] method in order to distinguish case 2 from case 3.

Show an image in the metadata dialog box

In the course of a customer project, there was a request to show an image in the metadata dialog
box located within the same folder.

Fig.: metadata with image

This is fairly easy in the Java Client – you need to create a script that is active when the metadata
dialog box is open. When the relevant metadata form is open, it searches the folder for an image
named "Image" and loads the file in a Java ImageIcon object. This is bundled into a JLabel, which is
loaded in the corresponding location in the metadata dialog box.

importPackage([Link]);

function eloIndexDialogSetDocMask() {
var sord = [Link];
if ([Link] == "EMail") {
var parentId = [Link];
if (parentId > 0) {
116 Programming for ELO

try {
var image = [Link](parentId, "¶Image");
if ([Link]()) {
var file = [Link];
var icon = new ImageIcon([Link]);

var label = new JLabel();


[Link](icon);
[Link](1, 14, 4, 30, 3, label);
}
} catch(e) {
[Link]("No Image: " + e);
}
}
}
}

Custom keyword lists

You can create a keyword list for an index field in the form editor. However, sometimes these aren't
static and have to be adapted to the situation. In this case, you can put together an array of strings
at runtime in the Java Client and pass this on to the index field as a keyword list.

const FROM_FIELD = 0;
function eloIndexDialogSetDocMask() {
if ([Link] == 2) {
var key = [Link](FROM_FIELD);
var swl = [
new Date(),
[Link],
[Link]
];
[Link]("Special entries", swl);
}
}

In this example, the dynamic keyword list consists of three entries: the current date, the current
user, and the client version number. This is all data that can't be statically entered into the form
editor.
117 Programming for ELO

Fig.: Displaying the dynamic keyword list

Pass a string to the Windows Clipboard

You can use the following function to pass on a text from the ELO Java Client to the Windows
Clipboard (in the example, the short name of the currently selected entry):

importPackage([Link]);
importPackage([Link]);

function getScriptButton100Name() {
return "Copy to Clipboard";
}

function getScriptButtonPositions() {
return "100,home,view";
}

function eloScriptButton100Start(){
var str = [Link]

var toolkit = [Link]();


var clipboard = [Link]();
var strSel = new StringSelection(str);
118 Programming for ELO

[Link](strSel, null);

Show an address in Google Maps

This script reads the address information from the current metadata form and generates a URL for
Google Maps. This URL is passed for viewing in an external browser using [Link]().

Fig.: metadata with address fields

It is assumed that the postal code, city, and street address are saved to a metadata form. These
fields are read when the button is clicked and put together into a Google Maps URL. This is then
passed to the browser. The script uses a feature in the ELO Java Client that was introduced with
version 9.1: a script command shortens an index field and adds a script button ( [Link]).

The rest of the script is basically trivial. The eloIndexDialogSetDocMask function checks whether the
"Personnel file" metadata form (ID = 332 in my test repository) is active. If it is, a "Maps" button is
added to index field 6 for the city.
119 Programming for ELO

When the button is clicked, the event function reads the postal code, city, and street address and
uses this data to generate a URL. It isn't possible to use the string directly, since an address
contains several characters that aren't allowed in a URL – so we take a short detour through the
URLEncoder. Finally, [Link] opens the URL in the browser.

function eloIndexDialogSetDocMask() {
if ([Link] == 332) {
var key = [Link](6);
[Link]( "Maps", "triggerViewMap", 10 );
}
}

function triggerViewMap() {
var address = [Link]( "PA_PLZ" ) + " " +
[Link]("PA_ORT") + "," +
[Link]("PA_STR");

address = "[Link] + [Link](address, "UTF-8"


var mapURI = [Link]( address );
[Link](mapURI);
}

Fig.: Display in Google Maps

Information
120 Programming for ELO

You could make the script even more universal by not reacting to a specific metadata form,
but instead searching the group names for typical signs of an address (ZIP, CITY, STREET)
and generating the URL from them. However, you may not always get the correct fields.

[10] Edit the results list

You can highlight certain entries in the search results by checking them in the
eloSearchResultAvailable event and highlighting them accordingly as needed. This can be done by
setting the font color, for example.

function eloSearchResultAvailable(view, findResult) {


var sords = [Link];

for (var i = 0; i < [Link]; i++) {


var item = sords[i];
if ([Link] == "ZInvoice") {
[Link] = 3;
}
}
}

Please note that this script only temporarily changes the entry currently displayed. No changes are
made to the database information. As soon as the user edits the entry, the original version is
loaded again.

The event is not new with version 10 – it existed in older versions as well. In those versions,
though, it didn't have any parameters. For this reason, the script wasn't able to determine the
search view filter in which it existed and was therefore less useful.

[10] Click a word to apply it to a field in a script dialog box

In the ELO Java Client, you can click a term in a document to apply it to the metadata. This can also
be used for custom dialog boxes. Simply implement the functionality in the eloOcrWordClicked
event. This event is triggered when the user clicks a word in a document and it is recognized via
OCR.

The script event then only needs to find the target. This is not entirely simple, because when the
"click" occurs, the focus is on the document, not the text field that needs to receive the
information. Java, though, provides a getMostRecentFocusOwner property for dialog boxes. This
contains the dialog element most recently focused on.
121 Programming for ELO

Fig.: Section of an invoice

When the user clicks a word in a document, the event is activated. The script identifies the current
input field and enters the text accordingly.

Fig.: Field contents after clicking the document number

var dialog = null;

function eloScriptButton100Start() {
dialog = [Link]("Dialog", 3, 4);
122 Programming for ELO

var gPanel = [Link]();

[Link](1, 1, 1, "Zeile 1");


var tf1 = [Link](2, 1, 2);

[Link](1,2,1,"Zeile 2");
var tf2 = [Link](2,2,2);

[Link](1,3,1,"Zeile 3");
var tf3 = [Link](2,3,2);

var result = [Link]("close", "close");


}

function close() {
dialog = null;
}

function eloOcrWordClicked() {
if (!dialog) {
return;
}

try {
var destination = [Link]();
if (destination && ([Link] == "class [Link]")) {
[Link]([Link]);
}
} catch(e) {
[Link]("No destination or no setText method: " + e);
}
}

[10] Select e-mails with attachments

When filing e-mail messages using the ELO Macro, you can choose to assign e-mails with an
attachment a different metadata form. To do this, create the document type "E-mail with
attachment" in the ELO Administration Console (choosing an appropriate icon).

You can also use this display for e-mail messages that are filed to the repository via the Intray or
directly via drag-and-drop. To do so, you need a small event script that checks for the file type
when storing new documents to the repository. For EML and MSG files, the file is then read and
checked to see if it contains attachments. If this is the case, the entry is assigned the new "e-mail
with attachment" document type.
123 Programming for ELO

const MailWithAttachmentType = 260;

function eloInsertDocumentEnd(document) {
var file = [Link];
var ext = [Link](file).toLowerCase();
if ((ext == "msg") || (ext == "eml")) {
mail = [Link](file);
if ((mail != null) && ([Link] > 0)) {
var sord = [Link];
[Link] = MailWithAttachmentType;
[Link] = sord;
}
}
}

[10] Non-modal metadata dialog box

If you want to call the metadata dialog box in non-modal form, the script will not be able to wait for
the user to click "OK" or "Cancel". Otherwise, the dialog box would be modal. When
[Link] is called non-modally, the dialog box is shown and the script call returns
immediately. The script will also typically end as well here.

For the script to be able to react to user input, when it is called it must provide the names of the
event functions that will be run when OK or Cancel are clicked. These are two completely normal
JavaScript functions that are automatically started by the Java Client as soon as the user clicks the
corresponding button.
124 Programming for ELO

* Opens the Sord object for editing in the metadata dialog box. If write access is not provided wit
* [Link], the dialog box will appear in read-only mode.
*
* @param sord Metadata to be edited
* @param title Title of the metadata dialog box
* @param okCallbackMethod An event called when "OK" is clicked
* or null. In the scripting, you can prevent the dialog box from closing
* if a function returns a negative return value for this event.
* @param cancelCallbackMethod An event that is called on "Cancel"
* or "X", else null. Closing
* the dialog box cannot be canceled here.
* @return True if the Sord was changed, or else False
* @throws Exception
* @since 10.00.000

function eloScriptButton100Start[]([Link]) {
var item = [Link];

[Link]([Link], "Non-modal test", "callbackOk", "callbackAbort");


return -1;
}

function callbackOk() {
[Link]("The dialog box was closed with OK");
}

function callbackAbort() {
[Link]("The dialog box was closed with Cancel");
}

[10.2] Copy the metadata

Last year, a customer criticized the fact that the F3 function can be used to change read-only and
hidden fields. This is not normally possible for users. In process-oriented environments in
particular, this can result in errors since process data is taken from one object and transferred to
another unchanged. For this reason, we had to lock this function as soon as the metadata form
contained hidden or read-only fields.

For users that never got this type of error, this was frustrating: they were simply unable to copy the
metadata from this version onward. Since we can't decide which group the user is a member of, we
can't change the situation directly. However, from version 10.2 onward, there is a script interface
125 Programming for ELO

that allows you to decide whether the function is available in the project. You can base your
decision on the metadata or metadata form. Copying is permitted for all noncritical metadata
forms, but otherwise forbidden.

function eloEnableFillWithLastSord( actSord, lastSord, mode ) {


if ([Link] == "Image demo") {
[Link] = true;
return -1;
}
}

To be able to offer the copy function in process-oriented environments, an additional event is


available that permits the script to decide what is copied. In an ObjKey loop, you can decide
whether the content is copied or changed, or whether the row should remain blank or be filled with
a default value.

function eloFillWithLastSord( actSord, lastSord, findInfo ) {


if ((lastSord != null) && ([Link] == "Image demo")) {
[Link] = "*** " + [Link];
var keys = [Link];
for (var i = 0; i < [Link]; i++) {
var key = keys[i];
if ([Link][0]) {
try {
[Link]([Link], "+ " + [Link][0]);
} catch(e) {
[Link]("Copy error: " + e);
}
}
}
return -1;
}
return 0;
}

[10.2] Determine the metadata form by user selection

In one project, we were requested to suppress the metadata dialog box when filing via drag-and-
drop, instead displaying a metadata form query. The actual metadata is then entered in the
repository via a form.
126 Programming for ELO

This request is relatively easy to implement – once you have found the right events. The metadata
form is easy to suppress using a configuration setting that can also be set via a script.

function eloWorkspaceStarted() {
[Link]("[Link]", "false");
}

The correct event for filing is eloInsertDocumentEnd – the corresponding start method would be a
better fit, but it does not permit any changes to the Sord entry and cannot be used here for that
reason.

The best option for selecting a metadata form is a CommandLinkDialog. After the user has selected
the right metadata form, the current repository entry is changed accordingly.

var maskSelection = ["Invoice", "Order", "Basic Entry"];


var destPath = "¶Personal area¶Intray";

function eloInsertDocumentEnd(doc) {
var selection = [Link](
"Select metadata form",
"Select the correct document type.",
"", CONSTANTS.DIALOG_ICON.QUESTION, maskSelection, [], []
);

if (selection > 0) {
var changedSord = [Link]([Link], maskSelection[selection - 1], EditInfoC.
[Link](changedSord);

asyncGotoId([Link]);
}
}

In principle, the asyncGotoId method only triggers a gotoId.


However, the call is delayed so that the GotoId call no longer overlaps with the end of the script
event. This can result in sporadic processing errors that are difficult to find and eliminate.

function asyncGotoId(docid) {
var runnable = new [Link]({
run: function () { [Link](docid); }
});
127 Programming for ELO

[Link](runnable);
}

This project also saw a request to file Intray documents to a special "Repository Intray" folder in the
user's personal area. Of course, the selection should also be shown for these documents. The "File
to repository" button was redefined and given its own function.

function eloInsertIntoArchiveStart() {
var id = [Link];
var dataItem = [Link](id, destPath);

var items = [Link];


while ([Link]()) {
var item = [Link]();
[Link]([Link], "", "");
}

return -1;
}

[21.0] Customize level icons

There was an issue in a customer project which meant that the level icons were set randomly over
time although the customer wanted to keep the cabinet – folder – tab structure. Fixing this involves
a lot of manual work but it can easily be done by script.

To avoid support problems with Solutions, it is now possible to specify which icons may be
changed, and which may not (e.g. file, contract). Since the Business Solutions use the icons starting
from ID 15, the script now only changes the first 14 levels. However, you can define this
individually for each level using a configuration.

Important

If you created and want to maintain a manual folder structure, this script destroys this order
by writing the cabinet-folder-tab number sequence. This affects all Business Solutions
folders, for example. This script should therefore only be available to administrators and
should be deleted once successfully executed.

// AdjustLevelIcons
// (c) by ELO Digital Office GmbH, Jan 2021
128 Programming for ELO

//
// This script walks over a sub-tree and adjusts
// all sub-level icons according to the root icon
//
// MAX_LEVEL defines the biggest folder type number,
// all deeper sub-folders will get the same type.
//
// MAX_RECURSION defines the maximum search/ walk
// depth in case of recursive loops in the folder
// structure.

[Link]("Script started: AdjustLevelIcons, Version 20.03");

const MAX_LEVEL = 20;


const MAX_RECURSION = 20;
// const SHIELD_ALLOWED = undefined; // all Icons can be changed
const SHIELD_ALLOWED = [ // only icons with 'true' can be changed
false, true, true, true, true,
true, true, true, true, true,
true, true, true, true, true
];

importPackage([Link]);

function getScriptButton598Name() {
return "Adjust Icons";
}

function getScriptButtonPositions() {
return "598,home,edit";
}

function eloScriptButton598Start() {
[Link]();
}

var adjustLevelIcons = {

// Button function: read root element and start conversion.


execute: function() {
var rootItem = [Link];
[Link] = 0;
[Link] = 0;
[Link] = 0;
[Link] = 0;
129 Programming for ELO

if (rootItem && [Link]()) {


[Link](rootItem, MAX_RECURSION);
}

var msg = "<h2>Adjust Icons of " + [Link] + "</h2>"


+ [Link] + " items protected,<br>"
+ [Link] + " items locked,<br>"
+ [Link] + " items changed,<br>"
+ [Link] + " errors.";

[Link]("Adjust Icons Done", msg);


},

// recursive walk over the selected sub-tree.


adjustLevel: function(root, actRecursion) {
var level = [Link](MAX_LEVEL, [Link] + 1);
var children = [Link];
while ([Link]()) {
var child = [Link]();
if ([Link]()) {
var sord = [Link];
var sordLevel = [Link];
if ([Link](sordLevel)) {
if (sordLevel != level) {
[Link](child, sord, level);
}
} else {
[Link]("Protected item: " + [Link]);
[Link]++;
}

if (actRecursion > 0) {
[Link](child, actRecursion - 1);
}
}
}
},

// change the sord icon if not locked.


processSord: function(child, sord, level) {
[Link] = level;
if ([Link] == -1) {
try {
[Link] = sord; // executes saveSord
[Link]++;
} catch (ex) {
130 Programming for ELO

[Link]("Cannot update: " + [Link]);


[Link]++;
}
} else {
[Link]("Locked item: " + [Link]);
[Link]++;
}
},

// check for protected icons.


checkAllowed: function(level) {
if (!SHIELD_ALLOWED) {
return true;
}

return SHIELD_ALLOWED[level];
}

[21.0] Delete old e-mails

Under the new data privacy regulation, it is becoming increasingly important to delete old
documents. In the case of e-mails, it is especially difficult to know where all entries are stored in
the system. This script runs through the currently selected subtree and deletes all entries of the
type e-mail which are older than the specified date. Because this process can take a long time, the
script runs in the background. This allows you to continue working in the client. You can check the
progress in the background processes. A report file of the deleted entries is generated at the end.

// JavaScript file
var delDateLimit = "20100101";
var maskName = "EMail";

function getScriptButton693Name() {
return "Mail Cleanup";
}

function getScriptButtonPositions() {
return "693,home,new";
}

var process;
131 Programming for ELO

var rootId;
var rootName;

function eloScriptButton693Start(){
var item = [Link];
if (item) {
rootId = [Link];
rootName = [Link];
process = [Link]("Delete old mails", "processMailDeletion");
}
}

function processMailDeletion() {
try {
totalCount = 0;
totalDeleted = 0;
doDelete(rootId, rootName, 10);
msg = "Entries processed: " + totalCount + ", deleted: " + totalDeleted;
[Link](CONSTANTS.PROTOCOL_LEVEL.INFO, msg);
} finally {
[Link]();
}
}

var totalCount = 0;
var totalDeleted = 0;

function doDelete(rootId, rootName, levelCount) {


var findInfo = new FindInfo();
var findChildren = new FindChildren();
[Link] = rootId;
[Link] = false;
[Link] = findChildren;

var allDeleted = true;


var cnt = 0;
var ix = 0;
[Link]("Process folder " + rootId + " : " + rootName);
var findResult = [Link](findInfo, 1000, [Link]);
for (;;) {
if ([Link]()) {
break;
}
var sords = [Link];
for (var i = 0; i < [Link]; i++) {
if ([Link]()) {
132 Programming for ELO

break;
}
totalCount++; cnt++;
[Link]("Total processed/ level/ deleted: " + totalCount + " / " + cnt + " / "
var sord = sords[i];
if ([Link] < 254) {
if (levelCount > 0) {
if ([Link] == rootId) {
allDeleted &= doDelete([Link], [Link], levelCount - 1);
} else {
var msg = "Referenced folder ignored: " + [Link] + " : " + [Link];
[Link](msg);
[Link](CONSTANTS.PROTOCOL_LEVEL.INFO, msg);
}
} else {
[Link]("Too many nested levels: " + [Link]);
}
} else {
if (([Link] < delDateLimit) && ([Link] == maskName)) {
var msg = "Deleted " + [Link] + " : " + [Link];
[Link](msg);
[Link](CONSTANTS.PROTOCOL_LEVEL.INFO, msg);
[Link](rootId, [Link], [Link], null);
totalDeleted++;
} else {
allDeleted = false;
}
}
}
ix += [Link];
if ([Link]) {
[Link]("Next Batch " + ix + " of " + rootId + " : " + rootName);
findResult = [Link]([Link], ix, 1000, [Link]);
} else {
break;
}
}

[Link]([Link]);

if (allDeleted) {
var delOpts = new DeleteOptions();
[Link] = true;
try {
[Link](null, rootId, [Link], delOpts);
var msg = "Empty folder deleted: " + rootId + " - " + rootName;
133 Programming for ELO

[Link](msg);
[Link](CONSTANTS.PROTOCOL_LEVEL.INFO, msg);
} catch(e) {
[Link]("Folder was not empty: " + rootId + " - " + rootName);
}
}

return allDeleted;
}
134 Programming for ELO

Microsoft Office integration

Complete form fields during checkout

This script shows how to complete the form fields in a Microsoft Word document using metadata
when the document is checked out.

The names of the Word form fields must be identical to the group names in the ELO form definition.
By mapping the group form field name, no additional configuration is required.

// FillFormFields
// (c) by ELO Digital Office GmbH, Jan 2014
//
// This script fills all Word form fields
// from the indexing data of the item and
// the parent of the item when a new Document
// is created.
//
// The name of the Word form field has to
// be ELO_<group_name> - e.g. the indexing
// line KDNR will be inserted into the Word
// form field ELO_KDNR.
//
// ELO Java Client 9.00.000 or newer required.

[Link]("Script started: FillFormField, Version 9.00");

function eloCheckoutDocumentAvailable( id, file ) {


var ext = [Link](file);
if (ext && ([Link]() == "docx")) {
[Link](id, file);
}
}

function FillFormFields() {
}

var fillFormFields = new FillFormFields();

[Link] = function(id, file) {


var item = [Link]( id );
if ([Link] == 0) {
var parent = [Link];
135 Programming for ELO

[Link](parent, item, file);


}
}

[Link] = function(parent, item, file) {


[Link]();
try {
var word = new ActiveXComponent("[Link]");
[Link](word, "Visible", false);

var documents = [Link](word, "Documents").toDispatch();


var doc = [Link](documents, "Open", [Link]).toDispatch();

[Link](doc, parent);
[Link](doc, item);

[Link](doc, "Save");
var aw = [Link](doc, "ActiveWindow").toDispatch();
[Link](aw, "Close");
[Link](word, "Quit");
} catch(e) {
[Link]("Error processing FormFields: " + e);
[Link]("ELO", e);
} finally {
[Link]();
}

[Link] = function(doc, item) {


var objKeys = [Link];

for (var i = 0; i < [Link]; i++) {


var key = objKeys[i];
var name = "ELO_" + [Link];
if ([Link] && ([Link] > 0)) {
var value = [Link][0];
[Link](doc, name, value);
}
}
}

[Link] = function(doc, propertyName, value) {


try {
var obj = [Link](doc, "FormFields", propertyName).toDispatch();
136 Programming for ELO

[Link](obj, "Result", value);


} catch(e) {
[Link]("Error writing word property " + propertyName + " : " + e);
}
}

Read or write form fields from a script

This library helps you edit Word documents from scripts.

myDoc = new Word();


[Link]("c:\\temp\\[Link]", true);
[Link]("c:\\temp\\[Link]");
[Link](false);

Save the following section as lib_Word, then load it with an include into your ELO Java Client
scripts.

importPackage([Link]);
importPackage([Link]);
importPackage([Link]);
importPackage([Link]);

[Link]();

const lib_msoffice_SaveAsPdf = 17;


const lib_msoffice_IgnoreNotSaved = 0;

function Word(){
}// Opens a Word document for editing
//
// fileName: File name with path
// isVisible: Show or hide Word
//
[Link] = function (fileName, isVisible) {
try {
[Link] = new ActiveXComponent("[Link]");
[Link]([Link], "Visible", isVisible);

[Link] = [Link]([Link], "Documents").toDispatch();


[Link] = [Link]([Link], "Open", fileName).toDispatch();
137 Programming for ELO

} catch(e) {
[Link]("100: Error opening document " + fileName + ", reason: " + e);
throw("100: Error opening Word document");
}
}

// Closes the current Word document. Optionally it will


// be saved before closing. Otherwise
// any changes will be discarded.
//
// withSave: Save document before closing
//
[Link] = function (withSave) {
if (![Link]) {
// No document open, nothing to do.
return;
}

if (withSave) {
try {
[Link]([Link], "Save");
} catch(e) {
[Link]("102: Error saving Word document, reson: " + e);
throw("102: Error saving Word document");
}
}

try {
var aw = [Link]([Link], "ActiveWindow").toDispatch();
[Link](aw, "Close", 0);
// [Link]([Link], "Quit", lib_msoffice_IgnoreNotSaved);
} catch(e) {
[Link]("101: Error closing document, reason: " + e);
throw("101: Error closing Word document: " + e);
}
}

// Saves the current document in PDF format.


//
// pdfFileName: File name and path to target file
//
[Link] = function (pdfFileName) {
try {
[Link]([Link], "SaveAs", pdfFileName, lib_msoffice_SaveAsPdf);
} catch(e) {
[Link]("103: Error saving Word document as PDF, reason: " + e);
138 Programming for ELO

throw("103: Error saving PDF document");


}
}

// Sets the "protected" flag in the current Word document


//
[Link] = function[]([Link]) {
try {
[Link]([Link], "Protect", 2, true, "");
} catch (e) {
[Link]("104: Cannot protect document, reason: " + e);
//throw("104: Cannot protect document");
}
}

// Resets the "protected" flag


//
[Link] = function[]([Link]) {
try {
[Link]([Link], "Protect", -1, true, "");
} catch (e) {
[Link]("105: Cannot unprotect document: " + e);
//throw("105: Cannot unprotect document");
}
}

[Link] = function (name) {


var properties = [Link]([Link], "BuiltInDocumentProperties").toDispatch();
var prop = [Link](properties, "Item", name).toDispatch();
var ty = [Link](prop, "type").getInt();
if (ty == 4) {
var value = [Link](prop, "Value").getString();
return value;
}

return "";
}

// Reads a Word property field and returns the value.


//
// propertyName: Name of the field to be read
//
[Link] = function (propertyName) {
try {
var property = [Link]([Link], "FormFields", propertyName).toDispatch();
139 Programming for ELO

var fieldType = [Link](property, "Type");


if (fieldType == 71) {
var checkBox = [Link](property, "CheckBox").toDispatch();
var checked = [Link](checkBox, "Value");
return checked;
} else {
var text = String([Link](property, "Result"));
return text;
}
} catch(e) {
[Link]("106: Error reading property " + propertyName + ", reason: " + e);
throw("106: Error reading property " + propertyName);
}
}

// Writes a new value to a Word property field.


//
// propertyName: Name of the field to be written to
// newValue: New content of the field
//
[Link] = function (propertyName, newValue) {
try {
var property = [Link]([Link], "FormFields", propertyName).toDispatch();
var fieldType = [Link](property, "Type");
if (fieldType == 71) {
var checkBox = [Link](property, "CheckBox").toDispatch();
[Link](checkBox, "Value", newValue);
} else {
[Link](property, "Result", newValue);
}
} catch(e) {
[Link]("106: Error writing property " + propertyName + ", reason: " + e);
throw("106: Error writing property " + propertyName);
}
}

// Copies the form fields from the specified Word document


// into the current Word document. A prefix can optionally
// be specified, which only copies fields that start with
// this character string.
//
// sourceDokument: lib_msoffice Word document with source data
// propertiesPrefix: Optional restriction to field names
//
[Link] = function (sourceDocument, propertiesPrefix) {
var ff = [Link]([Link], "FormFields").toDispatch();
140 Programming for ELO

var count = [Link](ff, "Count");

for (var i = 1; i < count; i++) {


var field = [Link](ff, "Item", i).toDispatch();
var name = [Link](field, "Name").getString();
[Link]("Field name: " + name);

if (!propertiesPrefix || [Link](propertiesPrefix)) {
try {
var value = [Link](name);
[Link](name, value);
} catch(e) {
[Link]("107: Cannot copy property " + name);
}
}
}
}

// Copies a Word range from a document into another document.


// The copy process occurs through the clipboard and includes formatting
// and embedded graphics.
[Link] = function (docFrom, sourceRangeNumber, destRangeNumber) {
try {
var sectionS = [Link]([Link], "Sections", sourceRangeNumber).toDispatch();
var rangeS = [Link](sectionS, "Range").toDispatch();
[Link](rangeS, "Copy");

var sectionD = [Link]([Link], "Sections", destRangeNumber).toDispatch();


var rangeD = [Link](sectionD, "Range").toDispatch();
[Link](rangeD, "Paste");
} catch(e) {
[Link]("Error moving range " + sourceRangeNumber + " : " + e);
}
}

// Copies the metadata into Word form fields. A prefix


// can optionally be specified. In this case, only
// fields will be copied that have the name
// <prefix><ELO group name> in the Word form. Otherwise, all
// fields will be copied that have a Word form field with the ELO
// group name.
//
// indexing: Java Client ArchivElement Objekt oder IX Sord Objekt
// propertiesPrefix: Optional prefix for the Word fields
//
[Link] = function (indexing, propertiesPrefix) {
141 Programming for ELO

var sord = [Link];


if (!sord) { sord = indexing };

var objKeys = [Link];

for (var k = 0; k < [Link]; k++) {


var key = objKeys[k];
var value = ([Link] && [Link] > 0) ? [Link][0] : "";
var name = [Link];
if (propertiesPrefix) {
name = propertiesPrefix + name;
}

try {
[Link](name, value);
} catch(e) {
[Link]("Field not available: " + name);
}
}
}

// Copies the form fields in a Word document into the


// metadata of a Java Client ArchiveElement object.
// A prefix can optionally be specified, which causes only
// the fields to be copied that start with the prefix and
// then have the group name.
//
// indexing: Java Client ArchiveElement object or IX Sord object
// propertiesPrefix: Optional prefix for the Word fields
//
[Link] = function (indexing, propertiesPrefix) {
var sord = [Link];
if (!sord) { sord = indexing };

var objKeys = [Link];


var found = false;

for (var k = 0; k < [Link]; k++) {


var key = objKeys[k];
var name = [Link];
if (propertiesPrefix) {
name = propertiesPrefix + name;
}

try {
var value = [Link](name);
142 Programming for ELO

[Link] = [value];
found = true;
} catch(e) {
[Link]("Field not available: " + name);
}
}

if (found && [Link]) {


[Link] = sord;
}
}

Automatic check-in when closing Word

In order to edit a document, you have to check it out. It is then marked as locked and a copy of the
file is saved in the checkout directory. You can then edit it for as long as you like. Once you are
finished, you create a new document version with the check-in function, which also removes the
lock.

If you only want to create smaller documents, like phone memos, this explicit check-in requires
more work than is necessary. When the program used to edit the document keeps the file open the
entire time it is being edited, you can use the auto check-in function in the ELO Java Client to
generate a document from a template, then automatically check it in once you are finished editing.
143 Programming for ELO

Fig.: Template marked for auto check-in and metadata form assignment

In this case, you only need to create an entry named elotemplate on the "Additional information"
tab of the template document and enter the value autocheckin. When the user creates a new
document from a template in this way, a copy of the file is created in the checkout directory and
the associated application, such as Microsoft Word, is opened. The Java Client now checks regularly
in the background whether it can get exclusive access to the file. When the document is saved and
closed in Word, this is the case and the Java Client automatically checks in the document.

Information

There is a second option, mask, which can be used to copy the metadata form and index field
contents from the template document instead of giving the new document the default
metadata form for new documents.

Create a new document from an external source

By using the "Document from template" function, a user can create a copy of a template document
in the checkout directory, where it can be edited. If the document comes from an external source
instead of from a template, you can accomplish something similar with a script.
144 Programming for ELO

function createWorkItem(parent, maskName, itemName, sourceFile) {


var doc = [Link]( [Link], maskName, null, [Link] );
var sord = [Link];
[Link] = [Link];
[Link] = itemName;
var newElem = [Link](sord);
[Link]();
[Link] = [Link];
var destFile = [Link](sord, sourceFile);
[Link]().edit(destFile);
}

First, by using createDoc, a Sord object is generated for the new document, then the short name is
entered and the document is locked so only the current user can edit it. Afterwards, addStructure is
called instead of addDocument, as we don't want to insert the document file in the repository yet.
Finally, the external file is copied to the checkout directory and activated for editing.

Complete a Microsoft Excel document using the metadata

It is relatively simple to enter data from the metadata into Excel documents using the COM
interface. This example goes through all selected documents in the current work area (such as a
list of search results) and writes the short name, metadata form name, and all index fields to an
Excel document loaded from the file system.

function storeItems() {
var excel = new Excel();
[Link]("d:\\temp\\Results_list.xlsx", true);
var items = [Link];

var line = 3;
while ([Link]()) {
var item = [Link]();
[Link](line, 2, [Link]);
[Link](line, 3, [Link]);

var keys = [Link];


for (var k = 0; k < [Link]; k++) {
var data = keys[k].data;
if (data && ([Link] > 0)) {
[Link](line, 4 + k, data[0]);
}
}
145 Programming for ELO

line++;
}
}

This script uses a wrapper class that we have used in various projects to easily access the Excel
COM interface over JACOB.

// Constructor
function Excel() {
[Link] = null;
}

// Opens an existing Excel document for editing


//
// fileName: File name with path
// isVisible: Show or hide Excel
//
[Link] = function (fileName, isVisible) {
try {
[Link] = new ActiveXComponent("[Link]");
[Link]([Link], "Visible", isVisible);
[Link]([Link], "DisplayAlerts", false);

[Link] = [Link]([Link], "Workbooks").toDispatch();


[Link] = [Link]([Link], "Open", fileName).toDispatch();
[Link] = [Link]([Link], "Sheets", 1).toDispatch();
} catch(e) {
[Link]("108: Error opening Excel document " + fileName + ", reason: " + e);
throw("108: Error opening Excel document");
}
}

// Closes the current Excel document. Optionally it will


// be saved before closing. Otherwise
// any changes will be discarded.
//
// withSave: Save document before closing
//
[Link] = function (withSave) {
if (![Link]) {
// No document open, nothing to do.
return;
146 Programming for ELO

if (withSave) {
try {
[Link]([Link], "Save");
} catch(e) {
[Link]("109: Error saving Excel document, reson: " + e);
throw("109: Error saving Excel document");
}
}

try {
[Link]([Link], "Close", false);
[Link]();
[Link]([Link], "Quit");
[Link]();
} catch(e) {
[Link]("110: Error closing document, reason: " + e);
throw("110: Error closing Word document.");
}
}

// Saves the current document in PDF format.


//
// pdfFileName: File name and path to target file
//
[Link] = function (pdfFileName) {
try {
[Link]([Link], "ExportAsFixedFormat", 0, pdfFileName, 0, 1, 1,1, 1, false
} catch(e) {
[Link]("111: Error saving Excel document as PDF, reason: " + e);
throw("111: Error saving PDF document");
}
}

// Enters the specified value and color to a cell


//
// row : Row in the Excel worksheet
// col : Column
// value : Value to enter
// color : Excel color index
//
[Link] = function (row, col, value, color) {
var cell = [Link]([Link], "Cells", row, col).toDispatch();
[Link](cell, "Value", String(value));
if (color) {
147 Programming for ELO

var interior = [Link](cell, "Interior").toDispatch();


[Link](interior, "ColorIndex", color);
}
}

Fig.: Generated Excel document

When Excel freezes

Excel likes to give the impression of being able to start multiple instances in parallel and allowing
the user to work on them independently from each other. But in practice, this is only true to a
limited extent. When an instance of Excel is in a modal state, it will block all the other instances.
But that's not all: the Microsoft Excel Preview Handler is also an Excel instance. This means that the
ELO Java Client may "freeze" when paging through Excel documents in the preview. The Java Client
isn't actually freezing up here, but rather only the Excel instance used for the preview. From the
perspective of the user, though, it's the client's fault.

It is possible to resolve this problem by stopping the frozen Excel instance in the Windows Task
Manager. However, it isn't possible to see which of the instances is responsible. You can only cross
your fingers and stop one instance after the other, checking to see if the client has been freed up in
the meantime.

The following batch file implements a particularly harsh approach: It simply stops all running
instances of Excel. You can place the file on the desktop and double-click it to resolve any Excel
deadlocks.

@echo off

echo .
echo This call stops all running Excel instances.
echo Changed contents will not be saved.
echo .
echo Are you sure you want to continue? (Y)
echo .

set QUERY=
set /P QUERY=Enter Y or N: %=%
148 Programming for ELO

if /I "%QUERY%"=="J" goto yes


if /I "%QUERY%"=="JA" goto yes

goto no

:yes
taskkill /F /IM [Link]

:no

Information

But as the saying goes, everything comes with a price. Running the batch file will kill all
Excel windows without saving. All changes since the last time you saved will be lost.
149 Programming for ELO

ELO Automation Services

Convert a Microsoft Office document to PDF

ELOas has a method in the fu module for converting Microsoft Office documents (doc, docx, xls,
xlsx, ppt, pptx) into PDF format and saving them as a new document version.

<script>
[Link]([Link]);
</script>

If you want to perform an action other than creating a new version, you can use the
[Link](sourceName, destinationName) method.

Create a feed post from ELOas

The ELO feed is designed for communication among users as well as from the system to the user.
For this reason, it can be useful to automatically create a new feed post in the course of a process
whenever a certain milestone is reached.

The ix module provides a simple function to do this:

<script>
var milestoneInfo = "The 'Finished' process status has been reached.”;
[Link]( [Link], 0, milestoneInfo );
</script>

Extract metadata from the full text contents

Specific metadata terms with a fixed formal construction are well-suited to be extracted from the
document text contents. The ELOas rule should read the full text as a string via the ELO
Indexserver then search for the term via a regular expression.

<script>
var text = String([Link]([Link]));
if (text) {
[Link](text);
var id = [Link](/TTS[0-9]{6}/);
if (id) {
150 Programming for ELO

[Link](id);
COMMENT = id;
EM_WRITE_CHANGED = true;
}
}
</script>

The relevant expression must be written so that the document will not occur again in the search
result, as otherwise the document will continue to be edited and edited again. Additionally, you
should ensure that the server does not wait for an arbitrary length of time to get the full text
contents for older documents – such as when a document cannot provide any full text data.

Return values from a DIRECT rule

A direct rule can set a string as a return value, which is then passed to the program calling it. If you
want to return several values, it is more practical to return a complete JavaScript object. The JSON
library is well-designed for this scenario. You can use it and the stringify function to generate a
JSON string from a JavaScript object. This can be easily re-converted into a JavaScript object on the
receiving side.

<script>
var result = [
{ v: 'abc', description: '123' },
{ v: 'def', description: '145' },
{ v: 'ghi', description: '678' }
];

[Link]([Link](result));
</script>

Forward a workflow to multiple successors

An ELOas workflow node is forwarded after processing by setting the EM_WF_NEXT variable. In many
cases, there is only one successor to an ELOas node, so you will often see the command EM_WF_NEXT
= "0";

But this is only a small part of the many possibilities in forwarding. In a simple case, the workflow
is forwarded to the first successor node (index 0 in the successor list). But you can also define a list
of successors. In this case, enter the index numbers to the list separated by a pilcrow character.

The following example checks whether the workflow node name is CompareELOOUTL12 and if the
value in index field ELOOUTL1 is the same as the value in index field ELOOUTL2. If it is, it is forwarded
to the first and second successor. Otherwise, it is only forwarded to the first successor. These
151 Programming for ELO

differentiations are useful in invoice workflows, for example. If the invoice amount doesn't exceed
a certain value, it is only forwarded to a payables employee, and otherwise will be forwarded to
the employee and their manager.

<script>
if (EM_WF_NODE.nodeName == "CompareELOOUTL12") {
if (ELOOUTL1 == ELOOUTL2) {
EM_WF_NEXT = "0¶1";
} else {
EM_WF_NEXT = "0";
}
}
</script>

Expand the successor list dynamically

Often, the decision as to who will take part in a decision is made when a workflow has already been
started. In this case, it is not possible to define the participant list statically in the workflow
designer, as at this time neither the names nor the exact number of participants is available.

For this case, you can use the [Link] and [Link] functions in the
ELOas wf module. In the designer, only a single node for the distributor is created and assigned to
ELOas. When such a node is active in ELOas, the script checks whether it is an expanding node (for
example, based on the node name). If so, the user list is retrieved, e.g. from an index field that has
been filled with the name of the individuals involved in the process and then the expandNode
method is called. Finally, the workflow is forwarded.

<script>
[Link]("Process Rule expand.");
var names = ["Thiele", "Thiele2", "Administrator"];
[Link]( EM_WF_NODE.flowId, EM_WF_NODE.nodeId,
names, "Check invoice" );
EM_WF_NEXT = "0";
</script>
152 Programming for ELO

Fig.: Workflow
153 Programming for ELO

Fig.: Expanded workflow

Change the user of a workflow node

Sometimes, it is only possible to determine who should process a specific node after the workflow
is already in progress. In an invoice workflow, the accounting department will determine the
approval processor and enter the person to the CONTROLLER index field. The actual approval node
is assigned to the owner or a dummy user in the template.

In the workflow, an ELOas node is active after the accounting node, which looks for the approval
node, reads the index field for the approval processor, and enters this employee to the node. This
can be done in a single line in ELOas.

<script>
[Link](“Accounting approval”, CONTROLLER);
</script>
154 Programming for ELO

Increment a date field by one year

A request from the forum: in a contract management project, a check on the contract termination
date should be performed. When it is reached or exceeded, an action should be performed and the
date should be incremented by one year. This request can be easily fulfilled with ELOas.

First, you must create a ruleset in the ELO Administration Console that searches for all active
contract documents. In the onStart method, the current date must be reformatted to an ISO date,
since this can be easily compared with the termination date.

nowIso = [Link]( new Date() );

In rule 1, you first check whether the entry has a termination date and whether it has passed. If so,
execute the action. Afterwards, the year is extracted from the termination date and increased by
one.

if ((nowIso > TERMINATE) && ([Link] > 4)) {


// Perform the action here
var year = [Link](0, 4);
year = parseInt(year, 10) + 1;
TERMINATE = year + [Link](4);
EM_WRITE_CHANGED = true;
}

Information

This simple script does not check whether the date is valid. February 29, 2016 will be
converted to February 29, 2017 – which, of course, does not exist. Although this does not
cause problems here, it might in other places.

Search for multiple metadata terms

In principle, ELOas performs a search for a value in an index field. Since you will often need to
combine several index fields in order to create a meaningful selection, you can optionally also
provide a list of index field names and search terms. Each of the values must be written separated
by a pilcrow character. The number of index field names must also be identical to the number of
values.

Perform a custom search

In principle, ELOas processes a predefined search on a metadata form and one or more index fields.
Sometimes, however, you need to perform a very specific search with additional restrictions or
option settings. In this case, you can create a custom FindInfo object and register it in the
EM_FIND_INFO variable for processing. These variables have to be completed during the onStart
155 Programming for ELO

event before the search is run. The search first checks whether the
EM_FIND_INFO variable contains a FindInfo object. If not, a FindInfo object is generated using the
ruleset parameters.

In the example, the ruleset searches for all entries with a margin note containing the text "TEST".

var findInfo = new FindInfo();


var fbn = new FindByNotes();
[Link] = “TEST”;
[Link] = fbn;

EM_FIND_INFO = findInfo;

Read contents of third-party websites

The www library in ELOas provides a get method that you can use to easily read the contents of
another website as a string.

<script>
var elo = [Link]("[Link]
[Link](elo);
</script>

From the returned string, you can use regular expressions to look for specific information – such as
exchange rates or stock prices.

Read and write map fields

Map fields exist parallel to the Sord object in their own structure. As they can be very long, they are
not read automatically by ELOas. They must be read by the Indexserver by a script if needed, and if
they are changed, they must also be explicitly written to as well.

The following presents a small library that Mr. Weiler wrote for this purpose:

// var sordMap = new SordMap(EM_ACT_SORD.id);


// [Link]("VAR1", "value1");
// [Link]("VAR2", "value2");
// [Link]("VAR3", "value3");
// [Link]();
156 Programming for ELO

// sordMap = new SordMap(EM_ACT_SORD.id);


// [Link]();
// [Link]("VAR2=" + [Link]("VAR2"));
// [Link]("VAR5=" + [Link]("VAR5"));

function SordMap(sordId) {

[Link] = sordId;
[Link] = [];
}

[Link] = function (key, value) {

[Link](new KeyValue(key, value));


};

[Link] = function[]([Link]) {

[Link]().checkinMap(MapDomainC.DOMAIN_SORD, [Link], [Link], [Link], LockC


};

[Link] = function[]([Link]) {

var data = {};


var items = [Link]().checkoutMap(MapDomainC.DOMAIN_SORD, [Link], null, [Link]
[Link](function (item) { data[[Link]] = [Link]; });
[Link] = data;
};

[Link] = function (key) {

if ([Link][key]) {
return [Link][key];
}
return "";
};

Decrypt encrypted passwords

In the ELO configuration, passwords can be saved in an encrypted format. There is a small tool that
allows you to enter the password and outputs the encrypted version, which is then entered into the
[Link] file.

If a script needs access to the encrypted password, or you want to use the ELO encryption for your
own passwords, you can do so as follows:
157 Programming for ELO

var password = "52-247-139-10-8-11-59-34"; // crypted password

try{
var des = new [Link]();
password = [Link](password);
} catch (desEx){
[Link]("Cannot decypt password");
}

Determine the ELO document type from a file extension

When you file a document to the ELO client, the client automatically identifies the corresponding
ELO type from the extension. If you file a document using ELOas, the ELOas script assumes this
task. The ix module provides a method that also accomplishes this.

[Link]([Link]("[Link]"));

Move a document to another document path

You may not be able to file documents to any file path, but you can at least move documents
between the document paths defined in ELO. This is helpful, for example, when you want to export
documents to a different physical medium based on a specific criterion. In this case, create a new
document path in the ELO Administration Console.

Next, create a ruleset that performs a search for the criterion – such as ARCHIVESTATUS = "Planned
for archiving". You can create this ruleset using a timer control so that it runs during the night or on
the weekend, meaning it doesn't disrupt normal operations when processing large document
volumes. You now have to perform two actions within the rule:

1. Set the document path to the other medium (PathId in ARCHIVE_PATH).


2. Switch the archiving status to "Archived" to remove the entry from the search.

[Link]([Link], ARCHIVE_PATH);
ARCHIVSTATUS = "Archived";

Search for timestamps

The ELO database contains a timestamp indicating the last time an entry was edited. This is
primarily intended for the Replication module, but it can also be used in ELOas.
158 Programming for ELO

For example, if you want to regularly perform a plausibility check on entries with a specific
document type, you would theoretically need to hook into every write access and perform the
check then. But it is easier to check asynchronously in ELOas.

To perform this check, create a ruleset for this metadata form. You must manually edit the search
section, as the GUI does not offer the settings required for a timestamp search.

<search>
<name>"ELOTIMESTAMP"</name>
<value>"2013.[Link].00...2013.[Link].59"</value>
<mask>0</mask>
<max>200</max>
</search>

In practice, of course, you will not have a fixed time range, but rather a moving window, such as
"24 hours ago until now". In this case, you have to set the value in the value tag via the script. The
appropriate place for this is the onStart event, which is performed before the search.

var now = new Date();


nowTS = [Link] ( now );
[Link]( [Link]() – 1 );
yesterdayTS = [Link] ( now );
EM_SEARCHVALUE = yesterdayTS + “...” + nowTS;

Insert a watermark into a PDF document

ELOas has a number of utility functions that insert text and images into PDF and TIFF files. As an
example, the following shows how to insert a watermark into a PDF document.

To do this, you must first load the document file to the temp directory, then call the insertTextInPdf
function. This contains the text, the position, and the text color and transparency level as
parameters. Last, the edited PDF file is checked in as a new version and the temporary file is
deleted.

var file = [Link]([Link]);


[Link]("Confidential", file, 3, 200, 50, 150, 255, 0, 0, 0.5, 50);
[Link]([Link], file);
[Link](file);
159 Programming for ELO

Fig.: PDF document with a watermark

Processing order in the treewalk

When processing a treewalk in ELOas, each node is processed twice. Once when "descending" –
meaning that the folder is called first, followed by all of its child entries. The second time is on the
"return journey" – after all child entries (and the child entries of the children) are processed, the
folder is called again.
160 Programming for ELO

The global EM_TREE_STATE variable is set so that you can distinguish between the two calls. It is set
to 0 when descending and 1 when ascending. If a value is changed, this only happens on the return
journey. For this reason, you should generally check for EM_TREE_STATE == 1 during processing.

[Link]("# Rename document");


if (EM_TREE_STATE == 1 && NAME == "Wait for Fulltext") {
[Link]("################ 1b " + NAME);
NAME = NOPROJECT;
EM_WRITE_CHANGED = true;
[Link]("# Document successfully renamed");
}

Form-independent rulesets

Normally, ELOas performs a search for a specific metadata form and search term, then processes
the results list, where all entries have the same metadata form. When you perform a treewalk or
workflow search, though, you may get results with different metadata forms. The same thing
happens when you use a search-only form.

To process entries that don't match the form, you have to query the EM_MASK_LOADED global
variable. If the value is set to true, the Sord object is read and entered into the predefined variables
(such as NAME, ELOOUTL1, CUSTNO, etc.). However, this only happens when the Sord object
corresponds to either the search form or one of the other target forms. Otherwise,
EM_MASK_LOADED is false, which means that you need to process the Sord object directly.

Unfortunately, saving is just as complicated. The basic data is always written back from the global
variables. However, these are always completed during reading. The index fields are only written
back from the global variables when an appropriate metadata form is found. In this case case, you
would need to enter the index fields to the global variables for appropriate metadata forms, and
otherwise would need to enter them directly to the Sord object. You can avoid this work by setting
the EM_MASK_LOADED variable to -1. The index values are then only written back from the Sord object
and the global variables will be ignored.

Information

The basic data (short name, date, etc.) is ALWAYS entered into the global variables and
written back from there, as it is independent of the metadata form.

There is a separate process for workflows. Workflow entries in a ruleset are normally only
processed when the metadata form matches the search form configured in the ruleset. As
workflows are generally linked to metadata forms, this setting usually makes sense. However,
sometimes you may have a task that requires multiple metadata forms. In this case, you need to
specify in the onstart event that you want to process all workflow tasks.
161 Programming for ELO

<onstart>
EM_ALLOWALLMASKS = true;
</onstart>

Active workflow tasks for deleted documents

By default, ELO Indexserver does not send out workflow tasks for documents that have been
deleted. However, this can mean that the system accumulates unprocessed workflows. As these
are not visible, they cannot be completed or forwarded.

However, you can change an option to force these tasks to be sent. You only need to set the
EM_WF_WITH_DELETED variable to true in the onstart event.

<onstart>
EM_WF_WITH_DELETED = true;
</onstart>

In this case, however, you should be cautious with actions that will be performed – not all actions
are possible or allowed for deleted entries.

When a document is deleted via ELOas, you can use the [Link] command
to ensure that all workflows are deleted for an entry.

[Link]([Link]);

[10] Processing list from a registered Indexserver function

With registered functions, you execute predefined scripts on the ELO Indexserver. You can use this
function to determine the processing list in ELOas.

To use it, a script must exist on the Indexserver with the registered function. These script functions
start with an "RF_" prefix in their name – such as RF_selectedCustomers. An ELOas search of the
registered function can use a string as an input parameter (using EM_SEARCHVALUE). The function
must deliver a list of ELO object IDs – comma separated and without spaces – as return value.

function RF_ selectedCustomers ( ec, args ) {


[Link]("Start RF_ selectedCustomers : " + args);
if (args == "NORTH") {
return "3581501,3453004";
} else {
return "3581489";
162 Programming for ELO

}
}

In the ELOas ruleset, you only have to enter the name of the registered function to EM_SEARCHNAME,
and optionally the parameters to EM_SEARCHVALUE. This can also be adjusted at runtime in the
onstart event.

<ruleset>
<base>
<name>processCustomers</name>
<search>
<name>"RF_ selectedCustomers"</name>
<value>"WEST"</value>
<mask>0</mask>
<max>10</max>
</search>
<interval>1M</interval>
<onstart>
</onstart>
</base>
<rule>
<name>Rule1</name>
<script>
[Link]("process customer: " + [Link] + " : " + [Link]);
</script>
</rule>
<rule>
<name>Global Error Rule</name>
<condition>OnError</condition>
<script></script>
</rule>
</ruleset>

[10] Edit all document types in a treewalk

When ELOas loads an entry, it enters the contents of the index fields to predefined global variables
with the group name of the index field. When writing to the entry, this occurs in the reverse order –
the index fields in the Sord object are completed with the global variables before the entry is
saved. The major advantage for users is that they do not have to search the objkeys array and
read or write the data array; rather, they can simply write CUSTOMERNO = 12345.
163 Programming for ELO

During a search, this is useful because the search is generally performed on a metadata form. It
also works when the user changes the metadata form within the rule, as the predefined global
variables are generated, read, and written for all specified forms in the ruleset.

This becomes more difficult when the search runs on a search form or in a treewalk. In these cases,
the results list may contain multiple metadata forms. As long as you only need to deal with a few
forms, you can enter the additional forms to the ruleset. Both while reading and while writing,
ELOas looks at the current metadata form for the Sord object and reads or writes the corresponding
global variables.

A treewalk may generally encounter every existing metadata form, meaning it is no longer
practical to enter all of the metadata forms as additional forms. In this case, you can work directly
with the Sord object. The EM_MASK_LOADED variable contains the metadata form number if the
metadata written to the global variable for a specified form could be loaded, or a value of -1 if this
was not possible because an unknown form number was found. The EM_INDEX_LOADED variable is set
to true if the form is known or false if not.

The current values can easily be read from the Sord object. However, you should be more cautious
when writing. If the metadata form is unknown, you can write directly to the Sord object. If it was
predefined, though, the global variables are written back to the Sord object at runtime, which
immediately overwrites any changes you made. For these metadata forms, you have to enter data
for the global variables, not the Sord object. This may complicate programming significantly — but
there is a way out. When you set the value of EM_INDEX_LOADED to false in your rule, the runtime
environment will assume that the global variables weren't completed and it will not overwrite the
Sord object.

Information

Form-independent data, such as NAME, OBJCOLOR, OBJDESC, OBJTYPE, and DOCDATE, is


always written to the global variables and written back from there. You should edit the
NAME and DOCDATE variables directly, not in the Sord object.

For workflow rulesets, the search is automatically restricted to the specified metadata form. If you
want to process workflows for all metadata forms, you must set the EM_ALLOWALLMASKS value in the
onstart function to true.

[10.1] Merge TIFF documents

As of version 10.1, ELOas uses a different graphics library to merge TIFF documents. The original
Java library occasionally has problems loading plug-ins when running as a servlet, particularly in
combination with Aspose libraries. In addition, the Java library imported and wrote out the images,
meaning that they had to be decompressed and compressed again.

This is not necessary with the new library as it works at the level of the TIFF directory level instead
of at the image level. This is faster and more reliable. If you want to use the new library, you need
to call mergeTiffFiles2 instead of mergeTiffFiles.

ELOAsTiffUtils.mergeTiffFiles2(File[] sourceTiffFiles, File outputFile)


164 Programming for ELO

General remarks on JavaScript

Delete the first element in an array

One available JavaScript command is shift, which deletes the first element from an array. This can
sometimes be really useful.

If you use the call on a Java array, you do not get an error message – even though the command
does not exist in Java. It also does not offer handling as in JavaScript. Instead, you get something in
between that you are unable to use. The array length remains the same, the first entry is deleted,
all other entries are brought forward by one, and the last entry is doubled.

Strictly speaking, this is not a bug in the Rhino Engine, but rather a programming error in the script.
Though the Rhino Engine could have been nice enough to throw an exception. Since it doesn't, I can
only try to inform others of this ugly behavior. Real JavaScript arrays have no problems working
with the shift command.

Property or function notation

Several examples here use the property notation, such as [Link] instead of [Link](). This
is a utility function in the Rhino Engine. When Java objects are accessed that provide a setXyz and
getXyz function, you can also directly read and write to the xyz property in JavaScript instead.

In principle, both notation methods work the same. We decided to use the property notation
because it is clearer and easier to read.

For example: An entry is forwarded, so it should be given the prefix FW:

[Link] = "WG: " + [Link];

[Link]( "WG: " + [Link]() );

In the first case, you can much more quickly and more clearly what happens. However, that's just
our preference. In your own scripts, you can decide for yourself whichever variant you prefer. We
only suggest to remain consistent. Decide on one of the variants and use it throughout your
projects.

Line breaks in regular expressions

If you want to analyze text in the Java Client (or in ELOas or the Web Client) that has line breaks
and the line break is an important position specification, you can simply work with \n.

The world of line breaks is complex. Sometimes, you will see a line feed, sometimes a carriage
return, and often in the Windows world, you will see both at once. You will need to take this into
165 Programming for ELO

account when you are creating your regular expressions. The notation for "A and in the next line
B..." would be "A(?:\r\n|\n|\r)B…". But fortunately, there is also a way to abbreviate it: "A\RB...", i.e.
\R as a placeholder for any kind of line break.

[10.1] Switch case with Java String objects

There are regular posts in the forum from programmers who are faced with the problem of being
able to run switch statements on JavaScript strings but not on Java strings.

var user = **String(**[Link] );

switch (user) {
case "Ad":
[Link]("SUPPLIER", "Administrator");
break;

case "HM":
[Link]("SUPPLIER", "Heinz Mustermann");
break;

default:
[Link]("SUPPLIER", "Unknown default");

A switch on a Java String always runs to the default option because the Java String does not equal
a JavaScript string! This is one of the few times where you have to be sure about whether you are
working with a Java object or a JavaScript object.

[10.1] Access files from scripts

Direct file access is not an innate function in JavaScript. Since a sandbox is built into the browser,
access to local resources is blocked. This has been worked around to some extent since the launch
of html5 in that users now have restricted access to local memory, but they are still not able to
access entire file systems.

ELO scripts that run on the Rhino engine (Java Client, ELOas, Indexserver) can access any Java
classes and therefore manage file access. To be able to do so, you need to know what is available.
Unfortunately, the Java file interface is somewhat complicated, so I have listed the most important
classes and methods:

File[]
166 Programming for ELO

The File object is a base for files and folders. It can be used to query whether the file exists as well
as to create folders. This requires a packages import for "[Link]".

var myDir = new File("d:\\temp\\dirTest");


if ([Link]()) {
if ([Link]()) {
[Link]("Directory already exists");
} else {
[Link]("Directory cannot be created as a file with that name already exists"
}
} else {
[Link]();
[Link]("Directory was created");
}

In addition to the method mkdir, which creates a directory, there are also mkdirs, which you can use
to create a directory as well as to create the complete path if it does not already exist. This means
that you do not have to go through entire directory level by level to check whether the child
directory already exists.

The File object has a number of other useful methods for determining file size ( length()), write
protection (isWriteable(), setWriteable()), file date (lastModified(), setLastModified()), rename
(renameTo()), and delete (delete()).

Files

The Files class provides some useful functions for reading and writing entire files. This requires a
packages import for "[Link]".

var fileContent = "This is my file content\r\nWith two lines and umlauts: äöü.";
var file = new File(myDir, "[Link]");

var bytes = new [Link](fileContent).getBytes("UTF-8");


[Link]([Link](), bytes, [Link]);

var readBack = [Link]([Link]());


var content = new [Link](readBack, "UTF-8");

[Link](content);

If you need a temporary directory (in the user's Temp folder), you can create it using the
createTempDirectory method (there is also a createTempFile method):
167 Programming for ELO

var tempDir = [Link]("scripttest").toFile();


[Link]([Link]);

FileStore

You can also use the [Link]() method to query file system parameters for a file path.

var file = new File("d:\\temp");


var store = [Link]([Link]());
var msg = "Total: " + [Link]
+ ", usable: " + [Link]
+ ", unallocated: " + [Link]
+ ", name: " + [Link]();

[Link]("FileStore", msg);

[12.0] Date format

If you'd like to create a filing structure based on years or months, you have to generate an output
such as "March 2019" from the ELO ISO date. If you often need different formats, you should
familiarize yourself with the Java SimpleDateFormat, which unfortunately is not so simple. If you
only need this simple format, you can stick to JavaScript.

The formatMonthYear function expects an ELO ISO date format (YYYYMMDD) as the parameter and
returns the name of the month and year.

The testFormatter() function is used for test purposes. You can leave it out of your finished script.

function testFormatter() {
var result = "";
for (var i = 1; i <= 12; i++) {
var isoDate = "2019" + ((i < 10) ? "0" + i : i) + "22";
var fmtDate = formatMonthYear(isoDate);
result = result + isoDate + " : " + fmtDate + "<br>";
}

[Link]("Date Test", result);


}

var translate = [];


168 Programming for ELO

translate["01"] = "January";
translate["02"] = "February";
translate["03"] = "March";
translate["04"] = "April";
translate["05"] = "May";
translate["06"] = "June";
translate["07"] = "July";
translate["08"] = "August";
translate["09"] = "September";
translate["10"] = "October";
translate["11"] = "November";
translate["12"] = "December";

function formatMonthYear(eloIsoDate) {
var month = translate[[Link](4, 6)];
return [Link](0, 4) + " " + month;
}
169 Programming for ELO

Indexserver functions

Check logons

If you need to prevent a user from logging on, you can simply lock the user in the ELO
Administration Console.

If you only want to keep users from logging on for a certain amount of time (such as at certain
hours or on certain days), or only allow certain clients, the best way to do so is with the
onBeforeLogin Indexserver event.

The advantage of this is that it works with all (ELO Indexserver) clients, not just for the ELO Java
Client.

function onBeforeLogin(ec, userName, options) {


if (userName == "OnlyWithWebClient" && [Link]() !=
LoginScriptOptionsC.CLIENT_NAME_WEBCLIENT) {
throw "User may login only with the WebClient";
}
}

Complete index field values when forwarding workflows (1)

Within workflows, it is frequently necessary to change an index field value. If this is a calculated
value, you can enter a corresponding script that performs the calculation. However, often these are
static values, such as changing a status value to "Finished" when an end state is reached.

A task like this can be handled using a generic script that is created once and can then be used in
all workflows. The script is entered into the end event and reads the information to be entered into
the field from the workflow node comment field, updating the metadata accordingly.

The comment field formatting looks like this: Each entry contains one line, so several metadata
terms can be changed at once. Each line consists of three parts, separated by pipe symbols:
metadata form name|group name|text.

Figure : Text in the comment field to be entered in the metadata


170 Programming for ELO

If you want to complete an index field in different metadata forms, you can enter an * as the
metadata form name. In this case, each form is searched for the field. If the search locates the
field, the script enters the new value to it. If it doesn't, nothing will happen. The same applies if the
metadata form is not applicable. This isn't an error either; the line is skipped.

function onExitNode( ci, userId, workflow, nodeId ){


var node = [Link][nodeId];
[Link]("This is node: " + [Link] + " : " + [Link]);
var desc = [Link];
var lines = [Link]("\\R");

var checkinPending = false;


var objid = [Link];
var editInfo = [Link](ci, objid, [Link]\_INFO.mbSord, [Link]);

for (var i = 0; i < [Link]; i++) {


var parts = lines[i].split("\\|");
if ([Link] != 3) {
[Link]("Invalid entry ignored, number of parts: " + [Link] + " : " + lines[i]);
} else {
if (parts[0] == "*") {
parts[0] = [Link];
}

if ([Link] == parts[0]) {
var keys = [Link];

for (var j = 0; j < [Link]; j++) {


var key = keys[j];
if ([Link] == parts[1]) {
[Link] = [parts[2] ];
checkinPending = true;
[Link]("Key found and set: " + parts[2]);
}
}
} else {
[Link]("Other mask: " + parts[2]);
}
}
}

if (checkinPending) {
[Link](ci, [Link], [Link], [Link]);
} else {
[Link](ci, [Link], [Link], [Link]);
171 Programming for ELO

}
}

Complete index field values when forwarding workflows (2)

If you want to enter a calculated value to an index field, you cannot avoid using a custom script.
But this is also made significantly easier by the fact that it only needs to fulfill a single task.

function onExitNode( ci, userId, workflow, nodeId ){


var objid = [Link];
var editInfo = [Link](ci, objid, CONST.EDIT_INFO.mbSord, [Link]);

keys = [Link];

// The calculation follows here


keys[0].data = ["Forwarded on " + new Date()];

[Link](ci, [Link], [Link], [Link]);


}

Consistency checks when saving

Many processes have consistency requirements that always need to be fulfilled. For example: A
"CHECK" status field can only be set to "Complete" when the "AUDITOR" field is not empty. You can
check these dependencies in the client with a script. If users work with various clients, though, this
may require a lot of work to make sure the check is carried out in all required places.

Here it can be useful to put the task on the Indexserver. You can check the index fields in the
onBeforeCheckinSord script event and throw an exception in case of errors. This prevents incorrect
data from being written.

function onBeforeCheckinSord(ec, sord, sordDB, parentSord, sordZ, unlockZ) {


if ([Link] == 2) {
var keys = [Link];
if (keys) {
var hasName = false;
var isClosed = false;
for (var i = 0; i < [Link]; i++) {
var key = keys[i];
if (([Link] == "ELOOUTL1") && [Link] && ([Link] > 0)) {
hasName = [Link][0] != "";
172 Programming for ELO

}
if (([Link] == "ELOOUTL2") && [Link] && ([Link] > 0)) {
isClosed = [Link][0] == "closed";
}
}

if (isClosed && !hasName) {


throw new [Link]("[ELOIX:" + IXExceptionC.NOT_IX + "]
Cannot close without name");
}
}
}
}

Fig.: Error message when saving invalid data

Information

When you are performing the check on the server side, it can be useful to include an
additional check on the client as well. This approach has the advantage of being closer to
the user: for example, it can happen right when the user leaves the index field and output a
meaningful error message. The Indexserver check is only performed during saving and
returns a more general error message.

Delete annotations during check-in

When checking in a document, users have the option to delete existing annotations. For some
documents, though, you will want to make sure that all annotations have been removed when a
new version is created. You can do this with an Indexserver event: onAfterCheckinDocEnd. The script
event first checks whether the special document type is available (based on the metadata form
173 Programming for ELO

name in this case). Next, the notes are read, then annotations filtered out and entered into an array
for deletion. The margin notes remain stored in this example.

function onAfterCheckinDocEnd(ec, sord, sordDB, parentSord, doc, sordz, lockz) {


if ([Link] == "TestForm") {
var id = [Link];
var editInfo = [Link]([Link], id, CONST.EDIT_INFO.mbNotes, [Link]);
var ids = [];
var notes = [Link];
for (var i = 0; i < [Link]; i++) {
var nt = notes[i].type;
if ((nt != [Link].TYPE_NORMAL) && (nt != [Link].TYPE_PERSONAL)
&& (nt != [Link].TYPE_STAMP)) {
[Link](notes[i].id);
}
}

if (id [Link] > 0) {


[Link]([Link], ids, [Link]);
}
}
}

Create a preview for documents

To see if a document has already been sent to the Preview Converter, you can check the elodmdocs
table in the previewsize column. Unfortunately, there is currently no function in the Indexserver API
to control this value, so you may need to manually access the database to work with it.

If a document hasn't yet been converted, the column contains a value of 0. The Preview Converter
frequently requests this list from the Indexserver and processes the entries. If a document could
not be converted, previewsize is set to -1. This indicates to the ELO Indexserver that no preview
image exists, but the entry has already been processed.

When a single document needs to be converted again, you can do so using the following statement
(please note that the docid is the file ID and not the logical ELO object ID):

update elodmdocs set previewsize = 0 where docid = 3450695

If a new version of the Preview Converter can better handle a certain document type than in the
past, it is easy to register all documents with that type for conversion again:
174 Programming for ELO

update elodmdocs set previewsize = 0 where ext = 'pdf'

It is also possible that a new version is able to convert documents successfully that an older
version could not handle. In this case, you can simply reset all locked documents to 0:

update elodmdocs set previewsize = 0 where previewsize = -1

[10.1] Run Indexserver scripts with elevated permissions

Indexserver event scripts are usually run with the permissions of the user that triggers the event.
This ensures that users are unable to perform actions in the script if they do not have the
permissions required.

However, there are cases where that is exactly what is required. In such cases, the Indexserver
provides the administrative ClientInfo ciAdmin in addition to the normal ClientInfo ci. If you use this
ClientInfo in the script for Indexserver calls, you will be able to work with elevated permissions.

var subsInfos = [Link](ciAdmin, userId, [Link]);

Please note

It is important to note that you are now responsible for managing permissions/restrictions in
your script. Errors in your script could mean that users have full access to all objects in the
repository!

[10.2] Blacklist for specific document types

A customer security audit criticized the fact that EXE files could be checked in. If another user
opens this file for viewing, a ShellExecute("view"...) is called. For Windows "Show EXE" means
"Run EXE".

To prevent this, you can use an Indexserver event script that checks the extension of a new
document and throws an exception in the event of an error. From a usability perspective, this isn't
the most elegant solution since the user is confronted with a very technical error message – but it
serves the purpose.

var forbiddenExtensions = ["exe", "bat", "scr", "xyz"];


175 Programming for ELO

function onBeforeCheckinDocEnd(ec, sord, sordDB, parentSord, doc, sordZ, lockZ) {


[Link]("onBeforeCheckinDocEnd: " + [Link]);
if ([Link]) {
for (var i = 0; i < [Link]; i++) {
check([Link][i]);
}
}

if ([Link]) {
for (var i = 0; i < [Link]; i++) {
check([Link][i]);
}
}
}

function check(doc) {
var ext = [Link]();
[Link]("check: " + ext);
for (var i = 0; i < [Link]; i++) {
if (forbiddenExtensions[i] == ext) {
throw "Invalid document extension";
}
}
}

Fig.: Error on drag-and-drop

[10.2] Show external files in the repository

When migrating from another archiving system to ELO, in some cases it is not desirable to copy the
documents because they would then exist twice, at least temporarily. In case of WORM media or
hard disk appliances with a retention period in particular, this can waste memory permanently.
176 Programming for ELO

For this reason, the Indexserver has a function to create new documents and transfer the file path
instead of a complete file.

Please note

Note that ELO does not have any control over the files, the life cycle of these files, or their
write protection. These are all tasks of the administrator.

In preparation, you have to configure access to these documents such that they are visible from
within ELOdm, e.g. via a drive or file share. Then, you have to create a path to this path in the
Administration Console. The structure for this path must be "Custom path". As the file path, enter
the part of the path common to all documents. In our example, this is C:\elo10\external.

Fig.: ELO Administration Console document paths

In the following, I'd like to show how the Indexserver function can be used to create this type of
document. This example uses the Java Client, but can easily be applied to ELOas.

function addFile(parentId, name, relpath) {


var ed = [Link](parentId, "0", null, [Link]);
var sord = [Link];
[Link] = name;

var dv = new DocVersion();


177 Programming for ELO

[Link] = 4;
[Link] = relpath;

var doc = [Link];


[Link] = [dv];

[Link](sord, [Link], doc, [Link]);

function externalTest() {
addFile("211081", "Picture 1", "group1\\[Link]")
addFile("211081", "Picture 2", "group1\\[Link]")
addFile("211081", "Picture 3", "group1\\[Link]")
addFile("211081", "Text 1", "group2\\[Link]")

First, you create an empty logical document using createDoc. To keep things simple, only enter the
name in the metadata. Next, create a DocVersion object and enter the path ID from the new path.
The
Administration Console indicates this in the path definition. Enter this section of the path under
relativePath, which is not covered by the path definition. An example: For "c:
\elo10\extern\group1\[Link]", the relative path group1\[Link] is entered since the
beginning "c:\elo10\external" is covered by the path definition. Finally, enter the DocVersion object
into the metadata object and save with checkinDocEnd. In this case, a document file not copied, but
ELOdm creates a reference to the original file.

In version 10.2, these document cannot be added to the full text database since ELOdm is unable to
save the full text file. This will change in version 11.

[20.0] Unexpected behavior with [Link]

The utility function [Link] also directly executes a checkinSord. This can lead to
unnecessary write actions if you want to update multiple index fields. It causes an even bigger
problem if you call the function in the checkinSord event, which performs a new check-in action
and you may get stuck in an infinite recursion (it is actually not really infinite at some point the
stack is full and the Rhino thread is killed).

We don't want this unexpected behavior but for compatibility reasons, you can't just change it,
because there might be a lot of scripts that use this feature.

Instead of the SordUtil utility function you can also use a self-defined function that does without a
checkinSord:
178 Programming for ELO

function setObjKeyData(sord, okeyName, okeyData) {


for (var i = 0; i < [Link]; i++) {
var okey = [Link][i];
if ([Link] == okeyName) {
if (okeyData) {
[Link] = [Link](okeyData) ? okeyData : [okeyData];
}
else {
[Link] = [];
}
break;
}
}
}
179 Programming for ELO

Forms

Send a message to the client

A browser script is run in a sandbox and therefore can perform hardly any local actions. To improve
integration with the environment and the client, a form script can send messages to a client script.
The necessary local actions can then be performed by a client script.

The example uses a small form with an MSG_TEXT input field for the message and a JS_SEND
button to trigger the action. The reply from the client script is shown in a separate field
(MSG_RESPONSE).

Fig.: Form with an input field for the client script

To test it, complete the message field and click the Send button. The form script is then started,
which reads the text field and creates a message. This is then sent to the client script, which in turn
generates a reply that is shown in the MSG_RESPONSE field. Only simple strings are transferred in
the example, but in principle you can send any object you like.

function JS_SEND() {
var message = $val('MSG_TEXT');
var data = {text: message};
[Link]('ThmMsg', data, function(data, event){
$update('MSG_RESPONSE', [Link]);
});
};

The sendCustomMessage method carries out the actual sending. The first parameter contains a name
for the message, since various sources are able to send data, and the recipient must be able to
recognize the relevant data for it. The second parameter contains the data object to be transferred.

The third parameter is somewhat strange for "traditional" script developers, but it's normal for web
developers – it is a callback function, which is called when the client script sends the reply. This
approach is common in the area of web development. Actions that require more time are sent, but
the script does not wait for the reply. The script continues on right away. A callback function is
180 Programming for ELO

defined for the reply, which only becomes active when the reply arrives. This helps avoid a long-
running external action from locking up the complete browser scripting interface.

The callback function has two parameters — the first contains the data object returned by the client
script. The second contains an event object.

In the client, an event function is created that is called when a message arrives from the browser. It
first checks whether the name of the message is intended for the script, and only then is it
activated.

function eloReceiveBrowserMessage(msg, compName){


if ([Link] == "ThmMsg") {
var respObject = {response: 'jc: ' + [Link]};

var browserComp = [Link](compName);


[Link](msg, respObject);
}
}

An object with a response property is created as the reply. The value is set to the fixed string "jc:",
followed by the sent text. In this way, the client script works as an echo function. Next, the client
script determines the source, and there calls the sendCustomMessage method to send the reply.

Send a message to a form

Sending a message from a client script to a form works similarly to the previous example. I have
added an MSG_CALL output field to the form in the example, which displays the client message.

Fig.: Form with an input field for the client script

There is a button click event in the client that sends a message to the currently visible form.

var count = 2;
181 Programming for ELO

function eloScriptButton100Start(){
var browserComp = [Link](
CONSTANTS.BROWSER_COMPONENT_NAME.FORMULAR_PANEL);

var fctParamsObject = {
text: "This is text from the Java Client",
count: count++
};

[Link]( "ThmCall", fctParamsObject, "browserResponse" );

The script first identifies the current browser window. Next, the message object is generated and
filled with the values "This is text from the Java Client" and an incrementing counter. Finally, the
object is sent to the browser with the name ThmCall.

Fig.: Display in the form

The browserResponse callback function is registered for the reply in the browser.

function browserResponse(data, msg) {


[Link]("Response", [Link]);
}

The callback function simply shows the returned value in an InfoBox.

An event function must be registered in the browser that is called for messages with the name
ThmCall. This also occurs with the onCustomMessage method. It receives two parameters – the
message name to monitor for and a callback function.

[Link]('ThmCall', function(data, event){


$update('MSG_CALL', [Link] + ' ' + [Link]);
[Link](
event, {response: [Link] + ' ' + new Date()}

);
182 Programming for ELO

});

In the callback function, first the sent text is transferred to the MSG_CALL output field in the form.
Next, the reply is sent back, which is performed by the sendResponse call. In the example, the
current date is simply added to the sent text.

Fig.: Showing the return value

Set the column width in a form

A form consists of a two-dimensional grid with input and output elements. In the form editor, you
can change the size of the elements (such as by selecting the number of characters in an input
field). However, you can't directly change the width of a column, which is usually determined
dynamically from the contents.

You set this explicitly by making a CSS entry under "Edit form header scripts". Since the grid
consists of a TABLE element, you can create a CSS entry for it that defines the width of the nth TD
entry.

In the example, the second column is preset to a width of 600 pixels. Please note that the browser
may deviate from this request if there is insufficient space.

<style type='text/css'>

td:nth-child(2) {
width: 600px;
}

</style>
183 Programming for ELO

Long scripts in forms

The form designer provides you the ability to integrate custom script functions into a form through
the "Edit form header scripts" function. This can be done quickly and easily for smaller scripting
tasks. For longer scripts, though, you would want to use a better structured and feature-filled
JavaScript editor. In this case, it makes sense to put the longer script into one or more files on its
own.

You can save the script file in the "ELOwf Base/Webapp" folder. You can then enter the script file
name into the Frame document. You can then use the script functions right away.

Fig.: Custom script files in ELOwf forms

Please note

Please note that the browser keeps these script files in a cache. This can be especially hard
during the debugging phase. If changes in the script do not appear to take effect, you should
clear the browser cache.

Use a check box as a stamp

This script function shows how to use a check box that can be selected, but cannot be cleared
again. An example of this would be an "Invoice approved" function. The user confirms by clicking
184 Programming for ELO

the check box, but is unable to undo this later. Additionally, a text field with the name of the user
and the date is saved.

It's easy to do this. In the inputChanged script event, the function is called for each monitored check
box. The source parameter of the event is passed as a parameter, as well as the name of the check
box and the editor field.

lockedCheckbox(source, "IX_MAP_CHECKED", "IX_MAP_CHECKEDBY");

When the program starts (source == null), the function checks whether the check box is enabled.
In this case, it is set to read-only, so it can't be changed again. When the check box is set during
editing, the function enters the name and time into the editor field and locks the check box. The
editor field should already have been set to read-only in the form designer. It never needs to be
edited manually.
185 Programming for ELO

// Manages a check box that can be selected but cannot


// be cleared. To record states that cannot be taken
// back after they are set.
//
// source : Input element of the check box
// cbName : Name of the variable containing the check box state
// confirmName : Name of the variable containing the signature text
// enabled : Release check box
//
function lockedCheckbox(source, cbName, confirmName, enabled) {
if (source) {
// Processing
if ([Link] == cbName) {
if ([Link]) {
$update(confirmName, ELO_PARAMS.ELO_CONNECTUSERNAME + " - " + toDay());
}
}
} else {
// Initialization
var check = $var(cbName);

if ([Link] && ($val(confirmName) == "")) {


[Link] = false;
}

if ([Link] || !enabled) {
[Link] = true;
}
}
}

Get name from array variables

Arrays in index fields or map fields are generated in ELOwf by using an ascending numbering
scheme in the name: customer1, customer2, etc.

If you have a name and an index, it is no work at all to create the map field name:

var mapName = name + index;

The way in reverse is somewhat more involved. When you have the combined name, what is the
basis name? The following function searches for the last number combination in a string and cuts it
off. The basis name then remains.
186 Programming for ELO

// Returns the part of the name without the row number.


getNameFromName : function(name) {
var pos = [Link](/\d+$/);
if (pos > 0) {
name = [Link](0, pos);
}

return name;
}

Get index from array variables

Similarly to cutting of the name portion from an array variable, you can also extract the index. The
process is just like the name separation, but here the number portion is cut off and passed to the
parseInt method, so you get a number as return value instead of a string.

function getIndexFromName( name ) {


var pos = [Link](/\d+$/);
if (pos > 0) {
name = [Link](pos);
return parseInt(name, 10);
}

return 0;
}

Access the Indexserver from a form

A form contains access to the ticket of the client calling it in ELO_PARAMS. This ticket can be used
by the form to open a JSON Indexserver connection – without needing the user to enter a password
again.

In the example, the form opens an Indexserver connection when the form loads and retrieves the
Indexserver version number. This is output to the text field with the name MSG_VERSION. The
Indexserver commands and objects can only be used if you embed the "[Link]" file into the
page.

<script type="text/javascript" src="/ix-elo90-web/[Link]"></script>


187 Programming for ELO

function inputChanged(source) {
if (!source) {
var fact = new [Link]('/ix-elo90-web/ix', 'WFScript', '1.0');
var con = [Link](ELO_PARAMS.ELO_TICKET);
var version = [Link]();
$update('MSG_VERSION', version );
}
}

Information

The JSON connection uses the client logon. For this reason, it is closed automatically when
the client closes. You cannot pass on such a URL to other users.

Filter for field contents

For form fields, you can enter a preset standard validation to check specific properties. However,
you also have the ability to enter a filter function here to check the field contents during input and
to change them.

To do so, first create the JavaScript function – which is easiest to do in the form script – and enter
the name of the function to the validation field of the input field. You can use this to exclude
characters from the input or convert characters during input.

function JS_FILTER_Uppercase(front, inserted, back) {


return [Link]();
}

This function is called every time a key is pressed and receives the newly added field contents in
the inserted parameter. The front and back parameters contain the text before and after the new
text (if new characters are inserted into the middle). The return value then determines the actual
new field contents.

Another application could be to enter a serial number that only allows numbers. Letters would be
automatically filtered out, an O (uppercase O) is converted to a 0 (zero), and an l (lowercase L) is
converted to a 1 (one).

function JS_FILTER_Uppercase(front, inserted, back) {


if ((inserted >= '0') && (inserted <= '9')) {
188 Programming for ELO

return inserted;
}

if (inserted == 'O') {
return '0';
}

if (inserted == 'l') {
return '1';
}

return '';
}

Fig.: Entering the filter function

Information

If you need the complete field contents for control purposes, not just the new part, you can
put it together with var all = front + inserted + back.

Lock a tab

It is possible to lock individual tabs in the form depending on the current status or user. The
setTabStatus utility function is available for this. Enter the name of the tab template and the name
of the tab page to be locked (which is also a template) as parameters.
189 Programming for ELO

function inputChanged(source) {
var ena = $val("IX_NAME") == "test";
setTabStatus("repos", "more", ena);
}
190 Programming for ELO

Fig.: Locking a tab

[10] Show custom images in the form

It is very easy to show images from the ELO repository in the form by using the image element in
the form designer. You cannot use it to show images from a web server, though, and definitely not
various images depending on the current metadata.

Such a function can be easily realized with a script. To calculate the URL from the metadata, you
would need a script anyway to keep the additional effort to a minimum.

In the form, create an image element as usual in the appropriate location. The image element must
be given a name, even if it cannot be clearly assigned to an index field. The name is required to
access it in the script later on. In the example, I have named the image element "XYZ".

Fig.: Named image element in the form

Next, go into the form script editor. The inputChanged script event is available there. It is first called
when the page is shown. At this time (and only at this time), the source parameter is null. There
you can enter a function that evaluates the metadata and computes a URL out of it. This simple
example takes the first letter of the entry name and shows a different image depending on whether
it begins with a number, the letters A-H, or another character.
191 Programming for ELO

function inputChanged(source) {
if (!source) {
adjustImage()
}
}

function adjustImage() {
var images = [Link]("XYZ");
if ([Link] > 0) {
var select = ELO_PARAMS.IX_NAME.charAt(0).toUpperCase();
var url = "[Link]

if ((select >= '0') && (select <= '9')) {


url = "[Link]
} else if ((select >= 'A') && (select <= 'H')) {
url = "[Link]
}

images[0].src=url;
}
}
192 Programming for ELO

Fig.: Image in the form

[10.1] Custom background images in a form

It is fairly easy to integrate custom background images into a form. To do so, you need to upload
the image to the Images folder in ELOwf Base. The short name of the file must include the file
extension, i.e. "[Link]". Documents that are stored here can be accessed in the Images
folder of the web server.

Fig.: Images folder in ELOwf


193 Programming for ELO

You still need to use CSS to set the background image in the body tag. You can either do this in the
frame document so that it applies for all forms or in the form itself under "Edit form header
scripts".

The form contains a "<style>" section where you need to enter the name of the image and the
formatting:

<style type='text/css'>
body {
background-image: url("images/[Link]");
background-repeat: no-repeat;
background-position: right top;
}
</style>

The background image is included in the form after you restart ELOwf. Since it is a background
image, it will be overlapped by all the form elements. If you want to have the image in the
foreground, you can use a standard image form element.

Fig.: Background image in the form


194 Programming for ELO

[11.0] Color-code form fields by values

In the Community, someone asked whether a form field can be color-coded as a function of a value
from another field. This can be done using a script.

The updateColor function first checks whether the value field has changed or whether the form is
just loading (source == null). In these cases, the CSS class name has to be entered into the target
field. If another field has been changed, the function does not have to take action, and is left
immediately.

If the target field contains additional CSS classes, you are unable to enter the name, but instead
have to add to it or replace it. The example assumes that no other classes exist.

function inputChanged(source) {
updateColor(source);
}

function updateColor(source) {
if (!source) {
source = $var("IX_GRP_ELOOUTL1");
} else if (source != $var("IX_GRP_ELOOUTL1")) {
return;
}

var text = [Link];


var nameField = $var("IX_NAME");

if ([Link]("Thiele") >= 0) {
[Link] = "formred";
} else {
[Link] = "formgreen";
}
}
195 Programming for ELO

Indexserver events

Generate a margin note when workflow is forwarded

When forwarding a workflow, the comment field was originally intended as a field for job
instructions. It relates exclusively to this node and can be neither written to by a predecessor, nor
be viewed by the successor. Unfortunately, this is not obvious to the user. For this reason, some
users enter a message for the next user there.

In principle, the right place for this information is the feed. However, this feature is not used
actively by all customers. This is why the following script was developed:

When the script is entered into the end event of a person node, it reads the comment field in the
node and, if not empty, creates a margin note containing the text. The margin note is immediately
visible to the next user.

function onExitNode(ci, userId, workflow, nodeId) {


var objid = [Link];
var node = [Link][nodeId];
var desc = [Link];
if (desc) {
var note = ix.createNote2(ci, objid, [Link].TYPE_NORMAL, "");
[Link] = desc;

[Link](ci, objid, [note], [Link], [Link]);


}
}
196 Programming for ELO

Fig.: Margin note created by an event script

E-mail for new workflow tasks

Users who don't regularly work with ELO may not notice when they receive a new workflow task. If
this task is relatively urgent, it is helpful to send the user an e-mail notification.

The onEnterNode Indexserver event is used for this. It becomes active when a new task is assigned
to a user. The script first checks whether a user has forwarded the task to themself (meaning no e-
mail will be sent). Next, an e-mail containing an elodms reference is created and the target address
is found in the ELO user data.

unction onEnterNode( ci, userId, workflow, nodeId ){


try {
var node = getNodeById(workflow, nodeId);
if (node && (userId != [Link])) {
var mail = getUserAddress(ci, [Link]);
var subject = "Workflow task: " + [Link];
var body = '<h1>' + [Link] + '</h1><h4>' + [Link] + '</h4><a href = "elodms://wf/'
sendMail(mail, subject, body);
}
} catch(ex) {
[Link]("Error processing Workflow Info: " + ex);
}
}

function sendMail(to, subject, body) {


var sendMail = new [Link]("mail/SRVPEMAIL02vm");
[Link] = subject;
197 Programming for ELO

[Link] = "eloservice@[Link]";
[Link] = to;
[Link] = body;
[Link]();
}

function getUserAddress(ci, userId) {


var userData = [Link](ci, [userId], CheckoutUsersC.BY_IDS_RAW, [Link]);
var mail = userData[0].userProps[UserInfoC.PROP_NAME_EMAIL];
[Link]("Mail address of user " + userId + " is " + mail);

return mail;

function getNodeById(workflow, nodeId) {


var nodes = [Link];
for (var i = 0; i < [Link]; i++) {
var node = nodes[i];
if ([Link] == nodeId) {
return node;
}
}

return null;
}

The recipient receives an e-mail message with a clickable link that brings the user straight to the
task in ELO.

Fig.: Notification in e-mail message


198 Programming for ELO

Web apps

Lists in ELO web apps

AngularJS provides simple options for displaying lists in a web interface. In principle, you only need
to design a single entry, then place it in a DIV tag with the parameter ng-repeat.

<div ng-repeat="picture in [Link]">


<div class="thumbnail">
<a ng-href="elodms://{{[Link]}}">
<image ng-src="{{[Link]}}">
<h3>{{[Link]}}</h3>
</a>
</div>
</div>

Information

This example also shows how to resolve a call in the ELO Java Client from a web app:
elodms://{entry GUID}

This also works for tasks with the workflow node ID:

<a ng-href="elodms://wf/{{[Link]}}/{{[Link]}}" ng-click="$[Link] = true

Indexserver calls using the JSON API

In the web apps and workflow forms, you can perform direct Indexserver calls using the JSON API.
This is very useful, as it means you don't need to roll out a new server version every time you
extend a function.

However, working with the JSON API is significantly different from normal Indexserver access. JSON
access can be performed synchronously or asynchronously – the first variant is simpler, but not
appropriate for live use and can easily lead to malfunctions. It's better to forget about it. What
we're left with is asynchronous access. In this case, the script continues to run instead of waiting
for the response to an Indexserver command. A callback function is provided for the response,
which is automatically called when the Indexserver provides the data.

The great advantage of this approach is that the user interface remains fluid, instead of constantly
freezing when larger amounts of data are transferred over a slow connection. The disadvantage
from the perspective of the developer is that the program flow is more difficult to see, since it
occurs during a series of independent functions.
199 Programming for ELO

In these cases, a concept should be recommended that uses asynchronous calls, but keeps the
program flowing in the same way. For this purpose, the individual callback functions are embedded
in an object that represents the overall function.

The following example is taken from the UserManager web app and only intended to demonstrate
the concept. It cannot be executed on its own.

Only the overall createTasksFolder function is externally visible. It receives the current user object
as a parameter, as well as a callback function for when the operation completes successfully or an
error occurs.

Since the function contains four Indexserver calls in total, it is divided internally into a
corresponding number of partial functions: checkoutRoot, doCreateSord, doSaveSord, and
doFinalizeSord. These are arranged so that the progress remains in view, even if each function is
individually called as a callback.

In principle, each partial function ends with an Indexserver call. This receives an AsyncCallback
object with the next partial function, which is executed as soon as the results are available. It is a
completely different programming methodology that you will need to get used to. In the area of
web development (not just for ELO), there is no way around this.

/**
* Asynchronous call to generate the user's tasks folder.
*
* @param {Model} user
* @param {Function} okResult
* @param {Function} errorResult
* @returns {undefined}
*/

createTasksFolder: function(user, okResult, errorResult) {


var checkoutRoot = function() {
// Step 1: Find the "Users" base folder
var cb = new [Link](doCreateSord, errorResult);
var path = "ARCPATH[" + [Link] + "]:/Users";
[Link]().checkoutSord(path, [Link].EDIT_INFO.mbSord, [Link], cb);
};

var doCreateSord = function(parentFolder) {


// Step 2: Generate the user folder Sord object
var cb = new [Link](doSaveSord, errorResult);
[Link]().createSord([Link], "1", [Link].EDIT_INFO.mbSord, cb);
};

var doSaveSord = function(destinationFolder) {


// Step 3: Configure and save the user folder
200 Programming for ELO

var sord = [Link];


var userid = [Link]().[Link];
[Link] = "User." + userid;
var acl = [new [Link](31, 1, null, 0), new [Link](17
[Link] = acl;
[Link] = new [Link]();
[Link] = sord;
var cb = new [Link](doFinalizeSord, errorResult);
[Link]().checkinSord(sord, [Link], [Link], cb);
};

var doFinalizeSord = function(id) {


// Step 4: Enter the folder ID
[Link] = id;
okResult([Link]);
};

checkoutRoot();
},

Configuration variables in the scope of an app

AngularJS provides a scope for each call that serves as a model for the current data. In other words,
this is where you save your own values. However, you shouldn't place all of your variables in the
scope, but rather create a custom object for the variables.

This has two reasons: first, this avoids conflicts caused by random matching names with different
values in various parts of the program. Specifically when using libraries, it isn't always clear to the
developer which names can be used within the library. Also, saving variables directly may lead to
unexpected problems in inherited scopes. Saving variables in the scope may result in them existing
in the (possibly temporary) inherited scope and not in the base scope. A developer will then wonder
why the value is saved, but no longer exists just a bit later. Using an object avoids this problem, a
problem that is only magnified through the asynchronous processing of callback functions.

[Link]('PictureCtrl', function($scope) {
$[Link] = {};

$[Link] = function(result) {
$[Link] = result;
$[Link] = [Link];


201 Programming for ELO

Drag-and-drop

ELO web apps provide built-in support for dragging and dropping entries. An entry that can be
moved with drag-and-drop must contain the attribute ui-draggable="true". If you need a
notification at the end of the drag-and-drop operation, you have to register an on-drop-success
callback function. This function can delete an object from the old folder list, for example.

<div ui-draggable="true" drag="item" on-drop-success="dropSuccessHandler($event, item,


area)" ng-repeat="item in [Link]">

On the possible target page, you have to save a callback function to handle the drop process. This
function can, for example, enter the object into the new folder list.

<div ui-on-drop="dropItemHandler($event, $data, area)">

Fig.: Dragging and dropping an entry

You might also like