Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions config/system.php
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,31 @@
//
],

/*
|--------------------------------------------------------------------------
| File Uploads Disk
|--------------------------------------------------------------------------
|
| Temporary file uploads are stored here before being moved to their
| final destination. You may configure this to use a shared filesystem
| in multiserver environments. This disk may be shared by other kinds
| of temporary file uploads (e.g. forms) that use their own path below.
|
*/

'file_uploads_disk' => env('STATAMIC_FILE_UPLOADS_DISK', 'local'),

/*
|--------------------------------------------------------------------------
| File Uploads Path
|--------------------------------------------------------------------------
|
| The path (on the file uploads disk above) where temporary file uploads
| from the Files fieldtype are stored before being moved to their final
| destination. These files are automatically cleaned up over time.
|
*/

'file_uploads_path' => env('STATAMIC_FILE_UPLOADS_PATH', 'statamic/file-uploads'),

];
3 changes: 2 additions & 1 deletion src/Actions/ReuploadAsset.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ public function run($assets, $values)
{
$asset = $assets->first();

$file = new ReplacementFile('statamic/file-uploads/'.$values['file']);
$basePath = config('statamic.system.file_uploads_path', 'statamic/file-uploads');
$file = new ReplacementFile($basePath.'/'.$values['file']);

try {
$asset->reupload($file);
Expand Down
4 changes: 2 additions & 2 deletions src/Assets/FileUploader.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ protected function uploadPath(UploadedFile $file)

protected function uploadPathPrefix()
{
return 'statamic/file-uploads/';
return config('statamic.system.file_uploads_path', 'statamic/file-uploads').'/';
}

protected function preset()
Expand All @@ -40,6 +40,6 @@ protected function preset()

protected function disk()
{
return Storage::disk('local');
return Storage::disk(config('statamic.system.file_uploads_disk', 'local'));
}
}
7 changes: 3 additions & 4 deletions src/Assets/ReplacementFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ public function basename()

public function writeTo(Filesystem $disk, $path)
{
$disk->put(
$path,
Storage::disk('local')->readStream($this->path)
);
$sourceDisk = Storage::disk(config('statamic.system.file_uploads_disk', 'local'));

$disk->put($path, $sourceDisk->readStream($this->path));
}
}
7 changes: 5 additions & 2 deletions src/Forms/DeleteTemporaryAttachments.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ public function __construct(public Submission $submission)

public function handle()
{
$disk = Storage::disk(config('statamic.system.file_uploads_disk', 'local'));
$basePath = config('statamic.system.file_uploads_path', 'statamic/file-uploads');

$this->submission->form()->blueprint()->fields()->all()
->filter(fn (Field $field) => $field->type() === 'files')
->each(function (Field $field) {
->each(function (Field $field) use ($disk, $basePath) {
Collection::wrap($this->submission->get($field->handle(), []))
->each(fn ($path) => Storage::disk('local')->delete('statamic/file-uploads/'.$path));
->each(fn ($path) => $disk->delete($basePath.'/'.$path));

$this->submission->remove($field->handle());
});
Expand Down
5 changes: 4 additions & 1 deletion src/Forms/Email.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,11 @@ private function attachFiles($field)
return;
}

$disk = config('statamic.system.file_uploads_disk', 'local');
$basePath = config('statamic.system.file_uploads_path', 'statamic/file-uploads');

foreach ($value as $file) {
$this->attachFromStorageDisk('local', 'statamic/file-uploads/'.$file);
$this->attachFromStorageDisk($disk, $basePath.'/'.$file);
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/Http/Middleware/DeleteTemporaryFileUploads.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ public function handle($request, Closure $next)

private function deleteFilesOverAnHourOld()
{
$disk = File::disk('local');
$disk = File::disk(config('statamic.system.file_uploads_disk', 'local'));
$directory = config('statamic.system.file_uploads_path', 'statamic/file-uploads');

$disk
->getFilesRecursively($dir = 'statamic/file-uploads')
->getFilesRecursively($directory)
->filter(function ($path) {
$bits = explode('/', $path);
$timestamp = $bits[count($bits) - 2];
Expand All @@ -32,6 +33,6 @@ private function deleteFilesOverAnHourOld()
})
->each(fn ($path) => $disk->delete($path));

$disk->deleteEmptySubfolders($dir);
$disk->deleteEmptySubfolders($directory);
}
}
15 changes: 15 additions & 0 deletions tests/Assets/ReplacementFileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,19 @@ public function it_writes_the_file_to_another_disk()

$targetDisk->assertExists('the/new/path.jpg');
}

#[Test]
public function it_reads_from_configured_disk()
{
config(['statamic.system.file_uploads_disk' => 'uploads']);

$originDisk = Storage::fake('uploads');
$targetDisk = Storage::fake('target');
$originDisk->write('foo/bar/baz.jpg', 'contents');

(new ReplacementFile('foo/bar/baz.jpg'))->writeTo($targetDisk, 'the/new/path.jpg');

$targetDisk->assertExists('the/new/path.jpg');
$this->assertEquals('contents', $targetDisk->get('the/new/path.jpg'));
}
}
29 changes: 29 additions & 0 deletions tests/Feature/Fieldtypes/FilesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,35 @@ public static function uploadProvider()
];
}

#[Test]
public function it_uploads_a_file_to_the_configured_disk_and_path()
{
config([
'statamic.system.file_uploads_disk' => 'uploads',
'statamic.system.file_uploads_path' => 'temp-uploads',
]);

$localDisk = Storage::fake('local');
$uploadsDisk = Storage::fake('uploads');

$file = UploadedFile::fake()->create('test.txt');

Date::setTestNow(Date::createFromTimestamp(1671484636, config('app.timezone')));

$this
->actingAs(tap(User::make()->makeSuper())->save())
->post('/cp/fieldtypes/files/upload', ['file' => $file])
->assertOk()
->assertJson([
'data' => [
'id' => '1671484636/test.txt',
],
]);

$uploadsDisk->assertExists('temp-uploads/1671484636/test.txt');
$localDisk->assertMissing('statamic/file-uploads/1671484636/test.txt');
}

#[Test]
public function it_replaces_dimensions_rule()
{
Expand Down
29 changes: 29 additions & 0 deletions tests/Forms/SendEmailsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Tests\Forms;

use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Contracts\Forms\SubmissionRepository;
Expand Down Expand Up @@ -139,6 +140,34 @@ public function delete_attachments_job_only_saves_submission_when_enabled()
$this->assertEmpty($submissions);
}

#[Test]
public function delete_attachments_job_deletes_files_from_the_configured_disk_and_path()
{
config([
'statamic.system.file_uploads_disk' => 'uploads',
'statamic.system.file_uploads_path' => 'temp-uploads',
]);

$localDisk = Storage::fake('local');
$uploadsDisk = Storage::fake('uploads');
$uploadsDisk->put('temp-uploads/1234567/file.txt', 'contents');
$localDisk->put('statamic/file-uploads/1234567/file.txt', 'contents');

$form = tap(FacadesForm::make('attachments_test')->email([
'from' => 'first@sender.com',
'to' => 'first@recipient.com',
]))->save();

$form->blueprint()->ensureField('attachments', ['type' => 'files'])->save();

$submission = $form->makeSubmission()->data(['attachments' => ['1234567/file.txt']]);

(new DeleteTemporaryAttachments($submission))->handle();

$uploadsDisk->assertMissing('temp-uploads/1234567/file.txt');
$localDisk->assertExists('statamic/file-uploads/1234567/file.txt');
}

#[Test]
#[DataProvider('noEmailsProvider')]
public function no_email_jobs_are_queued_if_none_are_configured($emailConfig)
Expand Down
40 changes: 40 additions & 0 deletions tests/Http/Middleware/DeleteTemporaryFileUploadsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace Tests\Http\Middleware;

use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Http\Middleware\DeleteTemporaryFileUploads;
use Tests\TestCase;

class DeleteTemporaryFileUploadsTest extends TestCase
{
#[Test]
public function it_deletes_files_over_an_hour_old_from_the_configured_disk_and_path()
{
config([
'statamic.system.file_uploads_disk' => 'uploads',
'statamic.system.file_uploads_path' => 'temp-uploads',
]);

$localDisk = Storage::fake('local');
$uploadsDisk = Storage::fake('uploads');

Date::setTestNow(now());

$oldTimestamp = now()->subHours(2)->timestamp;
$newTimestamp = now()->timestamp;

$uploadsDisk->put("temp-uploads/{$oldTimestamp}/old.txt", 'old');
$uploadsDisk->put("temp-uploads/{$newTimestamp}/new.txt", 'new');
$localDisk->put("statamic/file-uploads/{$oldTimestamp}/old.txt", 'old');

(new \ReflectionMethod(DeleteTemporaryFileUploads::class, 'deleteFilesOverAnHourOld'))
->invoke(new DeleteTemporaryFileUploads);

$uploadsDisk->assertMissing("temp-uploads/{$oldTimestamp}/old.txt");
$uploadsDisk->assertExists("temp-uploads/{$newTimestamp}/new.txt");
$localDisk->assertExists("statamic/file-uploads/{$oldTimestamp}/old.txt");
}
}
Loading