Zum Inhalt

Scripting-Überblick

Skripte machen Formulare lebendig. Sie sind in JavaScript (ECMAScript 2022, GraalVM) geschrieben und laufen auf dem Server in einer Sandbox. Browser-Funktionen (window, document, fetch …) gibt es dort nicht — stattdessen stellt KCForms eine Reihe Host-Objekte bereit (ctx, db, mail, http …).

Neu im Programmieren?

Zwei Seiten holen dich ohne Vorwissen ab: JavaScript-Grundlagen — Variablen, Text, Zahlen, Datum, Entscheidungen, Schleifen, eigene Funktionen — und JSON und Objekte verstehen, denn alles, was ein Skript anfasst (ctx.form, Detail-Tabellen, Datenbank-Ergebnisse, Antworten aus dem Internet), ist ein Objekt.

Lieber erst ausprobieren?

Unter Skripte in Beispielen stehen zu jedem Werkzeug mindestens 20 fertige, getestete Skripte — von der ersten Zeile bis zur Transaktion, in einfachen Worten erklärt.

Hooks am Formular

Skripte eines Formulars pflegst du im Formular-Editor im Reiter Skripte über der Vorschau. Jeder Hook bekommt denselben ctx und dieselben Host-Objekte.

Hook Wann Typische Aufgabe
onLoad Datensatz wird geöffnet vorbelegen, externe Daten laden
onFieldChange ein Feld wurde verlassen bzw. geändert Berechnungen, abhängige Werte
onValidate Validieren und vor dem Speichern Regeln über mehrere Felder
onBeforeSave vor dem Speichern (kann abbrechen) letzte Aufbereitung, Veto
onSubmit nach erfolgreichem Speichern Mail, PDF, Dateien, Schnittstellen
onAfterSave am Ende der Speicher-Pipeline (ctx.isNew) Folgeaktionen
onBeforeDelete vor dem Löschen (kann abbrechen) Löschen verhindern
onAfterDelete nach dem Löschen (ctx.deletedId) aufräumen, Umsysteme informieren

Reihenfolge beim Speichern: onValidate → onBeforeSave → [Speichern] → onSubmit → onAfterSave; beim Löschen onBeforeDelete → [Löschen] → onAfterDelete. Details: Lifecycle.

Dazu kommen eigene Hooks (beliebiger Name), die eine Schaltfläche (kc:button mit scriptHook) oder eine Rückfrage auslöst, sowie die Workflow-Skripte (vor und nach einem Stufenwechsel) und onCopy für Workflows.

Beispiel — onFieldChange schreibt die Summe zweier Felder in ein drittes:

// onFieldChange
ctx.form.summe = kc.num(ctx.form.wert1) + kc.num(ctx.form.wert2);

Kürzer mit den $feld-Aliasen, die KCForms für alle Felder des Formulars anlegt:

$summe = kc.num($wert1) + kc.num($wert2);

Und am kürzesten über das Feld-Handle, das es ebenfalls zu jedem Feld gibt:

field.summe.num = field.wert1.num + field.wert2.num;

.num gibt den Wert als Zahl, .isEmpty beantwortet „nichts eingetragen?", .error(…) hängt eine Meldung an genau dieses Feld. Übersicht: field.<name>.

Rechnen ohne Skript

Summen und einfache Rechnungen gehen ohne Skript mit Berechnungsfeldern.

Weitere Skript-Arten

Art Wo Besonderheit
Skript-Konsole Zahnrad → Skript-Konsole freie Skripte ohne Formular (Diagnose, Migrationen); ctx.formId ist leer, Testwerte $text, $number, $date, $flag
Jobs Zeitplaner zeitgesteuert, zusätzlich Binding job — siehe Jobs
REST-Services System-Administration → REST-Services eigene HTTP-Endpunkte — siehe REST-Services
Anmelde-Skripte Anmeldung per Skript eigene Login-Logik — siehe Anmeldung
Lookup-/Picker-Skripte kc:lookup.script, kc:picker.script liefern Listen über handle({...})
Vorbelegungen kc:defaultExpression ein Ausdruck, volle onLoad-API

Der ctx-Kontext

ctx trägt die Daten des aktuellen Aufrufs — Details in der ctx-Referenz:

Pfad Inhalt
ctx.form Feldwerte (lesen und schreiben); Detail-Tabellen als Listen
ctx.formId, ctx.instanceId Formular-ID, Datensatz-ID (null bei neuem Datensatz)
ctx.isNew true, solange der Datensatz noch nicht gespeichert ist
ctx.user angemeldeter Benutzer (name, firstName, lastName, email, locale, roles = Gruppen, permissions ohne PERM_-Präfix …)
ctx.lang aktuelle Sprache (de, sv, en …)
ctx.params Properties des Formulars
ctx.attachments Dateianlagen des Datensatzes
ctx.model Lese-Sicht auf Datenmodell und Detail-Tabellen
ctx.addError(feld, text) Prüffehler melden
ctx.cancel(grund) Speichern/Löschen abbrechen (in onBefore…)
ctx.persist(feld…) Felder nachträglich in den gespeicherten Datensatz schreiben (onSubmit)
ctx.requestClose() Formular nach dem Hook schließen

Host-Objekte

Objekt Zweck Seite
db SQL mit Parametern, auch auf externen Datenquellen (db.on(...)) db
forms Datensätze anderer Formulare lesen und anlegen forms
log Protokoll (info, debug, warn, error) kc-Helfer
kc kc.num, kc.text, kc.bool, kc.isEmpty — für alles ohne Feld-Handle kc-Helfer
field Handle je Formularfeld: .value, .num, .text, .bool, .isEmpty, .error(…) field.<name>
macros Makros auflösen (macros.resolve('${user:firstName}')) Makros
view Meldungen und Rückfragen im Formular view
pdf, delivery PDF erzeugen und ausliefern PDF
docx Word-Vorlagen füllen, Serienbriefe docx
mail Mails mit Anlagen und Ad-hoc-Links mail
http, ws REST- und SOAP-Aufrufe HTTP, WebService
ldap Verzeichnis-Abfragen LDAP
fs Dateien in der Sandbox fs
csv, xml, zip Formate lesen und schreiben, ZIP-Archive CSV, ZIP
encoder Ad-hoc-Links erzeugen Web-Links
workflow Workflow-Informationen des Datensatzes Workflow
modules Module laden (modules.use(...)), z. B. den CSV-Service CSV
job nur in Zeitplaner-Jobs Jobs

Sandbox

  • Ein Skript sieht nur die Objekte oben. Java-Klassen nachladen, direkt auf Dateien oder das Netz zugreifen oder Threads starten kann es nicht.
  • Zeitlimit: 6 Sekunden je Lauf (kcforms.scripting.max-exec-millis), Lookup- und Picker-Skripte mindestens 30 Sekunden. Endlosschleifen werden abgebrochen.
  • Date.prototype.format(muster) und Date.fromString(text, muster) sind eingebaut — formatieren und parsen ohne Bibliothek.
  • return ist überall erlaubt: return; beendet Hooks, Jobs und Konsolen-Skripte vorzeitig; in onBeforeSave/onBeforeDelete bricht return false; den Vorgang ab (siehe Lifecycle).

Beispiel: onSubmit mit Mail und PDF

// onSubmit — Bestätigung mit dem Formular als PDF-Anlage
log.info('Bestellung gespeichert: ' + ctx.instanceId);

var mp = new mail.MessageMultipart();
mp.addText('Neue Bestellung — siehe Anlage.');
mp.addOutput('bestellung-' + ctx.instanceId + '.pdf');   // aktuelles Formular als PDF

var msg = new mail.Message();
msg.to      = 'orders@example.test';
msg.subject = 'Neue Bestellung #' + ctx.instanceId;
msg.content = mp;
mail.send(msg);

view.add(new view.Message('Die Bestellung wurde versendet.', 'Gesendet'));

Fehlersuche

  1. log.info(...) großzügig einsetzen. Skript-Protokolle landen in logs/script.log (und je Benutzer in logs/by-user/<login>.log), jede Zeile mit [form:<id>] davor — einsehbar unter System-Administration → Betrieb & Überwachung → System-Logs. In der Docker-Konsole erscheinen sie nicht.
  2. In der Skript-Konsole Schnipsel ausprobieren — mit demselben db-Zugang wie in Hooks.
  3. Prüffehler vor dem Skript: Die Schema-Prüfung läuft vor onValidate. Fehlt ein Pflichtfeld, meldet das schon sie.

Weiter