best practice web client scripting - docs.hyland.com · 6.1.2 beispiel-code ... + ausführung über...

44
Best Practice Web Client Scripting

Upload: leminh

Post on 30-Jul-2019

232 views

Category:

Documents


2 download

TRANSCRIPT

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.

Inhaltsverzeichnis

1 Einführung ........................................................................................................... 1

1.1 Scriptfähige Web Client Komponenten .............................................................. 1

1.2 Überblick Scripting-Typen ................................................................................... 1

1.2.1 Clientseitiges Scripting ................................................................................... 1

1.2.2 Serverseitiges Scripting ................................................................................... 2

1.2.3 ZUL-Scripte ...................................................................................................... 2

2 Scripting API und Richtlinie ............................................................................... 2

2.1 Der Platzhalter "$component$" ......................................................................... 3

2.2 Voll qualifizierte Java Klassennamen .................................................................. 3

2.3 Masken-Cache ...................................................................................................... 3

3 Web Client Scripting API .................................................................................... 4

3.1 Masken API ......................................................................................................... 4

3.2 Elemente API ....................................................................................................... 4

3.3 Ergebnisliste API ................................................................................................. 5

4 Ereignisse (Events) .............................................................................................. 5

4.1 ZK-Events ............................................................................................................. 5

4.2 Ergebnislisten-Events .......................................................................................... 6

4.3 Masken-Events ..................................................................................................... 6

5 Anleitung zum Scripting ..................................................................................... 6

5.1 Masken-Designer ................................................................................................. 6

6 Script-Beispiele .................................................................................................... 8

6.1 Validierung von Feldern ...................................................................................... 8

6.1.1 Szenario .......................................................................................................... 8

6.1.2 Beispiel-Code ................................................................................................... 8

6.1.3 Ablaufbeschreibung ......................................................................................... 8

6.2 Standardwerte einer Nachschlagetabelle .......................................................... 9

6.2.1 Szenario ........................................................................................................... 9

6.2.2 Beispiel-Code ................................................................................................... 9

6.2.3 Ablaufbeschreibung ......................................................................................... 10

6.3 Ausgewählte Daten an eine externe Anwendung senden ................................. 10

6.3.1 Szenario ........................................................................................................... 10

6.3.2 Beispiel-Code ................................................................................................... 10

Beispiel serverseitiger Code .......................................................................... 10

Beispiel clientseitiger Code ........................................................................... 11

6.3.3 Ablaufbeschreibung ......................................................................................... 11

6.4 Abfrage eingrenzen ............................................................................................ 12

6.4.1 Szenario ........................................................................................................... 12

6.4.2 Beispiel-Code ................................................................................................... 12

6.4.3 Ablaufbeschreibung ......................................................................................... 13

6.5 Dialog zur Benutzerauswahl anzeigen ............................................................... 14

6.5.1 Szenario ........................................................................................................... 14

6.5.2 Beispiel-Code ................................................................................................... 14

6.5.3 Ablaufbeschreibung ......................................................................................... 15

6.6 Indexmaske einer Aktenanwendung im Pop-Up-Fenster öffnen ....................... 16

6.6.1 Szenario ........................................................................................................... 16

6.6.2 Beispiel-Code ................................................................................................... 16

6.6.3 Ablaufbeschreibung ......................................................................................... 18

6.7 Differenzierte Ergebnislisten-Anzeige ................................................................ 18

6.7.1 Szenario ........................................................................................................... 18

6.7.2 Beispiel-Code ................................................................................................... 19

Listener der ersten [Ergebnis]-Schaltfläche ("SHOW_MODE_ORDER") ... 19

Listener der zweiten [Ergebnis]-Schaltfläche ("SHOW_MODE_RECEIPT")

.........................................................................................................................

19

Listener der Ergebnisliste ............................................................................. 20

6.7.3 Ablaufbeschreibung ......................................................................................... 20

6.8 Mash-Up mit abgeleiteten Daten nutzen .......................................................... 21

6.8.1 Szenario ........................................................................................................... 21

6.8.2 Beispiel-Code ................................................................................................... 21

Listener am Mash-Up (Serverscript) ............................................................ 21

Mash-Up Script ............................................................................................. 23

6.8.3 Ablaufbeschreibung ......................................................................................... 25

6.9 Deaktivieren von Kontextmenü-Einträgen in der Navigation/ Ergebnisliste .... 26

6.9.1 Szenario ........................................................................................................... 26

6.9.2 Beispiel-Code ................................................................................................... 26

Deaktivieren von Menüeinträgen in der Navigation ................................... 26

Deaktivieren von Menüeinträgen in der Ergebnisliste ................................ 27

6.9.3 Ablaufbeschreibung ......................................................................................... 27

6.10 Ergebnisliste in eine Indexmaske einbetten ...................................................... 28

6.10.1 Szenario ........................................................................................................... 28

6.10.2 Beispiel-Code ................................................................................................... 28

6.10.3 Ablaufbeschreibung ......................................................................................... 29

6.11 Einschränkung von Nachschlagefeldern mit mehreren Bedingungen .............. 30

6.11.1 Szenario ........................................................................................................... 30

6.11.2 Beispiel-Code ................................................................................................... 30

7 Praktische Anwendung: Dokumente über ein externes Applet herunterladen . 31

7.1 Szenario ............................................................................................................... 31

7.2 Erweiterung des Web Clients um eigene Klassen ............................................. 32

7.3 Java-Projekt anlegen ............................................................................................ 32

7.4 Logik und Verhalten ............................................................................................ 33

7.5 Code-Beispiele ..................................................................................................... 33

7.5.1 DocumentDownloader.java ............................................................................. 33

7.5.2 DownloadAppletCaller.jsp ............................................................................... 34

7.5.3 Applet-Klasse ................................................................................................... 35

7.6 Anwendung der Erweiterungen .......................................................................... 36

7.7 Serverseitiges Script erzeugen ........................................................................... 37

7.8 Berechtigungen .................................................................................................... 38

1.1 Scriptfähige Web Client Komponenten

1

Best Practice Web Client Scripting

1 Einführung

In diesem Handbuch erhalten Sie die Grundlagen zum Scripting für den Web Client. Wir möchten hier

grundlegende Fragen klären wie etwa:

+ Bei welchen Web Client Komponenten kann Scripting eingesetzt werden?

+ Welche Sprachen werden verwendet, welche Script-Arten gibt es?

+ Wie können Scripte angelegt werden?

1.1 Scriptfähige Web Client Komponenten

Eine typische Web Client Benutzeroberfläche besteht in der Regel aus den Komponenten Navigation,

Indexierungsmaske, Ergebnisliste sowie einem Viewer (siehe Abbildung unten).

Während Navigation und Viewer nicht scriptfähig sind, können Sie Scripting für die Ergebnisliste und

für die Indexierungsmaske einsetzen. Folglich unterstützen Elemente wie Eingabefelder, Schaltflächen

und Ergebnislisten (inklusive der Zeilen) das Scripting.

1.2 Überblick Scripting-Typen

Generell lässt sich das Scripting für den Web Client in das serverseitige und clientseitige Scripting

unterteilen.

1.2.1 Clientseitiges Scripting

Mit clientseitigem Scripting ist es möglich, benutzerdefinierte JavaScript in Masken zu implementieren.

Dies ermöglicht es Ihnen, die DOM (Document Object Model) Ihrer Webseite zu ändern bzw. zu

überwachen.

Clientseitige Scripte werden als <script> Elemente in eine Seite eingefügt. Hierdurch sind alle Elemente

einer aktuell geöffneten Webseite erreichbar. Beispielsweise können Elemente einer Maske geändert oder

eine Eingabe durch einen Anwender kann validiert werden.

i Die Ausführung der clientseitigen Scripte erfolgt, wenn der Browser diese Seite rendert. Details

der Syntax, mögliche Funktionen und Performance sind damit von der JavaScript- Engine des

Browsers abhängig.

Zusammenfassung

+ Script-Sprache: JavaScript

+ Ausführung durch den Browser

2 Scripting API und Richtlinie

2

+ ermöglicht Zugriff auf die aktuell in der Seite erhaltenen Elemente

+ Einsatzmöglichkeiten: Definition von eigenen JavaScript-Methoden, die bei Bedarf (z.B. als Reaktion

auf Events) aufgerufen werden können.

+ Für Geschäftslogik nicht geeignet

+ Vorsicht: In einer AJAX-Anwendung sind nicht immer alle Elemente in der Seite vorhanden, da sich

der Seiteninhalt dynamisch ändern kann.

1.2.2 Serverseitiges Scripting

Mit Hilfe von serverseitigem Scripting ist es möglich, die Businesslogik des Web Clients zu modifizieren.

Somit kann die Dokumentenarchivierung, Revisionierung oder auch Workflow-Funktionalitäten

angepasst werden. Weiterhin können eigene Logiken und Masken zum Web Client hinzugefügt werden.

Serverseitige Scripte werden mit der Java Programmiersprache implementiert und serverseitig durch

Servlet Container ausgeführt (z.B. durch Tomcat). Serverseitige Scripte können sowohl auf clientseitige

als auch serverseitige Komponenten zugreifen.

Zusammenfassung

+ Script-Sprache: Java

+ Ausführung über Application Server (z.B. Tomcat)

+ ermöglicht Zugriff serverseitige Objekte. ZK-API erlaubt Zugriff auf clientseitige Ressourcen (z.B.

Client-Script definierte JavaScript Funktionen)

+ Server-Script wird von Interpreter innerhalb der ZK-Engine, welcher auf dem Application Server

läuft, ausgeführt (hierdurch browserunabhängig)

+ Vorsicht: Wenn das Element in die Seite eingefügt wird, sind eventuell noch nicht alle anderen

Elemente verfügbar.

1.2.3 ZUL-Scripte

Eine Spezialform der serverseitigen Scripte bilden die ZUL-Dateien. "ZUL" steht für ZK User Interface

Markup Language. Über die ZUL-Dateien können individuelle Masken und auch deren Verhalten

implementiert werden. ZUL-Dateien haben ihre eigene XML-Syntax, die über das ZK Framework

serverseitig interpretiert und ausgeführt werden.

i ZK ist das GUI Framework, über welches der SAPERION Web Client implementiert ist.

Nähere Informationen zum ZK Framework und ZUL-Dateien erhalten Sie hier: http://www.zkoss.org/.

2 Scripting API und Richtlinie

Bevor Sie mit dem Implementieren der Scripte beginnen, sollten Sie die Richtlinie und auch einige

Hinweise zu API kennenlernen.

2.1 Der Platzhalter "$component$"

3

2.1 Der Platzhalter "$component$"

In der Regel wählen Sie die Elemente einer Maske aus, zu denen Sie ein Script implementieren möchten.

Elemente können dabei beispielsweise eine Ergebnisliste, Eingabefelder oder Schaltflächen sein.

Da für serverseitige Scripte die Programmiersprache Java verwendet wird, werden Referenzen benötigt,

um so einen Zugriff auf die Objekte zu erhalten. "$component$" fungiert hierbei als Platzhalter für

Variablen, der einen Zugriff auf die aktuelle Komponente ermöglicht.

i Das "$component$"-Konstrukt referenziert stets das Objekt, auf das das serverseitige Script

zugreifen soll.

2.2 Voll qualifizierte Java Klassennamen

Ein Server-Script wird während der Laufzeit vom ZK Framework interpretiert und ausgeführt, wenn

die entsprechende Maske im Web Client geladen wird. Die dynamische Code-Interpretation und

-Ausführung erfordert die ausschließliche Verwendung von voll qualifizierten Klassennamen, wenn es

sich um nicht-ZK-Klassen oder nicht aus java.lang stammende Klassen handelt.

! Klassen, die nicht von ZK oder aus java.lang stammen, müssen absolut benannt werden.

Beispiel 1

com.saperion.ngc.resultset.ResultSetViewImpl view = new com.saperion.ngc.resultset.ResultSetViewImpl();

view.doSort();

Beispiel 2

import com.saperion.ngc.resultset.ResultSetViewImpl;

ResultSetViewImpl view = new ResultSetViewImpl();

view.doSort();

2.3 Masken-Cache

Immer wenn eine Maske im Masken Designer geändert worden ist, z.B. durch Hinzufügen eines Scripts,

muss diese Maske entsprechend neu geladen werden, um so eine Aktualisierung zu erreichen. Ein

Neustart des Systems oder eine Neuanmeldung durch den Anwender ist dabei nicht notwendig.

Dies gilt auch für den Web Client, solange der Masken-Cache deaktiviert ist. Nur wenn der Masken-Cache

deaktiviert ist, wird die Maske vom SAPERION Core neu geladen, sobald sie erneut geöffnet wird. Das

Deaktivieren erfolgt in der "web.xml". Setzen Sie folgenden Parameter:

<env-entry>

<description>Turns cache for masks on or off.</description>

3 Web Client Scripting API

4

<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 Bitte deaktivieren Sie den Masken-Cache in der "web.xml", wenn Sie Web Client Scripte entwickeln.

3 Web Client Scripting API

In diesem Kapitel erhalten Sie eine Übersicht über wichtige API Calls, die für das Anlegen gültiger Scripte

notwendig sind.

3.1 Masken API

Da alle Komponenten, die Scripte unterstützen, in einer Maske eingebettet sind, ist die Masken API

gewöhnlich die geeignete Eingangsstelle für die Scripte. Um eine Referenz zu der Maske zu erhalten, ist

die Methode "getParentForm()" auf jeder "$component$"-Instanz aufrufbar.

Die folgende Tabelle enthält die wichtigsten API Calls:

Masken API

Methode Beschreibung

getFields () Liefert eine Liste aller Felder innerhalb der Maske.

getButtons () Liefert eine Liste aller Schaltflächen innerhalb der Maske.

getResultSets () Liefert eine Liste aller Ergebnislisten innerhalb der Maske.

doClearContent () Leert alle Felder der Maske.

DoCreateSysMask (String) Lädt die Systemabfragemaske zu dem gegebenen DDC-Namen.

DoCreateSysQueryMask (String) Lädt die Systemindexmaske zu dem gegebenen DDC-Namen.

3.2 Elemente API

Die Elemente API ist gültig für alle Maskenobjekte. Die folgende Tabelle enthält die wichtigsten API Calls:

Elemente API

Methode Beschreibung

getParentForm () Liefert eine Referenz zu der umgebenden Maske. Diese Methode ist aufrufbar auf jeder "$component$"-In-

stanz.

getFieldName () Liefert einen eindeutigen Namen eines Maskenobjekts.

setFieldInactive (TRUE/ FALSE) Aktiviert/ deaktiviert ein Feld.

3.3 Ergebnisliste API

5

Methode Beschreibung

setValueImplicity (SaPropertyVa-

lue)

Ändert einen Feldwert und meldet diese Änderung an die umgebende Maske.

3.3 Ergebnisliste API

Über die "ResultSet" API können Sie Ergebnislisten manipulieren, Informationen über das aktuelle

Suchergebnis erhalten und auch Abfragen starten.

Die folgende Tabelle enthält die wichtigsten API Calls:

Ergebnisliste API

Methode Beschreibung

clearResultSet () Leert die Ergebnisliste.

setSelectedIndex (int) Wählt die Zeile in der Ergebnisliste entsprechend des Indexes.

getSelectedIndex () Liefert den Index der gewählten Zeile.

getSelectedItems () Liefert alle gewählten Zeilen.

getNumberOfIndexes () Liefert die Anzahl der Zeilen.

refresh () Lädt die Ergebnisliste neu.

selectNext () Wählt die nächste Zeile.

selectPrevious () Wählt die vorherige Zeile.

selectFirst () Wählt die erste Zeile.

selectLast () Wählt die letzte Zeile

performQuery (SaQueryInfo) Führt eine Abfrage aus und zeigt das Ergebnis an.

i Eine komplette API-Übersicht bietet Javadoc aus dem Web Client Auslieferungspaket.

4 Ereignisse (Events)

Der Web Client operiert ereignisbasiert, was bedeutet, dass jede Aktion durch den Benutzer ein Event

auslöst. Hier lernen Sie die wichtigsten Ereignisse kennen.

4.1 ZK-Events

ZK Framework Events können auf Server- und Client-Seite ausgeführt werden.

ZK-Events

Event Beschreibung

onClick Wird durch einen Mausklick ausgelöst.

5 Anleitung zum Scripting

6

Event Beschreibung

onBlur Wird ausgelöst, wenn ein Eingabefeld seinen Fokus verliert (Cursor wechselt zu einem anderen Eingabefeld).

onFocus Wird ausgelöst, wenn ein Eingabefeld den Fokus erhält.

onChange Wird ausgelöst, wenn der Wert eines Elements sich ändert.

onSelect Wird ausgelöst, wenn die Auswahl eines Listenelements sich ändert.

onCheck Wird ausgelöst, wenn eine Checkbox aktiviert wird.

onCreate Wird ausgelöst, wenn ein Element und dessen Unterelemente angelegt werden.

4.2 Ergebnislisten-Events

Die Ergebnisliste hat ein grundlegendes Ereignis:

Ergebnislisten-Event

Event Beschreibung

onIndexChange Wird ausgelöst, wenn Einträge der Ergebnisliste sich ändern.

4.3 Masken-Events

Eine Maske hat zwei grundlegende Ereignisse:

Masken-Events

Event Beschreibung

onDisplayResultItem Wird ausgelöst, wenn ein Dokument in einer Maske angezeigt wird.

onSaveDoc Wird ausgelöst, wenn ein Dokument in der Maske gespeichert wird.

5 Anleitung zum Scripting

Dieser Abschnitt befasst sich mit der Vorgehensweise beim Scripting.

i Die folgenden Kapitel des Scripting fokussiert das serverseitige Scripting, dennoch erhalten Sie

clientseitige Code-Beispiele (siehe Kapitel "Ausgewählte Daten zu einer externen Applikation

senden")

5.1 Masken-Designer

Ausgangspunkt für das Scripting ist der SAPERION Masken-Designer, der über den Rich Client

erreichbar ist. Alle Masken werden mit Hilfe des Masken-Designers generiert, daher müssen Sie Ihre

eigenen Scripte hierüber implementieren.

5.1 Masken-Designer

7

1. Öffnen Sie den Masken-Designer im Rich Client, indem Sie im Ribbon DESIGN > Gruppe

ANWENDUNGEN > Befehl SUCHMASKE ERSTELLEN klicken. Hierdurch öffnet sich der

Masken-Designer.

2. Wählen Sie das Maskenelement aus, zu dem Sie ein Script hinterlegen möchten. In dem

"Eigenschaften"-Panel auf der rechten Seite, können Sie nun zwischen "Server-Script" und

"Client-Script" auswählen.

3. Klicken Sie auf die Schaltfläche [...] der gewünschten Scriptart. Es öffnet sich, abhängig

von Ihrer Auswahl, der Dialog "Server-Script" bzw. "Client-Script".

6 Script-Beispiele

8

4. Geben Sie nun das gewünschte Script ein.

6 Script-Beispiele

In diesem Abschnitt listen wir einige Script-Beispiele mit der entsprechenden Beschreibung auf.

6.1 Validierung von Feldern

6.1.1 Szenario

Dieses Beispiel demonstriert eine Validierung einer Eingabe (Feld "SupplierNo") in der Indexmaske

durch den Anwender. Bevor Änderungen im Archiv gespeichert werden, soll überprüft werden, ob alle

Felder ausgefüllt wurden. Die Validierung erfolgt, wenn der Anwender die Eingabe bzw. das auszufüllende

Formular abschickt.

Ist das Feld "SupplierNo" nicht ausgefüllt, erhält der Benutzer eine Benachrichtigung.

Zu diesem Zweck benötigt das Script Zugriff auf den Wert des Feldes und muss den Ablauf der

Event-Bearbeitung beeinflussen können. Verwenden Sie hierfür ein Server-Script und ordnen es der

[OK]-Schaltfläche der Indexmaske zu.

6.1.2 Beispiel-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 Ablaufbeschreibung

Um die Validierung durchführen zu lassen, ist der oben genannte Code der Schaltfläche [OK] in der

Maske zugeordnet. Ein neuer Event Listener für das Ereignis "onClick" wird registriert. Wird das Ereignis

ausgelöst, wird die Methode "onEvent" des registrierten Event Listeners abgerufen.

6.2 Standardwerte einer Nachschlagetabelle

9

In diesem Fall verweist "$component$" auf die [OK]-Schaltfläche. Über "getParentForm" wird die

umgebende Maskenreferenz erreicht, anhand dessen eine Liste von allen Maskenfeldern durch

"getFields" abgerufen wird. Die Zählschleife iteriert über alle Maskenelemente, jedoch wird für das Feld

"SupplierNo" die Länge des aktuellen Wertes überprüft (angewandte Methoden: "getFieldName" und

"getValue").

Ist dieses Feld leer (Länge ist gleich null), wird der Anwender durch eine Meldungsbox darauf

hingewiesen. Da das Formular nun nicht gültig ist, wird das Ereignis durch "event.stopPropagation"

angehalten, so dass keine Änderungen abgespeichert werden können.

6.2 Standardwerte einer Nachschlagetabelle

6.2.1 Szenario

In diesem Beispiel muss der Benutzer mehrere Dokumente nachindexieren, wobei immer die gleichen

Felder ausgefüllt werden müssen.

Die Werte der Felder sind voneinander abhängig, daher können einige Felder automatisch gefüllt werden,

sobald der Wert für ein bestimmtes Feld vorliegt. In diesem Fall macht der Anwender eine Eingabe in das

Feld "Lieferanten-Nr." und die Felder "Name", "Adresse" und "Telefonnummer" des Lieferanten werden

automatisch eingetragen. Die Werte werden aus einer Nachschlagetabelle geladen.

Zu diesem Zweck wird das Feld "SupplierNr" benötigt.

6.2.2 Beispiel-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 Script-Beispiele

10

}

}

}

}

});

6.2.3 Ablaufbeschreibung

Der oben genannte Code wird dem Feld zugeordnet, in das der Benutzer den Wert für "Lieferanten-Nr."

eingibt ("$component$" referenziert zu diesem Feld). Sobald dieses Feld einen Wert enthält und seinen

Fokus verliert, wird das Script ausgeführt, da der "onBlur"-Event Listener eingesetzt wird.

Zunächst wird überprüft, ob das Feld einen "Null" Wert und mehr als 0 Zeichen enthält. Ist dies

der Fall, wird eine Datenbankabfrage mit Hilfe des Classic Connectors ausgeführt. Eine bereits

eingeloggte Classic Connector Instanz wird durch den Classic Connector Provider abgerufen. Die

Methode "searchHQL" wird eingesetzt, um eine Abfrage durchzuführen.

Nachdem das Abfrageergebnis dahingehend überprüft worden ist, dass das Feld nicht leer ist, wird

die erste Zeile durch den Call "result.get(0)" ausgeschlossen. Abfrageergebnis-Zeilen werden durch

"SaDocumentInfo"-Objekte (die in diesem Beispiel für "Lieferanten-Nr." stehen) repräsentiert. Eine

Zählschleife iteriert dabei durch alle Felder der Indexmaske. Spezielle Felder (durch ihre Namen

identifiziert) werden mit den Werten "Name", "Adresse" und "Telefonnummer" des ausgewählten

Lieferanten ausgefüllt.

6.3 Ausgewählte Daten an eine externe Anwendung

senden

6.3.1 Szenario

In diesem Beispiel werden ausgewählte Daten einer Ergebnisliste zu einer externen Anwendung

gesendet. Die Übertragung erfolgt dabei an eine einfache Web Anwendung, Daten werden als

URL-Paramater übermittelt.

Um die Daten zu senden, wird dem Benutzer ein neues Kontextmenüelement zur Verfügung gestellt.

Zu diesem Zweck werden Server- und Client-Scripte verwendet. Der serverseitige Code ruft direkt eine

clientseitige JavaScript-Funktion ab.

6.3.2 Beispiel-Code

6.3.2.1 Beispiel serverseitiger Code

Menuitem item = new Menuitem("External");

item.addEventListener("onClick", new EventListener() {

public void onEvent(Event event) {

List rows = $component$.getSelectedRows();

6.3 Ausgewählte Daten an eine externe Anwendung senden

11

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 Beispiel clientseitiger 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,

dependent=yes, resizable=yes");

}

6.3.3 Ablaufbeschreibung

Das serverseitige Script ist der Ergebnisliste der Maske zugeordnet. Zu dem neu angelegten

Kontextmenüelement "Extern" wird ein Event Listener registriert. Der Listener wird dann aufgerufen,

sobald der Anwender den Menüeintrag wählt.

Zunächst erhält die "onEvent" Methode die ausgewählten Zeilen der Ergebnisliste, zu der das

Script zugeordnet ist. Sobald eine Zeile der Ergebnisliste ausgewählt wird, wird das Script weiter

ausgeführt. Über "rows.get(0)" wird die erste und die einzeln ausgewählte Zeile, die durch ein

"ResultProperties"-Objekt repräsentiert wird, ausgelesen. Durch Anwendung der Objektmethode

"getPropertyAsString" werden Daten ("Rechnungsnummer", "Betrag" und "Lieferanten-Nr.") abgerufen.

Da SAPERION auch Multivalue-Felder unterstützt, liefert ein Call zu "getPropertyAsString" einen Array.

Wenn [0] eingesetzt wird, wird der erste Array-Eintrag geliefert.

Mit den angefragten Daten wird der JavaScript-Funktionscall als String generiert. Über den ZK-Call

"Clients.evalJavaScript" wird die JavaScript-Funktion clientseitig ausgeführt.

Um sicherzustellen, dass die JavaScript-Funktion auch erfolgreich ausgeführt wird, muss die

entsprechende Funktion auch clientseitig vorhanden sein. Wie in dem Code-Beispiel oben ersichtlich,

6 Script-Beispiele

12

werden benötigte Daten als Parameter gesendet. Daten werden übertragen, indem per JavaScript ein

neues Browser-Fenster bzw. -Tab mit der URL der externen Anwendung geöffnet wird.

6.4 Abfrage eingrenzen

6.4.1 Szenario

Der aktuell angemeldete Benutzer führt eine Abfrage aus und soll nur eine auf ihn abgestimmte

Ergebnisliste zu sehen bekommen. Welche Dokumente er sehen darf, wird in einer speziellen

Nachschlagetabelle (der DDC mit dem Namen "invoices_user") gepflegt.

In diesem Beispiel geht es um Rechnungsdaten von Kunden in einer DDC. Die Kunden werden dabei

durch die Kundennummer (customerID) identifiziert.

6.4.2 Beispiel-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 =

(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);

6.4 Abfrage eingrenzen

13

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.4.3 Ablaufbeschreibung

Das serverseitige Script ist der Ergebnisliste zugewiesen. Ein Event Listener ist auf

das "onButtonQuery"-Ereignis registriert und wird gefeuert, sobald der Benutzer auf die

[Suche]/ [Ergebnis]- Schaltfläche klickt.

Die "onEvent"-Methode erzeugt sich zunächst eine Instanz des SaClassicConnectors, welche benötigt

wird, um die Benutzer-Kundennummer (customerID)-Beziehung in der Nachschlagetabelle zu suchen.

Um die Abfrage auszuführen, ist auch der Name des aktuellen Benutzers notwendig - dieser wird aus

der Web Client Session herangezogen.

Der Benutzername wird mit der Nachschlagetabelle anhand der SaClassicConnectors-Methode

"searchHQL" abgeglichen. Führt das Ergebnis des Abgleichs zu Treffern, wird eine kommaseparierte

Liste mit den entsprechenden Kundennummern generiert.

Um diese ID-Liste zusammen mit den vom Benutzer eingegebenen Suchkriterien zu senden, wird ein

"CustomConditionBean" verwendet. Dieses beschreibt das zusätzliche Suchkriterium und wird an die

bestehenden Abfragebedingungen über den Call "orig.getList().add" angefügt.

6 Script-Beispiele

14

Der Benutzer sieht als Resultat eine Ergebnisliste, die auf ihn abgestimmt bzw. eingeschränkt ist und in

der auch seine eigenen Suchkriterien berücksichtigt worden sind.

6.5 Dialog zur Benutzerauswahl anzeigen

6.5.1 Szenario

In einer Indexmaske des Web Clients soll auf Knopfdruck der Dialog "Benutzer auswählen..." geöffnet

werden können. Der Anwender kann dann in diesem Dialog den gewünschten Benutzer auswählen und

diesen in ein Index-Feld eintragen sobald er den Dialog zur Benutzerauswahl schließt.

6.5.2 Beispiel-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;

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("");

6.5 Dialog zur Benutzerauswahl anzeigen

15

// 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.5.3 Ablaufbeschreibung

In diesem Beispiel muss eine Klasse "ResultSetPresenter" aus dem Web Client genutzt werden, die

die Sichtbarkeit "Package" hat. Um eine Veränderung des ResultSetPresenter-Codes zu vermeiden,

wird das oben aufgeführte Script in das gleiche Package gelegt wie ResultSetPresenter selbst

- "com.saperion.ngc.resultset".

Sie müssen aus dem oben aufgeführten Script eine JAR-Datei erstellen. Durch "package

com.saperion.ngc.resultset;" wird definiert, dass diese Klasse, obwohl physisch in einer anderen JAR als

der Web Client, logisch doch zu dem gleichen Package gehört. In das Serverscript-Feld wird dann nur

der Aufruf "com.saperion.ngc.resultset.ChooseUser.method($component$);" eingetragen.

In dem obigen Code wird auf der Schaltfläche [Nutzer auswählen] ein onClick-Listener gelegt. Bei

Aktivierung wird das Textfeld bestimmt, in das dann der ausgewählte Nutzer eingetragen werden soll

(im Beispiel das Feld mit der ID 40). Die ResultSetView stellt den erwähnten ResultSetPresenter zur

Verfügung und der wiederum die Menge der Nutzer ("userTree").

6 Script-Beispiele

16

Es wird dann ein weiterer Listener "dlgEvtListener" erstellt werden, der im Dialog zur Benutzerauswahl

den Klick auf die Schaltfläche [OK] abfängt und den Nutzer in das vorher bestimmte Textfeld einträgt.

6.6 Indexmaske einer Aktenanwendung im

Pop-Up-Fenster öffnen

6.6.1 Szenario

In einer Aktenanwendung sollen Dokumente in Akten eingepflegt werden. Da es zu diesen Dokumenten

keine physischen Dateien gibt, sondern nur Metadaten, soll die Indexmaske nicht auf Grundlage

eines Dokuments, sondern per Schaltfläche in einem Pop-Up-Fenster geöffnet werden. Die eingegeben

Indexinformationen werden über die Schaltfläche [OK] gespeichert. D.h., dass die Indexmaske solange

angezeigt wird, bis der Anwender diese wieder aktiv schließt. Auf diese Weise können mehrere

Dokumente hintereinander eingepflegt und indexiert werden. Hat der Anwender keine weiteren

Dokumente zum Indexieren mehr, klickt er auf die Schaltfläche [Fertig] und schließt damit das

Pop-Up-Fenster.

6.6.2 Beispiel-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;

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+"]";

6.6 Indexmaske einer Aktenanwendung im Pop-Up-Fenster öffnen

17

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());

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>();

6 Script-Beispiele

18

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 Ablaufbeschreibung

Die Schaltfläche [Dokument indexieren] erhält einen Listener, der beim Anklicken die Methode

"createPopup" startet. Diese Methode verwendet intern IntelligentFormPopupWindow.show() und

schickt an das generierte Fenster ein Event, das definiert, welche Maske darin angezeigt werden soll und

auf welcher DDC die Maske beruht.

Die Maske enthält die zwei Schaltflächen [OK] und [Fertig]. Beide schicken beim Anklicken automatisch

Events an die Maske. Um die Schaltflächen mit Funktionalität auszustatten, werden zwei Listener an

die Maske gehängt, die auf die Events lauschen. Der "onFBOk"-Listener sammelt die eingegebenen

Indexdaten und legt das Dokument in einem passenden Datenformat per Classic Connector in der DDC

an. Da es sich hier um eine Aktenanwendung handelt, muss das Dokument in die entsprechende Akte

abgelegt werden. Dies wird durch den Aufruf "connector.addToFolder(…)" erreicht.

6.7 Differenzierte Ergebnislisten-Anzeige

6.7.1 Szenario

Im Masken-Designer besteht die Möglichkeit, z.B. per SQL-Code Bedingungen an die

[Ergebnis]-Schaltfläche zu knüpfen, welche eine vorgefilterte Anzeige der Ergbnisliste zur Folge haben.

6.7 Differenzierte Ergebnislisten-Anzeige

19

Dies hat jedoch nur Auswirkung auf den Rich Client. Um eine vorgefilterte Ergebnisliste per Schaltfläche

auch im Web Client zu realisieren, wird im Folgenden das entsprechende Script implementiert.

6.7.2 Beispiel-Code

Im Folgenden erhalten Sie für zwei [Ergebnis]-Schaltflächen das jeweilige Beispiel-Script. Es werden über

die Schaltflächen jeweils unterschiedliche Ergebnislisten geliefert.

6.7.2.1 Listener der ersten [Ergebnis]-Schaltfläche

("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 der zweiten [Ergebnis]-Schaltfläche

("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) {

IntelligentFormView parentForm = (IntelligentFormView) ((FormButton) $component$).

getParentForm();

parentForm.setAttribute(ScriptConstants.SHOW_MODE, ScriptConstants.SHOW_MODE_RECEIPT);

}

6 Script-Beispiele

20

});

6.7.2.3 Listener der Ergebnisliste

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.7.3 Ablaufbeschreibung

Die Ergebnisliste kennt das Event "onButtonQuery“. Dies wird bei Klick auf eine der

[Ergebnis]-Schaltflächen übergeben. Da nicht differenziert werden kann, welche Schaltfläche der

Urheber des Events war, erhalten beide Event-Listener jeweils ein spezifisches Attribut inkl.

6.8 Mash-Up mit abgeleiteten Daten nutzen

21

Wert, das beim Auslösen an die übergeordnete Maske gehängt wird (parentForm.setAttribute(

ScriptConstants.SHOW_MODE, ScriptConstants.SHOW_MODE_ORDER);).

Weiterhin erhält die Ergebnisliste zusätzlich einen Listener, der beim Event "onButtonQuery" das von

der jeweiligen Schaltfläche gesetzte Attribut ausliest und je nach Wert das Event mit der passenden

SQL-Bedingung erweitert (origEvent.getList().add(ccbReceipt);).

6.8 Mash-Up mit abgeleiteten Daten nutzen

6.8.1 Szenario

In einer Aktenanwendung soll durch Klick auf einen Aktendeckel eine Vorschau auf die Metadaten

der darin enthaltenen Dokumente tabellarisch angezeigt werden. Dazu wird ein sogenanntes Mash-Up

verwendet. Hierbei handelt es sich um ein Maskenelement, das per JavaScript programmiert werden

kann.

Hierzu werden die zwei Methoden "setValue(name,value)" und "execute()" zur Verfügung gestellt.

Ein serverseitiges Script in Java holt die Metadaten der Dokumente in dem angeklickten Aktendeckel.

Anschließend werden diese Daten auf der Client-Seite durch die beiden genannten JavaScript-Methoden

als Tabelle dargestellt.

6.8.2 Beispiel-Code

6.8.2.1 Listener am Mash-Up (Serverscript)

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;

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;

6 Script-Beispiele

22

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());

}

}

jsonValue = listToJSONString(values);

}

EditFieldType eft = new EditFieldType();

eft.setDbFieldName(JSCRIPT_KEY);

6.8 Mash-Up mit abgeleiteten Daten nutzen

23

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">

var valGlob = null;

function execute() {

var elTable;

var elRow;

6 Script-Beispiele

24

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;

}

function setValue(name, value) {

if (name.toLowerCase()=='mashup') {

valGlob = value;

6.8 Mash-Up mit abgeleiteten Daten nutzen

25

}

}

</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 Ablaufbeschreibung

Im Masken-Designer muss eine Verknüpfung von der Liste der Aktendeckel auf das Mash-Up gelegt

werden. Somit löst ein Klick auf einen Aktendeckel eine Übertragung der Metadaten des Aktendeckels

zum Mash-Up aus.

Es wird auf ein beliebiges Feld in der Maske ein Listener gelegt, der bei Erschaffung dieses Feldes

seinerseits einen Listener auf das in der Maske enthaltene Mash-Up legt. Wenn auf einen Aktendeckel

geklickt wird, wird das "onIndexChange"-Event zuerst von dem zusätzlich eingefügten Listener auf

dem Mash-Up behandelt. In dem Event sind die Metadaten des Aktendeckels enthalten. Diese

können jetzt dazu genutzt werden, über den Classic Connector die zugehörigen Dokumente zu finden

(connector.searchFolderDocuments(…)).

Aus den Metadaten der gefundenen Dokumente wird ein JSON-String (JavaScript Object Notation)

erstellt - dies entspricht der String-Repräsentation der Liste der Dokumente bzw. deren Metadaten (

[{key1:value1, key2:value2}, { key1:value1, key2:value2}, { key1:value1, key2:value2}] ). Dem aktuellen

Event wird dieser JSON-String als neue Property hinzugefügt, wobei der Schlüssel ein fest definierter

Wert ist ("mashup"), der wiederum im JavaScript-Code verwendet wird.

Das manipulierte Event wird so zum Mash-Up weitergereicht, wo dessen Daten zur Ausführung der

Methoden "setValue(name,value)" und "execute()" genutzt werden. In dem Javascript-Code wird per

CSS eine Tabelle inklusive Tabellenkopf definiert. Dabei wird für jede Property des Events die Methode

"setValue(name,value)" aufgerufen. Ist ein Property dabei, dessen Name genau der im Serverscript

definierte Wert ist ("mashup"), wird dessen Property-Wert (JSON-String) global gespeichert.

6 Script-Beispiele

26

Nachdem für alle Properties die "setValue"-Methode aufgerufen wurde, wird "execute()" ausgeführt. Hier

wird die evtl. vom letzten Event noch gefüllte Tabelle geleert und mittels DOM-Manipulation aufbereitet

(z.B. elRow.appendChild(elCell);).

6.9 Deaktivieren von Kontextmenü-Einträgen in der

Navigation/ Ergebnisliste

6.9.1 Szenario

Bei allen Anwendungsordnern in der Navigation werden die Menüeinträge "Löschen", "Umbenennen"

sowie "Neuen Ordner hinzufügen" deaktiviert. Hierzu kann der Navigation ein eigener Handler

für Menüeinträge zugewiesen werden, der für einen bestimmten Typ von Ordner dynamisch die

Verfügbarkeit der Menüeinträge festlegen kann.

Auch die Ergebnisliste erlaubt das Entfernen von Menüeinträgen. Dazu kann dem

Ergebnislisten-Element einer Maske mit einem Server-Script eine Liste von Menüeinträgen übergeben

werden, die dann nicht im Kontextmenü zur Verfügung stehen.

i Wie bei den anderen Script-Beispielen auch, empfehlen wir hierfür eine eigene JAR-Datei zu

erstellen und in den Web Client einzubinden. Der Einfachheit halber wird in diesem Beispiel darauf

verzichtet.

Das Script wird direkt in die Datei "index.zul" eingefügt, indem dem Navigation-Element ein zusätzliches

Attribut hinzugefügt wird.

6.9.2 Beispiel-Code

6.9.2.1 Deaktivieren von Menüeinträgen in der 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) {

6.9 Deaktivieren von Kontextmenü-Einträgen in der Navigation/ Ergebnisliste

27

NaviMenuRights rights = NaviMenuRights.getAllRights().disableDeleteFolder()

.disableRename().disableCreateFolder();

return rights;

}

}

// 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>

6.9.2.2 Deaktivieren von Menüeinträgen in der Ergebnisliste

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 Ablaufbeschreibung

Deaktivieren von Menüeinträgen in der Navigation

In diesem Beispiel werden jedem Ordner die gleichen Rechte zugewiesen. Da die Methode

getMenuRights aber jeweils den zu behandelnden Ordner als Parameter übergeben bekommt, können

die Rechte für jeden Ordner dynamisch bestimmt werden (z.B. anhand des Ordnernamens). Es gibt

auch die Möglichkeit, die Rechte vom aktuell angemeldeten Benutzer abhängig zu machen. Dazu kann

in der getMenuRights-Methode auf den aktuellen Benutzer zugegriffen werden:

User userAttribute = new HttpSessionWrapper().getUserAttribute();

Über "userAttribute" können dann Informationen wie Benutzertyp oder die ID des Benutzers abgefragt

und für die Vergabe der Rechte verwendet werden.

Für die Verwendung des HttpSessionWrappers folgender zusätzlicher Import notwendig:

import com.saperion.ngc.resultset.MenuItemKey;

6 Script-Beispiele

28

Deaktivieren von Menüeinträgen in der Ergebnisliste

Wie bei der Deaktivierung von Menüeinträgen in der Navigation kann auch hier die Liste der

Menüeinträge dynamisch bestimmt werden. In dem oben genannten Code-Beispiel wird der

Menüeintrag "Herunterladen" von einzelnen oder mehreren Dokumenten entfernt.

6.10 Ergebnisliste in eine Indexmaske einbetten

i Dieses Feature kann erst der SAPERION Version 7.5 SP0 Patchlevel 1 verwendet werden.

6.10.1 Szenario

Um Dokumente aus einer weiteren DDC oder einer Nachschlagetabelle in einer Indexmaske anzuzeigen,

können Sie eine Ergebnisliste in eine Indexmaske einbetten.

Beispiel

In der konkreten Anwendung können Web Client Benutzer zu einer Rechnung eine zweite DDC anzeigen

lassen, um einzelne Rechnungspositionen zu prüfen und ggf. dazugehörige Indexdaten zu bearbeiten.

Abb. 6–1: Beispiel: Eingebettete Ergebnisliste in der Indexmaske

Die Ansteuerung der Ergebnisliste erfolgt über die Scripting-Schnittstelle des Web Clients. Das unten

genannte Beispiel-Script wird direkt als Server-Script zur Indexmaske selbst hinzugefügt.

6.10.2 Beispiel-Code

In dem folgenden Beispiel wird die Ergebnisliste dazu benutzt, um Daten zu dem in der Indexmaske

geöffneten Dokument anzuzeigen, die aus einer Nachschlagetabelle geladen werden.

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.10 Ergebnisliste in eine Indexmaske einbetten

29

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 Ablaufbeschreibung

Im hier verwendeten Script wird der Indexmaske ein Event-Listener hinzugefügt, der auf das "Index

change"-Event reagiert. Dieses Event wird von der Ergebnisliste in der Abfragemaske dazu verwendet,

andere Komponenten davon zu benachrichtigen, wenn ein neues Element in der Liste ausgewählt wurde.

Zu Beginn der "onEvent"-Methode wird das weitergeleitete Event wieder in das Original-Event

konvertiert. Anschließend werden die Indexdaten des ausgewählten Dokuments aus dem Event

ausgelesen. Die Verknüpfung zwischen der Indextabelle und der Nachschlagetabelle erfolgt in diesem

Beispiel über das Feld "EX7_CUST" in der Indextabelle und dem Feld "COMPANYNAME" in der

Nachschlagetabelle. Dieser Indexwert wird dazu verwendet, um eine HQL-Abfrage zu erstellen und zu

parametrisieren.

Die in diesem Beispiel verwendete Nachschlagetabelle hat den Namen "CUSTOMERS". In der

HQL-Abfrage werden alle Werte der Nachschlagetabelle abgefragt. Abschließend wird die Ergebnisliste

in der Indexmaske angewiesen, die erstellte Abfrage auszuführen.

Nachdem die Abfrage erfolgreich ausgeführt wurde, verhält sich die Ergebnisliste wie eine normale

Ergebnisliste in einer Abfragemaske. So können, sofern die Berechtigungen dazu vorhanden sind, die

Indexdaten der angezeigten Dokumente bearbeitet oder in eine Nachschlagetabelle neue Elemente

hinzugefügt werden. Dazu ist es allerdings notwendig, dass in der Ergebnisliste eine Indexmaske

definiert wurde. Auch die Anzeige der Eigenschaften älterer Revisionen der Dokumente über die

Revisionshistorie ist nur möglich, wenn eine Indexmaske definiert wurde.

6 Script-Beispiele

30

i Bitte beachten Sie, dass eine in eine Indexmaske eingebettete Ergebnisliste selbst keine

Events auslöst, wenn ein Eintrag selektiert wird. Außerdem ist das Versenden der angezeigten

Dokumente nur über die MAILTO-Funktion möglich.

6.11 Einschränkung von Nachschlagefeldern mit mehreren

Bedingungen

6.11.1 Szenario

Zusätzlich zu den in der Maske definierten Einschränkungen auf einem Nachschlagefeld können weitere

Bedingungen hinzugefügt werden. Diese Bedingungen werden bei jeder Suche in der verknüpften

Nachschlagetabelle verwendet. Die Funktion lässt sich über die Scripting-Schnittstelle des Web Clients

integrieren.

Die Klasse "com.saperion.ngc.iform.field.LookupTextField" ist zu diesem Zweck um die beiden

folgenden Methoden erweitert:

public void addCondition(String fieldName, Object value, SaFieldType fieldType)

public void removeCondition(String fieldName)

+ Methode "addCondition"

Mit der Methode "addCondition" können eigene Bedingungen zum Feld hinzugefügt werden. Dabei

muss der Name der Spalte der Nachschlagetabelle, der Wert für die Bedingung und der Typ der

Spalte angegeben werden.

+ Methode "removeCondition"

Mit der Methode "removeCondition" kann eine bereits hinzugefügte Bedingung wieder entfernt

werden. Dazu muss nur der Name der Spalte, für die die Bedingung hinzugefügt wurde, angegeben

werden.

i Alle Bedingungen werden bei der Abfrage mit UND verknüpft.

6.11.2 Beispiel-Code

In den folgenden Beispielen werden zuerst zwei Bedingungen zu einem Nachschlagefeld hinzugefügt

und anschließend wieder entfernt. Das erste Script wurde direkt zu dem betroffenen Feld in der

Suchmaske als Server-Script, hinzugefügt, das zweite zu einem Button auf derselben Maske ebenfalls

als Server-Script.

Beispiel 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);

7.1 Szenario

31

$component$.addCondition("City", "Berlin", SaFieldType.FT_STRING);

}

});

Beispiel 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");

}

});

Die unterstützten Datentypen sind in der Enum SaFieldType aufgeführt:

http://portal.saperion.com/docsxml/javadocs/75/javadoc_CC/com/saperion/constants/SaConstants.SaFieldType.html

i Um eine Suche zu optimieren (schnellere Abfrage) können Sie optional in der Konfigurationsdatei

"webclient.properties" den Parameter "ExactMatch=TRUE" setzen.

7 Praktische Anwendung: Dokumente über ein

externes Applet herunterladen

In diesem Abschnitt wird der komplette Ablauf eines Dokumenten-Downloads über ein externes Applet

illustriert. Dokumente werden dabei aus SAPERION herunter geladen und anschließend in einem lokalen

Dateisystem abgespeichert.

Um dies zu erreichen, sind folgende Schritte notwendig:

+ Erweiterung des SAPERION Web Clients um eigene Klassen

+ Abruf der eigenen Klassen über serverseitige Scripte in Reaktion auf Ereignisse, die der Anwender

auslöst.

+ Abruf eines eigenen Applets, um SAPERION-Dokumente herunterzuladen und diese im lokalen

Dateisystem abzuspeichern

7.1 Szenario

Das Kontextmenü einer Ergebnisliste ist um einen Eintrag erweitert, der bei einem Anklick durch den

Benutzer ein JSP öffnet. Das JSP enthält ein Applet, welches das ausgewählte SAPERION-Dokument aus

der Ergebnisliste herunterlädt. Anschließend öffnet sich der "Öffnen"-Dialog, in dem der Benutzer den

lokalen Speicherort auswählen kann.

7 Praktische Anwendung: Dokumente über ein externes Applet herunterladen

32

Abb. 7–1: JSP mit Applet

7.2 Erweiterung des Web Clients um eigene Klassen

Mit der Erweiterung des Web Clients um eigene Klassen können Sie vielfach Vorteile erzielen.

Erstens können dadurch serverseitige Scripte direkt benutzerdefinierte Methoden nach eigener Logik

ansprechen. Zweitens ist der Umfang der Server-Scripte durch Anwendung eigener Klassen oder Jars

wesentlich verkürzt, so dass auf diese Weise potentielle Fehlerquellen minimiert werden.

i Wir empfehlen daher, eigene Klassen/ Jars in Server-Scripten zu verwenden, um dadurch die

Scripte zu verkürzen und Scriptingfehler zu vermeiden.

7.3 Java-Projekt anlegen

Der einfachste Weg, eine benutzerdefinierte Klasse/ Jar zu erzeugen ist, ein Java-Projekt in eine

integrierte Entwicklungsumgebung (IDE) anzulegen. Für das folgende Beispiel wird die Eclipse IDE

verwendet.

1. Klicken Sie in Eclipse auf DATEI > NEU > JAVA PROJEKT, um ein neues Projekt anzulegen.

2. Geben Sie den Projektnamen ein und klicken auf [OK].

3. Da die SAPERION und ZK-Logik von den Klassen verwendet werden, müssen folgende

Jars hinzugefügt werden:

+ src-classicconnector.jar

+ scr-webclient.jar

+ zcommon.jar

+ zk.jar

+ zul.jar

Diese Jars werden in der Auslieferung des Web Clients zur Verfügung gestellt.

7.4 Logik und Verhalten

33

i Das angelegte Projekt (inklusive aller Dateien) ist Gegenstand dieser Anleitung. Bitte nehmen Sie

sich direkt die Dateien vor, um alle Details der Implementierung zu beziehen. Wichtige Aspekte

werden in den folgenden Kapitel erläutert.

7.4 Logik und Verhalten

Die Logik in diesem Beispiel ist in den folgenden Klassen/ Dateien implementiert:

Klassen/ Dateien

Klasse Beschreibung

DocumentDownloader.java Diese Klasse ist im Server-Script instanziiert. Sie nimmt die ausgewählte Zeile der Ergebnisliste als Argument,

legt Download-URLs an, speichert diese in der Session und ruft "DownloadAppletCaller.jsp" in einem neuen

Browserfenster/ -tab auf.

DownloadAppletCaller.jsp Diese JSP-Datei fragt die Download-URL der Session ab und lädt das benutzerdefinierte Applet, während die

URLs als Applet-Parameter weitergegeben werden.

DownloadApplet.java Diese Klasse stellt das benutzerdefinierte Applet dar. Sie nimmt die vorgegebenen URL-Parameter und lädt das

dazugehörige SAPERION-Dokument mit Hilfe der Klasse "FileDownloader" herunter. Sind die Dokumente erst

herunter geladen, können diese im Lokalsystem gespeichert werden, sofern das Applet über entsprechende Be-

rechtigungen verfügt.

FileDownloader.java Diese Klasse nimmt die Download-URL als Parameter auf und lädt die entsprechenden Dokumente herunter.

7.5 Code-Beispiele

7.5.1 DocumentDownloader.java

Der folgende Auszug stellt die Methode "DownloadDocument" der "DocumentDownloader.java"-Datei

dar. Sie wird direkt vom Server-Script aufgerufen.

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,

7 Praktische Anwendung: Dokumente über ein externes Applet herunterladen

34

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}

In Zeile 7 wird das erste Dokument der Ergebnisliste bezogen. Die ID des Dokuments wird in Zeile 9

vorgegeben. "settingsService" ist eine Instanz der Klasse "SettingsClassicConnectorService".

In Zeile 11 werden alle Elemente des Dokuments über die Instanz der

"DocClassicConnectorService"-Klasse bezogen.

i Legen Sie Dokument-ID über "SettingsClassicConnector" fest. Auf diese Weise wird sichergestellt,

dass nur gültige IDs verwendet werden.

Für alle Elemente des Dokuments wird eine Download-URL durch die Klasse "ServletUrl" (Zeile 16)

hergestellt. Diese Klasse dient der Bildung von URLs zu SAPERION-Servlets.

i Verwenden Sie die Klasse "ServletUrl", um so auf einfache Weise die SAPERION-Servlet URLs zu

erhalten.

In Zeile 26 wird die Liste der Download-URLs der Session gespeichert. In Zeile 28 wird die

benutzerdefinierte JSP-Datei aufgerufen.

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;

7.5 Code-Beispiele

35

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"

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 Zeile 2 werden die Download-URLs des Session abgefragt. Die folgenden Zeilen bilden eine verkettete

Zeichenfolge von URLs und stellen diese als HTML-Listen dar.

In Zeile 27 wird das Applet in JSP eingebunden. Nochmals wird die Klasse "ServletUrl" verwendet, um

die Basis-URL der Abfrage zu erhalten. Ds Applet arbeitet mit dem Archiv "DownloadApplet.jar" und

der Klasse "com.saperion.ngc.custom.applet.DownloadApplet". Das Jar File, das das "DownloadApplet"

enthält, muss unter der vorgegebenen Basis-URL erreichbar sein.

In Zeile 36 werden die Download-URLs als Parameter an das Applet übergeben.

7.5.3 Applet-Klasse

1// get URLS to download

2givenUrls = getParameter("downloadURLS");

7 Praktische Anwendung: Dokumente über ein externes Applet herunterladen

36

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

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 Zeile 1 werden die Download-URL Parameter ausgelesen und die verketteten URLs werden in den

darauf folgenden Zeilen getrennt.

In Zeile 13 durchläuft das Script alle URLs, dadurch werden alle betreffenden Dateien

über "FileDownloader" heruntergeladen. Die Download-URLs zeigen DABEI auf das SAPERION

Download-Servlet.

7.6 Anwendung der Erweiterungen

Nun müssen Sie Ihre Web Client Erweiterungen zum Einsatz bringen. Dies wird im Folgenden

beschrieben.

1. Exportieren das Java-Projekt als Jar File und speichern es unter dem SAPERION Web Client

Klassenpfad.

2. Öffnen Sie durch einen rechten Mausklick das Kontextmenü zu Ihrem Eclipse Projekt.

3. Wählen Sie den Eintrag "Exportieren…" sowie "JAR-file". Der Dialog "JAR Export" öffnet

sich.

7.7 Serverseitiges Script erzeugen

37

4. Die Checkbox "lib" aus deaktiviert werden, da alle benötigten bereits vorhanden sind.

5. Geben Sie den Pfad zu dem SAPERION Web Client "lib"-Verzeichnis ein und klicken auf

die Schaltfläche [Beenden].

Nach dem Export stehen die Klassen unter dem angegebenen Web Client-Pfad zur

Verfügung.

Nun müssen Sie das Applet exportieren. In der Regel kann das Jar File auch als Applet verwendet werden,

da die Klasse "DownloadApplet.java" in dieser Datei ebenso vorhanden ist. Doch da das Applet auch in

dem Root-Verzeichnis des Web Clients gespeichert sein muss, nehmen Sie die folgenden Schritte vor:

1. Klicken Sie mit der rechten Maustaste auf das Eclipse-Projekt, um das Kontextmenü zu

öffnen.

2. Wählen Sie den Eintrag "Export…" aus und anschließend "JAR-file". Der Dialog "JAR

Export" öffnet sich.

3. Wählen Sie die Checkbox "lib" ab.

4. Ändern Sie Exportpfad zum Web Client Root-Verzeichnis.

Beispiel:

Der Web Client läuft unter einem Tomcat, der hier gespeichert ist: '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. Nennen Sie die JAR-Datei um in "DownloadApplet.jar" und klicken anschließend auf die

Schaltfläche [Fertigstellen].

6. Kopieren Sie zum Schluss die JSP-Datei "DownloadAppletCaller.jsp " in das Web Client

Web Root-Verzeichnis.

7.7 Serverseitiges Script erzeugen

Um die Erweiterungen anwenden zu können, müssen Sie einige serverseitige Scripte anlegen.

7 Praktische Anwendung: Dokumente über ein externes Applet herunterladen

38

1. Öffnen Sie die entsprechende Abfragemaske im SAPERION Masken-Designer.

2. Wählen Sie das Maskenelement "Ergebnisliste".

3. Öffnen Sie den Script-Editor, indem Sie im Bereich " Eigenschaften" bei Server-Script auf

die Schaltfläche [...] klicken.

4. Geben Sie folgendes Script ein:

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 Zeile 4-8 wird ein Menü mit Untermenü erzeugt. Dieses wird in Zeile 16 zu der

Ergebnisliste hinzugefügt.

+ In Zeile 11, 12 und 13 wird die Instanz der benutzerdefinierten Klasse

unter Verwendung eines voll qualifizierten Namens erzeugt. Die Methode

"DownloadDocument" wird abgerufen und die ausgewählten Zeilen der Ergebnisliste

zur Verfügung gestellt.

7.8 Berechtigungen

Da das Applet die heruntergeladenen Dateien im lokalen System abzuspeichern versucht, braucht

es die entsprechenden Berechtigungen. Um diese zu gewährleisten, müssen die folgenden Schritte

unternommen werden:

1. Signieren Sie das Applet unter Verwendung des Java "jarsigner" Werkzeugs.

Nähere Informationen zum jarsigner finden Sie in der folgenden Dokumentation:

http://download.oracle.com/javase/1.3/docs/tooldocs/win32/jarsigner.html

2. Stellen Sie die Java Sicherheitsrichtlinie im Java Installationsverzeichnis ein.

Wenn Java unter 'C:\Program Files\Java\jre6\' installiert ist, befindet sich die

Sicherheitsdatei unter 'C:\Program Files\Java\jre6\lib\security\java.policy'.

3. Um Berechtigungen auf allen Code-Ebenen zu gewährleisten, geben Sie folgende Zeilen

ein:

7.8 Berechtigungen

39

grant {

//all permissions to all domains

permission java.security.AllPermission;

};

! Bitte nehmen Sie dies nur zu Testzwecken vor, da sonst alle

Java-Programme diese Berechtigungen auf Ihrem System hätten.

Nähere Informationen zu Java Sicherheitsrichtlinien und -dateien finden Sie unter:

http://download.oracle.com/javase/1.4.2/docs/guide/security/PolicyFiles.html