$id, 'label' => $id === 'contact' ? 'Kontaktformular' : ucfirst(str_replace('-', ' ', $id)), 'description' => '', 'enabled' => true, 'submit' => [ 'store' => true, 'email' => true, 'subject' => '{form}: {name}', 'successText' => 'Vielen Dank für deine Nachricht.', ], 'fields' => [ ['key' => 'name', 'type' => 'text', 'label' => 'Name', 'required' => true, 'width' => 6], ['key' => 'email', 'type' => 'email', 'label' => 'E-Mail', 'required' => true, 'width' => 6], ['key' => 'phone', 'type' => 'tel', 'label' => 'Telefon', 'required' => false, 'width' => 12], ['key' => 'message', 'type' => 'textarea', 'label' => 'Nachricht', 'required' => true, 'width' => 12], ], ]; } function raijuFormNormalizeField(array $field, int $index = 0): array { $allowedTypes = ['text', 'email', 'tel', 'textarea', 'select', 'radio', 'checkbox', 'date', 'time', 'hidden', 'heading', 'note']; $key = preg_replace('/[^a-zA-Z0-9_-]+/', '_', (string)($field['key'] ?? 'field_' . ($index + 1))); $key = trim((string)$key, '_-') ?: 'field_' . ($index + 1); $type = in_array(($field['type'] ?? 'text'), $allowedTypes, true) ? $field['type'] : 'text'; $width = (int)($field['width'] ?? 12); if (!in_array($width, [3, 4, 6, 8, 12], true)) { $width = 12; } $options = []; if (is_array($field['options'] ?? null)) { foreach ($field['options'] as $option) { if (is_array($option)) { $value = (string)($option['value'] ?? $option['label'] ?? ''); $label = (string)($option['label'] ?? $value); } else { $value = (string)$option; $label = (string)$option; } if ($value !== '' || $label !== '') { $options[] = ['value' => $value, 'label' => $label]; } } } return [ 'key' => $key, 'type' => $type, 'label' => (string)($field['label'] ?? $key), 'placeholder' => (string)($field['placeholder'] ?? ''), 'help' => (string)($field['help'] ?? ''), 'required' => !empty($field['required']), 'width' => $width, 'options' => $options, 'value' => (string)($field['value'] ?? ''), ]; } function raijuFormNormalize(array $form, string $fallbackId = 'form'): array { $id = raijuFormIdClean((string)($form['id'] ?? $fallbackId)); $normalized = [ 'id' => $id, 'label' => trim((string)($form['label'] ?? '')) ?: ucfirst(str_replace('-', ' ', $id)), 'description' => (string)($form['description'] ?? ''), 'enabled' => !array_key_exists('enabled', $form) || !empty($form['enabled']), 'submit' => array_replace([ 'store' => true, 'email' => true, 'subject' => '{form}: {name}', 'successText' => 'Vielen Dank für deine Nachricht.', ], is_array($form['submit'] ?? null) ? $form['submit'] : []), 'fields' => [], ]; foreach (($form['fields'] ?? []) as $index => $field) { if (is_array($field)) { $normalized['fields'][] = raijuFormNormalizeField($field, (int)$index); } } if (!$normalized['fields']) { $normalized['fields'] = raijuFormDefaultDefinition($id)['fields']; } return $normalized; } function raijuFormExists(string $id): bool { return is_file(raijuFormPath($id)); } function raijuFormLoad(string $id): ?array { $path = raijuFormPath($id); if (!is_file($path)) { return null; } return raijuFormNormalize(raijuFormsReadJson($path), $id); } function raijuFormSave(array $form): array { $normalized = raijuFormNormalize($form, (string)($form['id'] ?? 'form')); raijuFormsEnsureDir(); raijuFormsWriteJson(raijuFormPath($normalized['id']), $normalized); return $normalized; } function raijuFormsList(): array { raijuFormsEnsureDir(); $forms = []; foreach (glob(RAIJU_FORMS_DIR . '/*.json') ?: [] as $file) { $form = raijuFormNormalize(raijuFormsReadJson($file), pathinfo($file, PATHINFO_FILENAME)); $forms[] = [ 'id' => $form['id'], 'label' => $form['label'], 'description' => $form['description'], 'enabled' => $form['enabled'], 'store' => !empty($form['submit']['store']), 'fieldCount' => count($form['fields']), ]; } usort($forms, fn($a, $b) => strcasecmp((string)$a['label'], (string)$b['label'])); return $forms; } function raijuFormEscape(string $value): string { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); } function raijuFormRenderField(array $field, string $formId): string { $key = (string)$field['key']; $id = 'raiju-form-' . $formId . '-' . $key; $label = raijuFormEscape((string)$field['label']); $required = !empty($field['required']); $requiredAttr = $required ? ' required' : ''; $requiredMark = $required ? ' *' : ''; $placeholder = raijuFormEscape((string)($field['placeholder'] ?? '')); $width = (int)($field['width'] ?? 12); $type = (string)($field['type'] ?? 'text'); $help = trim((string)($field['help'] ?? '')); if ($type === 'hidden') { return ''; } if ($type === 'heading') { return '

' . $label . '

'; } if ($type === 'note') { return '

' . raijuFormEscape((string)($field['help'] ?: $field['label'])) . '

'; } $html = '
'; if ($type !== 'checkbox') { $html .= ''; } if ($type === 'textarea') { $html .= ''; } elseif ($type === 'select') { $html .= ''; } elseif ($type === 'radio') { $html .= '
'; foreach (($field['options'] ?? []) as $option) { $html .= ''; } $html .= '
'; } elseif ($type === 'checkbox') { $html .= ''; } else { $inputType = in_array($type, ['email', 'tel', 'date', 'time'], true) ? $type : 'text'; $html .= ''; } if ($help !== '') { $html .= '' . raijuFormEscape($help) . ''; } $html .= '
'; return $html; } function raijuFormRender(string $formId, array $options = []): string { $form = raijuFormLoad($formId); if (!$form || empty($form['enabled'])) { return ''; } $basePath = raijuSafeFormBasePath((string)($options['basePath'] ?? ($GLOBALS['basePath'] ?? ''))); $lang = (string)($options['lang'] ?? ($GLOBALS['currentLang'] ?? 'en')); $lazy = array_key_exists('lazy', $options) ? !empty($options['lazy']) : true; if ($lazy && !raijuIsLazyFormRender()) { return raijuLazyFormPlaceholder($form['id'], [ 'basePath' => $basePath, 'params' => [ 'lang' => $lang, 'basePath' => $basePath, ], ]); } $html = '
'; $html .= '

' . raijuFormEscape((string)$form['label']) . '

'; if (!empty($form['description'])) { $html .= '

' . raijuFormEscape((string)$form['description']) . '

'; } $html .= '
'; $html .= ''; $html .= raijuFormProtectionFields($form['id']); $html .= '
'; foreach ($form['fields'] as $field) { $html .= raijuFormRenderField($field, $form['id']); } $html .= '
'; $send = (string)($options['submitLabel'] ?? ($lang === 'de' ? 'Absenden' : 'Send')); $sending = (string)($options['sendingLabel'] ?? ($lang === 'de' ? 'Wird gesendet…' : 'Sending…')); $success = (string)($form['submit']['successText'] ?? 'Vielen Dank.'); $html .= '
'; $error = $lang === 'de' ? 'Fehler beim Senden. Bitte versuch es später noch mal.' : 'Could not send the form. Please try again later.'; $html .= ''; $html .= '
'; return $html; } function raijuForm(string $formId, array $options = []): string { return raijuFormRender($formId, $options); } function raijuFormSubmittedValue(array $field): string { $key = (string)$field['key']; if (($field['type'] ?? '') === 'checkbox') { return !empty($_POST[$key]) ? '1' : ''; } $value = $_POST[$key] ?? ''; if (is_array($value)) { $value = implode(', ', array_map('strval', $value)); } return trim((string)$value); } function raijuFormValidateSubmission(array $form, string $lang = 'en'): array { $errors = []; $values = []; foreach ($form['fields'] as $field) { $type = (string)($field['type'] ?? 'text'); if (in_array($type, ['heading', 'note', 'hidden'], true)) { continue; } $value = raijuFormSubmittedValue($field); $values[$field['key']] = $value; if (!empty($field['required']) && $value === '') { $errors[] = ($field['label'] ?? $field['key']) . ($lang === 'de' ? ' ist erforderlich' : ' is required'); continue; } if ($type === 'email' && $value !== '' && !filter_var($value, FILTER_VALIDATE_EMAIL)) { $errors[] = $lang === 'de' ? 'Gültige E-Mail ist erforderlich' : 'Valid email is required'; } } return [$errors, $values]; } function raijuFormsApplySubjectTemplate(string $template, array $form, array $values): string { $subject = $template ?: '{form}'; $replacements = [ '{form}' => (string)$form['label'], '{name}' => (string)($values['name'] ?? ''), '{email}' => (string)($values['email'] ?? ''), ]; return trim(strtr($subject, $replacements)) ?: (string)$form['label']; } function raijuFormsSaveSubmission(array $submission): bool { return raijuJsonUpdate(RAIJU_MAILS_PATH, function (array &$mails) use ($submission): void { array_unshift($mails, $submission); $mails = array_slice($mails, 0, 500); }); } function raijuFormsLoadEmailSettings(): array { $settingsPath = dirname(__DIR__) . '/content/settings.json'; $settings = raijuFormsReadJson($settingsPath); return is_array($settings['email'] ?? null) ? $settings['email'] : []; } function raijuFormsParseEmailList($value): array { $items = is_array($value) ? $value : explode(',', (string)$value); $emails = []; foreach ($items as $item) { foreach (explode(',', (string)$item) as $email) { $email = trim(str_replace(["\r", "\n", "\t"], '', (string)$email)); if ($email !== '' && filter_var($email, FILTER_VALIDATE_EMAIL)) { $emails[] = $email; } } } return array_values(array_unique($emails)); } function raijuFormsSendNotification(array $form, array $values, string $subject, string $body): bool { $emailConfig = raijuFormsLoadEmailSettings(); $method = $emailConfig['method'] ?? 'inactive'; $toRecipients = raijuFormsParseEmailList($emailConfig['recipientEmail'] ?? ''); if ($method === 'inactive' || !$toRecipients) { return false; } require_once dirname(__DIR__) . '/api/SmtpMailer.php'; $fromEmail = trim((string)($emailConfig['fromEmail'] ?? '')) ?: ($toRecipients[0] ?? ''); $fromName = trim(str_replace(["\r", "\n", "\t"], '', (string)($emailConfig['fromName'] ?? ''))); $replyTo = filter_var(($values['email'] ?? ''), FILTER_VALIDATE_EMAIL) ? (string)$values['email'] : ''; $bccRecipients = raijuFormsParseEmailList($emailConfig['bccEmail'] ?? ''); if ($method === 'smtp') { $mailer = new SmtpMailer( $emailConfig['smtpHost'] ?? '', (int)($emailConfig['smtpPort'] ?? 587), $emailConfig['smtpUsername'] ?? '', $emailConfig['smtpPassword'] ?? '', $emailConfig['smtpEncryption'] ?? 'tls' ); return $mailer->send($toRecipients, $subject, $body, $fromEmail, $fromName, $replyTo, $bccRecipients); } if ($method === 'sendmail') { $headers = []; $headers[] = 'From: ' . ($fromName ? "=?UTF-8?B?" . base64_encode($fromName) . "?= <$fromEmail>" : $fromEmail); if ($replyTo) { $headers[] = 'Reply-To: ' . $replyTo; } $headers[] = 'Content-Type: text/plain; charset=UTF-8'; $headers[] = 'X-Mailer: Raiju CMS'; $sent = @mail(implode(', ', $toRecipients), '=?UTF-8?B?' . base64_encode($subject) . '?=', $body, implode("\r\n", $headers)); foreach ($bccRecipients as $bccRecipient) { $sent = @mail($bccRecipient, '=?UTF-8?B?' . base64_encode($subject) . '?=', $body, implode("\r\n", $headers)) && $sent; } return $sent; } return false; } function raijuFormsBuildBody(array $form, array $values): string { $body = 'Neue Einsendung über Formular: ' . $form['label'] . "\n"; $body .= str_repeat('-', 40) . "\n\n"; foreach ($form['fields'] as $field) { if (in_array(($field['type'] ?? ''), ['heading', 'note', 'hidden'], true)) { continue; } $body .= ($field['label'] ?? $field['key']) . ': ' . ($values[$field['key']] ?? '') . "\n"; } return $body; }