Skip to content

Commit

Permalink
Merge pull request #587 from getformwork/process-file-uploads
Browse files Browse the repository at this point in the history
Process file uploads in `FileUploader`
  • Loading branch information
giuscris authored Oct 19, 2024
2 parents e56a6a5 + 9c98bad commit b2bf91a
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 84 deletions.
3 changes: 3 additions & 0 deletions formwork/src/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Formwork\Fields\Dynamic\DynamicFieldValue;
use Formwork\Files\FileFactory;
use Formwork\Files\FileUriGenerator;
use Formwork\Files\Services\FileUploader;
use Formwork\Http\Request;
use Formwork\Http\Response;
use Formwork\Images\ImageFactory;
Expand Down Expand Up @@ -222,6 +223,8 @@ protected function loadServices(Container $container): void
$container->define(ImageFactory::class);

$container->define(FileUriGenerator::class);

$container->define(FileUploader::class);
}

/**
Expand Down
42 changes: 0 additions & 42 deletions formwork/src/Files/FileUploader.php

This file was deleted.

78 changes: 78 additions & 0 deletions formwork/src/Files/Services/FileUploader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace Formwork\Files\Services;

use Formwork\Config\Config;
use Formwork\Exceptions\TranslatedException;
use Formwork\Files\File;
use Formwork\Files\FileFactory;
use Formwork\Http\Files\UploadedFile;
use Formwork\Images\Image;
use Formwork\Sanitizer\SvgSanitizer;
use Formwork\Utils\Arr;
use Formwork\Utils\FileSystem;
use Formwork\Utils\MimeType;
use Formwork\Utils\Str;

class FileUploader
{
/**
* @var list<string>
*/
protected array $allowedMimeTypes;

public function __construct(protected Config $config, protected FileFactory $fileFactory)
{
$this->allowedMimeTypes = Arr::map($this->config->get('system.files.allowedExtensions'), fn (string $ext) => MimeType::fromExtension($ext));
}

/**
* @return list<string>
*/
public function allowedMimeTypes(): array
{
return $this->allowedMimeTypes;
}

/**
* @param ?list<string> $allowedMimeTypes
*/
public function upload(UploadedFile $uploadedFile, string $destinationPath, ?string $name = null, bool $overwrite = false, ?array $allowedMimeTypes = null): File
{
$mimeType = MimeType::fromFile($uploadedFile->tempPath());

$allowedMimeTypes ??= $this->allowedMimeTypes;

if (!in_array($mimeType, $allowedMimeTypes, true)) {
throw new TranslatedException(sprintf('Invalid mime type %s for file uploads', $mimeType), 'upload.error.mimeType');
}

$filename = Str::slug($name ?? pathinfo($uploadedFile->clientName(), PATHINFO_FILENAME)) . '.' . MimeType::toExtension($mimeType);

$uploadedFile->move($destinationPath, $filename, $overwrite);

$file = $this->fileFactory->make(FileSystem::joinPaths($destinationPath, $filename));

if ($file instanceof Image) {
switch ($file->mimeType()) {
case 'image/jpeg':
case 'image/png':
case 'image/webp':
// Process JPEG, PNG and WebP images according to system options (e.g. quality)
if ($this->config->get('system.uploads.processImages')) {
$file->save();
}
break;

case 'image/svg+xml':
// Sanitize SVG images
$svgSanitizer = new SvgSanitizer();
$data = FileSystem::read($file->path());
FileSystem::write($file->path(), $svgSanitizer->sanitize($data));
break;
}
}

return $file;
}
}
4 changes: 4 additions & 0 deletions formwork/src/Http/Files/UploadedFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ public function getErrorTranslationString(): string

public function move(string $destination, string $filename, bool $overwrite = false): bool
{
if ($this->error !== UPLOAD_ERR_OK) {
throw new TranslatedException(sprintf('Cannot upload file "%s": %s', $this->fieldName(), $this->getErrorMessage()), $this->getErrorTranslationString());
}

if (strlen($filename) > FileSystem::MAX_NAME_LENGTH) {
throw new TranslatedException('File name too long', 'upload.error.fileNameTooLong');
}
Expand Down
7 changes: 5 additions & 2 deletions formwork/src/Images/Handler/AbstractHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ public function __construct(protected string $data, array $options = [])
$this->options = [...$this->defaults(), ...$options];
}

public static function fromPath(string $path): static
/**
* @param array<string, mixed> $options
*/
public static function fromPath(string $path, array $options = []): static
{
return new static(FileSystem::read($path));
return new static(FileSystem::read($path), $options);
}

public static function fromGdImage(GdImage $gdImage, array $options = []): static
Expand Down
10 changes: 5 additions & 5 deletions formwork/src/Images/Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -417,11 +417,11 @@ protected function handler(): AbstractHandler
protected function getHandler(): AbstractHandler
{
return match ($this->mimeType()) {
'image/jpeg' => JpegHandler::fromPath($this->path),
'image/png' => PngHandler::fromPath($this->path),
'image/gif' => GifHandler::fromPath($this->path),
'image/webp' => WebpHandler::fromPath($this->path),
'image/svg+xml' => SvgHandler::fromPath($this->path),
'image/jpeg' => JpegHandler::fromPath($this->path, $this->options),
'image/png' => PngHandler::fromPath($this->path, $this->options),
'image/gif' => GifHandler::fromPath($this->path, $this->options),
'image/webp' => WebpHandler::fromPath($this->path, $this->options),
'image/svg+xml' => SvgHandler::fromPath($this->path, $this->options),
default => throw new RuntimeException('Unsupported image type'),
};
}
Expand Down
27 changes: 8 additions & 19 deletions formwork/src/Panel/Controllers/PagesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@
use Formwork\Fields\FieldCollection;
use Formwork\Files\File;
use Formwork\Files\FileCollection;
use Formwork\Files\FileUploader;
use Formwork\Files\Services\FileUploader;
use Formwork\Http\Files\UploadedFile;
use Formwork\Http\JsonResponse;
use Formwork\Http\RedirectResponse;
use Formwork\Http\Request;
use Formwork\Http\RequestData;
use Formwork\Http\RequestMethod;
use Formwork\Http\Response;
use Formwork\Images\Image;
use Formwork\Pages\Page;
use Formwork\Panel\ContentHistory\ContentHistory;
use Formwork\Panel\ContentHistory\ContentHistoryEvent;
Expand All @@ -26,7 +25,6 @@
use Formwork\Utils\Constraint;
use Formwork\Utils\Date;
use Formwork\Utils\FileSystem;
use Formwork\Utils\MimeType;
use Formwork\Utils\Str;
use Formwork\Utils\Uri;
use RuntimeException;
Expand Down Expand Up @@ -792,28 +790,19 @@ protected function updatePage(Page $page, RequestData $requestData, FieldCollect
/**
* Process page uploads
*
* @param array<UploadedFile> $files
* @param list<string> $mimeTypes
* @param list<UploadedFile> $files
* @param list<string> $mimeTypes
*/
protected function processPageUploads(array $files, Page $page, ?array $mimeTypes = null, ?string $name = null, bool $overwrite = false): void
{
$mimeTypes ??= Arr::map($this->config->get('system.files.allowedExtensions'), fn (string $ext) => MimeType::fromExtension($ext));
$fileUploader = $this->app->getService(FileUploader::class);

$fileUploader = new FileUploader($mimeTypes);
if ($page->contentPath() === null) {
throw new UnexpectedValueException('Unexpected missing page path');
}

foreach ($files as $file) {
if (!$file->isUploaded()) {
throw new TranslatedException(sprintf('Cannot upload file "%s"', $file->fieldName()), $file->getErrorTranslationString());
}
if ($page->contentPath() === null) {
throw new UnexpectedValueException('Unexpected missing page path');
}
$uploadedFile = $fileUploader->upload($file, $page->contentPath(), $name, $overwrite);
// Process JPEG and PNG images according to system options (e.g. quality)
if ($this->config->get('system.uploads.processImages') && in_array($uploadedFile->mimeType(), ['image/jpeg', 'image/png'], true)) {
$image = new Image($uploadedFile->path(), $this->config->get('system.images'));
$image->save();
}
$fileUploader->upload($file, $page->contentPath(), $name, overwrite: $overwrite, allowedMimeTypes: $mimeTypes);
}

$page->reload();
Expand Down
32 changes: 16 additions & 16 deletions formwork/src/Panel/Controllers/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use Formwork\Exceptions\TranslatedException;
use Formwork\Fields\Exceptions\ValidationException;
use Formwork\Fields\FieldCollection;
use Formwork\Files\FileUploader;
use Formwork\Files\Services\FileUploader;
use Formwork\Http\FileResponse;
use Formwork\Http\Files\UploadedFile;
use Formwork\Http\RedirectResponse;
Expand Down Expand Up @@ -267,31 +267,31 @@ protected function updateUser(User $user, FieldCollection $fieldCollection): voi
*
* @param array<string> $mimeTypes
*/
protected function uploadUserImage(User $user, UploadedFile $file, array $mimeTypes): ?string
protected function uploadUserImage(User $user, UploadedFile $uploadedFile, array $mimeTypes): ?string
{
$imagesPath = FileSystem::joinPaths($this->config->get('system.users.paths.images'));

$fileUploader = new FileUploader($mimeTypes);
$fileUploader = $this->app->getService(FileUploader::class);

$uploadedFile = $fileUploader->upload($file, $imagesPath, FileSystem::randomName());
$file = $fileUploader->upload($uploadedFile, $imagesPath, FileSystem::randomName(), allowedMimeTypes: $mimeTypes);

if ($uploadedFile->type() === 'image') {
$userImageSize = $this->config->get('system.panel.userImageSize');
if (!($file instanceof Image)) {
return null;
}

// Square off uploaded image
$image = new Image($uploadedFile->path(), $this->config->get('system.images'));
$image->square($userImageSize)->save();
$userImageSize = $this->config->get('system.panel.userImageSize');

// Delete old image
if (!$user->hasDefaultImage()) {
$this->deleteUserImage($user);
}
// Square off uploaded image
$file->square($userImageSize)->save();

$this->panel()->notify($this->translate('panel.user.image.uploaded'), 'success');
return $uploadedFile->name();
// Delete old image
if (!$user->hasDefaultImage()) {
$this->deleteUserImage($user);
}

return null;
$this->panel()->notify($this->translate('panel.user.image.uploaded'), 'success');

return $file->name();
}

/**
Expand Down

0 comments on commit b2bf91a

Please sign in to comment.