Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.x] Ability to use files fieldtype in forms for attaching temporary files #9084

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1ac264e
When email has attachments enabled, filter out asset fields from emai…
duncanmcclean Nov 28, 2023
a96703f
Add delete attachments toggle to forms
duncanmcclean Nov 28, 2023
e4505a5
Delete attachments after submission emails are sent
duncanmcclean Nov 28, 2023
23380f2
Fix tests & handle no jobs
duncanmcclean Nov 29, 2023
30e0e01
Add test to ensure DeleteTemporaryAttachments job is dispatched
duncanmcclean Nov 29, 2023
82ea8f0
Ensure attachments are deleted
duncanmcclean Nov 29, 2023
e7133ba
Clear assets field value
duncanmcclean Nov 29, 2023
db216d1
whoops, didn't mean to commit a test user
duncanmcclean Nov 29, 2023
ac97b84
Fix styling
duncanmcclean Nov 29, 2023
7d894f4
dont fail fast
jasonvarga Nov 29, 2023
3f169f6
skip test
jasonvarga Nov 29, 2023
232276c
bump to fixed version
jasonvarga Nov 29, 2023
24fe8da
do fail fast
duncanmcclean Nov 29, 2023
2052543
Merge branch 'master' into add-option-to-delete-attachment-files-afte…
duncanmcclean Mar 25, 2024
62d7b91
Make it possible to use the Files fieldtype on forms
duncanmcclean Mar 25, 2024
3a88a1e
Get uploads working with the Files fieldtype
duncanmcclean Mar 25, 2024
dbd44e0
Ensure files are deleted after submission
duncanmcclean Mar 25, 2024
94db637
Fix styling
duncanmcclean Mar 25, 2024
e832218
Merge branch 'master' of github.com:statamic/cms into add-option-to-d…
duncanmcclean Mar 25, 2024
fab2586
Drop the "Delete attachments after submission" toggle
duncanmcclean Mar 25, 2024
a0201f8
remove test - attachment deleting isn't done on the `Submission` anymore
duncanmcclean Mar 25, 2024
3b1565b
wip
duncanmcclean Mar 25, 2024
1a599e4
only dispatch `DeleteTemporaryAttachments` when there are files to be…
duncanmcclean Mar 25, 2024
215eb83
Merge branch 'add-option-to-delete-attachment-files-after-form-submis…
duncanmcclean Mar 25, 2024
31c8f83
Fix styling
duncanmcclean Mar 25, 2024
0f4d888
Fix test
duncanmcclean Mar 25, 2024
2a19446
Merge branch 'master' of github.com:statamic/cms into add-option-to-d…
duncanmcclean Mar 25, 2024
953f22d
Merge branch 'add-option-to-delete-attachment-files-after-form-submis…
duncanmcclean Mar 25, 2024
91520e0
change handle of form in test
duncanmcclean Mar 26, 2024
d14716a
Specify Laravel 10.33 as a minimum version
duncanmcclean Mar 26, 2024
6a99b41
wip
duncanmcclean Mar 26, 2024
1d57627
wip
duncanmcclean Mar 26, 2024
b2a6153
Upload the same way as assets
duncanmcclean Mar 26, 2024
a22941a
wip
duncanmcclean Mar 26, 2024
e639119
in fact, we should be filtering out files fields here too
duncanmcclean Mar 26, 2024
d2dd4fb
check for the files fieldtype so files="true" gets added automaticall…
jasonvarga Mar 27, 2024
bfd7414
simplify ...
jasonvarga Mar 27, 2024
a4dda5c
in_array is shorter here
jasonvarga Mar 27, 2024
950ea37
arrow func and in_array
jasonvarga Mar 27, 2024
f1ea05b
split into methods and fix files fieldtype not working when max_files…
jasonvarga Mar 27, 2024
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
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"composer/composer": "^2.2.22",
"guzzlehttp/guzzle": "^6.3 || ^7.0",
"james-heinrich/getid3": "^1.9.21",
"laravel/framework": "^10.0 || ^11.0",
"laravel/framework": "^10.33 || ^11.0",
"laravel/helpers": "^1.1",
"league/commonmark": "^2.2",
"league/csv": "^9.0",
Expand Down
7 changes: 7 additions & 0 deletions resources/views/extend/forms/fields/files.antlers.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<input
type="file"
name="{{ handle }}{{ if max_files !== 1 }}[]{{ /if }}"
{{ if js_driver }}{{ js_attributes }}{{ /if }}
{{ if max_files !== 1 }}multiple{{ /if }}
{{ if validate|contains:required }}required{{ /if }}
>
2 changes: 2 additions & 0 deletions src/Fieldtypes/Files.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class Files extends Fieldtype
{
protected $defaultValue = [];
protected $selectable = false;
protected $selectableInForms = true;
protected $categories = ['media'];

protected function configFieldItems(): array
{
Expand Down
36 changes: 36 additions & 0 deletions src/Forms/DeleteTemporaryAttachments.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Statamic\Forms;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Statamic\Contracts\Forms\Submission;
use Statamic\Fields\Field;

class DeleteTemporaryAttachments implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

public function __construct(public Submission $submission)
{
}

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

$this->submission->remove($field->handle());
});

$this->submission->saveQuietly();
}
}
48 changes: 35 additions & 13 deletions src/Forms/Email.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,31 +111,53 @@ protected function addAttachments()
}

$this->getRenderableFieldData(Arr::except($this->submissionData, ['id', 'date', 'form']))
->filter(function ($field) {
return $field['fieldtype'] === 'assets';
})
->filter(fn ($field) => in_array($field['fieldtype'], ['assets', 'files']))
->each(function ($field) {
$value = $field['value']->value();

$value = array_get($field, 'config.max_files') === 1
? collect([$value])->filter()
: $value->get();

foreach ($value as $file) {
$this->attachFromStorageDisk($file->container()->diskHandle(), $file->path());
}
$field['value'] = $field['value']->value();
$field['fieldtype'] === 'assets' ? $this->attachAssets($field) : $this->attachFiles($field);
});

return $this;
}

private function attachAssets($field)
{
$value = $field['value'];

$value = array_get($field, 'config.max_files') === 1
? collect([$value])->filter()
: $value->get();

foreach ($value as $asset) {
$this->attachFromStorageDisk($asset->container()->diskHandle(), $asset->path());
}
}

private function attachFiles($field)
{
$value = $field['value'];

$value = array_get($field, 'config.max_files') === 1
? collect([$value])->filter()
: $value;

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

protected function addData()
{
$augmented = $this->submission->toAugmentedArray();
$fields = $this->getRenderableFieldData(Arr::except($augmented, ['id', 'date', 'form']));

if (array_has($this->config, 'attachments')) {
$fields = $fields->reject(fn ($field) => in_array($field['fieldtype'], ['assets', 'files']));
}

$data = array_merge($augmented, $this->getGlobalsData(), [
'config' => config()->all(),
'fields' => $this->getRenderableFieldData(Arr::except($augmented, ['id', 'date', 'form'])),
'fields' => $fields,
'site_url' => Config::getSiteUrl(),
'date' => now(),
'now' => now(),
Expand Down
2 changes: 1 addition & 1 deletion src/Forms/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ public function dateFormat()
public function hasFiles()
{
return $this->fields()->filter(function ($field) {
return $field->fieldtype()->handle() === 'assets';
return in_array($field->fieldtype()->handle(), ['assets', 'files']);
})->isNotEmpty();
}

Expand Down
32 changes: 25 additions & 7 deletions src/Forms/SendEmails.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Bus;
use Statamic\Contracts\Forms\Submission;
use Statamic\Fields\Field;
use Statamic\Sites\Site;

class SendEmails
Expand All @@ -21,18 +23,26 @@ public function __construct(Submission $submission, Site $site)
$this->site = $site;
}

public function handle()
public function handle(): void
{
$this->jobs()->each(fn ($job) => Bus::dispatch($job));
$jobs = $this->jobs();

if ($jobs->isNotEmpty()) {
Bus::chain($jobs)->dispatch();
}
}

private function jobs()
private function jobs(): Collection
{
return $this->emailConfigs($this->submission)->map(function ($config) {
$class = config('statamic.forms.send_email_job');
return $this->emailConfigs($this->submission)
->map(function ($config) {
$class = config('statamic.forms.send_email_job');

return new $class($this->submission, $this->site, $config);
});
return new $class($this->submission, $this->site, $config);
})
->when($this->shouldDeleteTemporaryAttachments(), function ($jobs) {
$jobs->push(new DeleteTemporaryAttachments($this->submission));
});
}

private function emailConfigs($submission)
Expand All @@ -43,4 +53,12 @@ private function emailConfigs($submission)

return collect($config);
}

protected function shouldDeleteTemporaryAttachments(): bool
{
return $this->submission->form()->blueprint()->fields()->all()
->filter(fn (Field $field) => $field->fieldtype()->handle() === 'files')
->filter()
->count() > 0;
}
}
8 changes: 7 additions & 1 deletion src/Forms/Submission.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
use Statamic\Events\SubmissionDeleted;
use Statamic\Events\SubmissionSaved;
use Statamic\Events\SubmissionSaving;
use Statamic\Facades\Asset;
use Statamic\Facades\File;
use Statamic\Facades\YAML;
use Statamic\Forms\Uploaders\AssetsUploader;
use Statamic\Forms\Uploaders\FilesUploader;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;

Expand Down Expand Up @@ -120,7 +122,11 @@ public function formattedDate()
public function uploadFiles($uploadedFiles)
{
return collect($uploadedFiles)->map(function ($files, $handle) {
return AssetsUploader::field($this->fields()->get($handle))->upload($files);
$field = $this->fields()->get($handle);

return $field['type'] === 'files'
? FilesUploader::field($field)->upload($files)
: AssetsUploader::field($field)->upload($files);
})->all();
}

Expand Down
76 changes: 76 additions & 0 deletions src/Forms/Uploaders/FilesUploader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace Statamic\Forms\Uploaders;

use Statamic\Assets\FileUploader;
use Statamic\Support\Arr;

class FilesUploader
{
protected $config;

/**
* Instantiate files uploader.
*
* @param array $config
*/
public function __construct($config)
{
$this->config = collect($config);
}

/**
* Instantiate files uploader.
*
* @param array $config
* @return static
*/
public static function field($config)
{
return new static($config);
}

/**
* Upload the files and return their IDs.
*
* @param mixed $files
* @return array|string
*/
public function upload($files)
{
$ids = $this->getUploadableFiles($files)->map(function ($file) {
return FileUploader::container()->upload($file);
});

return $this->isSingleFile()
? $ids->first()
: $ids->all();
}

/**
* Get uploadable files.
*
* @param mixed $files
* @return \Illuminate\Support\Collection
*/
protected function getUploadableFiles($files)
{
$files = collect(Arr::wrap($files))->filter();

// If multiple uploads is not enabled for this field, we will
// simply take the first uploaded file and ignore the rest.
return $this->isSingleFile()
? $files->take(1)
: $files;
}

/**
* Determine if uploader should only upload a single file.
*
* @return bool
*/
protected function isSingleFile()
{
return $this->config->get('max_files') === 1;
}
}
2 changes: 1 addition & 1 deletion src/Http/Requests/FrontendFormRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ private function normalizeAssetsValues($fields)
{
// The assets fieldtype is expecting an array, even for `max_files: 1`, but we don't want to force that on the front end.
return $fields->all()
->filter(fn ($field) => $field->fieldtype()->handle() === 'assets' && $this->hasFile($field->handle()))
->filter(fn ($field) => in_array($field->fieldtype()->handle(), ['assets', 'files']) && $this->hasFile($field->handle()))
->map(fn ($field) => Arr::wrap($this->file($field->handle())))
->all();
}
Expand Down
Loading
Loading