File Uploading

Fixed transliterator for invalid file names

Change-Id: I850c462989ca331b02640ca45c52227a7e2cbe85
This commit is contained in:
smarcet 2019-10-22 17:35:15 -03:00
parent 8c6d6a9386
commit ca971cd510
6 changed files with 191 additions and 38 deletions

View File

@ -15,7 +15,11 @@ use App\Services\Model\IFolderService;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use models\exceptions\ValidationException;
use models\main\File;
use Behat\Transliterator\Transliterator;
use models\main\IFolderRepository;
/**
* Class FileUploader
* @package App\Http\Utils
@ -32,16 +36,38 @@ final class FileUploader implements IFileUploader
*/
private $bucket;
/**
* @var IFolderRepository
*/
private $folder_repository;
/**
* FileUploader constructor.
* @param IFolderService $folder_service
* @param IFolderRepository $folder_repository
* @param IBucket $bucket
*/
public function __construct(IFolderService $folder_service, IBucket $bucket){
$this->folder_service = $folder_service;
$this->bucket = $bucket;
public function __construct
(
IFolderService $folder_service,
IFolderRepository $folder_repository,
IBucket $bucket
)
{
$this->folder_service = $folder_service;
$this->folder_repository = $folder_repository;
$this->bucket = $bucket;
}
private static $default_replacements = [
'/\s/' => '-', // remove whitespace
'/_/' => '-', // underscores to dashes
'/[^A-Za-z0-9+.\-]+/' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot
'/[\-]{2,}/' => '-', // remove duplicate dashes
'/^[\.\-_]+/' => '', // Remove all leading dots, dashes or underscores
];
/**
* @param UploadedFile $file
* @param $folder_name
@ -54,6 +80,27 @@ final class FileUploader implements IFileUploader
try {
$client_original_name = $file->getClientOriginalName();
$client_original_name = Transliterator::utf8ToAscii($client_original_name);
foreach(self::$default_replacements as $regex => $replace) {
$client_original_name = preg_replace($regex, $replace, $client_original_name);
}
$idx = 1;
$ext = pathinfo($client_original_name, PATHINFO_EXTENSION);
$name = pathinfo($client_original_name, PATHINFO_FILENAME);
while($this->folder_repository->existByName($client_original_name)){
$client_original_name = $name.$idx.'.'.$ext;
++$idx;
}
$title = pathinfo($client_original_name, PATHINFO_FILENAME);
if(empty($title)){
throw new ValidationException("empty file name is not valid");
}
Log::debug(sprintf("FileUploader::build: folder_name %s client original name %s", $folder_name, $client_original_name));
$local_path = Storage::putFileAs(sprintf('/public/%s', $folder_name), $file, $client_original_name);
@ -67,8 +114,8 @@ final class FileUploader implements IFileUploader
$attachment->setName($client_original_name);
$file_name = sprintf("assets/%s/%s", $folder_name, $client_original_name);
Log::debug(sprintf("FileUploader::build file_name %s", $file_name));
$title = str_replace(array('-', '_'), ' ', preg_replace('/\.[^.]+$/', '', $file->getClientOriginalName()));
$attachment->setFilename($file_name);
$title = str_replace(['-','_'],' ', preg_replace('/\.[^.]+$/', '', $title));
Log::debug(sprintf("FileUploader::build title %s", $title));
$attachment->setTitle($title);
$attachment->setShowInSearch(true);
@ -79,7 +126,10 @@ final class FileUploader implements IFileUploader
$attachment->setCloudMeta('LastPut', time());
$attachment->setCloudStatus('Live');
$attachment->setCloudSize(filesize($local_path));
}
catch(ValidationException $ex){
Log::warning($ex);
throw $ex;
}
catch (\Exception $ex){
Log::error($ex);

View File

@ -24,6 +24,12 @@ interface IFolderRepository extends IBaseRepository
*/
public function getFolderByName($folder_name);
/**
* @param string $name
* @return bool
*/
public function existByName(string $name):bool;
/**
* @param string $file_name
* @return File

View File

@ -95,4 +95,13 @@ SQL;
return $native_query->getOneOrNullResult();
}
/**
* @param string $name
* @return bool
*/
public function existByName(string $name): bool
{
return $this->count(['name'=> trim($name)]) > 0;
}
}

View File

@ -17,6 +17,7 @@
"php": "^7.1.3",
"ext-json": "*",
"ext-pdo": "*",
"behat/transliterator": "^1.2",
"cocur/slugify": "^2.3",
"ezyang/htmlpurifier": "4.7.0",
"fideloper/proxy": "^4.0",

108
composer.lock generated
View File

@ -4,8 +4,52 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "e8960c590d5b3ce6a88c1a2544a3751f",
"content-hash": "36e3d6f7cdd125988acca84a07b3bcf9",
"packages": [
{
"name": "behat/transliterator",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/Behat/Transliterator.git",
"reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Behat/Transliterator/zipball/826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c",
"reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"chuyskywalker/rolling-curl": "^3.1",
"php-yaoi/php-yaoi": "^1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2-dev"
}
},
"autoload": {
"psr-0": {
"Behat\\Transliterator": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Artistic-1.0"
],
"description": "String transliterator",
"keywords": [
"i18n",
"slug",
"transliterator"
],
"time": "2017-04-04T11:38:05+00:00"
},
{
"name": "cocur/slugify",
"version": "v2.5",
@ -1190,13 +1234,13 @@
"authors": [
{
"name": "Maciej Łebkowski",
"email": "m.lebkowski@gmail.com",
"role": "Contributor"
"role": "Contributor",
"email": "m.lebkowski@gmail.com"
},
{
"name": "Markus Poerschke",
"email": "markus@eluceo.de",
"role": "Developer"
"role": "Developer",
"email": "markus@eluceo.de"
}
],
"description": "The eluceo/iCal package offers a abstraction layer for creating iCalendars. You can easily create iCal files by using PHP object instead of typing your *.ics file by hand. The output will follow RFC 2445 as best as possible.",
@ -1800,8 +1844,8 @@
"authors": [
{
"name": "Bartosz Pachołek",
"email": "bartosz@idct.pl",
"role": "lead"
"role": "lead",
"email": "bartosz@idct.pl"
}
],
"description": "Library that provides wrapper methods around SSH2 and SFTP to simplify file download/upload over SSH/SCP/SFTP.",
@ -2506,14 +2550,14 @@
"authors": [
{
"name": "Alex Bilbie",
"role": "Developer",
"email": "hello@alexbilbie.com",
"homepage": "http://www.alexbilbie.com",
"role": "Developer"
"homepage": "http://www.alexbilbie.com"
},
{
"name": "Woody Gilk",
"homepage": "https://github.com/shadowhand",
"role": "Contributor"
"role": "Contributor",
"homepage": "https://github.com/shadowhand"
}
],
"description": "OAuth 2.0 Client Library",
@ -3618,9 +3662,9 @@
"authors": [
{
"name": "Evert Pot",
"role": "Developer",
"email": "me@evertpot.com",
"homepage": "http://evertpot.com/",
"role": "Developer"
"homepage": "http://evertpot.com/"
}
],
"description": "Functions for making sense out of URIs.",
@ -3675,14 +3719,14 @@
"authors": [
{
"name": "Evert Pot",
"role": "Developer",
"email": "me@evertpot.com",
"homepage": "http://evertpot.com/",
"role": "Developer"
"homepage": "http://evertpot.com/"
},
{
"name": "Markus Staab",
"email": "markus.staab@redaxo.de",
"role": "Developer"
"role": "Developer",
"email": "markus.staab@redaxo.de"
}
],
"description": "sabre/xml is an XML library that you may not hate.",
@ -4460,7 +4504,7 @@
},
{
"name": "Gert de Pagter",
"email": "backendtea@gmail.com"
"email": "BackEndTea@gmail.com"
}
],
"description": "Symfony polyfill for ctype functions",
@ -5160,8 +5204,8 @@
"authors": [
{
"name": "Tijs Verkoyen",
"email": "css_to_inline_styles@verkoyen.eu",
"role": "Developer"
"role": "Developer",
"email": "css_to_inline_styles@verkoyen.eu"
}
],
"description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
@ -5753,18 +5797,18 @@
"authors": [
{
"name": "Arne Blankerts",
"email": "arne@blankerts.de",
"role": "Developer"
"role": "Developer",
"email": "arne@blankerts.de"
},
{
"name": "Sebastian Heuer",
"email": "sebastian@phpeople.de",
"role": "Developer"
"role": "Developer",
"email": "sebastian@phpeople.de"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "Developer"
"role": "Developer",
"email": "sebastian@phpunit.de"
}
],
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
@ -6133,8 +6177,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
"role": "lead",
"email": "sebastian@phpunit.de"
}
],
"description": "FilterIterator implementation that filters files based on a list of suffixes.",
@ -6175,8 +6219,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
"role": "lead",
"email": "sebastian@phpunit.de"
}
],
"description": "Simple template engine.",
@ -6926,8 +6970,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
"role": "lead",
"email": "sebastian@phpunit.de"
}
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",

View File

@ -37,7 +37,6 @@ final class OAuth2SummitApiTest extends ProtectedApiTest
$app->instance(\App\Http\Utils\IFileUploader::class, $fileUploaderMock);
return $app;
}
@ -1451,4 +1450,48 @@ final class OAuth2SummitApiTest extends ProtectedApiTest
return intval($video_id);
}
public function testAddPresentationSlideInvalidName($summit_id=25){
$repo = EntityManager::getRepository(\models\summit\Summit::class);
$summit = $repo->getById($summit_id);
$presentation = $summit->getPublishedPresentations()[0];
$params = array
(
'id' => $summit_id,
'presentation_id' => $presentation->getId(),
);
$headers = array
(
"HTTP_Authorization" => " Bearer " . $this->access_token,
"CONTENT_TYPE" => "application/json"
);
$video_data = array
(
'name' => 'test slide',
'description' => 'test slide',
'display_on_site' => true,
);
$response = $this->action
(
"POST",
"OAuth2PresentationApiController@addPresentationSlide",
$params,
array(),
array(),
[
'file' => UploadedFile::fake()->image('invalid image.jpeg')
],
$headers,
json_encode($video_data)
);
$video_id = $response->getContent();
$this->assertResponseStatus(201);
return intval($video_id);
}
}