diff --git a/app/Http/Controllers/UploadController.php b/app/Http/Controllers/UploadController.php index cc13409..81799ec 100644 --- a/app/Http/Controllers/UploadController.php +++ b/app/Http/Controllers/UploadController.php @@ -25,6 +25,7 @@ use Illuminate\Support\Str; use Illuminate\Validation\ValidationException; use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException; use Pion\Laravel\ChunkUpload\Receiver\FileReceiver; +use Symfony\Component\Mime\MimeTypes; class UploadController extends Controller { @@ -80,17 +81,22 @@ class UploadController extends Controller $uplodedFile = $save->getFile(); - /** @throws \Illuminate\Validation\ValidationException */ - $this->validateUploadedFile($uplodedFile); + try { + /** @throws \Illuminate\Validation\ValidationException */ + $this->validateUploadedFile($uplodedFile); - /** @var \App\Models\Media */ - $media = Media::whereId($request->media_id) - ->whereHas('dossier', function ($q) use ($dossier) { - $q->whereId($dossier->id); - })->firstOrNew(); + /** @var \App\Models\Media */ + $media = Media::whereId($request->media_id) + ->whereHas('dossier', function ($q) use ($dossier) { + $q->whereId($dossier->id); + })->firstOrNew(); - // Save the file and return any response you need. - return $this->createOrUpdateMedia($user, $uplodedFile, $media, $dossier); + // Save the file and return any response you need. + return $this->createOrUpdateMedia($user, $uplodedFile, $media, $dossier); + } catch (ValidationException $e) { + // 400 instead of 422 is for resumablejs to handle properly. + return \response()->json($e->errors(), 400); + } } // We are in chunk mode, lets send the current progress. @@ -121,6 +127,7 @@ class UploadController extends Controller if ($receiver->isUploaded() === false) { throw new UploadMissingFileException(); } + // Receive the file. $save = $receiver->receive(); @@ -131,19 +138,24 @@ class UploadController extends Controller $uplodedFile = $save->getFile(); - /** @throws \Illuminate\Validation\ValidationException */ - $this->validateUploadedFile($uplodedFile); + try { + /** @throws \Illuminate\Validation\ValidationException */ + $this->validateUploadedFile($uplodedFile); - /** @var \App\Models\Media */ - $media = Media::whereId($request->media_id) - ->whereHas('sujet', function ($q) use ($sujet) { - $q->whereId($sujet->id); - })->whereHas('dossier', function ($q) use ($dossier) { - $q->whereId($dossier->id); - })->firstOrNew(); + /** @var \App\Models\Media */ + $media = Media::whereId($request->media_id) + ->whereHas('sujet', function ($q) use ($sujet) { + $q->whereId($sujet->id); + })->whereHas('dossier', function ($q) use ($dossier) { + $q->whereId($dossier->id); + })->firstOrNew(); - // Save the file and return any response you need. - return $this->createOrUpdateMedia($user, $uplodedFile, $media, $dossier, $sujet); + // Save the file and return any response you need. + return $this->createOrUpdateMedia($user, $uplodedFile, $media, $dossier, $sujet); + } catch (ValidationException $e) { + // 400 instead of 422 is for resumablejs to handle properly. + return \response()->json($e->errors(), 400); + } } // We are in chunk mode, lets send the current progress. @@ -170,22 +182,40 @@ class UploadController extends Controller ], [ 'files.mimes' => trans('Ce type de fichier n\'est pas accepté.') ])->validate(); - $this->validateExtension($file->getClientOriginalExtension()); + $this->validateExtension($file); } /** * Validate file extension matches allowed extensions enum * - * @param string $extension + * @param \Illuminate\Http\UploadedFile $file * @return void */ - private function validateExtension(string $extension) + private function validateExtension(UploadedFile $file) { - if (!\is_null(MediaExtension::tryFrom($extension))) { - ValidationException::withMessages([ + if (\is_null(MediaExtension::tryFrom($file->getClientOriginalExtension()))) { + throw ValidationException::withMessages([ 'file' => trans('Ce type de fichier n\'est pas accepté.') ]); } + + /** @throws \ValueError If value is not defined in enum */ + $mediaExtension = MediaExtension::from($file->getClientOriginalExtension()); + + if ($file->guessExtension()) { + $mt = new MimeTypes(); + $guessedExts = $mt->getExtensions($mt->guessMimeType($file->path())); + + if (!\in_array($mediaExtension->value, $guessedExts)) { + $guessedExts = \implode(',', $guessedExts); + throw ValidationException::withMessages([ + 'file' => trans('File guessed extensions :guessedExts does not match file name extension :extension', [ + 'guessedExt' => $guessedExts, + 'extension' => $mediaExtension, + ]) + ]); + } + } } /** @@ -232,7 +262,6 @@ class UploadController extends Controller /** @var string */ $sanitizedFileExtension = \pathinfo($sanitizedFileName, \PATHINFO_EXTENSION); - // * Validate Extension /** @throws \ValueError If value is not defined in enum */ $mediaExtension = MediaExtension::from($sanitizedFileExtension); @@ -309,9 +338,7 @@ class UploadController extends Controller $media->saveOrFail(); // * Get props inserted by DB. - $media->refresh(); - - return response()->json($media); + return response()->json($media->refresh()); }); } } diff --git a/config/chunk-upload.php b/config/chunk-upload.php new file mode 100644 index 0000000..3e42b9e --- /dev/null +++ b/config/chunk-upload.php @@ -0,0 +1,41 @@ + [ + /* + * Returns the folder name of the chunks. The location is in storage/app/{folder_name} + */ + 'chunks' => 'chunks', + 'disk' => 'chunk_upload', + ], + 'clear' => [ + /* + * How old chunks we should delete + */ + 'timestamp' => '-3 HOURS', + 'schedule' => [ + 'enabled' => true, + 'cron' => '25 * * * *', // run every hour on the 25th minute + ], + ], + 'chunk' => [ + // setup for the chunk naming setup to ensure same name upload at same time + 'name' => [ + 'use' => [ + 'session' => true, // should the chunk name use the session id? The uploader must send cookie!, + 'browser' => false, // instead of session we can use the ip and browser? + ], + ], + ], + 'handlers' => [ + // A list of handlers/providers that will be appended to existing list of handlers + 'custom' => [], + // Overrides the list of handlers - use only what you really want + 'override' => [ + // \Pion\Laravel\ChunkUpload\Handler\DropZoneUploadHandler::class + ], + ], +]; diff --git a/config/filesystems.php b/config/filesystems.php index 2d05174..41e0849 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -63,12 +63,19 @@ return [ 'root' => storage_path('arcadia_link/send'), 'throw' => true, ], + 'arcadia_link_r' => [ 'driver' => 'local', 'root' => storage_path('arcadia_link/receive'), 'throw' => true, ], + 'chunk_upload' => [ + 'driver' => 'local', + 'root' => storage_path('upload'), + 'throw' => true, + ], + 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), diff --git a/resources/js/components/DepotComponent.vue b/resources/js/components/DepotComponent.vue index 004aa64..426e26e 100644 --- a/resources/js/components/DepotComponent.vue +++ b/resources/js/components/DepotComponent.vue @@ -316,10 +316,22 @@ export default { this.setupResumable(); }); }, - uploadFileError(file, message) { - this.uploadErrors.push( - `Une erreur durant le transfert du fichier ${file.fileName}` - ); + uploadFileError(file, httpMessage) { + let message = `Une erreur durant le transfert du fichier ${file.fileName}`; + + const error = JSON.parse(httpMessage); + if (error.file) { + const errors = error.file; + if (errors instanceof String) { + message = errors; + } + if (errors instanceof Object) { + message = Object.values(errors).reduce(function (previous, current) { + return String(previous) + String(current); + }); + } + } + this.uploadErrors.push(message); }, removeFile(fileToRemove) { this.r.removeFile(fileToRemove); diff --git a/resources/js/components/StandAlonePanierComponent.vue b/resources/js/components/StandAlonePanierComponent.vue index f692d7e..d4a8e95 100644 --- a/resources/js/components/StandAlonePanierComponent.vue +++ b/resources/js/components/StandAlonePanierComponent.vue @@ -61,8 +61,12 @@ class="media" :style="`background-image:url('${media.url}')`" > -
- +{{ media.libelle }}
+ {{ file.libelle }} + {{ file.extension }} + {{ file.message }} +
{{ file.libelle }}.{{ file.extension }}
-
{{ media.libelle }}
diff --git a/resources/scss/partials/media.scss b/resources/scss/partials/media.scss index a032e30..cdb8264 100644 --- a/resources/scss/partials/media.scss +++ b/resources/scss/partials/media.scss @@ -119,7 +119,7 @@ & > #actionsBar { float: left; - button { + & > div > button { @include resetButton; font-size: 13px; padding: 15px 13px; @@ -365,7 +365,7 @@ background-position: center; position: relative; - &::before { + & svg { transform: translate(-50%, -50%); display: block; position: absolute; @@ -377,26 +377,6 @@ font-size: 59px; z-index: 1; } - &.video { - &::before { - content: "\f167"; - font-family: "Font Awesome 6 Brands"; - } - } - &.son { - &::before { - content: "\f58f"; - font-family: "Font Awesome 6 Free"; - font-weight: 900; - } - } - &.doc { - &::before { - content: "\f15c"; - font-family: "Font Awesome 6 Free"; - font-weight: 900; - } - } } .libelle { padding: 4px; @@ -758,7 +738,7 @@ background-position: center; position: relative; - &::before { + & svg { transform: translate(-50%, -50%); display: block; position: absolute; @@ -770,26 +750,6 @@ font-size: 59px; z-index: 1; } - &.video { - &::before { - content: "\f167"; - font-family: "Font Awesome 6 Brands"; - } - } - &.son { - &::before { - content: "\f58f"; - font-family: "Font Awesome 6 Free"; - font-weight: 900; - } - } - &.doc { - &::before { - content: "\f15c"; - font-family: "Font Awesome 6 Free"; - font-weight: 900; - } - } } &:hover { diff --git a/resources/scss/partials/panier.scss b/resources/scss/partials/panier.scss index f17c196..b80c9be 100644 --- a/resources/scss/partials/panier.scss +++ b/resources/scss/partials/panier.scss @@ -185,26 +185,6 @@ font-size: 59px; z-index: 1; } - &.video { - &::before { - content: "\f167"; - font-family: "Font Awesome 6 Brands"; - } - } - &.son { - &::before { - content: "\f58f"; - font-family: "Font Awesome 6 Free"; - font-weight: 900; - } - } - &.doc { - &::before { - content: "\f15c"; - font-family: "Font Awesome 6 Free"; - font-weight: 900; - } - } } .libelle { padding: 4px;