diff --git a/src/Controller/FrontendModule/MemberSurveyEditController.php b/src/Controller/FrontendModule/MemberSurveyEditController.php index 978c209..c93f9d3 100644 --- a/src/Controller/FrontendModule/MemberSurveyEditController.php +++ b/src/Controller/FrontendModule/MemberSurveyEditController.php @@ -75,7 +75,12 @@ final class MemberSurveyEditController extends AbstractFrontendModuleController return $template->getResponse(); } - $survey = $this->resolveSurvey($request, $memberId); + $survey = $this->resolveSurvey($request, $memberId, $model); + + if ($survey instanceof Response) { + return $survey; + } + $createQuestionMode = $this->isCreateQuestionRequested($request); if ($survey instanceof SurveyModel && $request->isMethod('POST') && $request->request->has('_survey_action')) { @@ -225,7 +230,12 @@ final class MemberSurveyEditController extends AbstractFrontendModuleController return $template->getResponse(); } - private function resolveSurvey(Request $request, int $memberId): ?SurveyModel + /** + * Liefert die angeforderte Umfrage, null (kein survey-Parameter, z. B. Anlegen-Modus) + * oder einen Redirect zur Übersicht: Aufrufe aus veralteten Tabs oder Lesezeichen + * (Umfrage gelöscht, Zuweisung entzogen) dürfen keine 500er-Fehlerseite erzeugen. + */ + private function resolveSurvey(Request $request, int $memberId, ModuleModel $model): SurveyModel|Response|null { $surveyId = (int) $request->query->get('survey', 0); @@ -236,11 +246,15 @@ final class MemberSurveyEditController extends AbstractFrontendModuleController $survey = $this->surveyRepository->findById($surveyId); if (!$survey instanceof SurveyModel) { - throw new \RuntimeException('Die Umfrage wurde nicht gefunden.'); + $this->addFlash('error', 'Die Umfrage wurde nicht gefunden – sie wurde möglicherweise inzwischen gelöscht.'); + + return new RedirectResponse($this->resolveListUrl($model)); } if (!$this->surveyEditorRepository->isEditor((int) $survey->id, $memberId)) { - throw new \RuntimeException('Sie dürfen diese Umfrage nicht bearbeiten.'); + $this->addFlash('error', 'Sie sind dieser Umfrage nicht (mehr) zugewiesen.'); + + return new RedirectResponse($this->resolveListUrl($model)); } return $survey; @@ -301,7 +315,9 @@ final class MemberSurveyEditController extends AbstractFrontendModuleController $token = (string) $request->request->get('_token'); if (!$this->isCsrfTokenValid($action.'-'.$itemId, $token)) { - throw new \RuntimeException('Ungültiger CSRF-Token.'); + $this->addFlash('error', 'Ihre Sitzung war zwischenzeitlich abgelaufen. Bitte versuchen Sie es erneut.'); + + return new RedirectResponse($this->buildEditUrl($model, $request, (int) $survey->id)); } try { diff --git a/src/Controller/FrontendModule/MemberSurveyListController.php b/src/Controller/FrontendModule/MemberSurveyListController.php index ff3914a..cf7e585 100644 --- a/src/Controller/FrontendModule/MemberSurveyListController.php +++ b/src/Controller/FrontendModule/MemberSurveyListController.php @@ -100,18 +100,39 @@ final class MemberSurveyListController extends AbstractFrontendModuleController $action = (string) $request->request->get('_survey_action'); $token = (string) $request->request->get('_token', ''); + // Aktionen aus veralteten Tabs (Umfrage inzwischen gelöscht, Sitzung + // abgelaufen) dürfen keine 500er-Fehlerseite erzeugen: stattdessen + // Hinweis als Flash-Meldung und zurück zur frisch geladenen Übersicht. + $redirect = new RedirectResponse($request->getBaseUrl().$request->getPathInfo()); + if ($surveyId <= 0 || '' === $action) { - throw new \RuntimeException('Ungültige Umfrage-Aktion.'); + $this->addFlash('error', 'Die Aktion konnte nicht zugeordnet werden. Bitte versuchen Sie es erneut.'); + + return $redirect; } // Session-basierter Symfony-Token (wie im Editor): zuverlässig für // eingeloggte Mitglieder. Der Contao-REQUEST_TOKEN ist cookie-gebunden // und im Frontend-Fragment-Flow nicht stabil verfügbar. if (!$this->isCsrfTokenValid($action.'-'.$surveyId, $token)) { - throw new \RuntimeException('Ungültiger CSRF-Token.'); + $this->addFlash('error', 'Ihre Sitzung war zwischenzeitlich abgelaufen. Bitte versuchen Sie es erneut.'); + + return $redirect; } - $survey = $this->resolveEditableSurvey($surveyId, $memberId); + $survey = $this->surveyRepository->findById($surveyId); + + if (!$survey instanceof SurveyModel) { + $this->addFlash('error', 'Diese Umfrage existiert nicht mehr – sie wurde möglicherweise inzwischen gelöscht. Die Übersicht wurde aktualisiert.'); + + return $redirect; + } + + if (!$this->surveyEditorRepository->isEditor($surveyId, $memberId)) { + $this->addFlash('error', 'Sie sind dieser Umfrage nicht mehr zugewiesen. Die Übersicht wurde aktualisiert.'); + + return $redirect; + } try { if ('duplicate-survey' === $action) { @@ -140,17 +161,6 @@ final class MemberSurveyListController extends AbstractFrontendModuleController return new RedirectResponse($request->getBaseUrl().$request->getPathInfo()); } - private function resolveEditableSurvey(int $surveyId, int $memberId): SurveyModel - { - $survey = $this->surveyRepository->findById($surveyId); - - if (!$survey instanceof SurveyModel || !$this->surveyEditorRepository->isEditor($surveyId, $memberId)) { - throw new \RuntimeException('Sie dürfen diese Umfrage nicht bearbeiten.'); - } - - return $survey; - } - private function resolvePage(int $pageId): ?PageModel { if ($pageId <= 0) { diff --git a/src/Controller/FrontendModule/ShowSurveyController.php b/src/Controller/FrontendModule/ShowSurveyController.php index fe6e490..0054553 100644 --- a/src/Controller/FrontendModule/ShowSurveyController.php +++ b/src/Controller/FrontendModule/ShowSurveyController.php @@ -65,7 +65,11 @@ final class ShowSurveyController extends AbstractFrontendModuleController $submission = $this->surveySubmissionService->resolveActiveSubmission($survey, $request->getSession(), null); if ($request->isMethod('POST') && 'back' === (string) $request->request->get('_survey_navigation')) { - $this->assertNavigationTokenValid($request, 'survey-back-'.(int) $survey->id); + // Ungültiger Token (Sitzung abgelaufen, veralteter Tab): kein 500er, + // sondern die aktuelle Frage per Redirect neu laden. + if (!$this->isNavigationTokenValid($request, 'survey-back-'.(int) $survey->id)) { + return new RedirectResponse($request->getUri()); + } if ($this->surveySubmissionService->goBack($survey, $submission, $request->getSession())) { return new RedirectResponse($request->getUri()); @@ -170,7 +174,11 @@ final class ShowSurveyController extends AbstractFrontendModuleController $question = $this->resolvePreviewQuestion($survey, (int) $request->request->get('preview_question', 0)); if ($request->isMethod('POST') && 'back' === (string) $request->request->get('_survey_navigation')) { - $this->assertNavigationTokenValid($request, 'survey-preview-back-'.(int) $survey->id); + // Ungültiger Token: Entwurfsansicht ohne Fehlerseite von vorn beginnen + // (der Zustand wandert nur durch POST-Felder, ein GET startet neu). + if (!$this->isNavigationTokenValid($request, 'survey-preview-back-'.(int) $survey->id)) { + return new RedirectResponse($request->getUri()); + } if ([] !== $history) { $question = $this->resolvePreviewQuestion($survey, (int) array_pop($history)); @@ -285,13 +293,11 @@ final class ShowSurveyController extends AbstractFrontendModuleController return implode(',', $history); } - private function assertNavigationTokenValid(Request $request, string $tokenId): void + private function isNavigationTokenValid(Request $request, string $tokenId): bool { $token = (string) $request->request->get('_navigation_token', ''); - if (!$this->isCsrfTokenValid($tokenId, $token)) { - throw new \RuntimeException('Ungültiger CSRF-Token.'); - } + return $this->isCsrfTokenValid($tokenId, $token); } private function resolvePublicAlias(Request $request): string