From 8193ac99367c5ac4f48d19a05be9482bda386ab5 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Tue, 20 Feb 2024 15:59:03 +0000 Subject: [PATCH] [4.x] Remove any uploaded assets when submission silently fails or validation fails (#9549) --- src/Http/Controllers/FormController.php | 27 +++++- tests/Tags/Form/FormCreateTest.php | 109 ++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/src/Http/Controllers/FormController.php b/src/Http/Controllers/FormController.php index 8a88ab5bf0..c86f61554b 100644 --- a/src/Http/Controllers/FormController.php +++ b/src/Http/Controllers/FormController.php @@ -10,6 +10,7 @@ use Statamic\Events\FormSubmitted; use Statamic\Events\SubmissionCreated; use Statamic\Exceptions\SilentFormFailureException; +use Statamic\Facades\Asset; use Statamic\Facades\Form; use Statamic\Facades\Site; use Statamic\Forms\Exceptions\FileContentTypeRequiredException; @@ -50,7 +51,9 @@ public function submit(FrontendFormRequest $request, $form) try { throw_if(Arr::get($values, $form->honeypot()), new SilentFormFailureException); - $values = array_merge($values, $submission->uploadFiles($assets)); + $uploadedAssets = $submission->uploadFiles($assets); + + $values = array_merge($values, $uploadedAssets); $submission->data( $fields->addValues($values)->process()->values() @@ -60,8 +63,14 @@ public function submit(FrontendFormRequest $request, $form) // If they want to add validation errors, they can throw an exception. throw_if(FormSubmitted::dispatch($submission) === false, new SilentFormFailureException); } catch (ValidationException $e) { + $this->removeUploadedAssets($uploadedAssets); + return $this->formFailure($params, $e->errors(), $form->handle()); } catch (SilentFormFailureException $e) { + if (isset($uploadedAssets)) { + $this->removeUploadedAssets($uploadedAssets); + } + return $this->formSuccess($params, $submission, true); } @@ -156,4 +165,20 @@ private function formSuccessRedirect($params, $submission) return $redirect; } + + /** + * Remove any uploaded assets + * + * Triggered by a validation exception or silent failure + */ + private function removeUploadedAssets(array $assets) + { + collect($assets) + ->flatten() + ->each(function ($id) { + if ($asset = Asset::find($id)) { + $asset->delete(); + } + }); + } } diff --git a/tests/Tags/Form/FormCreateTest.php b/tests/Tags/Form/FormCreateTest.php index 557a6314a7..d097f5d699 100644 --- a/tests/Tags/Form/FormCreateTest.php +++ b/tests/Tags/Form/FormCreateTest.php @@ -2,6 +2,11 @@ namespace Tests\Tags\Form; +use Illuminate\Http\UploadedFile; +use Illuminate\Support\Facades\Event; +use Illuminate\Support\Facades\Storage; +use Illuminate\Validation\ValidationException; +use Statamic\Facades\AssetContainer; use Statamic\Facades\Form; use Statamic\Statamic; @@ -822,4 +827,108 @@ public function it_fetches_form_data() $this->assertEquals($form['honeypot'], 'winnie'); $this->assertEquals($form['js_driver'], 'alpine'); } + + /** @test */ + public function it_uploads_assets() + { + Storage::fake('avatars'); + AssetContainer::make('avatars')->disk('avatars')->save(); + + $this->createForm([ + 'tabs' => [ + 'main' => [ + 'sections' => [ + [ + 'display' => 'One', + 'instructions' => 'One Instructions', + 'fields' => [ + ['handle' => 'alpha', 'field' => ['type' => 'text']], + ['handle' => 'bravo', 'field' => ['type' => 'assets', 'container' => 'avatars']], + ], + ], + ], + ], + ], + ], 'survey'); + + $this + ->post('/!/forms/survey', [ + 'alpha' => 'test', + 'bravo' => UploadedFile::fake()->image('avatar.jpg'), + ]); + + Storage::disk('avatars')->assertExists('avatar.jpg'); + } + + /** @test */ + public function it_removes_any_uploaded_assets_when_a_submission_silently_fails() + { + Storage::fake('avatars'); + AssetContainer::make('avatars')->disk('avatars')->save(); + + Event::listen(function (\Statamic\Events\FormSubmitted $event) { + return false; + }); + + $this->createForm([ + 'tabs' => [ + 'main' => [ + 'sections' => [ + [ + 'display' => 'One', + 'instructions' => 'One Instructions', + 'fields' => [ + ['handle' => 'alpha', 'field' => ['type' => 'text']], + ['handle' => 'bravo', 'field' => ['type' => 'assets', 'container' => 'avatars']], + ], + ], + ], + ], + ], + ], 'survey'); + + $this + ->post('/!/forms/survey', [ + 'alpha' => 'test', + 'bravo' => UploadedFile::fake()->image('avatar.jpg'), + ]); + + Storage::disk('avatars')->assertMissing('avatar.jpg'); + } + + /** @test */ + public function it_removes_any_uploaded_assets_when_a_listener_throws_a_validation_exception() + { + Storage::fake('avatars'); + AssetContainer::make('avatars')->disk('avatars')->save(); + + Event::listen(function (\Statamic\Events\FormSubmitted $event) { + throw ValidationException::withMessages(['custom' => 'This is a custom message']); + }); + + $this->createForm([ + 'tabs' => [ + 'main' => [ + 'sections' => [ + [ + 'display' => 'One', + 'instructions' => 'One Instructions', + 'fields' => [ + ['handle' => 'alpha', 'field' => ['type' => 'text']], + ['handle' => 'bravo', 'field' => ['type' => 'assets', 'container' => 'avatars']], + ], + ], + ], + ], + ], + ], 'survey'); + + $this + ->post('/!/forms/survey', [ + 'alpha' => 'test', + 'bravo' => UploadedFile::fake()->image('avatar.jpg'), + ]); + + Storage::disk('avatars')->assertMissing('avatar.jpg'); + } }