Zum Inhalt

Skript-Module (Java-Routinen einbinden)

Ein Skript-Modul ist eine Java-Routine, die du im Skript benutzt wie ein eingebautes Werkzeug. Du holst es mit modules.use("name") und rufst dann seine Funktionen auf:

var qr = modules.use("qrcode");

var bild = qr.png("https://example.com/vorgang/4711", 300);
log.info("PNG mit " + bild.length + " Bytes");

Damit lässt sich alles anbinden, wofür es eine Java-Bibliothek gibt — ein Barcode, eine Prüfroutine, der Zugriff auf ein Fachverfahren —, ohne dass KCForms dafür neu gebaut werden muss.

Wann welcher Weg?

Du brauchst … Nimm …
eine Funktion in JavaScript, für ein Formular Hilfsfunktionen
eine Funktion in JavaScript, für viele Formulare Bibliotheken
etwas, das es nur als Java-Bibliothek gibt, oder das schnell und genau rechnen muss ein Skript-Modul

Welche Module gibt es?

Unter System-Administration → Formular-Bausteine → Skript-Module steht jedes Modul mit seinen Funktionen und einem Satz Erklärung:

Das Modul „qrcode" in der Liste: Name, Zustand, Beschreibung und jede Funktion mit einem Satz Erklärung

Die Zeile Im Skript kannst du so übernehmen. Links steht, wie du eine Funktion aufrufst und was sie zurückgibt, rechts, was sie tut. Mit Abschalten nimmt ein Administrator ein Modul aus dem Verkehr, ohne es zu entfernen.

Im Skript fragst du dasselbe ab:

log.info("Module: " + Array.from(modules.list()).join(", "));

if (modules.has("qrcode")) {
  log.info("QR-Codes sind verfügbar.");
}

modules.use("name") liefert null, wenn es kein Modul mit diesem Namen gibt. Ist ein Modul abgeschaltet, bricht der Aufruf mit einer Meldung ab, die den Ort der Einstellung nennt.

Mitgeliefert: qrcode

Funktion Ergebnis
png(text) / png(text, groesse) QR-Code als PNG-Bild (Bytes); Kantenlänge 64–2000 Pixel, sonst 300
svg(text) QR-Code als SVG-Vektorgrafik (Text)
dataUrl(text, groesse) Text data:image/png;base64,… — direkt als Bildquelle in HTML
giroCode(angaben) / giroCode(angaben, groesse) Überweisungs-Code (GiroCode) als PNG-Bild (Bytes)
giroCodeText(angaben) der Text, der im Überweisungs-Code steht

Die Bytes gibst du weiter wie jede andere Datei — als Anlage, in die Datei-Ablage oder an eine Mail:

var qr = modules.use("qrcode");

// Überweisungs-Code für eine Rechnung
var code = qr.giroCode({
  name:    "Muster GmbH",
  iban:    "DE02 1203 0000 0000 2020 51",
  amount:  119.50,
  purpose: "Rechnung 4711"
}, 300);

var datei = fs["/beispiele/module/girocode.png"];
datei.writeBytes(code);
log.info("Code abgelegt: " + datei.size + " Bytes");
datei.remove();

Das Ergebnis — mit genau diesen Angaben erzeugt. Eine Banking-App liest daraus Empfänger, IBAN, Betrag und Verwendungszweck:

Der Überweisungs-Code aus dem Beispiel: ein QR-Code mit 300 Pixel Kantenlänge

giroCode kennt die Angaben name (Empfänger), iban, amount (Euro), purpose (Verwendungszweck) und optional bic. Fehlt eine Pflichtangabe, bricht der Aufruf mit einer Meldung ab, die sagt, welche.

Den Code weitergeben

Das Modul liefert das Bild — was damit geschieht, bestimmst du mit den Werkzeugen, die du schon kennst. Drei Wege, die in der Praxis vorkommen:

Zum Herunterladen anbieten. delivery.file macht aus den Bytes eine Adresse, view.Link zeigt sie dem Anwender als Link:

var qr = modules.use("qrcode");

var bild = qr.png("https://example.com/vorgang/4711", 400);
var url  = delivery.file(bild, { name: "qr-code.png", mime: "image/png" });

view.add(new view.Link(url, "QR-Code herunterladen"));
log.info("Download bereit: " + url);

Als Anlage an den Vorgang hängen. Dann liegt der Code beim Datensatz und ist später wieder da — siehe Dateianlagen:

var qr = modules.use("qrcode");

var code = qr.giroCode({
  name: "Muster GmbH", iban: "DE02 1203 0000 0000 2020 51",
  amount: 119.50, purpose: "Rechnung 4711"
});

var anlage = ctx.attachments.add("girocode.png", code, "image/png");
log.info("Anlage: " + anlage.filename);

In eine Mail legen. Mit einer Content-ID (cid:) erscheint der Code im Text der Mail statt als Anhang darunter — mehr dazu unter Mails:

var qr = modules.use("qrcode");

var code = qr.giroCode({
  name: "Muster GmbH", iban: "DE02 1203 0000 0000 2020 51",
  amount: 119.50, purpose: "Rechnung 4711"
}, 300);

var mp = new mail.MessageMultipart("related");
mp.addHTML("<p>Bitte überweisen Sie 119,50 €. Am schnellsten geht es, "
         + "wenn Sie diesen Code mit Ihrer Banking-App einlesen:</p>"
         + "<p><img src='cid:girocode' alt='Überweisungs-Code' width='200'></p>");
mp.addBytes("girocode.png", code, "image/png", "girocode");

var msg = new mail.Message();
msg.to      = "kunde@beispiel.example";
msg.subject = "Rechnung 4711";
msg.content = mp;
mail.send(msg);

Welche Größe?

Für den Bildschirm und für Mails reichen 200 bis 300 Pixel. Soll der Code gedruckt werden, nimm 600 Pixel oder mehr — oder gleich svg(text), das in jeder Größe scharf bleibt. Je länger der Text, desto feiner das Muster; eine kurze Adresse liest jedes Telefon, ein Absatz Text braucht ein größeres Bild.

Was hinter qrcode steckt

Das Modul ist selbst ein Beispiel für das, was diese Seite beschreibt: eine schmale Java-Klasse, die eine vorhandene Bibliothek für Skripte zugänglich macht.

Bibliothek ZXing („Zebra Crossing"), Version 3.5.3
Lizenz Apache-Lizenz 2.0 — frei verwendbar, auch gewerblich
Bezugsquelle github.com/zxing/zxing, über Maven Central als com.google.zxing:core und com.google.zxing:javase
Installation keine — die Bibliothek ist in KCForms enthalten
QR-Code Zeichensatz UTF-8, Fehlerkorrektur Stufe M (rund 15 % des Codes dürfen beschädigt sein)
Überweisungs-Code Aufbau nach der Richtlinie EPC069-12 des European Payments Council, Version 002, SEPA-Überweisung in Euro

„QR Code" ist eine eingetragene Marke der Denso Wave Incorporated; das Erzeugen und Benutzen der Codes ist frei.

Ein eigenes Modul einbinden

Eigene Module kommen als JAR-Datei in den Modul-Ordner des Servers. Der Ablauf hat drei Schritte, und der zweite ist Absicht:

  1. Ablegen. Das JAR in den Modul-Ordner legen. Das geht nur über das Dateisystem des Servers — in der Weboberfläche gibt es kein Hochladen.
  2. Freigeben. Unter Skript-Module auf Neu einlesen klicken. Das JAR erscheint unter „JAR-Dateien" mit dem Zustand wartet auf Freigabe. Ein Administrator gibt es frei.
  3. Benutzen. Erst jetzt wird das JAR geladen, und das Modul steht mit seinen Funktionen in der Liste.

So sieht das am Beispiel-Modul iban aus. Nach Neu einlesen steht das JAR unten auf der Seite und wartet:

Die Karte „JAR-Dateien im Modul-Ordner": kcforms-iban.jar wartet auf Freigabe, darunter die angemeldete Klasse und die Prüfsumme

Zu sehen ist nur, was sich ohne Laden feststellen lässt: Dateiname, Größe, die angemeldete Klasse und die Prüfsumme. Einen Modul-Namen und Funktionen gibt es noch nicht — die kennt erst das geladene JAR.

Ein Klick auf Freigeben fragt nach, bevor etwas passiert:

Die Rückfrage vor der Freigabe nennt die Folgen, die angemeldete Klasse und die Prüfsumme

Vergleiche hier die Prüfsumme mit der, die dir der Lieferant des JARs genannt hat. Stimmt sie, ist es genau die Datei, die du erwartest.

Nach der Freigabe steht das Modul oben in der Liste — mit dem Hinweis, aus welchem JAR es stammt:

Das Modul „iban" nach der Freigabe: aktiv, aus kcforms-iban.jar, mit seinen fünf Funktionen

Ein freigegebenes JAR läuft mit allen Rechten des Servers

Anders als ein Skript kann Java-Code alles, was der Server kann: Dateien lesen, ins Netz gehen, die Datenbank ansprechen. Deshalb wird ein JAR vor der Freigabe gar nicht geladen — KCForms liest nur seinen Dateinamen, seine Prüfsumme und die Klassen, die es anmeldet.

Die Freigabe gilt genau dieser Datei. Wird das JAR ausgetauscht, steht es als geändert in der Liste und muss neu freigegeben werden. Gib nur Dateien frei, deren Herkunft du kennst.

Der Ordner heißt standardmäßig script-modules und liegt neben den übrigen Verzeichnissen der Installation; ein anderer Ort lässt sich mit der Umgebungsvariable KCFORMS_SCRIPT_MODULES_DIR einstellen. Wo er auf deinem Server liegt, zeigt die Seite Skript-Module an.

Ein eigenes Modul schreiben

Ein Modul besteht aus zwei Dingen: einer Java-Klasse und einer Textdatei, die sie anmeldet. Das mitgelieferte Beispiel prüft IBANs und ist als Vorlage gedacht:

Beispiel-Modul herunterladen (ZIP, 9 KB)

Im Paket liegen der Quelltext, die Anmelde-Datei, die Schnittstelle von KCForms als kleines JAR und zwei Bauskripte (Windows und Linux/macOS). Du brauchst dafür nur ein JDK 21 oder neuer — nicht den Quelltext von KCForms. Entpacken, build.ps1 bzw. sh build.sh ausführen, und in dist liegt das fertige JAR. Die Datei LIESMICH.md im Paket führt durch die Schritte.

Hinter den Kulissen: die Java-Klasse
package com.example.kcforms.iban;

import com.kcforms.modules.ScriptFunction;
import com.kcforms.modules.ScriptModule;
import com.kcforms.modules.ScriptModuleContext;

public class IbanModule implements ScriptModule {

    public String name()        { return "iban"; }            // modules.use("iban")
    public String title()       { return "IBAN-Prüfung"; }
    public String description() { return "Prüft und formatiert IBANs."; }

    public Object create(ScriptModuleContext ctx) { return new Api(); }

    /** Was das Skript sieht: jede public-Methode ist aufrufbar. */
    public static final class Api {

        @ScriptFunction("Ist die IBAN gültig?")
        public boolean isValid(String iban) { /* … */ return true; }
    }
}
  • name() ist der Name im Skript: klein beginnend, nur Buchstaben und Ziffern, 2 bis 40 Zeichen.
  • create(...) liefert das Objekt, das das Skript benutzt. Alles, was daran public ist, kann das Skript aufrufen — was es nicht können soll, ist nicht public.
  • @ScriptFunction("…") ist der Satz, der in der Admin-Liste neben der Funktion steht.
  • ScriptModuleContext sagt dem Modul, in welchem Formular, für welchen Vorgang und für welchen Benutzer das Skript läuft.
  • Das Modul braucht aus KCForms nur das Paket com.kcforms.modules.
Hinter den Kulissen: die Anmeldung

Im JAR liegt eine Textdatei mit genau diesem Namen:

META-INF/services/com.kcforms.modules.ScriptModule

Sie enthält eine Zeile je Modul-Klasse:

com.example.kcforms.iban.IbanModule

Fehlt die Datei, meldet die Seite Skript-Module das JAR als Fehler — mit genau diesem Hinweis.

Fremdbibliotheken: mit ins JAR — und auf die Lizenz achten

Benutzt dein Modul eine Bibliothek, die KCForms nicht mitbringt, müssen ihre Klassen in deinem JAR liegen. Wer eine Bibliothek so weitergibt, muss ihre Lizenz einhalten — meist heißt das: Lizenztext und Urhebervermerk beilegen. Die Schnittstelle von KCForms (kcforms-module-api.jar) gehört dagegen nicht hinein; die bringt der Server selbst mit.

Wer mit dem Quelltext von KCForms arbeitet, baut das Beispiel stattdessen mit examples\script-module-iban\build.ps1; das JAR landet dann direkt im Modul-Ordner des Entwicklungs-Stacks. Nach der Freigabe:

// onValidate
var iban = modules.use("iban");

if (!iban.isValid($iban)) {
  ctx.addError("iban", iban.check($iban).reason);
} else {
  ctx.form.iban = iban.format($iban);     // DE02 1203 0000 0000 2020 51
}

Was hinein- und herausgeht

Zwischen Skript und Java-Routine werden Werte übersetzt:

Im Skript In Java
Text String
Zahl int, long, double — oder String/BigDecimal, wenn es genau sein muss
true/false boolean
Objekt { … } Map<String, Object>
Liste [ … ] List<Object>
Bytes (z. B. aus pdf.render().bytes) byte[]

Geldbeträge als Text übergeben

Eine Zahl mit Nachkommastellen ist im Skript ungenau (0.1 + 0.2 ergibt nicht genau 0.3). Eine Routine, die mit Geld rechnet, nimmt den Betrag deshalb am besten als Text entgegen und rechnet in Java mit BigDecimal.

Wenn etwas nicht klappt

Was du siehst Ursache
modules.use("x") liefert null Es gibt kein Modul x — oder sein JAR wartet noch auf die Freigabe. Im Skript-Protokoll steht, welche Module es gibt.
„Das Skript-Modul 'x' ist abgeschaltet" Ein Administrator hat es unter Skript-Module abgeschaltet.
JAR steht als Fehler in der Liste meist fehlt die Anmelde-Datei unter META-INF/services/.
JAR steht als geändert in der Liste Die Datei wurde seit der Freigabe ausgetauscht — neu freigeben.
Modul steht als nicht benutzt in der Liste Sein Name ist schon vergeben. Eingebaute Module gewinnen; sonst das JAR, dessen Dateiname im Alphabet zuerst kommt.