denyAccessUnlessGranted('ROLE_PUBLISHER'); return $this->render('admin/forms.html.twig', [ 'forms' => $this->forms->all(), 'counts' => $this->forms->submissionCounts(), 'submissions' => $this->forms->submissions($request->query->getInt('seite', 1)), 'languages' => array_map(fn (string $code): array => $this->languages->describe($code), $this->languages->extra()), ]); } /** Ein Formular bearbeiten, oder mit der Adresse „neu“ eines anlegen. */ #[Route('/%kant.admin_path%/forms/{slug}', name: 'admin_form_edit', requirements: ['slug' => '[a-z0-9-]{1,80}|neu'], methods: ['GET'])] public function edit(string $slug): Response { $this->denyAccessUnlessGranted('ROLE_PUBLISHER'); $current = 'neu' === $slug ? null : $this->forms->findBySlug($slug); if ('neu' !== $slug && null === $current) { throw $this->createNotFoundException('Dieses Formular gibt es nicht.'); } $theme = $this->theme->load(); return $this->render('admin/forms.html.twig', [ 'editing' => true, 'current' => $current, 'types' => FormRepository::TYPES, 'colors' => FormRepository::COLORS, // Die Bilder fuer eine Bildauswahl: die Mediathek als Liste, damit je Option gewaehlt statt getippt wird. 'images' => array_map(static fn (array $item): array => ['id' => $item['id'], 'title' => $item['title'], 'src' => $item['variants'][0]['src'] ?? ''], $this->media->page(1, false, 96)['items']), 'languages' => array_map(fn (string $code): array => $this->languages->describe($code), $this->languages->extra()), // Die Vorschau zeichnet den Absendeknopf mit dem Standardbutton der Website und // kennt die Palette, damit eine Farbe gewaehlt statt getippt wird. 'schema' => ['colors' => $theme['colors'], 'fonts' => $theme['fonts'], 'buttons' => $theme['buttons'] ?? ButtonDesign::defaults($theme['base']), 'base' => $theme['base']], ]); } #[Route('/%kant.admin_path%/forms/{slug}', name: 'admin_form_save', requirements: ['slug' => '[a-z0-9-]{1,80}|neu'], methods: ['POST'])] public function save(Request $request, string $slug): Response { $this->denyAccessUnlessGranted('ROLE_PUBLISHER'); if (!$this->isCsrfTokenValid('forms', $request->request->getString('_csrf_token'))) { return $this->back('Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.'); } $fields = []; // Je weitere Sprache: Felder tr__label, _help, _unit, _options am Feld, dazu // success_ und buttonLabel_ am Formular (ADR 111). Die Optionen stehen // als Kommaliste in derselben Reihenfolge wie die der Hauptsprache. $translations = []; foreach ($this->languages->extra() as $code) { $translations[$code] = ['success' => $request->request->getString('success_'.$code), 'buttonLabel' => $request->request->getString('buttonLabel_'.$code), 'fields' => []]; } foreach ($request->request->all('fields') as $row) { if (!is_array($row) || '' === trim((string) ($row['label'] ?? ''))) { continue; } $originals = array_map(trim(...), explode(',', (string) ($row['options'] ?? ''))); foreach (array_keys($translations) as $code) { $translatedOptions = array_map(trim(...), explode(',', (string) ($row['tr_'.$code.'_options'] ?? ''))); $translations[$code]['fields'][(string) ($row['name'] ?? '')] = [ 'label' => (string) ($row['tr_'.$code.'_label'] ?? ''), 'help' => (string) ($row['tr_'.$code.'_help'] ?? ''), 'unit' => (string) ($row['tr_'.$code.'_unit'] ?? ''), 'options' => array_combine($originals, array_pad(array_slice($translatedOptions, 0, count($originals)), count($originals), '')), ]; } $fields[] = [ 'name' => (string) ($row['name'] ?? ''), 'type' => (string) ($row['type'] ?? 'text'), 'label' => (string) $row['label'], 'required' => '' !== (string) ($row['required'] ?? ''), 'help' => (string) ($row['help'] ?? ''), 'options' => array_map(trim(...), explode(',', (string) ($row['options'] ?? ''))), 'images' => (string) ($row['images'] ?? ''), 'min' => (string) ($row['min'] ?? ''), 'max' => (string) ($row['max'] ?? ''), 'unit' => (string) ($row['unit'] ?? ''), // Die Bedingung: technischer Name eines früheren Auswahlfeldes und die Werte, bei // denen dieses Feld gilt, als Kommaliste. 'showIfField' => (string) ($row['showIfField'] ?? ''), 'showIfValues' => (string) ($row['showIfValues'] ?? ''), ]; } try { $this->forms->save( 'neu' === $slug ? $request->request->getString('slug') : $slug, $request->request->getString('name'), $fields, [ 'recipient' => $request->request->getString('recipient'), 'sender' => $request->request->getString('sender'), 'subject' => $request->request->getString('subject'), 'success' => $request->request->getString('success'), 'replyToField' => $request->request->getString('replyToField'), 'buttonLabel' => $request->request->getString('buttonLabel'), 'summary' => '' !== $request->request->getString('summary'), 'translations' => $translations, ...array_map(fn (string $field): string => $request->request->getString($field), array_combine(array_keys(FormRepository::COLORS), array_keys(FormRepository::COLORS))), ], $request->request->getInt('expectedVersion', -1), ); } catch (InvalidForm|StaleWrite $e) { return $this->back($e->getMessage(), 'error', $slug); } return $this->back('Das Formular wurde gespeichert.', 'success', 'neu' === $slug ? $request->request->getString('slug') : $slug); } private function back(string $message, string $type = 'error', ?string $slug = null): Response { $this->addFlash($type, $message); // Zurück zum bearbeiteten Formular; ohne Adresse (abgelaufene Sitzung) zur Übersicht. return null === $slug ? $this->redirectToRoute('admin_forms') : $this->redirectToRoute('admin_form_edit', ['slug' => $slug]); } }