Best Practice Web Client Scripting
Copyright © 2016 Lexmark. All rights reserved.
Lexmark is a trademark of Lexmark International, Inc., registered in the U.S. and/or other countries. All other trademarksare the property of their respective owners. No part of this publication may be reproduced, stored, or transmitted in anyform without the prior written permission of Lexmark.
Table of Contents
1 Introduction ......................................................................................................... 1
1.1 Scriptable Web Client Components ................................................................... 1
1.2 Scripting Prospects and Types ........................................................................... 1
1.2.1 Client-side Scripting ........................................................................................ 1
1.2.2 Server-side Scripting ....................................................................................... 2
1.2.3 ZUL Scripts ...................................................................................................... 2
2 Scripting API and Principles ............................................................................... 2
2.1 The $component$ Placeholder .......................................................................... 3
2.2 Full Qualified Class Names ................................................................................ 3
2.3 Mask Cache ......................................................................................................... 3
3 Web Client Scripting API .................................................................................... 3
3.1 Form API .............................................................................................................. 4
3.2 Element API ......................................................................................................... 4
3.3 Results List API ................................................................................................... 4
4 Events ................................................................................................................... 5
4.1 ZK Events ............................................................................................................. 5
4.2 ResultSet Events .................................................................................................. 5
4.3 Form Events ......................................................................................................... 5
5 How to Start Scripting ........................................................................................ 6
5.1 The Forms Designer ........................................................................................... 6
6 Script Examples ................................................................................................... 7
6.1 Field Validation .................................................................................................... 7
6.1.1 Scenario ........................................................................................................... 7
6.1.2 Example Code .................................................................................................. 8
6.1.3 Process Description ........................................................................................ 8
6.2 Default Values of Lookup Table .......................................................................... 8
6.2.1 Scenario ........................................................................................................... 8
6.2.2 Example Code .................................................................................................. 9
6.2.3 Process Description ........................................................................................ 9
6.3 Sending Selected Data to an External Application ............................................ 10
6.3.1 Scenario ........................................................................................................... 10
6.3.2 Example Code .................................................................................................. 10
Example Server-side Code ............................................................................ 10
Example Client-side Code ............................................................................. 10
6.3.3 Process Description ........................................................................................ 11
6.4 Restricting a Query ............................................................................................. 11
6.4.1 Scenario ........................................................................................................... 11
6.4.2 Example Code .................................................................................................. 11
6.4.3 Process Description ........................................................................................ 13
6.5 Display "Select user..." Dialog ............................................................................ 13
6.5.1 Scenario ........................................................................................................... 13
6.5.2 Example Code .................................................................................................. 13
6.5.3 Process Description ........................................................................................ 15
6.6 Open Index Form of eFile Application in a Pop-up Window ............................. 15
6.6.1 Scenario ........................................................................................................... 15
6.6.2 Example Code .................................................................................................. 15
6.6.3 Process Description ........................................................................................ 17
6.7 Display of Differentiated Results ...................................................................... 18
6.7.1 Scenario ........................................................................................................... 18
6.7.2 Example Code .................................................................................................. 18
Listener of the first [Results] Button ("SHOW_MODE_ORDER") .............. 18
Listener of the second [Results] Button ("SHOW_MODE_RECEIPT") ....... 18
Listener of the Results List ........................................................................... 19
6.7.3 Process Description ........................................................................................ 20
6.8 Using Mash-Up with deduced Data ................................................................... 20
6.8.1 Scenario ........................................................................................................... 20
6.8.2 Example Code .................................................................................................. 20
Listener at Mash-Up (Server Script) ............................................................ 20
Mash-Up Script ............................................................................................. 22
6.8.3 Process Description ........................................................................................ 24
6.9 Deactivate Context Menu Entires in the Navigation/ Results List ................... 25
6.9.1 Scenario ........................................................................................................... 25
6.9.2 Example Code .................................................................................................. 25
6.9.3 Process Description ........................................................................................ 26
6.10 Embedding a Results List in an Index Form ..................................................... 27
6.10.1 Scenario ........................................................................................................... 27
6.10.2 Example Code .................................................................................................. 27
6.10.3 Process Description ........................................................................................ 28
6.11 Limitation of Lookup Fields with multiple Conditions ...................................... 28
6.11.1 Scenario ........................................................................................................... 28
6.11.2 Example Code .................................................................................................. 29
7 Practical Application: Downloading Documents via an External Applet ........... 30
7.1 Scenario ............................................................................................................... 30
7.2 Extending the Web Client by Own Classes ........................................................ 30
7.3 Create a Java Project ........................................................................................... 31
7.4 Implementing Logic and Behaviour ................................................................... 31
7.5 Code Examples .................................................................................................... 32
7.5.1 DocumentDownloader.java ............................................................................. 32
7.5.2 DownloadAppletCaller.jsp ............................................................................... 33
7.5.3 Applet Class ..................................................................................................... 34
7.6 Deploying the Extensions ................................................................................... 35
7.7 Creating Server-side Script ................................................................................. 36
7.8 Permissions ......................................................................................................... 37
1.1 Scriptable Web Client Components
1
Best Practice Web Client Scripting
1 Introduction
This manual describes the fundamentals of Web Client scripting. Here you can learn more details, such
as
+ Which Web Client components can be adjusted by scripting?
+ Which scripting possibilities are available?
+ How do I start?
1.1 Scriptable Web Client Components
In the Web Client a set of components, that are the navigation panel, a query or index form, a results
list and a viewer can be opened.
While the navigation and the viewer component are not adjustable by scripting, scripts can be inserted
in forms and result lists. As a result, all input fields, buttons and result lists (including contained rows)
can be customized.
1.2 Scripting Prospects and Types
Generally, Web Client scripting can be divided in client-side scripting and server-side scripting.
1.2.1 Client-side Scripting
With client-side scripting it is possible to insert custom JavaScript Script code to a form. This enables
you to change or supervise the DOM Document Object Model) of the web page.
Client-side scripts are included by <script> tags in the HTML markup and are executed by the user agent.
As a result, all elements of a currently displayed web page are accessible. For example, the elements of
a form can be changed or a user’s form entry can be validated.
i The execution of client-side scripts take place when the browser is rendering the respective
page. Details of the syntax, possible functions and performance are therefore depending on the
JavaScript engine of your browser.
Summary
+ Scripting language: JavaScript
+ Execution via the browser
+ Enables the access to elements of the currently loaded page
2 Scripting API and Principles
2
+ Fields of application: definition of own JavaScript methods which are called when required (e.g.,
as reaction to events)
+ Unsuitable for business logic
+ Pitfalls: In an AJAX application not all elements of a page are contained because the page content
may change dynamically.
1.2.2 Server-side Scripting
With server-side scripting it is possible to change the Web Client’s business logic. Thus, document
archiving, document revisioning or workflow functionalities can be customized by server-side scripting.
Additionally own logic and masks can be added to the Web Client.
Server-side scripts are implemented using the Java programming language and are executed at
server-side by a servlet-container, such as Tomcat. Server-side scripts can access both, client-side and
server-side components.
Summary
+ Scripting language: Java
+ Execution via application server (e.g., Tomcat)
+ enables access to server-side objects. ZK-API allows also access to client-side resources (e.g., client
script defined JavaScript functions)
+ Server scripts are executed by an interpreter within the ZK engine which runs on the application
server
+ Pitfalls: In cases the element is added to the page, possibly other elements are not available yet.
1.2.3 ZUL Scripts
A special form of server-side scripts are ZUL-files. “ZUL” stands for ZK User Interface Markup Language.
Via ZUL-files individual masks and its behaviour can be implemented. ZUL-files have an own XML syntax
and are interpreted and executed by the ZK-framework at server-side.
i ZK is the GUI framework by which the SAPERION Web Client is implemented.
For more information on the ZK-framework and ZUL-files, please refer to: http://www.zkoss.org/.
2 Scripting API and Principles
Before the first script is implemented, let’s take a look at the provided scripting API and some scripting
principles.
2.1 The $component$ Placeholder
3
2.1 The $component$ Placeholder
Normally, you select the element in the mask to which you want to allocate your script. Element are for
example, a result list, input fields or buttons.
Since for server-side scripts the programming language is Java, references are needed to access objects.
Thus, a reference to the result list for example, is needed to access it from Java. This can be achieved
in using the provided $component$ construct.
i The $component$ construct always references the object the server-script is allocated to.
2.2 Full Qualified Class Names
Server script is interpreted and executed at runtime by the ZK-framework when the corresponding mask
is loaded by the Web Client. The dynamic code interpretation and execution requires the script to use only
full qualified Java class names, whenever non-ZK classes or classes from other packages than java.lang
are used
i To avoid failures in the source, we recommend to always use full qualified class names in
server-scripts when not using ZK or java.lang classes.
2.3 Mask Cache
After a mask definition has been changed in the Forms Designer (for example by adding a script) the
corresponding mask needs to be reloaded for the changes to take effect. A restart of the system or a new
registration by the user is not necessary.
The same applies for the Web Client as long as the mask cache is deactivated. Only when the mask cache
is deactivated the mask will be reloaded from the SAPERION core when the mask is reopened. The mask
cache is deactivated in the "web.xml". Set the following property:
<env-entry>
<description>Turns cache for masks on or off.</description>
<env-entry-name>masksCache</env-entry-name>
<env-entry-type>java.lang.Boolean</env-entry-type>
<env-entry-value>false</env-entry-value>
</env-entry>
i Please deactivate the mask cache in the "web.xml" when developing Web Client scripts.
3 Web Client Scripting API
This section provides an overview over important API calls needed to create valuable scripts.
3 Web Client Scripting API
4
3.1 Form API
Since all components to which you can allocate scripts are embedded in a mask, usually the form API
is an appropriate entry point for all scripts. To get a reference to the form, the method getParentForm()
can be called on every $component$ instance. Following table shows the most important API calls.
Form API
Method Name Description
getFields () Returns a list of all fields contained in the form.
getButtons () Returns a list of all buttons contained in the form.
getResultSets () Returns a list of all result lists contained in the form.
doClearContent () Empties all fields of the form.
DoCreateSysMask (String) Loads the system query mask for the given DDC name.
DoCreateSysQueryMask (String) Loads the system index mask for the given DDC name.
3.2 Element API
The element API is valid for all form elements. Following table shows the most important API calls.
Element API
Method Name Description
getParentForm () Returns a reference to the surrounding form. This method can be called on every $component$ instance.
getFieldName () Returns the unique name of a form element.
setFieldInactive (TRUE/ FALSE) Activates/ deactivates a field.
setValueImplicity (SaPropertyVa-
lue)
Changes a field's value and notifies the surrounding form about the change.
3.3 Results List API
The resultSet API provides methods to manipulate a result list, get information about the currently
displayed results and performing queries. Following table shows the most important API calls.
Result List API
Method Name Description
clearResultSet () Empties the result list.
setSelectedIndex (int) Selects the row with the given index.
getSelectedIndex () Returns the index of the selected row.
getSelectedItems () Returns all selected rows.
getNumberOfIndexes () Returns the number of rows.
refresh () Reloads the result list.
selectNext () Selects the next row.
4.1 ZK Events
5
Method Name Description
selectPrevious () Selects the previous row.
selectFirst () Selects the first row.
selectLast () Selects the last row.
performQury (SaQueryInfo) Performs the given query and displays the result.
i A complete API overview can be found in the Javadoc of the Web Client distribution package.
4 Events
The Web Client works event-based which means that every action triggers an event and thus this event
can be used to perform event-based actions. This section provides an overview of important events.
4.1 ZK Events
ZK-framework events can be captured on server and client side.
ZK Events
Event Name Description
onClick Is triggered on a mouse click.
onBlur Is triggered when an insert field loses its focus.
onFocus Is triggered when an insert field gets the focus.
onChange Is triggered when the value of an element changes.
onSelect Is triggered when the selection of a list element changes.
onCheck Is triggered when checkbox is selected.
onCreate Is triggered when an element and its child elements are created.
4.2 ResultSet Events
A result list basically has one essential event:
ResultSet Event
Event Name Description
onIndexChange Is triggered when the result list entries are changed.
4.3 Form Events
A Form has basically two important events:
5 How to Start Scripting
6
Form Events
Event Name Description
onDisplayResultItem Is triggered when a document is shown in the form.
onSaveDoc Is triggered when a document was saved by the form.
5 How to Start Scripting
i The following chapters of this scripting guide will focus on server-side scripting, anyhow in section
#"Sending Selected Data to an External Application" you can find an example of a client-side
script.
This section describes the first scripting steps.
5.1 The Forms Designer
The starting point of all scripting actions is the SAPERION Forms Designer which can be opened via
the Rich Client. All masks are created by means of the Forms Designer, therefore you need to integrate
your own scripts here.
1. Open the Forms Designer in the Rich Client by clicking DESIGN ribbon > APPLICATIONS
group > CREATE SEARCH FORMS command. The Forms Designer opens.
2. Select the required element that you want to be scripted. In the "Properties" panel, on the
right, the two property entries "Server-script" and "Client-Script" can be found.
3. Select the kind of script by clicking the according [...] button. Depending on you selection,
the "Server-Script" resp. the "Client-Script" window opens.
6.1 Field Validation
7
4. Enter the script code as needed.
6 Script Examples
This section provides some scripting examples along with description.
6.1 Field Validation
6.1.1 Scenario
This example demonstrates how to validate an user’s entry in a specific field (named "SupplierNo") of
an index form. Before the changes are saved in the archive, a validation should be executed for checking
whether all fields are completed. The validation is processed when the user submits his entry.
In cases the field "SupplierNo" has not been filled, the user receives a notification.
6 Script Examples
8
For this purpose the script needs access to the field value and must have influence to the event editing.
Use a server-script and allocate it to the [OK] button of the index form.
6.1.2 Example Code
$component$.addEventListener("onClick", new EventListener(){
public void onEvent(Event event) {
com.saperion.ngc.iform.IntelligentFormView form = $component$.getParentForm();
List fields = form.getFields();
for (com.saperion.ngc.iform.field.IntelligentField field : fields) {
if (field.getFieldName().equalsIgnoreCase("SupplierNo")) {
if (field.getValue().length() == 0) {
Messagebox.show("SupplierNo muss angegeben werden.", "Fehler",
Messagebox.OK, Messagebox.EXCLAMATION);
event.stopPropagation();
}
}
}
}
});
6.1.3 Process Description
In order to perform the validation, when the user submits the form, the above mentioned script has been
allocated to the [OK] button of the form. A new event listener for the "onClick" event is registered. When
the event was triggered, the "onEvent" method of the registered event listener is called.
In this case "$component$" points to the [OK] button. Via "getParentForm" the surrounding form
reference is obtained. With the form reference at hand a list of all form fields is delivered by calling
"getFields". The FOR loop iterates over all form elements. But only for the "SupplierNo" field the length
of the current value is checked (methods used: "getFieldName" and "getValue").
In case the field is empty (value length is zero) a message box is shown to the user. Since the form is
not valid the event is stopped via "event.stopPropagation" in order to not save the form changes.
6.2 Default Values of Lookup Table
6.2.1 Scenario
In this example the user has to index various documents by filling the same fields for each document.
The values of the fields are interdependent so that some fields are filled automatically as soon as a
particular field is available. In our example the user enters a value into the field "SupplierNo" and the
corresponding fields "Name", "Address" and "Phone" are filled automatically. The values are loaded
from a lookup table.
6.2 Default Values of Lookup Table
9
For this purpose the field "SuppplierNo" is necessary.
6.2.2 Example Code
$component$.addEventListener("onBlur", new EventListener() {
public void onEvent(Event event) {
String value = $component$.getValue();
if (value != null && value.length() > 0) {
com.saperion.ngc.model.ClassicConnectorProvider provider =
new com.saperion.ngc.model.ClassicConnectorProvider();
com.saperion.connector.SaClassicConnector connector = provider.get();
com.saperion.rmi.SaQueryInfo info = new com.saperion.rmi.SaQueryInfo(
"from suppliers s where s.SUPPLIERNR = " + value);
List result = connector.searchHQL(info);
if (result.size() > 0) {
com.saperion.intf.SaDocumentInfo supplier =
(com.saperion.intf.SaDocumentInfo) result.get(0);
for (com.saperion.ngc.iform.field.IntelligentField field :
$component$.getParentForm().getFields()) {
if (field.getFieldName().equalsIgnoreCase("SupplierName")) {
field.setValueImplicitly(supplier.getValue("Name"));
} else if (field.getFieldName().equalsIgnoreCase("SupplierAddress")) {
field.setValueImplicitly(supplier.getValue("Address"));
} else if (field.getFieldName().equalsIgnoreCase("SupplierPhone")) {
field.setValueImplicitly(supplier.getValue("Phone"));
}
}
}
}
}
});
6.2.3 Process Description
The code above is allocated to the field where the user enters the supplier number ($component$ refers
to the "Field" entry). Once this field has been filled and lost its focus the script is executed since an
"onBlur" event listener is used.
First, the entered value is checked to be not "null" and to have more than zero characters. If so, a database
query is performed. This is done by the means of the Classic Connector. An already logged in Classic
Connector instance is retrieved by the Classic Connector Provider. The method "searchHQL" is used to
perform the query.
After the query result is checked not to be empty, the first row is excluded by the call "result.get(0)".
Query result rows are represented by "SaDocumentInfo" objects which stand for the suppliers in this
6 Script Examples
10
example. A FOR-loop is used to iterate through the form fields. Specific fields (identified by their name)
are manipulated with the values "Name", "Address" and "Phone" of the selected supplier.
6.3 Sending Selected Data to an External Application
6.3.1 Scenario
This example shows how to send selected result list data to an external application. The external program
is a simple web application and the data are transmitted as URL parameters.
For the purpose of sending data, a new context menu element is available for the user.
This example covers both, server-side and client-side scripts. The server-side code directly calls a
client-side JavaScript function.
6.3.2 Example Code
6.3.2.1 Example Server-side Code
Menuitem item = new Menuitem("External");
item.addEventListener("onClick", new EventListener() {
public void onEvent(Event event) {
List rows = $component$.getSelectedRows();
if (rows.size() == 1) {
com.saperion.ngc.model.resultset.ResultProperties row = rows.get(0);
String invoiceNo = row.getPropertyAsString("InvoiceNo")[0];
String amount = row.getPropertyAsString("Amount")[0];
String supplierNo = row.getPropertyAsString("SupplierNo")[0];
String jscript = "openExternal('" + invoiceNo + "', '" + amount +
"', '" + supplierNo +"');";
Clients.evalJavaScript(jscript);
}
}
});
$component$.addCustomMenuItem(item, false);
});
6.3.2.2 Example Client-side Code
function openExternal(invoiceNo, amount, supplierNo) {
window.open("http://localhost:8080/SimpleApp/index.jsp?InvoiceNo="
+ invoiceNo + "&Amount=" + amount + "&SupplierNo=" + supplierNo,
"_blank", "width=500, height=600, location=no, status=no,
6.4 Restricting a Query
11
dependent=yes, resizable=yes");
}
6.3.3 Process Description
The server-side script is allocated to result list of forms. First, a custom menu item with the name
"External" is created. Then an event listener is registered to this menu item. This listener is invoked when
the user clicks on the menu item.
The "onEvent" method first gets the selected rows of the result list the script is allocated to. If only
a single result list row is selected, the scripts perform any further. Via "rows.get(0)" the first and
only selected row is retrieved which is represented by a "ResultProperties" object. Using the object’s
"getPropertyAsString" method the data by the name of "Invoice no.", "Amount" and "Supplier no." are
retrieved. Since SAPERION supports multivalue fields a call to "getPropertyAsString" returns an array.
By using [0], the first array entry is returned.
Having the requested data, the JavaScript function call is built as a String. Via the ZK call
"Clients.evalJavaScript" the JavaScript function call is executed at client side.
To ensure that the JavaScript function will be performed successfully the corresponding function must
also exist at client-side. As shown in the script example above, required data are taken as parameters.
The script opens a new browser tab/ window and calls a simple JSP-site passing in the data as URL
parameters.
6.4 Restricting a Query
6.4.1 Scenario
The current logged in user executes a query ans should only see the results list that is restricted to his
rights. Which documents are displayed to him is maintained in a special lookup-table (DDC named
"invoices_user").
In this example customer's invoice data in a DDC are pre-filtered. Customers are identified by the
customerID.
6.4.2 Example Code
$component$.addEventListener("onButtonQuery", new EventListener() {
public void onEvent(Event event) {
// get classic connector
com.saperion.ngc.model.ClassicConnectorProvider provider = new
com.saperion.ngc.model.ClassicConnectorProvider();
com.saperion.connector.SaClassicConnector conn = provider.get();
// get current user name
com.saperion.ngc.main.HttpSessionWrapper wrapper = new
com.saperion.ngc.main.HttpSessionWrapper();
com.saperion.ngc.model.authorization.User user =
6 Script Examples
12
(com.saperion.ngc.model.authorization.User) wrapper
.getAttribute(com.saperion.ngc.main.HttpSessionWrapper.USER_ATTR);
String username = user.getName();
// check database for user enclosures
String sql = "select KDNR from invoices_user where USERNAME =
:user_name";
com.saperion.rmi.SaQueryInfo query = new
com.saperion.rmi.SaQueryInfo(sql);
query.setParameter("user_name", username);
try {
List enclosures = conn.searchHQL(query);
// return if no enclosure is available for the user
if (enclosures.isEmpty()) {
System.out.println("Empty list: No enclosure found for user: " +
username);
return;
}
com.saperion.intf.SaDocumentInfo data =
(com.saperion.intf.SaDocumentInfo) enclosures.get(0);
String[] numbers = data.getValue("KDNR").getStringValues();
String inStm = com.google.common.base.Joiner.on(",").join(numbers);
if (inStm.isEmpty()) {
System.out.println("No enclosure found for user: " + username);
return;
}
com.saperion.ngc.events.iform.ResultFieldEvent orig =
(com.saperion.ngc.events.iform.ResultFieldEvent) event;
if (null != orig) {
// create custom condition
String condition = " IN (" + inStm + ")";
com.saperion.ngc.iform.CustomConditionBean ccb = new
com.saperion.ngc.iform.CustomConditionBean("Kundennummer",
"KUNDENNUMMER", false, condition);
//append condition to form filter list
orig.getList().add(ccb);
}
} catch (Exception e) {
e.printStackTrace();
}
}
6.5 Display "Select user..." Dialog
13
});
6.4.3 Process Description
The server-side script is allocated to the results list. An event listener is registered to the
"onButtonQuery" event which is fired as soon as the user clicks on the [Results]/ [Search] button.
First, the "onEvent" method creates an instance of the SaClassicConnector which is needed for the search
of the user-customerID correlation in the lookup-DDC. To perform the query, the user name of the current
user is also needed. The name can be retrieved from the Web Client session.
The user name can then be matched via the SaClassicConnector-method "searchHQL" against the
lookup-DDC. The matching results are listed in a comma-separated list.
In order to send this generated list of IDs along with the search criteria that is stated by the user, the
"CustomConditionBean" is used. This bean describes the additional search criteria and is attached to
the existing query conditions via the "orig.getList().add" call.
The user gets a results list where his search criteria are considered, but which is also restricted to his
rights.
6.5 Display "Select user..." Dialog
6.5.1 Scenario
In an index form of the Web Client the "Select user..." dialog should be opened at the push of a button.
The user can then select and assume the required user for the index field.
6.5.2 Example Code
package com.saperion.ngc.resultset;
import com.saperion.intf.wf.WorkflowMember;
import com.saperion.ngc.dialogs.userselect.UserSelectionDialog;
import com.saperion.ngc.dialogs.userselect.UserSelectionDialog.UserSelectionResult;
import com.saperion.ngc.exception.NgcUiException;
import com.saperion.ngc.iform.IntelligentFormView;
import com.saperion.ngc.iform.button.FormButton;
import com.saperion.ngc.iform.field.TextField;
import com.saperion.ngc.resultset.ResultSetPresenter;
import com.saperion.ngc.resultset.ResultSetView;
import com.saperion.ngc.resultset.ResultSetViewImpl;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
6 Script Examples
14
public class ChooseUser {
public static void method(final Object component){
final FormButton button = (FormButton) component;
button.addEventListener("onClick", new EventListener() {
public void onEvent(Event event) throws NgcUiException{
IntelligentFormView parentForm = button.getParentForm();
final TextField textField = (TextField) parentForm.
getFieldByFrameId(40);
ResultSetView resultSetView = parentForm.getResultSets().get(0);
ResultSetPresenter resultSetPresenter = ((ResultSetViewImpl)
resultSetView).getPresenter();
WorkflowMember userTree = resultSetPresenter.getUserTreeRoot("");
// event listener used by UserSelectionDialog
EventListener dlgEvtListener = new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
UserSelectionResult userResult = (UserSelectionResult)
event.getData();
if (userResult.getButton() == UserSelectionDialog.
OK_BUTTON) {
if (userResult.getSelectedUsers().size() > 0){
textField.setText(userResult.
getSelectedUsers().get(0).
getName().toString());
textField.setChangedImplicitly();
}
}
}
};
UserSelectionDialog.show(resultSetView, resultSetPresenter, userTree,
false, dlgEvtListener);
}
});
}
6.6 Open Index Form of eFile Application in a Pop-up Window
15
}
6.5.3 Process Description
In this example a class "ResultSetPresenter" that has the visibility "Package" from the Web Client has to
be used. In order to avoid a modification of the ResultSetPresenter code the above mentioned script is
set into the same package as the ResultSetPresenter itself - "com.saperion.ngc.resultset".
You have to create a JAR out of the script. By "package com.saperion.ngc.resultset;"
it is defined that this class belongs logically to the same package (although it is
physically in a different JAR) as the Web Client. Into the server script field only the call
"com.saperion.ngc.resultset.ChooseUser.method($component$);" is entered.
In the example code an onClick listener is assigned to the [Select user] button. When clicking, the text
field in which the selected user has to be filled is specified (in the sample code: field with ID 40).
The ResultSetView provides the mentioned ResultSetPresenter which represents the amount of users
("userTree").
A further listener ("dlgEvtListener") catches the click on the [OK] button in the user selection dialog and
fills the selection into the defined text field.
6.6 Open Index Form of eFile Application in a Pop-up
Window
6.6.1 Scenario
In an eFile application documents should be populated into a record. Due to the reason that documents
filed are not physically existing files but only meta data, the index form should be opened not via
documents but at a push of a button. This pop-up window should be opened until the user closes
it actively. In this way, multiple documents can be indexed successively. When there are no further
documents to be indexed the user clicks on the [Done] button in order to close the pop-up-window.
6.6.2 Example Code
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import com.saperion.connector.SaClassicConnector;
import com.saperion.intf.SaSaveInfo;
import com.saperion.ngc.events.navigation.FolderSearchInfos;
import com.saperion.ngc.events.resultset.NewItemEvent;
6 Script Examples
16
import com.saperion.ngc.iform.FieldResultBean;
import com.saperion.ngc.iform.FormMode;
import com.saperion.ngc.iform.IFormPopupResult;
import com.saperion.ngc.iform.IntelligentFormPopupWindow;
import com.saperion.ngc.iform.IntelligentFormView;
import com.saperion.ngc.iform.button.FormButton;
import com.saperion.ngc.model.ClassicConnectorProvider;
import com.saperion.ngc.resultset.ResultSetView;
import com.saperion.structures.DocumentInfo;
public static final String MASK_NAME = "dokument_i";
public static final String DDC_NAME = "dokumente";
public static final String POPUP_LABEL = "Indexierung-["+MASK_NAME+"]";
final FormButton button = (FormButton)$component$;
final IntelligentFormView parentForm = button.getParentForm();
final FolderSearchInfos folderInfos = parentForm.getCurrentFolderSearchInfos();
public void onEvent(Event event) {
createPopup();
}
private void createPopup(){
final Component window = IntelligentFormPopupWindow.show(POPUP_LABEL, null, true,
false);
Events.sendEvent(window, new NewItemEvent(window, MASK_NAME, DDC_NAME,
FormMode.INDEX_POPUP));
window.addEventListener("onFBOk", new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
IFormPopupResult popupResult = (IFormPopupResult) event.getData();
ClassicConnectorProvider provider = new ClassicConnectorProvider();
SaClassicConnector connector = provider.get();
Map<String, Object> beanMap = fieldResultsToMap(popupResult.
getFieldResultBeans());
DocumentInfo doc = new DocumentInfo(DDC_NAME, beanMap, null, "");
SaSaveInfo info = connector.createDocument(doc);
connector.addToFolder(folderInfos.getContainingFolder().getDdcName(),
info.getSysRowId(), DDC_NAME, folderInfos.
getContainingFolder().getObjectId());
6.6 Open Index Form of eFile Application in a Pop-up Window
17
for (ResultSetView resultSet : parentForm.getResultSets()){
resultSet.refresh(true);
}
window.detach();
//do a loop as long as not chanceled ("Fertig")
createPopup();
}
private Map<String,Object> fieldResultsToMap (
List<FieldResultBean> fieldResultBeans){
Map<String,Object> map = new HashMap<String, Object>();
for (FieldResultBean bean : fieldResultBeans){
map.put(bean.getDatabaseFieldName().replace(
"[", "").replace("]", ""),
bean.getValue()[0].asString());
}
return map;
}
});
window.addEventListener("onChancel", new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
window.detach();
}
});
}
});
6.6.3 Process Description
The [Index document] button has a listener that starts the method "createPopup" when clicking on. This
method uses internally IntelligentFormPopupWindow.show() and sends an event which defines which
form to be displayed in the created window and on which DDC it is based.
The form contains the two buttons [OK] and [Done] which send automatically events to the form when
clicking on. In order to equip the buttons with functionality two listeners are attached to the form which
listen to events. The "onFBOk"-listener collects the entered index data and creates a document in the
6 Script Examples
18
appropriate data format via Classic Connector in the DDC. Because it is an eFile application here, the
document must be filed into the correct record. This is achieved by the "connector.addToFolder(…)"-call.
6.7 Display of Differentiated Results
6.7.1 Scenario
In the forms designer you have the facility of linking conditions to the the [Results] button, e.g., via SQL
code, which leads to a prefiltered display of results. But this linking has only effect to the Rich Client. For
the purpose of a prefiltered results list in the Web Client the following script is implemented.
6.7.2 Example Code
In the following you are provided the example scripts for the [Results] buttons that deliver the respective
results list.
6.7.2.1 Listener of the first [Results] Button ("SHOW_MODE_ORDER")
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import com.saperion.ngc.iform.IntelligentFormView;
import com.saperion.ngc.iform.button.FormButton;
$component$.addEventListener("onClick", new EventListener() {
public void onEvent(Event event) {
IntelligentFormView parentForm = (IntelligentFormView) ((FormButton) $component$).
getParentForm();
parentForm.setAttribute(ScriptConstants.SHOW_MODE, ScriptConstants.SHOW_MODE_ORDER);
}
});
6.7.2.2 Listener of the second [Results] Button ("SHOW_MODE_RECEIPT")
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import com.saperion.ngc.iform.IntelligentFormView;
import com.saperion.ngc.iform.button.FormButton;
$component$.addEventListener("onClick", new EventListener() {
public void onEvent(Event event) {
6.7 Display of Differentiated Results
19
IntelligentFormView parentForm = (IntelligentFormView) ((FormButton) $component$).
getParentForm();
parentForm.setAttribute(ScriptConstants.SHOW_MODE, ScriptConstants.SHOW_MODE_RECEIPT);
}
});
6.7.2.3 Listener of the Results List
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import com.saperion.ngc.events.iform.ResultFieldEvent;
import com.saperion.ngc.iform.CustomConditionBean;
import com.saperion.ngc.iform.IntelligentFormView;
import com.saperion.ngc.resultset.ResultSetView;
$component$.addEventListener("onButtonQuery", new EventListener() {
public final static String ATTRIBUTE = "ID";
@Override
public void onEvent(Event event) throws Exception {
IntelligentFormView parentForm = (($component$)ResultSetView).getParentForm();
ResultFieldEvent origEvent = (ResultFieldEvent) event;
if (null != origEvent) {
//delete evtl. former additional beans
CustomConditionBean ccbOrder = new CustomConditionBean(ATTRIBUTE,
ATTRIBUTE.toUpperCase(), false, " IS NULL ");
CustomConditionBean ccbReceipt = new CustomConditionBean(ATTRIBUTE,
ATTRIBUTE.toUpperCase(), false, " IS NOT NULL ");
origEvent.getList().remove(ccbOrder);
origEvent.getList().remove(ccbReceipt);
//create custom condition
if (ScriptConstants.SHOW_MODE_RECEIPT.equals(
parentForm.getAttribute(ScriptConstants.SHOW_MODE))){
origEvent.getList().add(ccbReceipt);
} else if (ScriptConstants.SHOW_MODE_ORDER.equals(
parentForm.getAttribute(ScriptConstants.SHOW_MODE))){origEvent.getList().add(ccbOrder);
}
}
}
6 Script Examples
20
});
6.7.3 Process Description
The results list knows the event "onButtonQuery“ which is handed over when clicking on one of
the [Results] buttons. Because there is no differentiation which of the buttons has caused the event
both of the event listener obtain an appropriate attribute including value which is then attached to
the superordinated form when triggered (parentForm.setAttribute( ScriptConstants.SHOW_MODE,
ScriptConstants.SHOW_MODE_ORDER);).
Furthermore, at the event "onButtonQuery" the results list obtains additionally a listener that reads
the attribute and - depending the value - extends the event by the appropriate SQL condition
(origEvent.getList().add(ccbReceipt);).
6.8 Using Mash-Up with deduced Data
6.8.1 Scenario
In an eFile application metadata of documents are previewed in a table when clicking on the file cover.
For this purpose a so called mash-up is used. This is a form element which can be programmed by
JavaScript.
For this, the two methods "setValue(name,value)" and "execute()" are provided. A server-side script in
Java obtains the metadata of the documents and on the client side these data are presented in a table
by means of the above mentioned JavaScript methods.
6.8.2 Example Code
6.8.2.1 Listener at Mash-Up (Server Script)
Listener am Mash-Up (serverscript)
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import org.zkoss.json.JSONObject;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import com.saperion.connector.SaClassicConnector;
import com.saperion.constants.SaConstants.SaFieldType;
import com.saperion.intf.SaDocumentInfo;
import com.saperion.intf.SaPropertyValue;
import com.saperion.jni.SaJNIValue;
import com.saperion.ngc.events.resultset.IndexResultItemEvent;
import com.saperion.ngc.iform.IntelligentFormView;
6.8 Using Mash-Up with deduced Data
21
import com.saperion.ngc.iform.field.IntField;
import com.saperion.ngc.iform.xml.EditFieldType;
import com.saperion.ngc.model.ClassicConnectorProvider;
import com.saperion.ngc.model.resultset.ResultProperties;
import com.saperion.ngc.model.resultset.ResultProperty;
import com.saperion.rmi.SaPropertyValueImpl;
import com.saperion.rmi.SaQueryInfo;
$component$.addEventListener("onCreate", new EventListener() {
public static final String DDC_NAME = "akte";
public static final String JSCRIPT_KEY = "mashup";
@Override
public void onEvent(Event arg0) throws Exception {
mask.getMashups().get(0).addEventListener("onIndexChange", new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
ClassicConnectorProvider provider = new ClassicConnectorProvider();
SaClassicConnector connector = provider.get();
IndexResultItemEvent selectEvent = (IndexResultItemEvent) event;
ResultProperties props = selectEvent.getProperties();
if (props != null){
List<SaDocumentInfo> positions =
connector.searchFolderDocuments(
DDC_NAME, props.getProperty("SYSROWID").
getValue().getStringValue(),
new SaQueryInfo(""));
String jsonValue = "";
if (!positions.isEmpty()){
List<HashMap<String, String>> values = new
LinkedList<HashMap<String,String>>();
for (SaDocumentInfo pos : positions){
HashMap<String,String> position = new
HashMap<String,String>();
values.add(position);
for (SaPropertyValue val : pos.getValues()){
position.put(val.getName(),
val.getStringValue());
}
6 Script Examples
22
}
jsonValue = listToJSONString(values);
}
EditFieldType eft = new EditFieldType();
eft.setDbFieldName(JSCRIPT_KEY);
eft.setFName(JSCRIPT_KEY);
SaPropertyValueImpl saVal = new SaPropertyValueImpl(DDC_NAME,
JSCRIPT_KEY, new SaJNIValue(jsonValue, SaFieldType.FT_STRING));
selectEvent.getProperties().setProperty(new
ResultProperty(saVal, eft));
}
}
private String listToJSONString(
List<HashMap<String, String>> list) {
String s = JSONObject.toString("",list);
//omit the "<key>:"
return s.substring(s.indexOf(":") + 1, s.length());
}
});
}
});
6.8.2.2 Mash-Up Script
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
<style type="text/css">
table#positiondata {
border: 1px dotted gray;
font-size: small;
max-width: 500px;
}
table#positiondata thead td {
padding-right: 10px;
font-weight: bold;
}
table#positiondata tbody td {
border-top: 1px solid gray;
}
</style>
<script type="text/javascript">
6.8 Using Mash-Up with deduced Data
23
var valGlob = null;
function execute() {
var elTable;
var elRow;
var elCell;
var posData;
elTable = document.getElementById("data-body");
while (elTable.firstChild) {
elTable.removeChild(elTable.firstChild);
}
if (valGlob == null) {
return;
}
if (valGlob.length == 0) {
return;
}
posData = eval(valGlob);
for (i=0; i<posData.length; i++)
{
elRow = document.createElement("tr");
elCell = document.createElement("td");
elCell.innerHTML = posData[i].POS_MENGE;
elRow.appendChild(elCell);
elCell = document.createElement("td");
elCell.innerHTML = posData[i].POS_BESCHREIBUNG;
elRow.appendChild(elCell);
elCell = document.createElement("td");
elCell.innerHTML = posData[i].POS_GESAMTNETTO;
elRow.appendChild(elCell);
elCell = document.createElement("td");
elCell.innerHTML = posData[i].POS_KOSTENSTELLE;
elRow.appendChild(elCell);
elCell = document.createElement("td");
elCell.innerHTML = posData[i].POS_ARTIKELGRUPPE;
elRow.appendChild(elCell);
elTable.appendChild(elRow);
}
valGlob = null;
6 Script Examples
24
}
function setValue(name, value) {
if (name.toLowerCase()=='mashup') {
valGlob = value;
}
}
</script>
</head>
<body>
<table id="data">
<thead>
<tr>
<td>Menge</td>
<td>Beschreibung</td>
<td>Netto Gesamt</td>
<td>Kostenträger</td>
<td>Artikelgruppe</td>
</tr>
</thead>
<tbody id="data-body">
</tbody>
</table>
</body>
</html>
6.8.3 Process Description
In the forms designer the linking from the lists of file covers to the mash-up has to be set. A click on the
file cover triggers the transfer of metadata to the mash-up. Data are then listed in a table by the mash-up.
A listener is set onto any field of the form. When creating this field the listener itself creates a
listener that is set onto the mash-up contained in the form. When clicking on the file cover the
"onIndexChange" event is treated first by the added listener. In the event the metadata of the file
cover are contained and can now be used for finding the according documents via Classic Connector
(connector.searchFolderDocuments(…)).
A JSON string (JavaScript Object Notation) is created out of the found metadata. This corresponds to
the string representation of the list of documents resp. their metadata ( [{key1:value1, key2:value2}, {
key1:value1, key2:value2}, { key1:value1, key2:value2}] ). This JSON string is added as new property to the
current event whereby a fix defined value ("mashup") is the key used in the JavaScript code.
The manipulated event is passed to the mash-up where its data are used to execute the
"setValue(name,value)" and "execute()" methods. In the JavaScript a table including table header is
defined by CSS. For each property of the event the "setValue(name,value)" method is called. In cases
a property which name corresponds to the defined value ("mashup") is found its JSON-String is saved
globally.
6.9 Deactivate Context Menu Entires in the Navigation/ Results List
25
After the "setValue" method is called for all properties "execute()" will be carried out. The table that is
eventually filled by the last event will be emptied and prepared by means of DOM manipulation (e.g.,
elRow.appendChild(elCell);).
6.9 Deactivate Context Menu Entires in the Navigation/
Results List
6.9.1 Scenario
In all application folders the menu entries "Delete", "Rename" and "Create new folder" should be
deactivated. For this purpose, an own handler for menu entries can be allocated to the navigation that
determines dynamically the availability of context menu entries for certain folder types.
i Similar to other script samples, we recommend to create an own JAR-file and integrate it in the
Web Client for this purpose. In the interest of simplicity, the example below is not in a JAR-file.
The script is directly integrated in the "index.zul" file by adding a further attribute to the the navigation
element.
It is also possible to remove context menu entries of the results list. To this end, a list of menu entries
that should not be available can be handed over to the results list element of a form by means of a
server script.
6.9.2 Example Code
Deactivation of Menu Entries in the Navigation
<navigation id="navigation"
allowedMaxNrOfNodesPerLevel="20" fastAccessEnabled="true">
<attribute name="onCreate">
import com.saperion.ngc.navigation.NaviTreeNodeMenuHandler;
import com.saperion.ngc.model.navigation.intf.NavigationElement.TREE_ITEM_TYPE;
import com.saperion.ngc.navigation.NavigationView;
import com.saperion.ngc.navigation.NaviTreeNode;
import com.saperion.ngc.navigation.NaviMenuRights;
// the handler that will set the rights for each folder
class TestNaviTreeMenuHandler extends NaviTreeNodeMenuHandler {
public TestNaviTreeMenuHandler(NavigationView naviView) {
super(naviView);
}
public NaviMenuRights getMenuRights(NaviTreeNode node) {
NaviMenuRights rights = NaviMenuRights.getAllRights().disableDeleteFolder()
.disableRename().disableCreateFolder();
return rights;
6 Script Examples
26
}
}
// Add the TestNaviTreeMenuHandler to the navigation tree
// Handler will be used for nodes of type FORM (Applications) only
navigation.addNodeMenuHandler(new TestNaviTreeMenuHandler(navigation), TREE_ITEM_TYPE.FORM);
</attribute>
</navigation>
Deactivation of Menu Entries in the Results List
import com.saperion.ngc.resultset.MenuItemKey;
// create the list of menu entries to remove
// entries are identified by a MenuItemKey
List itemsToRemove = new ArrayList();
itemsToRemove.add(MenuItemKey.DOC_DOWNLOAD);
// tell the result list to use the list for multi-selection menus
$component$.setRemoveMenuItemList(itemsToRemove, true);
// tell the result list to use the list for single selection menus
$component$.setRemoveMenuItemList(itemsToRemove, false);
6.9.3 Process Description
Deactivation of Menu Entries in the Navigation
In this sample, same rights are allocated to each folder. Due to the reason that the folder to be handled
is transferred as a parameter to the method getMenuRights, the rights for each folder can be determined
dynamically (e.g., by means of the folder name). It is also possible to make the rights subject to the
currently logged in user by accessing the current user:
User userAttribute = new HttpSessionWrapper().getUserAttribute();
Via the userAttribute information (like user type or user ID) are retrieved and can be used for the
allocation of rights.
Using the HttpSessionWrapper, the following additional import is necessary:
import com.saperion.ngc.resultset.MenuItemKey;
6.10 Embedding a Results List in an Index Form
27
Deactivation of Menu Entries in the Results List
Similar to the deactivation of menu entries in the navigation the list of menu entries can also be
determined dynamically. In the above mentioned example code the menu entry "Download" of single
or multiple documents is removed.
6.10 Embedding a Results List in an Index Form
i This feature is available from SAPERION version 7.5 SP0 patch level 1 on.
6.10.1 Scenario
In order to display documents from another DDC or lookup table in an index form you can embed a
results list in the index form.
Example
In the implemented application the Web Client user is allowed to display a second DDC to an open
invoice in order to check invoice items or to edit according index data if required.
Fig. 6–1: Example: Embedded results list in the index form
The results list is triggered via the scripting interface of the Web Client. The below mentioned scripting
sample must be added directly to the index form as server script.
6.10.2 Example Code
In the following example the results list is used to display data of the document opened in the index
form loaded from a lookup table.
import com.saperion.ngc.events.resultset.IndexResultItemEvent;
import com.saperion.ngc.model.resultset.ResultProperties;
import com.saperion.intf.SaPropertyValue;
import com.saperion.ngc.resultset.ResultSetView;
import com.saperion.rmi.SaQueryInfo;
$component$.addEventListener("onIndexChange", new EventListener() {
public void onEvent(Event event) throws Exception {
// convert the received event
IndexResultItemEvent orig;
if (event instanceof ForwardEvent) {
orig = (IndexResultItemEvent) Events.getRealOrigin((ForwardEvent) event);
} else {
orig = (IndexResultItemEvent) event;
}
ResultSetView resultSetView = $component$.getResultSets().get(0);
// row contains the index data of the selected document
6 Script Examples
28
ResultProperties row = (orig).getProperties();
if (row != null) {
SaPropertyValue val = row.getPropertyValue("EX7_CUST");
String query = "select d from CUSTOMERS d where COMPANYNAME = :param1";
SaQueryInfo info = new SaQueryInfo(query);
info.setParameter("param1", val.getValues()[0].getStringValue());
resultSetView.performQuery(info);
} else {
// clear the result list when nothing was selected in the query mask
resultSetView.clearResultSet();
}
}
});
6.10.3 Process Description
In the used script an event listener which responds to the "Index change"-event is added to the index
form. This event is used by the results lists of query forms to inform other components when a new
element is selected from the list.
At the beginning of the "onEvent"-method the forwarded event is converted to the original event. After
that index data of the selected document are read out of the event. The linking of index table and lookup
table of this example is obtained by the "EX7_CUST" field in the index table and the "COMPANYNAME"
field of the lookup table. This index value is used to create and parametrize a HQL query.
The lookup table of this example is named "CUSTOMERS". In the HQL query all values of the lookup
table are requested. Finally, the results list in the index form is instructed to carry out the request.
After the query has been executed successfully the results list behaves the same way as a normal results
list. That way, index data of displayed documents can be edited or new elements could be added to to
the lookup table if the according permissions are existing. However, it is necessary that an index form is
defined in the results list. Also for the display of properties of former document revisions via the revision
history an index form must be defined.
i Please note that an embedded results list in an index form is not triggering an event when one
entry is selected. Furthermore the dispatch of selected documents is only allowed by means of
the MAILTO-function.
6.11 Limitation of Lookup Fields with multiple Conditions
6.11.1 Scenario
In addition to the limitations defined for the lookup field in the form, further conditions can be added.
These conditions are then used at every query in the linked lookup table. The function can be integrated
via the scripting interface of the Web Client.
For this purpose the class "com.saperion.ngc.iform.field.LookupTextField" has been enhanced by the
following two methods:
6.11 Limitation of Lookup Fields with multiple Conditions
29
public void addCondition(String fieldName, Object value, SaFieldType fieldType)
public void removeCondition(String fieldName)
+ "addCondition" method
By means of the "addcondition" method own conditions can be added to the field. Here, the column
name of the lookup table, the value of the condition and the column type must be specified.
+ "removeCondition" method
By means of the "removeCondition" method already added conditions can be removed. For this,
the column name for which the condition has been added must be specified.
i All conditions at the query are linked by AND.
6.11.2 Example Code
In the following samples, two conditions are added to a lookup field first and are removed afterwards.
The first script has been assigned directly to the respective field in the query form as server script. The
second has also been assigned as server script to a button in the same form.
Example 1
import com.saperion.constants.SaConstants.SaFieldType;
// $component$ is the LookupTextField
$component$.addEventListener("onCreate", new EventListener() {
public void onEvent(Event event) {
$component$.addCondition("Country", "Germany", SaFieldType.FT_STRING);
$component$.addCondition("City", "Berlin", SaFieldType.FT_STRING);
}
});
Example 2
import com.saperion.ngc.iform.field.LookupTextField;
// $component$ is a button
$component$.addEventListener("onClick", new EventListener() {
public void onEvent(Event event) {
// get the LookupTextField from the mask
LookupTextField field = (LookupTextField) $component$.getParentForm().getFieldByFrameId(9);
field.removeCondition("City");
}
});
The supported data types are listed in Enum SaFieldType:
http://portal.saperion.com/docsxml/javadocs/75/javadoc_CC/com/saperion/constants/SaConstants.SaFieldType.html
7 Practical Application: Downloading Documents via an External Applet
30
i In order to optimize the the query (faster request) you can optionally set the following parameter
in the "webclient.properties": "ExactMatch=TRUE".
7 Practical Application: Downloading Documents
via an External Applet
In this section you will be offered a complete illustration of how to call an external applet which
downloads selected documents from SAPERION and stores them in the local file system.
The following steps within the process are demonstrated:
+ Extending the SAPERION Web Client by own classes
+ Calling own classes from server-side script as reaction to user triggered events
+ Calling an own applet to download SAPERION documents and store them on a local file system.
7.1 Scenario
A forms result list is extended by a custom context menu item. Once this item is clicked a JSP should
be opened containing an applet. This applet should download the selected document from SAPERION.
For the purpose of selecting the local storage location the "Open" dialog opens.
Fig. 7–1: Custom JSP File with Applet
7.2 Extending the Web Client by Own Classes
There are a number of benefits associated with the extension of the Web Client by own classes. First, the
server-side script can directly call your custom methods to execute your logic. Second, using own classes
or jars the server-side scripts itself are shortened considerably which decreases potential failures.
7.3 Create a Java Project
31
i We recommend to use own classes/ jars in server-side scripts to avoid scripting failures and
shorten up the script itself.
7.3 Create a Java Project
The easiest way to create custom Java classes/ jars is to create a Java project in an integrated
development environment (IDE). For this example the Eclipse IDE is used.
1. In Eclipse, click on FILE > NEW > JAVA PROJECT in order to create a new project.
2. Enter the project name and click [OK].
3. In this example the created classes use SAPERION and ZK logic. As a result, a number of
libraries need to be included to the project.
The following jars have been added to the class path of the project:
+ src-classicconnector.jar
+ scr-webclient.jar
+ zcommon.jar
+ zk.jar
+ zul.jar
All jars can be found in the SAPERION Web Client distribution.
i The created project (including all files) is a part of this guide. Please refer to the files directly to
see all implementation details. Important parts are explained in the following section.
7.4 Implementing Logic and Behaviour
The logic of this example is implemented in the following classes/ files:
Classes/ Files
Class Description
DocumentDownloader.java This class is instantiated and used in the server-side script. It takes the selected rows of the result set as an ar-
gument, creates download URLS, stores them in the session and calls the "DownloadAppletCaller.jsp" which is
then opened in a new browser window/ tab.
DownloadAppletCaller.jsp This JSP-file retrieves the download URLs from the session and loads our custom applet while passing the
URLs as applet parameters.
DownloadApplet.java This class represents our custom applet. It takes the given download URL parameter and downloads the corre-
sponding SAPERION documents with the help of the class "FileDownloader". Once the files are downloaded
they can be stored to the local file system, if the applet has the required permissions.
7 Practical Application: Downloading Documents via an External Applet
32
Class Description
FileDownloader.java This class takes the download URL as a parameter and downloads the files.
7.5 Code Examples
7.5.1 DocumentDownloader.java
The following listing shows the "DownloadDocument" method of the "DocumentDownloader.java" file.
It is called directly from the server script.
1public void downloadDocument(List<ResultProperties> documents) throws Exception {
2try {
3 // current request
4 HttpServletRequest request = (HttpServletRequest) Executions.getCurrent()
5 .getNativeRequest();
6 // get first document
7 ResultProperties document = documents.get(0);
8 // get unique document id
9 DocumentId docId = settingsService.fixRevId(document.getDocumentId(), false);
10 // get number of elements
11 List<ElementInfo> elements = docService.getDocumentPages(docId, true);
12 // Applet arguments
13 String documentIdent = docId.getRevisionId();
14 Set<String> downloadURLs = new LinkedHashSet<String>();
15 for (ElementInfo element : elements) {
16 String documentURL = ServletUrl.constructDocumentUrl(documentIdent, true,
17 element.getNumber(), element.getName(), true, request);
18 downloadURLs.add(documentURL);
19 }
20 if (downloadURLs.isEmpty()) {
21 Messagebox.show("There is no content to download.", "No content",
22 Messagebox.OK, Messagebox.ERROR);
23 } else {
24 // add download URLs to session
25 HttpSession session = request.getSession();
26 session.setAttribute("downloadURLS", downloadURLs);
27 // call download servlet
28 Executions.getCurrent().sendRedirect(
29 ServletUrl.getBaseUrl(request, "/downloadAppletCaller.jsp"), "_blank");
30 }
31} catch (Exception e) {
32 e.printStackTrace();
33 throw e;
34}
35}
7.5 Code Examples
33
In line 7 the first document from the result list is obtained. The ID of this document is fixed (completed)
in line 9. The "settingsService" is an instance of the class "SettingsClassicConnectorService".
In line 11 we get all elements of the document via an instance of the "DocClassicConnectorService" class.
i Fix document IDs via the "SettingsClassicConnector". By doing so, it is ensured that always valid
IDs are used.
For all document elements a download URL is built via the "ServletUrl" class (line 16). This class helps
you out when building URLs to SAPERION servlets.
i Use the "ServletUrl" class to obtain SAPERION servlet URLs easily.
In line 26 the list of download URLs are stored in the session and in line 28 our custom JSP file is called.
7.5.2 DownloadAppletCaller.jsp
1boolean showApplet = false;
2Object attribute = session.getAttribute("downloadURLS");
3StringBuilder sb = new StringBuilder("Download URLS: <br/>");
4StringBuilder urls = new StringBuilder();
5if (attribute != null) {
6 @SuppressWarnings("unchecked")
7 Set<String> downloadURLS = (Set<String>) attribute;
8 //show URLS
9 sb.append("<ul>");
10 int count = 0;
11 for (String url : downloadURLS) {
12 count++;
13 sb.append("<li>").append(url).append("</li>");
14 urls.append(url);
15 If(downloadURLS.size() > count) {
16 urls.append("$");
17 }
18 }
19 sb.append("</ul>");
20 showApplet = true;
21}
22[…]
23<% if(showApplet) { %>
24 <h1>Initializing download applet</h1>
25 <% out.write(sb.toString()); %>
26 <div style="text-align: center">
27 <applet CODEBASE="<%= ServletUrl.getBaseUrl(request) %>"
28 ARCHIVE="DownloadApplet.jar"
29 CODE="com.saperion.ngc.custom.applet.DownloadApplet"
30 WIDTH="95%"
31 HEIGHT="400px"
7 Practical Application: Downloading Documents via an External Applet
34
32 HSPACE="0"
33 VSPACE="0"
34 ALIGN="middle"
35 >
36 <param name="downloadURLS" value="<%= urls.toString()%>>"/>
37 </applet>
38 </div>
39<% } else {
40 out.write("<h1>There are no download URLs!</h1>");
41}
42%>
In line 2 the download URLs are retrieved from the session. The following lines build a concatenated
string of the URLs and show them as HTML lists.
In line 27 the applet is included to the JSP. Once again, the "ServletUrl" class is used to
get the base URL of the request. The applet works with the archive "DownloadApplet.jar"
and the "com.saperion.ngc.custom.applet.DownloadApplet" class. The jar which contains the
"DownloadApplet" needs to be accessible under the given base URL.
In line 36 the download URLs are passed as a parameter to the applet.
7.5.3 Applet Class
1// get URLS to download
2givenUrls = getParameter("downloadURLS");
3if (null != givenUrls && givenUrls.length() > 0) {
4 StringTokenizer st = new StringTokenizer(givenUrls, "$");
5 if (st.countTokens() > 0) {
6 urlSet = new HashSet<String>();
7 while (st.hasMoreTokens()) {
8 urlSet.add(st.nextToken());
9 }
10 }
11}
12[…]
13for (String url : urlSet) {
14 count++;
15 htmlTextArea.append("Started download of: " + url + "\n");
16 String fileName = "unknown";
17 String flag = "filename=";
18 if (url.indexOf(flag) > -1) {
19 fileName = url.substring(url.indexOf(flag) + flag.length());
20 fileName = fileName.substring(0, fileName.indexOf("&"));
21 fileName = URLDecoder.decode(fileName, "UTF-8");
22 } else {
23 fileName += count;
24 }
25 byte[] file = FileDownloader.downloadFile(url, htmlTextArea);
26 // store file in map
7.6 Deploying the Extensions
35
27 filesMap.put(fileName, file);
28 htmlTextArea.append("Downloaded file: " + fileName + " with size: "
29 + file.length / 1024 + " kb.\n\n");
30}
31button.setEnabled(true);
In line 1 the download URLs parameter is read and the concatenated URLs are separated in the following
lines.
In line 13 it is iterated over all URLs and the corresponding files are downloaded via the "FileDownloader".
The download URLs point to the SAPERION download servlet.
7.6 Deploying the Extensions
Now you need to deploy your custom extensions which are described in this section.
1. First, export the Java project as a jar file and place it in the SAPERION Web Client’s class
path.
2. Right click on the Eclipse project to open the context menu.
3. Select the "Export…" entry and choose "JAR-file". The "JAR Export" dialog opens.
4. Deselect the "lib" checkbox, because all libs are already present in the deployed Web Client.
5. Enter the path to the deployed SAPERION Web Client’s lib directory and click the [Finish]
button. After the export the classes are available for the Web Client's class path.
Now, the applet needs to be exported. Normally, the JAR-file that has been just exported can be used as
an applet as well because the required applet class "DownloadApplet.java" exists in that JAR-file. But the
applet needs to be stored to the Web Client’s root directory so that you need to export as follows:
1. Right click on the Eclipse project to open the context menu.
7 Practical Application: Downloading Documents via an External Applet
36
2. Select the "Export…" entry and choose "JAR-file". The "JAR Export" dialog opens.
3. Deselect the "lib" checkbox.
4. Change the export destination to the Web Client’s web root directory.
Example:
The Web Client runs under a Tomcat which is installed under ‘D:\develop\apache-
tomcat-6.0.29’. Then the Web Client's web root directory is ‘D:\develop\apache-tomcat-
6.0.29\webapps\ngclient\’.
5. Rename the JAR-file to "DownloadApplet.jar" and click the [Finish] button.
6. Finally, copy the JSP-file "DownloadAppletCaller.jsp " to the Web Client’s web root
directory.
7.7 Creating Server-side Script
For the purpose of using the extension some server-side scripts need to be created.
1. Open the correponding query mask in the SAPERION Forms Designer.
2. Select the mask element "Result list".
3. Open the Script Editor.
4. Enter the following script:
1//--------------------------------------------------------
2// Add own contextmenu submenu with item
3//--------------------------------------------------------
4Menu menu = new Menu("My custom menus");
5Menupopup popup = new Menupopup();
6menu.appendChild(popup);
7Menuitem open = new Menuitem("Open in applet");
8popup.appendChild(open);
9open.addEventListener("onClick", new EventListener() {
10 public void onEvent(Event event) {
11 com.saperion.ngc.custom.DocumentDownloader downloader = new
12 com.saperion.ngc.custom.DocumentDownloader();
13 downloader.downloadDocument($component$.getSelectedRows());
14 }
15});
16$component$.addCustomMenu(menu, false);
+ In line 4-8 a menu with a sub-menu item is created. This menu is added to the result
list in line 16.
7.8 Permissions
37
+ Line 11, 12 and 13 create the instance of our custom class using a full qualified name.
The method "DownloadDocument" is called passing in the selected result set rows.
7.8 Permissions
Since the applet tries to store the downloaded files to the local file system it needs the according
permissions. To grant the required permissions two steps need to be performed:
1. Sign the applet using Javas "jarsigner" tool.
For more information on the jarsigner, please refer to the official documentation: http://
download.oracle.com/javase/1.3/docs/tooldocs/win32/jarsigner.html
2. Adjust Javas security policy file in the Java installation directory.
If Java is installed under ‘C:\Program Files\Java\jre6\’ the security file can be found under
‘C:\Program Files\Java\jre6\lib\security\java.policy’.
3. To grant all permission to all code bases, enter to following lines:
grant {
//all permissions to all domains
permission java.security.AllPermission;
};
! Please note that this should only be done for testing, since all Java
programs now have all permission on the system.
For more information about Java security and policy files, please refer to: http://
download.oracle.com/javase/1.4.2/docs/guide/security/PolicyFiles.html