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:

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:

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:
- Ablegen. Das JAR in den Modul-Ordner legen. Das geht nur über das Dateisystem des Servers — in der Weboberfläche gibt es kein Hochladen.
- 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.
- 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:

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:

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:

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 daranpublicist, kann das Skript aufrufen — was es nicht können soll, ist nichtpublic.@ScriptFunction("…")ist der Satz, der in der Admin-Liste neben der Funktion steht.ScriptModuleContextsagt 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:
Sie enthält eine Zeile je Modul-Klasse:
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. |