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

Glide process image assets on upload #6040

Merged
merged 69 commits into from
Jan 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
975b902
Extract this path logic to clean up `upload()` method.
jesseleite May 13, 2022
f830cd0
Extract `put()` disk logic for file uploads.
jesseleite May 13, 2022
5381669
Process images using glide on upload.
jesseleite May 13, 2022
18e61c4
Ensure we delete tmp glide cache in storage after successful upload.
jesseleite May 13, 2022
2919b30
Move private methods to bottom and docblock.
jesseleite May 13, 2022
266ab53
Add `Glide Processing` config field to asset containers.
jesseleite May 13, 2022
eb9add3
Implement reading/writing of `glide` config on containers.
jesseleite May 13, 2022
3bbfa7a
Check container for glide params when uploading.
jesseleite May 13, 2022
24ec7f1
Pass tests again.
jesseleite May 13, 2022
7a2cf09
Apply fixes from StyleCI
StyleCIBot May 13, 2022
262bf0b
Copy driver and file extensions config from `GlideManager`.
jesseleite May 19, 2022
52c3ed3
Add some basic test coverage for glide on upload.
jesseleite May 19, 2022
6c85db3
Extract upload logic to `Assets\Uploader` class.
jesseleite May 19, 2022
43d02c9
This was failing test 🤔.
jesseleite May 19, 2022
0a76aee
Import `ServerFactory`.
jesseleite May 19, 2022
0a89a0e
Refactor `glideTmpPath` to property.
jesseleite May 19, 2022
64e10ab
Pass test for non-glideable filetypes.
jesseleite May 19, 2022
235254e
Enable watermarks.
jesseleite May 19, 2022
fd690f7
Merge branch '3.3' of https://github.com/statamic/cms into feature/gl…
jesseleite May 20, 2022
239b1cd
Add boolean `label_html` to select fieldtypes to allow rendering of h…
jesseleite May 27, 2022
e2b95c2
Update glide config fields on asset container edit form.
jesseleite May 27, 2022
c51e0e0
Finish controller and ensure `glide_warm_intelligent` toggle state lo…
jesseleite May 28, 2022
e0614b5
Store `glide_source_preset` and `glide_warm_presets` instead of `glide`.
jesseleite May 28, 2022
2ebac81
Load glide preset params from `assets.php` config.
jesseleite May 28, 2022
bc35a09
Update tests.
jesseleite May 28, 2022
69877d5
This is redundant.
jesseleite May 31, 2022
6104ebe
Ensure user can explicitly disable intelligent warming of glide presets.
jesseleite May 31, 2022
b7f47f8
Intelligently determine which glide presets to ignore based on contai…
jesseleite May 31, 2022
70b8635
Ensure container’s configured glide settings are respected when gener…
jesseleite May 31, 2022
b41c2b8
Ditch this, since it errors on tmp files in `UploadedFile` instances.
jesseleite May 31, 2022
9d8daa8
Add test coverage for other existing container settings.
jesseleite May 31, 2022
145ee98
Add test coverage for new glide preset configs.
jesseleite May 31, 2022
34e68dd
Pass tests again.
jesseleite May 31, 2022
e77672f
Add basic test coverage for glide preset generation on upload.
jesseleite May 31, 2022
c1d2843
Our `PresetGenerator` already skips ignored presets in console comman…
jesseleite May 31, 2022
2f581ab
Make new instruction messages translatable.
jesseleite May 31, 2022
3858974
Show params in multi-select as well.
jesseleite Jun 1, 2022
78bcac0
Merge branch '3.3' of https://github.com/statamic/cms into feature/gl…
jesseleite Jul 13, 2022
6e4894c
Re-implement `getSafeFilename()` on Uploader as static helper to pass…
jesseleite Jul 13, 2022
807874a
Merge branch 'master' of https://github.com/statamic/cms into feature…
jesseleite Jul 13, 2022
a0ed878
Clarify that this is now configured on asset containers (but with sam…
jesseleite Aug 11, 2022
9da4702
Merge branch 'master' into feature/glide-asset-on-upload
jasonvarga Dec 19, 2022
4c680c9
reapply asset changes
jasonvarga Dec 19, 2022
18476e3
missed this
jasonvarga Dec 19, 2022
2281e84
simplify
jasonvarga Dec 20, 2022
0eb5454
Merge branch 'master' into feature/glide-asset-on-upload
jasonvarga Jan 5, 2023
8041402
Files fieldtype can accept a container so it can perform the appropri…
jasonvarga Jan 5, 2023
a355041
Pass the preset instead of converting to params
jasonvarga Jan 6, 2023
71e0b0c
Allow Glide::server to accept config overrides, and use that.
jasonvarga Jan 6, 2023
82ebe62
Pass disk into method so its identical, to help with upcoming refactor
jasonvarga Jan 6, 2023
7317217
Refactor uploader to be DRY between asset uploading and file fieldtyp…
jasonvarga Jan 6, 2023
b8d3574
Only delete the single temporary file. Don't interfere with uploads i…
jasonvarga Jan 6, 2023
311221e
Remove now unnecessary property
jasonvarga Jan 6, 2023
1c56e00
apply b8d35740
jasonvarga Jan 6, 2023
aa443c0
no longer parent constructor
jasonvarga Jan 6, 2023
5c033e1
Create the uploaded file each time, rather than once across all provi…
jasonvarga Jan 6, 2023
72f5a69
remove glide prefixes
jasonvarga Jan 6, 2023
c446354
wording
jasonvarga Jan 6, 2023
f659518
Change behavior of warmPresets so that it returns the appropriate pre…
jasonvarga Jan 6, 2023
8dc5a06
Rework generate-presets command to loop through containers and only g…
jasonvarga Jan 6, 2023
69211c3
unnecessary
jasonvarga Jan 6, 2023
6528bab
More refactoring ...
jasonvarga Jan 9, 2023
24f9d03
wording
jasonvarga Jan 9, 2023
38903bd
wip
jasonvarga Jan 9, 2023
fd2f836
generate presets command uses asset specific presets now
jasonvarga Jan 9, 2023
68155c7
throw exception if you try to get a thumbnail preset that doesnt exist
jasonvarga Jan 9, 2023
72933a2
Merge branch 'master' into feature/glide-asset-on-upload
jasonvarga Jan 10, 2023
805c2f9
wording
jasonvarga Jan 10, 2023
580c7fe
unused
jasonvarga Jan 10, 2023
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
1 change: 1 addition & 0 deletions config/assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
| Rather than specifying your manipulation params in your templates with
| the glide tag, you may define them here and reference their handles.
| They may also be automatically generated when you upload assets.
| Containers can be configured to warm these caches on upload.
|
*/

Expand Down
1 change: 1 addition & 0 deletions resources/js/components/fieldtypes/FilesFieldtype.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<uploader
ref="uploader"
:url="meta.uploadUrl"
:container="config.container"
@updated="uploadsUpdated"
@upload-complete="uploadComplete"
@error="uploadError"
Expand Down
11 changes: 10 additions & 1 deletion resources/js/components/fieldtypes/SelectFieldtype.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,22 @@
v-bind="attributes"
>
</template>
<template #option="{ label }">
<div v-if="config.label_html" v-html="label"></div>
<template v-else v-text="label"></template>
</template>
<template #selected-option="{ label }">
<div v-if="config.label_html" v-html="label"></div>
<template v-else v-text="label"></template>
</template>
<template #no-options>
<div class="text-sm text-grey-70 text-left py-1 px-2" v-text="__('No options to choose from.')" />
</template>
<template #footer="{ deselect }" v-if="config.multiple">
<div class="vs__selected-options-outside flex flex-wrap">
<span v-for="option in selectedOptions" :key="option.value" class="vs__selected mt-1">
{{ option.label }}
<div v-if="config.label_html" v-html="option.label"></div>
<template v-else>{{ option.label }}</template>
<button v-if="!readOnly" @click="deselect(option)" type="button" :aria-label="__('Deselect option')" class="vs__deselect">
<span>×</span>
</button>
Expand Down
3 changes: 3 additions & 0 deletions resources/lang/en/messages.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
'asset_container_quick_download_instructions' => 'When enabled will add a quick download button in the Asset Manager.',
'asset_container_rename_instructions' => 'When enabled will allow users to rename the files in this container.',
'asset_container_title_instructions' => 'Usually a plural noun, like Images or Documents',
'asset_container_source_preset_instructions' => 'Uploaded images will be permanently processed using this preset.',
'asset_container_warm_intelligent_instructions' => 'Generate appropriate presets on upload.',
'asset_container_warm_presets_instructions' => 'Specify which presets to generate on upload.',
'asset_folders_directory_instructions' => 'We recommend avoiding spaces and special characters to keep URLs clean.',
'asset_replace_confirmation' => 'References to this asset within content will be updated to the asset you select below.',
'asset_reupload_confirmation' => 'Are you sure you want to reupload this asset?',
Expand Down
5 changes: 4 additions & 1 deletion resources/sass/vendors/vue-select.scss
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
@apply min-w-0;
}

.vs__selected-options-outside .hidden-outside {
display: none;
}

.v-select:not(.vs--unsearchable) .vs__selected-options {
@apply appearance-none text-grey-80 text-sm;
@apply flex flex-wrap flex-1 border border-grey-50 border-r-0 rounded-l relative h-full;
Expand Down Expand Up @@ -172,7 +176,6 @@
cursor: text;
}


.v-select:not(.vs--single) .vs__selected {
@include clickable;
@apply flex items-center text-sm rounded py-0 px-1 mr-1;
Expand Down
1 change: 1 addition & 0 deletions src/Actions/ReuploadAsset.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ protected function fieldItems()
'type' => 'files',
'max_files' => 1,
'validate' => ['required'],
'container' => $this->items->first()->container()->handle(),
],
];
}
Expand Down
70 changes: 20 additions & 50 deletions src/Assets/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Statamic\Assets\AssetUploader as Uploader;
use Statamic\Contracts\Assets\Asset as AssetContract;
use Statamic\Contracts\Assets\AssetContainer as AssetContainerContract;
use Statamic\Contracts\Data\Augmentable;
Expand All @@ -33,7 +34,6 @@
use Statamic\Support\Arr;
use Statamic\Support\Str;
use Statamic\Support\Traits\FluentlyGetsAndSets;
use Stringy\Stringy;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Mime\MimeTypes;

Expand Down Expand Up @@ -678,7 +678,7 @@ public function rename($filename, $unique = false)
*/
public function move($folder, $filename = null)
{
$filename = $this->getSafeFilename($filename ?: $this->filename());
$filename = Uploader::getSafeFilename($filename ?: $this->filename());
$oldPath = $this->path();
$oldMetaPath = $this->metaPath();
$newPath = Str::removeLeft(Path::tidy($folder.'/'.$filename.'.'.pathinfo($oldPath, PATHINFO_EXTENSION)), '/');
Expand Down Expand Up @@ -844,29 +844,12 @@ public function title()
*/
public function upload(UploadedFile $file)
{
$ext = $file->getClientOriginalExtension();
$filename = $this->getSafeFilename(pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME));
$path = Uploader::asset($this)->upload($file);

$directory = $this->folder();
$directory = ($directory === '.') ? '/' : $directory;
$path = Path::tidy($directory.'/'.$filename.'.'.$ext);
$path = ltrim($path, '/');

// If the file exists, we'll append a timestamp to prevent overwriting.
if ($this->disk()->exists($path)) {
$basename = $filename.'-'.Carbon::now()->timestamp.'.'.$ext;
$path = Str::removeLeft(Path::assemble($directory, $basename), '/');
}

$stream = fopen($file->getRealPath(), 'r');
$this->disk()->put($path, $stream);
if (is_resource($stream)) {
fclose($stream);
}

$this->path($path)->syncOriginal();

$this->save();
$this
->path($path)
->syncOriginal()
->save();

AssetUploaded::dispatch($this);

Expand Down Expand Up @@ -909,32 +892,6 @@ public function stream()
return $this->disk()->filesystem()->readStream($this->path());
}

/**
* Ensure safe filename string.
*
* @param string $string
* @return string
*/
private function getSafeFilename($string)
{
$replacements = [
' ' => '-',
'#' => '-',
];

$str = Stringy::create(urldecode($string))->toAscii();

foreach ($replacements as $from => $to) {
$str = $str->replace($from, $to);
}

if (config('statamic.assets.lowercase')) {
$str = strtolower($str);
}

return (string) $str;
}

/**
* Get the asset file contents.
*
Expand Down Expand Up @@ -1066,4 +1023,17 @@ public function getQueryableValue(string $field)

return $field->fieldtype()->toQueryableValue($value);
}

public function warmPresets()
{
if (! $this->isImage()) {
return [];
}

$cpPresets = config('statamic.cp.enabled') ? [
'cp_thumbnail_small_'.$this->orientation(),
] : [];

return array_merge($this->container->warmPresets(), $cpPresets);
}
}
54 changes: 54 additions & 0 deletions src/Assets/AssetContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Statamic\Facades\Blink;
use Statamic\Facades\Blueprint;
use Statamic\Facades\File;
use Statamic\Facades\Image;
use Statamic\Facades\Search;
use Statamic\Facades\Stache;
use Statamic\Facades\URL;
Expand All @@ -38,6 +39,8 @@ class AssetContainer implements AssetContainerContract, Augmentable, ArrayAccess
protected $allowMoving;
protected $allowRenaming;
protected $createFolders;
protected $sourcePreset;
protected $warmPresets;
protected $searchIndex;
protected $afterSaveCallbacks = [];
protected $withEvents = true;
Expand Down Expand Up @@ -527,6 +530,55 @@ public function createFolders($createFolders = null)
->args(func_get_args());
}

/**
* The glide source preset to be permanently applied to source image on upload.
*
* @param string|null $preset
* @return string|null|$this
*/
public function sourcePreset($preset = null)
{
return $this
->fluentlyGetOrSet('sourcePreset')
->args(func_get_args());
}

/**
* The specific glide presets to be used when warming glide image cache on upload.
*
* @param array|null $presets
* @return array|null|$this
*/
public function warmPresets($preset = null)
{
return $this
->fluentlyGetOrSet('warmPresets')
->getter(function ($presets) {
if ($presets === false) {
return [];
}

if ($presets !== null) {
return $presets;
}

$presets = Image::userManipulationPresets();

$presets = Arr::except($presets, $this->sourcePreset);

return array_keys($presets);
})
->setter(function ($presets) {
return $presets === [] ? false : $presets;
})
->args(func_get_args());
}

public function warmsPresetsIntelligently()
{
return $this->warmPresets === null;
}

public function fileData()
{
$array = [
Expand All @@ -538,6 +590,8 @@ public function fileData()
'allow_renaming' => $this->allowRenaming,
'allow_moving' => $this->allowMoving,
'create_folders' => $this->createFolders,
'source_preset' => $this->sourcePreset,
'warm_presets' => $this->warmPresets,
];

$array = Arr::removeNullValues(array_merge($array, [
Expand Down
73 changes: 73 additions & 0 deletions src/Assets/AssetUploader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace Statamic\Assets;

use Illuminate\Support\Carbon;
use Statamic\Facades\Path;
use Statamic\Support\Str;
use Stringy\Stringy;
use Symfony\Component\HttpFoundation\File\UploadedFile;

class AssetUploader extends Uploader
{
private $asset;

public function __construct(Asset $asset)
{
$this->asset = $asset;
}

public static function asset(Asset $asset)
{
return new static($asset);
}

protected function uploadPath(UploadedFile $file)
{
$ext = $file->getClientOriginalExtension();
$filename = self::getSafeFilename(pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME));

$directory = $this->asset->folder();
$directory = ($directory === '.') ? '/' : $directory;
$path = Path::tidy($directory.'/'.$filename.'.'.$ext);
$path = ltrim($path, '/');

// If the file exists, we'll append a timestamp to prevent overwriting.
if ($this->disk()->exists($path)) {
$basename = $filename.'-'.Carbon::now()->timestamp.'.'.$ext;
$path = Str::removeLeft(Path::assemble($directory, $basename), '/');
}

return $path;
}

protected function preset()
{
return $this->asset->container()->sourcePreset();
}

protected function disk()
{
return $this->asset->container()->disk();
}

public static function getSafeFilename($string)
{
$replacements = [
' ' => '-',
'#' => '-',
];

$str = Stringy::create(urldecode($string))->toAscii();

foreach ($replacements as $from => $to) {
$str = $str->replace($from, $to);
}

if (config('statamic.assets.lowercase')) {
$str = strtolower($str);
}

return (string) $str;
}
}
42 changes: 42 additions & 0 deletions src/Assets/FileUploader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace Statamic\Assets;

use Illuminate\Support\Facades\Storage;
use Statamic\Facades\AssetContainer;
use Symfony\Component\HttpFoundation\File\UploadedFile;

class FileUploader extends Uploader
{
protected $container;

public function __construct($container)
{
$this->container = $container ? AssetContainer::find($container) : null;
}

public static function container(string $container = null)
{
return new static($container);
}

protected function uploadPath(UploadedFile $file)
{
return now()->timestamp.'/'.$file->getClientOriginalName();
}

protected function uploadPathPrefix()
{
return 'statamic/file-uploads/';
}

protected function preset()
{
return optional($this->container)->sourcePreset();
}

protected function disk()
{
return Storage::disk('local');
}
}
Loading