From 8107eb929471f3a07ea1eac9ec821538c7698d91 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 17 Aug 2026 17:39:40 +0100 Subject: [PATCH 1/2] skip missing files when attaching them to form emails Co-Authored-By: Claude Fable 5 --- src/Forms/Email.php | 5 ++++- tests/Forms/EmailTest.php | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Forms/Email.php b/src/Forms/Email.php index be52427b0ba..0400c0abd28 100644 --- a/src/Forms/Email.php +++ b/src/Forms/Email.php @@ -5,6 +5,7 @@ use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Storage; use Statamic\Contracts\Forms\Submission; use Statamic\Facades\Antlers; use Statamic\Facades\Blueprint; @@ -161,7 +162,9 @@ private function attachFiles($field) : config('statamic.system.file_uploads_path', 'statamic/file-uploads'); foreach ($value as $file) { - $this->attachFromStorageDisk($disk, $basePath.'/'.$file); + if (Storage::disk($disk)->exists($path = "{$basePath}/{$file}")) { + $this->attachFromStorageDisk($disk, $path); + } } } diff --git a/tests/Forms/EmailTest.php b/tests/Forms/EmailTest.php index c9d40b82f35..15d73a7a513 100644 --- a/tests/Forms/EmailTest.php +++ b/tests/Forms/EmailTest.php @@ -351,6 +351,24 @@ public function it_attaches_files_from_files_field() $this->assertTrue($email->hasAttachmentFromStorageDisk('local', 'statamic/file-uploads/'.$documentPath)); } + #[Test] + public function it_skips_attachments_whose_temporary_files_no_longer_exist() + { + Storage::fake('local'); + + $form = tap(Form::make('test')->formFields([ + 'fields' => [ + ['handle' => 'document', 'field' => ['type' => 'files', 'max_files' => 1]], + ], + ]))->save(); + + $submission = $form->makeSubmission()->data(['document' => now()->timestamp.'/resume.pdf']); + + $email = tap(new Email($submission, ['to' => 'test@test.com', 'attachments' => true], Site::default()))->build(); + + $this->assertEmpty($email->attachments); + } + #[Test] public function it_attaches_files_from_files_field_on_the_configured_disk_and_path() { From 13df8af327d0490fb5133ca7f89962b7f988afb9 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 17 Aug 2026 17:59:48 +0100 Subject: [PATCH 2/2] dispatch `DeleteTemporaryFiles` when deleting form submissions Co-Authored-By: Claude Fable 5 --- src/Forms/Submission.php | 4 ++++ src/Jobs/DeletePartialFormSubmissions.php | 8 +------ tests/Forms/SubmissionTest.php | 27 +++++++++++++++++++++++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/Forms/Submission.php b/src/Forms/Submission.php index 0999c583f5a..e916d4a7a18 100644 --- a/src/Forms/Submission.php +++ b/src/Forms/Submission.php @@ -292,6 +292,10 @@ public function delete() $withEvents = $this->withEvents; $this->withEvents = true; + if ($withEvents) { + DeleteTemporaryFiles::dispatchSync($this); + } + FormSubmission::delete($this); if ($withEvents) { diff --git a/src/Jobs/DeletePartialFormSubmissions.php b/src/Jobs/DeletePartialFormSubmissions.php index fefbea040a1..c3a4be56d41 100644 --- a/src/Jobs/DeletePartialFormSubmissions.php +++ b/src/Jobs/DeletePartialFormSubmissions.php @@ -6,9 +6,7 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; -use Statamic\Contracts\Forms\Submission; use Statamic\Facades\FormSubmission; -use Statamic\Forms\DeleteTemporaryFiles; class DeletePartialFormSubmissions implements ShouldQueue { @@ -26,10 +24,6 @@ public function handle(): void ->where('partial', true) ->where('date', '<', $threshold) ->get() - ->each(function (Submission $submission): void { - DeleteTemporaryFiles::dispatchSync($submission); - - $submission->delete(); - }); + ->each->delete(); } } diff --git a/tests/Forms/SubmissionTest.php b/tests/Forms/SubmissionTest.php index b6a2200b7a2..f70edf270b0 100644 --- a/tests/Forms/SubmissionTest.php +++ b/tests/Forms/SubmissionTest.php @@ -17,6 +17,7 @@ use Statamic\Facades\Form; use Statamic\Facades\Site; use Statamic\Forms\CreateAssetsFromFileUploads; +use Statamic\Forms\DeleteTemporaryFiles; use Statamic\Forms\SendEmails; use Tests\PreventSavingStacheItemsToDisk; use Tests\TestCase; @@ -272,6 +273,32 @@ public function it_deletes_quietly() $this->assertTrue($return); } + #[Test] + public function deleting_dispatches_delete_temporary_files() + { + Bus::fake(); + + $form = tap(Form::make('contact_us'))->save(); + $submission = tap($form->makeSubmission())->save(); + + $submission->delete(); + + Bus::assertDispatchedSync(DeleteTemporaryFiles::class); + } + + #[Test] + public function deleting_quietly_does_not_dispatch_delete_temporary_files() + { + Bus::fake(); + + $form = tap(Form::make('contact_us'))->save(); + $submission = tap($form->makeSubmission())->save(); + + $submission->deleteQuietly(); + + Bus::assertNotDispatched(DeleteTemporaryFiles::class); + } + #[Test] public function it_determines_its_status() {