135 lines
5.0 KiB
PHP
135 lines
5.0 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Esun\CustomerMessage\Model;
|
|
|
|
use Magento\Framework\App\Filesystem\DirectoryList;
|
|
use Magento\Framework\Exception\LocalizedException;
|
|
use Magento\Framework\Filesystem\Driver\File;
|
|
|
|
class AttachmentStorage
|
|
{
|
|
private const MAX_FILES = 5;
|
|
private const MAX_SIZE = 5242880;
|
|
private const MIME_EXTENSIONS = [
|
|
'image/jpeg' => 'jpg',
|
|
'image/png' => 'png',
|
|
'image/webp' => 'webp',
|
|
];
|
|
|
|
public function __construct(
|
|
private readonly DirectoryList $directoryList,
|
|
private readonly File $fileDriver
|
|
) {
|
|
}
|
|
|
|
public function saveUploads(array $files, int $threadId, int $entryId): array
|
|
{
|
|
$uploads = $this->normalize($files);
|
|
if (count($uploads) > self::MAX_FILES) {
|
|
throw new LocalizedException(__('You can upload up to %1 images per message.', self::MAX_FILES));
|
|
}
|
|
|
|
$saved = [];
|
|
try {
|
|
foreach ($uploads as $upload) {
|
|
if ((int)$upload['error'] === UPLOAD_ERR_NO_FILE) {
|
|
continue;
|
|
}
|
|
if ((int)$upload['error'] !== UPLOAD_ERR_OK) {
|
|
throw new LocalizedException(__('One of the images could not be uploaded.'));
|
|
}
|
|
if ((int)$upload['size'] <= 0 || (int)$upload['size'] > self::MAX_SIZE) {
|
|
throw new LocalizedException(__('Each image must be no larger than 5 MB.'));
|
|
}
|
|
|
|
$mime = (new \finfo(FILEINFO_MIME_TYPE))->file((string)$upload['tmp_name']);
|
|
if (!isset(self::MIME_EXTENSIONS[$mime])) {
|
|
throw new LocalizedException(__('Only JPG, PNG and WebP images are allowed.'));
|
|
}
|
|
|
|
$relativeDirectory = sprintf('esun_customer_message/%d/%d', $threadId, $entryId);
|
|
$absoluteDirectory = $this->directoryList->getPath(DirectoryList::VAR_DIR) . '/' . $relativeDirectory;
|
|
$this->fileDriver->createDirectory($absoluteDirectory, 0750);
|
|
$filename = bin2hex(random_bytes(20)) . '.' . self::MIME_EXTENSIONS[$mime];
|
|
$absolutePath = $absoluteDirectory . '/' . $filename;
|
|
if (!move_uploaded_file((string)$upload['tmp_name'], $absolutePath)) {
|
|
throw new LocalizedException(__('The image could not be stored.'));
|
|
}
|
|
chmod($absolutePath, 0640);
|
|
$saved[] = [
|
|
'original_name' => mb_substr(basename((string)$upload['name']), 0, 255),
|
|
'storage_path' => $relativeDirectory . '/' . $filename,
|
|
'mime_type' => $mime,
|
|
'file_size' => (int)$upload['size'],
|
|
];
|
|
}
|
|
} catch (\Throwable $exception) {
|
|
$this->delete(array_column($saved, 'storage_path'));
|
|
throw $exception;
|
|
}
|
|
|
|
return $saved;
|
|
}
|
|
|
|
public function hasUploads(array $files): bool
|
|
{
|
|
foreach ($this->normalize($files) as $upload) {
|
|
if ((int)$upload['error'] !== UPLOAD_ERR_NO_FILE) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function getAbsolutePath(string $relativePath): string
|
|
{
|
|
$base = $this->directoryList->getPath(DirectoryList::VAR_DIR) . '/esun_customer_message';
|
|
$path = $this->directoryList->getPath(DirectoryList::VAR_DIR) . '/' . ltrim($relativePath, '/');
|
|
$realBase = realpath($base);
|
|
$realPath = realpath($path);
|
|
if ($realBase === false || $realPath === false || !str_starts_with($realPath, $realBase . DIRECTORY_SEPARATOR)) {
|
|
throw new LocalizedException(__('The requested image is not available.'));
|
|
}
|
|
return $realPath;
|
|
}
|
|
|
|
public function delete(array $relativePaths): void
|
|
{
|
|
foreach ($relativePaths as $relativePath) {
|
|
try {
|
|
$path = $this->getAbsolutePath((string)$relativePath);
|
|
if ($this->fileDriver->isExists($path)) {
|
|
$this->fileDriver->deleteFile($path);
|
|
}
|
|
} catch (\Throwable) {
|
|
// Best-effort cleanup after a failed transaction.
|
|
}
|
|
}
|
|
}
|
|
|
|
private function normalize(array $files): array
|
|
{
|
|
if (!$files) {
|
|
return [];
|
|
}
|
|
if (isset($files['name']) && is_array($files['name'])) {
|
|
$result = [];
|
|
foreach ($files['name'] as $index => $name) {
|
|
$result[] = [
|
|
'name' => $name,
|
|
'type' => $files['type'][$index] ?? '',
|
|
'tmp_name' => $files['tmp_name'][$index] ?? '',
|
|
'error' => $files['error'][$index] ?? UPLOAD_ERR_NO_FILE,
|
|
'size' => $files['size'][$index] ?? 0,
|
|
];
|
|
}
|
|
return $result;
|
|
}
|
|
if (isset($files['name'])) {
|
|
return [$files];
|
|
}
|
|
return array_values(array_filter($files, 'is_array'));
|
|
}
|
|
}
|