best practice web client scripting - docs.hyland.com · 6.1.2 beispiel-code ... + ausführung über...
TRANSCRIPT
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