Quelltext des aktuellen Standes
<?php
/**
* Gemeinsamer Feld-Renderer für externe Antrags-Formulare (öffentliches
* Formular und Self-Service-Portal). Erwartet im Scope:
* $view – View-Instanz (für e())
* $lookups – Vorschlagslisten je field_key
* $dirOn – Verzeichnis-Auswahl erlaubt?
* Definiert die Closure $renderField(array $f): void.
*/
// Feld-Renderer: respektiert den Verzeichnis-Schalter (person/user/lookup → Freitext, wenn aus).
$renderField = function (array $f) use ($view, $lookups, $dirOn) {
$name = 'field_' . $f['field_key'];
$req = (int)$f['is_required'] === 1;
$opts = !empty($f['options_json']) ? (json_decode($f['options_json'], true) ?: []) : [];
$type = $f['field_type'];
$attrs = $req ? ' required' : '';
if (!empty($f['max_length'])) { $attrs .= ' maxlength="' . (int)$f['max_length'] . '"'; }
if (in_array($type, ['number','date','currency'], true)) {
if (($f['min_value'] ?? '') !== '') { $attrs .= ' min="' . $view->e($f['min_value']) . '"'; }
if (($f['max_value'] ?? '') !== '') { $attrs .= ' max="' . $view->e($f['max_value']) . '"'; }
}
// Bei deaktiviertem Verzeichnis verhalten sich diese Typen wie Freitext.
$hasList = isset($lookups[$f['field_key']]);
if (in_array($type, ['person','user','lookup'], true) && (!$dirOn || !$hasList)) {
$type = 'text';
}
// Reine Anzeige-Typen: Überschrift, HTML-Block, Zustimmung. Der Anzeige-
// Inhalt (options_json.html) stammt aus der Formularverwaltung, wird aber
// auf öffentlichen und Portal-Seiten gerendert – daher über eine
// Positivliste bereinigt statt roh ausgegeben.
$contentHtml = is_array($opts) && isset($opts['html'])
? \OpenNIT\Security\XSS::purifyHtml((string)$opts['html'])
: '';
if ($type === 'heading') { ?>
<div style="font-size:.78rem;font-weight:700;color:var(--gray-500);text-transform:uppercase;letter-spacing:.05em;margin:1rem 0 .5rem;border-bottom:1px solid var(--gray-200);padding-bottom:.3rem;">
<?= $view->e($f['label']) ?>
</div>
<?php if (!empty($f['help_text'])): ?><p class="text-muted" style="font-size:.8rem;margin:0 0 .5rem;"><?= $view->e($f['help_text']) ?></p><?php endif; ?>
<?php return;
}
if ($type === 'html') { ?>
<div class="form-group"><?= $contentHtml ?></div>
<?php return;
}
if ($type === 'consent') { ?>
<div class="form-group">
<label class="checkbox-inline" style="display:flex;gap:.55rem;align-items:flex-start;font-weight:400;cursor:pointer;">
<input type="checkbox" name="<?= $view->e($name) ?>" value="1" style="margin-top:.22rem;flex:0 0 auto;"<?= $req ? ' required' : '' ?>>
<span><?= $contentHtml !== '' ? $contentHtml : $view->e($f['label']) ?><?php if ($req): ?> <span class="text-danger">*</span><?php endif; ?></span>
</label>
</div>
<?php return;
}
?>
<div class="form-group">
<label for="f-fc6b-----------------------"><?= $view->e($f['label']) ?><?php if ($req): ?> <span class="text-danger">*</span><?php endif; ?></label>
<?php if ($type === 'textarea'): ?>
<textarea id="f-fc6b-----------------------" class="form-control form-control-sm" name="<?= $view->e($name) ?>" rows="4"<?= $attrs ?>></textarea>
<?php elseif ($type === 'dropdown'): ?>
<select class="form-control form-control-sm" name="<?= $view->e($name) ?>"<?= $req ? ' required' : '' ?>>
<option value=""><?= $this->te('– bitte wählen –') ?></option>
<?php foreach ($opts as $o): ?><option value="<?= $view->e($o) ?>"><?= $view->e($o) ?></option><?php endforeach; ?>
</select>
<?php elseif ($type === 'checkbox'): ?>
<label class="checkbox-inline"><input type="checkbox" name="<?= $view->e($name) ?>" value="1"> Ja</label>
<?php elseif ($type === 'number'): ?>
<input class="form-control form-control-sm" type="number" name="<?= $view->e($name) ?>"<?= $attrs ?>>
<?php elseif ($type === 'currency'): ?>
<input class="form-control form-control-sm" type="number" step="0.01" name="<?= $view->e($name) ?>"<?= $attrs ?>> <?= $view->e($view->currency()) ?>
<?php elseif ($type === 'date'): ?>
<input class="form-control form-control-sm" type="date" name="<?= $view->e($name) ?>"<?= $attrs ?>>
<?php elseif (in_array($type, ['person','user','lookup'], true)): ?>
<input class="form-control form-control-sm" list="lk-<?= $view->e($f['field_key']) ?>" name="<?= $view->e($name) ?>"<?= $attrs ?> autocomplete="off">
<datalist id="lk-<?= $view->e($f['field_key']) ?>">
<?php foreach (($lookups[$f['field_key']] ?? []) as $lv): ?><option value="<?= $view->e($lv) ?>"></option><?php endforeach; ?>
</datalist>
<?php elseif ($type === 'file'): ?>
<input class="form-control form-control-sm" type="file" name="<?= $view->e($name) ?>"<?= $req ? ' required' : '' ?><?= !empty($f['accept']) ? ' accept="' . $view->e($f['accept']) . '"' : '' ?>>
<?php elseif ($type === 'email'): ?>
<input class="form-control form-control-sm" type="email" name="<?= $view->e($name) ?>"<?= $attrs ?>>
<?php elseif ($type === 'repeater'): ?>
<div class="wf-repeater">
<div class="wf-rep-rows">
<div class="wf-rep-row" style="display:flex;gap:.4rem;margin-bottom:.35rem;">
<input class="form-control form-control-sm" type="text" name="<?= $view->e($name) ?>[]"<?= $req ? ' required' : '' ?>>
<button aria-label="<?= $this->te('Entfernen') ?>" type="button" class="btn btn-xs btn-secondary wf-rep-del"><i class="fas fa-minus"></i></button>
</div>
</div>
<button type="button" class="btn btn-xs btn-secondary wf-rep-add"><i class="fas fa-plus"></i> <?= $this->te('Zeile hinzufügen') ?></button>
</div>
<?php else: ?>
<input class="form-control form-control-sm" type="text" name="<?= $view->e($name) ?>"<?= $attrs ?>>
<?php endif; ?>
<?php if (!empty($f['help_text'])): ?><small class="text-muted"><?= $view->e($f['help_text']) ?></small><?php endif; ?>
</div>
<?php
};