diff --git a/app/Http/Controllers/Bo/ProductController.php b/app/Http/Controllers/Bo/ProductController.php index 4885073..203adaf 100644 --- a/app/Http/Controllers/Bo/ProductController.php +++ b/app/Http/Controllers/Bo/ProductController.php @@ -313,8 +313,8 @@ class ProductController extends Controller /** @var \Illuminate\Filesystem\FilesystemAdapter */ $storagePublicDisk = Storage::disk('public'); - $pictureBaseName = $picture['filename']; - $pictureExtension = \pathinfo($picture['filename'], \PATHINFO_EXTENSION); + $pictureBaseName = $picture['file_name']; + $pictureExtension = \pathinfo($picture['file_name'], \PATHINFO_EXTENSION); /** * We create one file in tmp to be resized diff --git a/app/Http/Controllers/Bo/XmlIncoController.php b/app/Http/Controllers/Bo/XmlIncoController.php index b168f54..680a349 100644 --- a/app/Http/Controllers/Bo/XmlIncoController.php +++ b/app/Http/Controllers/Bo/XmlIncoController.php @@ -41,7 +41,7 @@ class XmlIncoController extends Controller $stream = $disk->readStream($picturePath); \fpassthru($stream); \fclose($stream); - }, $inco_picture, [ + }, \pathinfo($inco_picture, \PATHINFO_BASENAME), [ 'Content-Type' => $disk->mimeType($picturePath), 'Content-Length' => $disk->size($picturePath), 'Content-Disposition' => 'inline', diff --git a/app/Jobs/XmlIncoReader.php b/app/Jobs/XmlIncoReader.php index 77b2172..1b8a4da 100644 --- a/app/Jobs/XmlIncoReader.php +++ b/app/Jobs/XmlIncoReader.php @@ -3,8 +3,10 @@ namespace App\Jobs; use App\Exceptions\AbortXmlImportException; +use App\Lib\Helpers\FileStorageHelper; use App\Models\XmlInco; -use SimpleXMLElement; +use Illuminate\Support\Facades\File; +use Illuminate\Support\Facades\Log; class XmlIncoReader extends XmlJobs { @@ -419,7 +421,7 @@ class XmlIncoReader extends XmlJobs public function handle() { - $this->parseData('INCO', XmlInco::class, function (SimpleXMLElement $xmlData, array &$line) { + $this->parseData('INCO', XmlInco::class, function (\SimpleXMLElement $xmlData, array &$line) { // Merge columns. foreach ($this->mergeColumns as $key => $values) { @@ -442,70 +444,83 @@ class XmlIncoReader extends XmlJobs (strtolower($line['prod_saison']) == 'non' ? false : true) : false; $arImgPaths = $this->parseImage($xmlData); - $line['picturse'] = $arImgPaths; + $line['pictures'] = $arImgPaths; }); } /** * Extract Image form XML file * - * @param SimpleXMLElement $xmlData + * @param \SimpleXMLElement $xmlData * @return array * @throws AbortXmlImportException When media tag does not contain an image. */ - private function parseImage(SimpleXMLElement $xmlData) + private function parseImage(\SimpleXMLElement $xmlData) { - $list_media = $xmlData->INCO->MEDIA_VISUEL_EAI->MEDIA_VISUEL_EAI; - $arImgPaths = array(); - $index = 1; + /** @var \Illuminate\Support\Collection<\SimpleXMLElement> */ + $list_media = collect($xmlData->INCO->MEDIA_VISUEL_EAI->MEDIA_VISUEL_EAI); + $arImgPaths = \collect(); - foreach ($list_media as $media) { + $i = -1; + + $list_media->each(function ($media) use ($xmlData, $arImgPaths, &$i) { + $i++; + + /** @var \SimpleXMLElement|null */ $image = $media->img; $imageData = trim((string)$image); - if (strlen($imageData)) { - $imageData = base64_decode($imageData); - $imgName = strval($xmlData->INCO->INCO_COMPO->TBL_INCO_COMPO->ID_INCO); - if (empty($imgName)) { - $imgName = strval($xmlData->INCO->NO_ART); - } - // * Store only picture name. - $imgNamePath = $imgName . "_" . $index; - $disk = $this->getDisk(); + if (!strlen($imageData)) { + return; + } - $info = getimagesizefromstring($imageData); - if ($info !== false) { - $mime = $info['mime']; - switch ($mime) { - case 'image/jpeg': - $imgExtension = 'jpg'; - break; - case 'image/png': - $imgExtension = 'png'; - break; - case 'image/gif': - $imgExtension = 'gif'; - break; - default: - throw new AbortXmlImportException(sprintf('Unknown image type %s', $imgName)); - } + $imageData = \strval(base64_decode($imageData)); + $imgName = strval($xmlData->INCO->INCO_COMPO->TBL_INCO_COMPO->ID_INCO ?? $xmlData->INCO->NO_ART); + $imgNamePath = "{$imgName}_" . \uniqid(); - $imgNamePath = sprintf('%s.%s', $imgNamePath, $imgExtension); + $info = getimagesizefromstring($imageData); + if ($info === false) { + Log::debug("Skipping file {$imgNamePath} on index {$i} as could not determine picture info."); + return; + } - if (!$disk->put($imgNamePath, $imageData)) { - throw new AbortXmlImportException(sprintf('The image %s could not be saved', $imgNamePath)); - } + $mime = $info['mime']; + switch ($mime) { + case 'image/jpeg': + $imgExtension = 'jpg'; + break; + case 'image/png': + $imgExtension = 'png'; + break; + case 'image/gif': + $imgExtension = 'gif'; + break; + default: + throw new AbortXmlImportException(sprintf('Unknown image type %s', $imgName)); + } - $arImgPaths[$index] = sprintf('%s/%s', $this->disk, $imgNamePath); - $index++; - }//end if - }//end if - }//end foreach + $imgNamePath = FileStorageHelper::sanitizeFileName("{$imgNamePath}-{$i}.{$imgExtension}"); - return $arImgPaths; + if (!($file = tempnam(sys_get_temp_dir(), $imgNamePath))) { + throw new \RuntimeException('Could not initiate a php temp file'); + } + + if (!File::put($file, $imageData)) { + throw new \RuntimeException('Could not write to the php temp file'); + } + + $arImgPaths->push(FileStorageHelper::storeFile( + new XmlInco(), + new \SplFileInfo($file), + true, + $imgNamePath, + true + )); + }); + + return $arImgPaths->all(); } - /** * Recursive extract to array * introduce sort of Hook to transform @@ -530,7 +545,7 @@ class XmlIncoReader extends XmlJobs protected function extract($sxe = null): array { - if (!$sxe instanceof SimpleXMLElement) { + if (!$sxe instanceof \SimpleXMLElement) { return array(); } $extract = array(); diff --git a/app/Jobs/XmlJobs.php b/app/Jobs/XmlJobs.php index b6e7bc9..33f155b 100644 --- a/app/Jobs/XmlJobs.php +++ b/app/Jobs/XmlJobs.php @@ -330,7 +330,7 @@ abstract class XmlJobs extends XArcadiaFlux */ protected function sendErrorParseEmailToServiceWeb(Throwable $e) { - Mail::send('emails.exception', [ + Mail::send('back::emails.exception', [ 'exception' => $e, 'appName' => config('app.name'), ], function ($m) { @@ -351,7 +351,7 @@ abstract class XmlJobs extends XArcadiaFlux protected function sendInvalidXmlEmail(string $filename, Throwable $e) { $message = $e->getPrevious()->getMessage(); - Mail::send('emails.invalidxml', [ + Mail::send('back::emails.invalidxml', [ 'strError' => sprintf( 'File %s could not be parsed => %s on line %d', $filename, diff --git a/app/Lib/Helpers/FileStorageHelper.php b/app/Lib/Helpers/FileStorageHelper.php index cc84f05..07ea0f5 100644 --- a/app/Lib/Helpers/FileStorageHelper.php +++ b/app/Lib/Helpers/FileStorageHelper.php @@ -82,13 +82,22 @@ class FileStorageHelper * @param \Illuminate\Http\UploadedFile|\SplFileInfo|string $file The file, will just return itself is string. * @param boolean $slugify Force filename slugification. * @param string|null $filename This string may be used to store the file. + * @param boolean $private To store the file in a private space. * @return string * @throws \RuntimeException If file copy fails. */ - public static function storeFile(Model $model, $file, bool $slugify = false, string $filename = null): string - { + public static function storeFile( + Model $model, + $file, + bool $slugify = false, + string $filename = null, + bool $private = false + ): string { $tableName = $model->getTable(); if (\is_object($file) and $file instanceof \Illuminate\Http\UploadedFile) { + if ($private) { + throw new \RuntimeException('Private storage for uploaded files is unhandled'); + } /** @var \Illuminate\Http\UploadedFile $file */ $filename = $filename ?: $file->getClientOriginalName(); $filename = $slugify ? self::slugifyFileName($filename) : $filename; @@ -104,7 +113,7 @@ class FileStorageHelper /** @var \SplFileInfo $file */ $filename = $filename ?: $file->getBaseName(); $filename = $slugify ? self::slugifyFileName($filename) : $filename; - $newPath = self::prepareMoveFile($tableName, $filename); + $newPath = self::prepareMoveFile($tableName, $filename, $private); if (!File::copy($file->getRealPath(), $newPath)) { throw new \RuntimeException(sprintf( 'Failed to copy file %s to %s', @@ -112,7 +121,9 @@ class FileStorageHelper $newPath )); } - return \ltrim(str_replace(\storage_path('app/public'), '/storage', $newPath), '/'); + return $private ? + \ltrim(str_replace(\storage_path('app/private'), '/storage/app/private', $newPath), '/') : + \ltrim(str_replace(\storage_path('app/public'), '/storage', $newPath), '/'); } return strval($file); } @@ -268,11 +279,12 @@ class FileStorageHelper * Prepare storage path and return * the file full path * - * @param string $tableName Model table name. - * @param string $filename The basename for the file. + * @param string $tableName Model table name. + * @param string $filename The basename for the file. + * @param boolean $private May the file be stored in a private dir. * @return string */ - private static function prepareMoveFile(string $tableName, string $filename): string + private static function prepareMoveFile(string $tableName, string $filename, bool $private = false): string { // * Security filter + beautifier $filename = self::sanitizeFileName($filename); @@ -283,7 +295,12 @@ class FileStorageHelper $filename = Str::slug(\pathinfo($filename, \PATHINFO_FILENAME)) . (\strlen($ext) ? ".{$ext}" : ''); - $folderPath = self::getStoragePath($tableName, $filename); + $folderPath = self::getStoragePath( + $private ? 'app/private/modelfiles' : 'app/public/modelfiles', + $tableName, + $filename, + $private + ); $filename = self::getFileUniqueName($folderPath, $filename); return "{$folderPath}/{$filename}"; } @@ -293,15 +310,17 @@ class FileStorageHelper * storage/modelfiles/posts/10/04/19/5f * year/month/day/md5 first 2 letters * + * @param string $storage_dir * @param string $table * @param string $filename * @return string */ - private static function getStoragePath(string $table, string $filename): string + private static function getStoragePath(string $storage_dir, string $table, string $filename): string { $filenameHash = \md5($filename); $pathFolder = \storage_path(sprintf( - 'app/public/modelfiles/%s/%s/%s/%s/%s', + '%s/%s/%s/%s/%s/%s', + $storage_dir, $table, gmdate('y'), gmdate('m'), diff --git a/app/Models/XmlInco.php b/app/Models/XmlInco.php index 6efe0ce..c1b3cfd 100644 --- a/app/Models/XmlInco.php +++ b/app/Models/XmlInco.php @@ -53,29 +53,22 @@ class XmlInco extends Model * * @var string */ - protected static $disk = 'inco'; - - /** - * Storage path - * - * @var string - */ - protected static $imagesPath = 'images'; + protected static $disk = 'private'; // * METHODS /** * Get xml inco picture path * - * @param string $pictureName + * @param string $inco_picture * @return null|string */ - public static function getXmlIncoPicturePath(string $pictureName): ?string + public static function getXmlIncoPicturePath(string $inco_picture): ?string { - $disk = static::getDisk(); - $picturePath = static::$imagesPath . "/{$pictureName}"; + /** @var \Illuminate\Filesystem\FilesystemAdapter */ + $disk = static::getDisk(); - return $disk->exists($picturePath) ? $picturePath : null; + return $disk->exists($inco_picture) ? $inco_picture : null; } /** @@ -85,16 +78,15 @@ class XmlInco extends Model */ public function getMediaListAttribute(): Collection { + /** @var \Illuminate\Filesystem\FilesystemAdapter */ $disk = static::getDisk(); /** @var \Illuminate\Support\Collection */ - $collFiles = new Collection(); - foreach ($this->pictures as $pictureName) { - $collFiles->add([ - 'filename' => $pictureName, - 'imgdata' => base64_encode($disk->get( - static::$imagesPath . "/{$pictureName}" - )) + $collFiles = \collect(); + foreach ($this->pictures as $picture_path) { + $collFiles->add(['file_name' => \pathinfo($picture_path, \PATHINFO_BASENAME), + 'file_path' => $picture_path, + 'imgdata' => base64_encode($disk->get($picture_path)) ]); } return $collFiles; diff --git a/config/filesystems.php b/config/filesystems.php index 6257567..3d19388 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -65,6 +65,12 @@ return [ 'endpoint' => env('AWS_ENDPOINT'), ], + 'private' => [ + 'driver' => 'local', + 'root' => storage_path('app/private'), + 'url' => env('APP_URL') . '/storage' + ], + 'inco' => [ 'driver' => 'local', 'root' => storage_path('inco') diff --git a/database/migrations/2023_02_24_103044_move_info_files.php b/database/migrations/2023_02_24_103044_move_info_files.php new file mode 100644 index 0000000..958c1b4 --- /dev/null +++ b/database/migrations/2023_02_24_103044_move_info_files.php @@ -0,0 +1,38 @@ +each(function ($file_path) use ($private_disk) { + if ( + !$private_disk->put( + pathinfo($file_path, PATHINFO_BASENAME), + File::get($file_path) + ) + ) { + throw new \RuntimeException('Failed to move inco file'); + } + }); + File::deleteDirectory(storage_path('inco/images')); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + } +} diff --git a/resources/views/pages/products/partials/form-xml-pictures.blade.php b/resources/views/pages/products/partials/form-xml-pictures.blade.php index ce42707..c278fdd 100644 --- a/resources/views/pages/products/partials/form-xml-pictures.blade.php +++ b/resources/views/pages/products/partials/form-xml-pictures.blade.php @@ -22,7 +22,7 @@
visuel Inco
diff --git a/routes/web-bo.php b/routes/web-bo.php index d3b1ddd..3d69e7d 100644 --- a/routes/web-bo.php +++ b/routes/web-bo.php @@ -92,7 +92,7 @@ Route::prefix('bo')->name('bo.')->group(function () { // * XML INCO Route::get('/xml_incos/search', [XmlIncoController::class, 'getXmlIncos'])->name('xml_incos.getXmlIncos'); Route::get('/xml_incos/picture/{inco_picture}', [XmlIncoController::class, 'getXmlIncoPicture']) - ->where('inco_picture', '[a-zA-Z0-9_-]+\.(png|jpg|jpeg)') + ->where('inco_picture', '.+[a-zA-Z0-9_-]+\.(png|jpg|jpeg)') ->name('xml_incos.getXmlIncoPicture'); // * Tags diff --git a/storage/inco/images/.gitkeep b/storage/inco/images/.gitkeep deleted file mode 100755 index e69de29..0000000