From b649cf6c8ad6acae24aaa1a55846c037ef630fe7 Mon Sep 17 00:00:00 2001 From: philipp lang Date: Tue, 27 Oct 2020 01:49:52 +0100 Subject: [PATCH] Add file attachments from database --- classes/CompressJob.php | 57 ++--- classes/UploadStorage.php | 74 +++---- formwidgets/Responsiveimage.php | 201 +----------------- .../responsiveimage/assets/src/App.vue | 9 +- .../partials/_responsiveimage.htm | 1 + models/Attachment.php | 68 ++++++ models/SourceFile.php | 49 +++++ updates/create_file_attachments_table.php | 36 ++++ updates/version.yaml | 3 + 9 files changed, 222 insertions(+), 276 deletions(-) create mode 100644 models/Attachment.php create mode 100644 models/SourceFile.php create mode 100644 updates/create_file_attachments_table.php diff --git a/classes/CompressJob.php b/classes/CompressJob.php index 492370f..461028a 100644 --- a/classes/CompressJob.php +++ b/classes/CompressJob.php @@ -5,25 +5,28 @@ namespace Aweos\Resizer\Classes; use Storage; use Aweos\Resizer\Models\Setting; use October\Rain\Database\Attach\Resizer; +use Aweos\Resizer\Models\Attachment; class CompressJob { - public $path; - public $filename; - public $disk; - public $strategy; - public $sizes; + public $attachment_id; public $maxFilesize = 1920; + public $disk = 'uploads'; + public $fullPath; + public $crop; + public $sizes; public function fire($job, $data) { - $this->path = $data['path']; - $this->filename = $data['filename']; - $this->disk = $data['disk']; - $this->strategy = $data['strategy']; + $this->attachment_id = $data['attachment_id']; $this->crop = $data['crop']; $this->sizes = Setting::get('srcx'); + Storage::disk($this->disk)->makeDirectory('cropped'); + + $this->attachment = Attachment::find($this->attachment_id); + $this->fullPath = Storage::disk($this->disk)->path($this->attachment->source->path); + $this->crop(); $this->createVersions(); } @@ -32,49 +35,35 @@ class CompressJob { if ($this->crop === null) { return; } - - $fullPath = Storage::disk($this->disk)->path($this->path.'/'.$this->filename); - - Storage::disk($this->disk)->makeDirectory($this->getStrategy()->croppedPath()); - - $r = Resizer::open($fullPath); - + $r = Resizer::open($this->fullPath); $r->crop(floor($this->crop['x']), floor($this->crop['y']), floor($this->crop['w']), floor($this->crop['h'])); - - $this->path = $this->getStrategy()->croppedPath($this->filename); - $this->filename = $this->getStrategy()->croppedFilename($this->filename, $this->crop); - - $r->save(Storage::disk($this->disk)->path($this->path.'/'.$this->filename)); + $r->save($this->fullPath); } public function createVersions() { - $fullPath = Storage::disk($this->disk)->path($this->path.'/'.$this->filename); - [ $width, $height ] = getimagesize($fullPath); + [ $width, $height ] = getimagesize($this->fullPath); if ($width > $this->maxFilesize) { - $r = Resizer::open($fullPath); + $r = Resizer::open($this->fullPath); $r->resize($this->maxFilesize, 0); - $r->save($fullPath); + $r->save($this->fullPath); } - [ $width, $height ] = getimagesize($fullPath); + [ $width, $height ] = getimagesize($this->fullPath); - Storage::disk($this->disk)->makeDirectory($this->getStrategy()->publicPath($this->filename)); + Storage::disk($this->disk)->makeDirectory($this->attachment->path); foreach ($this->sizes as $w) { - - $filename = $this->getStrategy()->smallFilename($this->filename, $w); - if ($width < $w) { continue; } - - $destination = Storage::disk($this->disk)->path($this->getStrategy()->publicPath($this->filename).'/'.$this->getStrategy()->smallFilename($this->filename, $w)); - $r = Resizer::open($fullPath); + Storage::disk($this->disk)->makeDirectory($this->attachment->path); + + $r = Resizer::open($this->fullPath); $r->resize($w, 0); - $r->save($destination); + $r->save(Storage::disk($this->disk)->path($this->attachment->getVersionPath($w))); } } diff --git a/classes/UploadStorage.php b/classes/UploadStorage.php index 13be1a5..bab99f2 100644 --- a/classes/UploadStorage.php +++ b/classes/UploadStorage.php @@ -4,6 +4,8 @@ namespace Aweos\Resizer\Classes; use Queue; use Storage; +use Aweos\Resizer\Models\SourceFile; +use Aweos\Resizer\Models\Attachment; class UploadStorage { @@ -16,75 +18,57 @@ class UploadStorage { $this->file = $uploadedFile; $this->data = $data; - $fileName = $this->getStrategy()->sourceFileBasename($this->file, $this->data); - $sourcePath = $this->getStrategy()->sourcePath($fileName); - $fileName = $this->transformFileName($sourcePath, $fileName).'.'.$this->file->getClientOriginalExtension(); + $sourceFile = new SourceFile([ + 'basename' => pathinfo($this->file->getClientOriginalName(), PATHINFO_FILENAME), + 'extension' => $this->file->getClientOriginalExtension(), + 'tags' => [] + ]); + $sourceFile->slugAttributes(); - $uploadedFile->storeAs($sourcePath, $fileName, $this->disk); + $uploadedFile->storeAs('', $sourceFile->path, $this->disk); + $sourceFile->save(); + + $attachment = $sourceFile->attachments()->create([ + 'title' => $this->data['title'], + 'description' => $this->data['description'] ?? '' + ]); Queue::push(CompressJob::class, [ - 'path' => $sourcePath, - 'filename' => $fileName, - 'disk' => $this->disk, - 'strategy' => $this->strategy, + 'attachment_id' => $attachment->id, 'crop' => $this->data['crop'] ]); - return $this->getFileData($sourcePath.'/'.$fileName); + return $attachment; } - public function getFileData($filename) { - $realname = preg_replace('/^source/', 'cropped', $filename); - - $realname = $this->storage()->exists($realname) ? $realname : $filename; + public function getFileData($id) { + $attachment = Attachment::find($id); return [ - 'url' => $this->storage()->url($realname), - 'type' => $this->storage()->mimeType($filename), - 'size' => $this->storage()->size($filename), - 'name' => pathinfo($filename, PATHINFO_BASENAME) + 'url' => $this->storage()->url($attachment->source->path), + 'type' => $this->storage()->mimeType($attachment->source->path), + 'size' => $this->storage()->size($attachment->source->path), + 'name' => $attachment->slug, + 'id' => $id ]; } - public function removeFile($filename) { - $sourcePath = $this->getStrategy()->sourcePath($filename).'/'.$filename; - $croppedPath = $this->getStrategy()->croppedPath().'/'.$filename; + public function removeAttachment($id) { + $attachment = Attachment::find($id); - if (!Storage::disk($this->disk)->exists($sourcePath)) { - return; - } - - if (Storage::disk($this->disk)->exists($croppedPath)) { - Storage::disk($this->disk)->delete($croppedPath); - } - - collect(Storage::disk($this->disk)->files($this->getStrategy()->publicPath($filename)))->filter(function($file) use ($filename) { - return preg_match($this->getStrategy()->versionRegex($filename), pathinfo($file, PATHINFO_BASENAME)); + collect(Storage::disk($this->disk)->files($attachment->path))->filter(function($file) use ($attachment) { + return preg_match($attachment->versionRegex, $file); })->each(function($file) { Storage::disk($this->disk)->delete($file); }); - Storage::disk($this->disk)->delete($sourcePath); + $attachment->delete(); } private function storage() { return Storage::disk($this->disk); } - private function transformFileName($path, $basename) { - if (count(glob($this->storage()->path($path).'/'.$basename.'*')) == 0) { - return $basename; - } - - $i = 1; - - while(count(glob($this->storage()->path($path).'/'.$basename.'-'.$i.'*')) != 0) { - $i++; - } - - return $basename.'-'.$i; - } - private function originalExtension() { return $this->file->getClientOriginalExtension(); } diff --git a/formwidgets/Responsiveimage.php b/formwidgets/Responsiveimage.php index 8e1bc39..2461fd4 100644 --- a/formwidgets/Responsiveimage.php +++ b/formwidgets/Responsiveimage.php @@ -35,51 +35,8 @@ class Responsiveimage extends FormWidgetBase public $aspectRatio; - /** - * @var mixed Collection of acceptable file types. - */ - public $fileTypes = false; - - /** - * @var mixed Collection of acceptable mime types. - */ - public $mimeTypes = false; - - /** - * @var mixed Max file size. - */ - public $maxFilesize; - - /** - * @var array Options used for generating thumbnails. - */ - public $thumbOptions = [ - 'mode' => 'crop', - 'extension' => 'auto' - ]; - - /** - * @var boolean Allow the user to set a caption. - */ - public $useCaption = true; - - /** - * @var boolean Automatically attaches the uploaded file on upload if the parent record exists instead of using deferred binding to attach on save of the parent record. Defaults to false. - */ - public $attachOnUpload = false; - - // - // Object properties - // - - /** - * @inheritDoc - */ protected $defaultAlias = 'responsiveimage'; - /** - * @var Backend\Widgets\Form The embedded form for modifying the properties of the selected file - */ protected $configFormWidget; /** @@ -87,8 +44,6 @@ class Responsiveimage extends FormWidgetBase */ public function init() { - $this->maxFilesize = $this->getUploadMaxFilesize(); - $this->fillFromConfig([ 'minWidth', 'aspectRatio' @@ -121,115 +76,13 @@ class Responsiveimage extends FormWidgetBase $this->vars['aspectRatio'] = $this->aspectRatio ?: 'null'; $this->vars['minWidth'] = $this->minWidth ?: '0'; $this->vars['name'] = $this->formField->getName(); - $this->vars['value'] = str_replace('"', "'", json_encode($this->getLoadValue())); + $this->vars['value'] = $this->getLoadValue() ?: 'null'; + $this->vars['meta'] = $this->getLoadValue() + ? str_replace('"', "'", json_encode(app(UploadStorage::class)->getFileData($this->getLoadValue()))) + : 'null'; $this->vars['model'] = $this->model; } - /** - * Get the file record for this request, returns false if none available - * - * @return System\Models\File|false - */ - protected function getFileRecord() - { - $record = false; - - return $record; - } - - /** - * Returns the escaped and translated prompt text to display according to the type. - * @return string - */ - protected function getPromptText() - { - if ($this->prompt === null) { - $isMulti = ends_with($this->getDisplayMode(), 'multi'); - $this->prompt = $isMulti - ? 'backend::lang.fileupload.upload_file' - : 'backend::lang.fileupload.default_prompt'; - } - - return str_replace('%s', '', e(trans($this->prompt))); - } - - /** - * Returns the CSS dimensions for the uploaded image, - * uses auto where no dimension is provided. - * @param string $mode - * @return string - */ - protected function getCssDimensions($mode = null) - { - if (!$this->imageWidth && !$this->imageHeight) { - return ''; - } - - $cssDimensions = ''; - - if ($mode == 'block') { - $cssDimensions .= $this->imageWidth - ? 'width: '.$this->imageWidth.'px;' - : 'width: '.$this->imageHeight.'px;'; - - $cssDimensions .= ($this->imageHeight) - ? 'max-height: '.$this->imageHeight.'px;' - : 'height: auto;'; - } - else { - $cssDimensions .= $this->imageWidth - ? 'width: '.$this->imageWidth.'px;' - : 'width: auto;'; - - $cssDimensions .= ($this->imageHeight) - ? 'max-height: '.$this->imageHeight.'px;' - : 'height: auto;'; - } - - return $cssDimensions; - } - - /** - * Removes a file attachment. - */ - public function onRemoveAttachment() - { - // - } - - /** - * Loads the configuration form for an attachment, allowing title and description to be set. - */ - public function onLoadAttachmentConfig() - { - // - } - - /** - * Commit the changes of the attachment configuration form. - */ - public function onSaveAttachmentConfig() - { - try { - $formWidget = $this->getConfigFormWidget(); - if ($file = $formWidget->model) { - $modelsToSave = $this->prepareModelsToSave($file, $formWidget->getSaveData()); - Db::transaction(function () use ($modelsToSave, $formWidget) { - foreach ($modelsToSave as $modelToSave) { - $modelToSave->save(null, $formWidget->getSessionKey()); - } - }); - - return ['displayName' => $file->title ?: $file->file_name]; - } - - throw new ApplicationException('Unable to find file, it may no longer exist'); - } - catch (Exception $ex) { - return json_encode(['error' => $ex->getMessage()]); - } - } - /** * @inheritDoc */ @@ -244,7 +97,7 @@ class Responsiveimage extends FormWidgetBase */ public function getSaveValue($value) { - return $value ? json_decode($value) : null; + return $value; } /** @@ -263,51 +116,13 @@ class Responsiveimage extends FormWidgetBase throw new ApplicationException('File is not valid'); } - $filename = app(UploadStorage::class)->storeFileFromUpload($uploadedFile, $data); + $attachment = app(UploadStorage::class)->storeFileFromUpload($uploadedFile, $data); - return Response::json([ - 'file' => $filename, - 'data' => array_only($data, ['title', 'description']) - ], 200); + return Response::make($attachment->id); } public function onDelete() { - app(UploadStorage::class)->removeFile(Input::get('fileName')); + app(UploadStorage::class)->removeAttachment(Input::get('file_id')); } - /** - * Adds the bespoke attributes used internally by this widget. - * - thumbUrl - * - pathUrl - * @return System\Models\File - */ - protected function decorateFileAttributes($file) - { - $path = $thumb = $file->getPath(); - - if ($this->imageWidth || $this->imageHeight) { - $thumb = $file->getThumb($this->imageWidth, $this->imageHeight, $this->thumbOptions); - } - - $file->pathUrl = $path; - $file->thumbUrl = $thumb; - - return $file; - } - - /** - * Return max upload filesize in Mb - * @return integer - */ - protected function getUploadMaxFilesize() - { - $size = ini_get('upload_max_filesize'); - if (preg_match('/^([\d\.]+)([KMG])$/i', $size, $match)) { - $pos = array_search($match[2], ['K', 'M', 'G']); - if ($pos !== false) { - $size = $match[1] * pow(1024, $pos + 1); - } - } - return floor($size / 1024 / 1024); - } } diff --git a/formwidgets/responsiveimage/assets/src/App.vue b/formwidgets/responsiveimage/assets/src/App.vue index 588fadb..f7c57b8 100644 --- a/formwidgets/responsiveimage/assets/src/App.vue +++ b/formwidgets/responsiveimage/assets/src/App.vue @@ -94,6 +94,7 @@ export default { components: { vueDropzone }, props: { + meta: {}, cropOptions: {}, name: { required: true @@ -109,7 +110,7 @@ export default { return Math.random().toString(36).substring(7); }, asString() { - return JSON.stringify(this.content); + return this.content; }, formData() { return $(this.$el).closest('form').serializeArray(); @@ -180,14 +181,14 @@ export default { onMount() { if (!this.value) { return; } this.addVisible = false; - var file = { size: this.value.file.size, name: this.value.file.name, type: this.value.file.type }; - this.$refs.dropzone.manuallyAddFile(file, this.value.file.url); + var file = { size: this.meta.size, name: this.meta.name, type: this.meta.type }; + this.$refs.dropzone.manuallyAddFile(file, this.meta.url); }, onRemove(file, error, xhr) { var _cls = this; $(this.$el).request(this.handlers.onDelete, { - data: { fileName: file.name }, + data: { file_id: this.content }, success: function(data) { _cls.addVisible = true; _cls.content = ''; diff --git a/formwidgets/responsiveimage/partials/_responsiveimage.htm b/formwidgets/responsiveimage/partials/_responsiveimage.htm index fba7509..15cd741 100644 --- a/formwidgets/responsiveimage/partials/_responsiveimage.htm +++ b/formwidgets/responsiveimage/partials/_responsiveimage.htm @@ -14,6 +14,7 @@ minWidth: , }" :value="" + :meta="" > diff --git a/models/Attachment.php b/models/Attachment.php new file mode 100644 index 0000000..d4c0cac --- /dev/null +++ b/models/Attachment.php @@ -0,0 +1,68 @@ + 'title']; + + /** + * @var string The database table used by the model. + */ + public $table = 'responsive_attachments'; + + /** + * @var array Guarded fields + */ + protected $guarded = ['*']; + + /** + * @var array Fillable fields + */ + protected $fillable = ['id', 'file_id', 'title', 'slug', 'description']; + + /** + * @var array Relations + */ + public $hasOne = []; + public $hasMany = []; + public $belongsTo = [ + 'source' => [SourceFile::class, 'key' => 'file_id'] + ]; + public $belongsToMany = []; + public $morphTo = []; + public $morphOne = []; + public $morphMany = []; + public $attachOne = []; + public $attachMany = []; + + public function getFilenameAttribute() { + return $this->slug.'.'.$this->source->extension; + } + + public function getPathAttribute() { + return strtolower($this->slug[0]); + } + + public function getVersionPath($w) { + return $this->path.'/'.$this->slug.'-'.$w.'.'.$this->source->extension; + } + + public function getVersionRegexAttribute() { + $path = preg_quote($this->path, '/'); + $ext = preg_quote($this->source->extension, '/'); + $name = preg_quote($this->slug, '/'); + + return "/^{$path}\/{$name}(-[0-9]+)?\.{$ext}$/"; + } + + public function getUrlAttribute() { + return Storage::disk('uploads')->url($this->source->path); + } +} diff --git a/models/SourceFile.php b/models/SourceFile.php new file mode 100644 index 0000000..482ddad --- /dev/null +++ b/models/SourceFile.php @@ -0,0 +1,49 @@ + 'basename']; + + /** + * @var string The database table used by the model. + */ + public $table = 'responsive_source_files'; + + /** + * @var array Guarded fields + */ + protected $guarded = ['*']; + + /** + * @var array Fillable fields + */ + protected $fillable = ['id', 'basename', 'extension', 'tags', 'slug']; + + public $jsonable = ['tags']; + + /** + * @var array Relations + */ + public $hasOne = []; + public $hasMany = [ + 'attachments' => [Attachment::class, 'key' => 'file_id'] + ]; + public $belongsTo = []; + public $belongsToMany = []; + public $morphTo = []; + public $morphOne = []; + public $morphMany = []; + public $attachOne = []; + public $attachMany = []; + + public function getPathAttribute() { + return 'source/'.$this->slug.'.'.$this->extension; + } +} diff --git a/updates/create_file_attachments_table.php b/updates/create_file_attachments_table.php new file mode 100644 index 0000000..93eca53 --- /dev/null +++ b/updates/create_file_attachments_table.php @@ -0,0 +1,36 @@ +engine = 'InnoDB'; + $table->increments('id'); + $table->string('basename'); + $table->string('slug'); + $table->string('extension'); + $table->json('tags'); + $table->timestamps(); + }); + + Schema::create('responsive_attachments', function(Blueprint $table) { + $table->engine = 'InnoDB'; + $table->increments('id'); + $table->integer('file_id')->unsigned(); + $table->string('title'); + $table->string('slug'); + $table->string('description')->nullable(); + $table->timestamps(); + }); + } + + public function down() + { + Schema::dropIfExists('aweos_resizer_settings'); + } +} diff --git a/updates/version.yaml b/updates/version.yaml index 0deacc4..6b9ae20 100644 --- a/updates/version.yaml +++ b/updates/version.yaml @@ -1 +1,4 @@ 1.0.1: First version of resizer +1.0.2: + - Create attachments + - create_file_attachments_table.php