Added an PHP CS fixer config file and applied it to files.

We now use the same the same style as the symfony project, and it allows us to simply fix the style by executing php_cs_fixer fix in the project root.
This commit is contained in:
Jan Böhmer 2019-11-09 00:47:20 +01:00
parent 89258bc102
commit e557bdedd5
210 changed files with 2099 additions and 2742 deletions

View file

@ -1,6 +1,6 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony)
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
*
@ -17,29 +17,11 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
namespace App\Services\Attachments;
use App\Entity\Attachments\Attachment;
use App\Entity\Attachments\AttachmentContainingDBElement;
use App\Entity\Attachments\AttachmentTypeAttachment;
use App\Entity\Attachments\CategoryAttachment;
use App\Entity\Attachments\CurrencyAttachment;
use App\Entity\Attachments\DeviceAttachment;
use App\Entity\Attachments\FootprintAttachment;
use App\Entity\Attachments\GroupAttachment;
use App\Entity\Attachments\ManufacturerAttachment;
use App\Entity\Attachments\MeasurementUnitAttachment;
use App\Entity\Attachments\PartAttachment;
use App\Entity\Attachments\StorelocationAttachment;
use App\Entity\Attachments\SupplierAttachment;
use App\Entity\Attachments\UserAttachment;
use App\Services\Attachments\AttachmentPathResolver;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* This service contains basic commonly used functions to work with attachments.
@ -47,11 +29,9 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
* (like filesize or if a file is existing).
*
* Special operations like getting attachment urls or handling file uploading/downloading are in their own services.
* @package App\Services
*/
class AttachmentManager
{
protected $pathResolver;
public function __construct(AttachmentPathResolver $pathResolver)
@ -61,11 +41,13 @@ class AttachmentManager
/**
* Gets an SPLFileInfo object representing the file associated with the attachment.
*
* @param Attachment $attachment The attachment for which the file should be generated
*
* @return \SplFileInfo|null The fileinfo for the attachment file. Null, if the attachment is external or has
* invalid file.
* invalid file.
*/
public function attachmentToFile(Attachment $attachment) : ?\SplFileInfo
public function attachmentToFile(Attachment $attachment): ?\SplFileInfo
{
if ($attachment->isExternal() || !$this->isFileExisting($attachment)) {
return null;
@ -77,7 +59,9 @@ class AttachmentManager
/**
* Returns the absolute filepath of the attachment. Null is returned, if the attachment is externally saved,
* or is not existing.
*
* @param Attachment $attachment The attachment for which the filepath should be determined
*
* @return string|null
*/
public function toAbsoluteFilePath(Attachment $attachment): ?string
@ -93,15 +77,16 @@ class AttachmentManager
$path = $this->pathResolver->placeholderToRealPath($attachment->getPath());
//realpath does not work with null as argument
if ($path === null) {
if (null === $path) {
return null;
}
$tmp = realpath($path);
//If path is not existing realpath returns false.
if ($tmp === false) {
if (false === $tmp) {
return null;
}
return $tmp;
}
@ -127,6 +112,7 @@ class AttachmentManager
* For external attachments or not existing attachments, null is returned.
*
* @param Attachment $attachment The filesize for which the filesize should be calculated.
*
* @return int|null
*/
public function getFileSize(Attachment $attachment): ?int
@ -140,22 +126,23 @@ class AttachmentManager
}
$tmp = filesize($this->toAbsoluteFilePath($attachment));
return $tmp !== false ? $tmp : null;
return false !== $tmp ? $tmp : null;
}
/**
* Returns a human readable version of the attachment file size.
* For external attachments, null is returned.
*
* @param Attachment $attachment
* @param int $decimals The number of decimals numbers that should be printed
*
* @return string|null A string like 1.3M
*/
public function getHumanFileSize(Attachment $attachment, $decimals = 2): ?string
{
$bytes = $this->getFileSize($attachment);
if ($bytes == null) {
if (null == $bytes) {
return null;
}
@ -163,7 +150,8 @@ class AttachmentManager
//Taken from: https://www.php.net/manual/de/function.filesize.php#106569 and slightly modified
$sz = 'BKMGTP';
$factor = (int) floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / 1024 ** $factor) . @$sz[$factor];
$factor = (int) floor((\strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / 1024 ** $factor).@$sz[$factor];
}
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony)
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
*
@ -17,19 +17,15 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
namespace App\Services\Attachments;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* This service converts the relative pathes for attachments saved in database (like %MEDIA%/img.jpg) to real pathes
* an vice versa.
* @package App\Services\Attachments
*/
class AttachmentPathResolver
{
@ -47,11 +43,12 @@ class AttachmentPathResolver
/**
* AttachmentPathResolver constructor.
* @param string $project_dir The kernel that should be used to resolve the project dir.
* @param string $media_path The path where uploaded attachments should be stored.
*
* @param string $project_dir The kernel that should be used to resolve the project dir.
* @param string $media_path The path where uploaded attachments should be stored.
* @param string|null $footprints_path The path where builtin attachments are stored.
* Set to null if this ressource should be disabled.
* @param string|null $models_path Set to null if this ressource should be disabled.
* Set to null if this ressource should be disabled.
* @param string|null $models_path Set to null if this ressource should be disabled.
*/
public function __construct(string $project_dir, string $media_path, string $secure_path, ?string $footprints_path, ?string $models_path)
{
@ -69,7 +66,7 @@ class AttachmentPathResolver
//Remove all disabled placeholders
foreach ($this->pathes as $key => $path) {
if ($path === null) {
if (null === $path) {
unset($this->placeholders[$key], $this->pathes[$key]);
}
}
@ -82,13 +79,16 @@ class AttachmentPathResolver
/**
* Converts a path passed by parameter from services.yaml (which can be an absolute path or relative to project dir)
* to an absolute path. When a relative path is passed, the directory must exist or null is returned.
*
* @internal
*
* @param string|null $param_path The parameter value that should be converted to a absolute path
*
* @return string|null
*/
public function parameterToAbsolutePath(?string $param_path) : ?string
public function parameterToAbsolutePath(?string $param_path): ?string
{
if ($param_path === null) {
if (null === $param_path) {
return null;
}
@ -97,17 +97,18 @@ class AttachmentPathResolver
if ($fs->isAbsolutePath($param_path)) {
$tmp = realpath($param_path);
//Disable ressource if path is not existing
if ($tmp === false) {
if (false === $tmp) {
return null;
}
return $tmp;
}
//Otherwise prepend the project path
$tmp = realpath($this->project_dir . DIRECTORY_SEPARATOR . $param_path);
$tmp = realpath($this->project_dir.\DIRECTORY_SEPARATOR.$param_path);
//If path does not exist then disable the placeholder
if ($tmp === false) {
if (false === $tmp) {
return null;
}
@ -119,38 +120,39 @@ class AttachmentPathResolver
* Create an array usable for preg_replace out of an array of placeholders or pathes.
* Slashes and other chars become escaped.
* For example: '%TEST%' becomes '/^%TEST%/'.
* @param array $array
*
* @return array
*/
protected function arrayToRegexArray(array $array) : array
protected function arrayToRegexArray(array $array): array
{
$ret = [];
foreach ($array as $item) {
$item = str_replace(['\\'], ['/'], $item);
$ret[] = '/' . preg_quote($item, '/') . '/';
$ret[] = '/'.preg_quote($item, '/').'/';
}
return $ret;
}
/**
* Converts an relative placeholder filepath (with %MEDIA% or older %BASE%) to an absolute filepath on disk.
* The directory separator is always /. Relative pathes are not realy possible (.. is striped)
* The directory separator is always /. Relative pathes are not realy possible (.. is striped).
*
* @param string $placeholder_path The filepath with placeholder for which the real path should be determined.
*
* @return string|null The absolute real path of the file, or null if the placeholder path is invalid
*/
public function placeholderToRealPath(string $placeholder_path) : ?string
public function placeholderToRealPath(string $placeholder_path): ?string
{
//The new attachments use %MEDIA% as placeholders, which is the directory set in media_directory
//Older path entries are given via %BASE% which was the project root
$count = 0;
$placeholder_path = preg_replace($this->placeholders_regex, $this->pathes, $placeholder_path,-1,$count);
$placeholder_path = preg_replace($this->placeholders_regex, $this->pathes, $placeholder_path, -1, $count);
//A valid placeholder can have only one
if ($count !== 1) {
if (1 !== $count) {
return null;
}
@ -160,7 +162,7 @@ class AttachmentPathResolver
}
//Path is invalid if path is directory traversal
if (strpos($placeholder_path, '..') !== false) {
if (false !== strpos($placeholder_path, '..')) {
return null;
}
@ -172,12 +174,14 @@ class AttachmentPathResolver
/**
* Converts an real absolute filepath to a placeholder version.
* @param string $real_path The absolute path, for which the placeholder version should be generated.
* @param bool $old_version By default the %MEDIA% placeholder is used, which is directly replaced with the
* media directory. If set to true, the old version with %BASE% will be used, which is the project directory.
*
* @param string $real_path The absolute path, for which the placeholder version should be generated.
* @param bool $old_version By default the %MEDIA% placeholder is used, which is directly replaced with the
* media directory. If set to true, the old version with %BASE% will be used, which is the project directory.
*
* @return string The placeholder version of the filepath
*/
public function realPathToPlaceholder(string $real_path, bool $old_version = false) : ?string
public function realPathToPlaceholder(string $real_path, bool $old_version = false): ?string
{
$count = 0;
@ -194,7 +198,7 @@ class AttachmentPathResolver
$real_path = preg_replace($this->pathes_regex, $this->placeholders, $real_path, -1, $count);
}
if ($count !== 1) {
if (1 !== $count) {
return null;
}
@ -208,9 +212,10 @@ class AttachmentPathResolver
/**
* The path where uploaded attachments is stored.
*
* @return string The absolute path to the media folder.
*/
public function getMediaPath() : string
public function getMediaPath(): string
{
return $this->media_path;
}
@ -218,28 +223,31 @@ class AttachmentPathResolver
/**
* The path where secured attachments are stored. Must not be located in public/ folder, so it can only be accessed
* via the attachment controller.
*
* @return string The absolute path to the secure path.
*/
public function getSecurePath() : string
public function getSecurePath(): string
{
return $this->secure_path;
}
/**
* The string where the builtin footprints are stored
* The string where the builtin footprints are stored.
*
* @return string|null The absolute path to the footprints folder. Null if built footprints were disabled.
*/
public function getFootprintsPath() : ?string
public function getFootprintsPath(): ?string
{
return $this->footprints_path;
}
/**
* The string where the builtin 3D models are stored
* The string where the builtin 3D models are stored.
*
* @return string|null The absolute path to the models folder. Null if builtin models were disabled.
*/
public function getModelsPath() : ?string
public function getModelsPath(): ?string
{
return $this->models_path;
}
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony)
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
*
@ -17,14 +17,11 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
namespace App\Services\Attachments;
use App\Entity\Attachments\Attachment;
use App\Services\Attachments\AttachmentPathResolver;
use App\Services\Attachments\AttachmentURLGenerator;
use Doctrine\ORM\EntityManagerInterface;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Symfony\Component\Filesystem\Filesystem;
@ -32,7 +29,6 @@ use Symfony\Component\HttpFoundation\File\File;
/**
* This service provides functions to find attachments via an reverse search based on a file.
* @package App\Services
*/
class AttachmentReverseSearch
{
@ -51,11 +47,13 @@ class AttachmentReverseSearch
}
/**
* Find all attachments that use the given file
* Find all attachments that use the given file.
*
* @param \SplFileInfo $file The file for which is searched
*
* @return Attachment[] An list of attachments that use the given file.
*/
public function findAttachmentsByFile(\SplFileInfo $file) : array
public function findAttachmentsByFile(\SplFileInfo $file): array
{
//Path with %MEDIA%
$relative_path_new = $this->pathResolver->realPathToPlaceholder($file->getPathname());
@ -63,20 +61,22 @@ class AttachmentReverseSearch
$relative_path_old = $this->pathResolver->realPathToPlaceholder($file->getPathname(), true);
$repo = $this->em->getRepository(Attachment::class);
return $repo->findBy(['path' => [$relative_path_new, $relative_path_old]]);
}
/**
* Deletes the given file if it is not used by more than $threshold attachments
* @param \SplFileInfo $file The file that should be removed
* @param int $threshold The threshold used, to determine if a file should be deleted or not.
* Deletes the given file if it is not used by more than $threshold attachments.
*
* @param \SplFileInfo $file The file that should be removed
* @param int $threshold The threshold used, to determine if a file should be deleted or not.
*
* @return bool True, if the file was delete. False if not.
*/
public function deleteIfNotUsed(\SplFileInfo $file, int $threshold = 1) : bool
public function deleteIfNotUsed(\SplFileInfo $file, int $threshold = 1): bool
{
/* When the file is used more then $threshold times, don't delete it */
if (count($this->findAttachmentsByFile($file)) > $threshold) {
if (\count($this->findAttachmentsByFile($file)) > $threshold) {
return false;
}
@ -86,8 +86,6 @@ class AttachmentReverseSearch
$fs = new Filesystem();
$fs->remove($file);
return true;
}
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony)
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
*
@ -17,12 +17,10 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
namespace App\Services\Attachments;
use App\Entity\Attachments\Attachment;
use App\Entity\Attachments\AttachmentContainingDBElement;
use App\Entity\Attachments\AttachmentTypeAttachment;
@ -38,14 +36,8 @@ use App\Entity\Attachments\StorelocationAttachment;
use App\Entity\Attachments\SupplierAttachment;
use App\Entity\Attachments\UserAttachment;
use App\Exceptions\AttachmentDownloadException;
use App\Services\Attachments\AttachmentManager;
use Doctrine\Common\Annotations\IndexedReader;
use Nyholm\Psr7\Request;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
use Symfony\Component\Mime\MimeTypes;
use Symfony\Component\Mime\MimeTypesInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
@ -53,7 +45,6 @@ use Symfony\Contracts\HttpClient\HttpClientInterface;
/**
* This service handles the form submitting of an attachment and handles things like file uploading and downloading.
* @package App\Services\Attachments
*/
class AttachmentSubmitHandler
{
@ -63,7 +54,7 @@ class AttachmentSubmitHandler
protected $httpClient;
protected $mimeTypes;
public function __construct (AttachmentPathResolver $pathResolver, bool $allow_attachments_downloads,
public function __construct(AttachmentPathResolver $pathResolver, bool $allow_attachments_downloads,
HttpClientInterface $httpClient, MimeTypesInterface $mimeTypes)
{
$this->pathResolver = $pathResolver;
@ -77,10 +68,10 @@ class AttachmentSubmitHandler
DeviceAttachment::class => 'device', FootprintAttachment::class => 'footprint',
GroupAttachment::class => 'group', ManufacturerAttachment::class => 'manufacturer',
MeasurementUnitAttachment::class => 'measurement_unit', StorelocationAttachment::class => 'storelocation',
SupplierAttachment::class => 'supplier', UserAttachment::class => 'user'];
SupplierAttachment::class => 'supplier', UserAttachment::class => 'user', ];
}
protected function configureOptions(OptionsResolver $resolver) : void
protected function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
//If no preview image was set yet, the new uploaded file will become the preview image
@ -94,11 +85,13 @@ class AttachmentSubmitHandler
/**
* Generates a filename for the given attachment and extension.
* The filename contains a random id, so every time this function is called you get an unique name.
*
* @param Attachment $attachment The attachment that should be used for generating an attachment
* @param string $extension The extension that the new file should have (must only contain chars allowed in pathes)
* @param string $extension The extension that the new file should have (must only contain chars allowed in pathes)
*
* @return string The new filename.
*/
public function generateAttachmentFilename(Attachment $attachment, string $extension) : string
public function generateAttachmentFilename(Attachment $attachment, string $extension): string
{
//Normalize extension
$extension = transliterator_transliterate(
@ -112,16 +105,18 @@ class AttachmentSubmitHandler
$attachment->getName()
);
return $safeName . '-' . uniqid('', false) . '.' . $extension;
return $safeName.'-'.uniqid('', false).'.'.$extension;
}
/**
* Generates an (absolute) path to a folder where the given attachment should be stored.
* @param Attachment $attachment The attachment that should be used for
* @param bool $secure_upload True if the file path should be located in a safe location
*
* @param Attachment $attachment The attachment that should be used for
* @param bool $secure_upload True if the file path should be located in a safe location
*
* @return string The absolute path for the attachment folder.
*/
public function generateAttachmentPath(Attachment $attachment, bool $secure_upload = false) : string
public function generateAttachmentPath(Attachment $attachment, bool $secure_upload = false): string
{
if ($secure_upload) {
$base_path = $this->pathResolver->getSecurePath();
@ -130,34 +125,32 @@ class AttachmentSubmitHandler
}
//Ensure the given attachment class is known to mapping
if (!isset($this->folder_mapping[get_class($attachment)])) {
throw new \InvalidArgumentException(
'The given attachment class is not known! The passed class was: ' . get_class($attachment)
);
if (!isset($this->folder_mapping[\get_class($attachment)])) {
throw new \InvalidArgumentException('The given attachment class is not known! The passed class was: '.\get_class($attachment));
}
//Ensure the attachment has an assigned element
if ($attachment->getElement() === null) {
throw new \InvalidArgumentException(
'The given attachment is not assigned to an element! An element is needed to generate a path!'
);
if (null === $attachment->getElement()) {
throw new \InvalidArgumentException('The given attachment is not assigned to an element! An element is needed to generate a path!');
}
//Build path
return
$base_path . DIRECTORY_SEPARATOR //Base path
. $this->folder_mapping[get_class($attachment)] . DIRECTORY_SEPARATOR . $attachment->getElement()->getID();
$base_path.\DIRECTORY_SEPARATOR //Base path
.$this->folder_mapping[\get_class($attachment)].\DIRECTORY_SEPARATOR.$attachment->getElement()->getID();
}
/**
* Handle the submit of an attachment form.
* This function will move the uploaded file or download the URL file to server, if needed.
* @param Attachment $attachment The attachment that should be used for handling.
* @param UploadedFile|null $file If given, that file will be moved to the right location
* @param array $options The options to use with the upload. Here you can specify that an URL should be downloaded,
* or an file should be moved to a secure location.
*
* @param Attachment $attachment The attachment that should be used for handling.
* @param UploadedFile|null $file If given, that file will be moved to the right location
* @param array $options The options to use with the upload. Here you can specify that an URL should be downloaded,
* or an file should be moved to a secure location.
*
* @return Attachment The attachment with the new filename (same instance as passed $attachment)
*/
public function handleFormSubmit(Attachment $attachment, ?UploadedFile $file, array $options = []) : Attachment
public function handleFormSubmit(Attachment $attachment, ?UploadedFile $file, array $options = []): Attachment
{
$resolver = new OptionsResolver();
$this->configureOptions($resolver);
@ -175,9 +168,9 @@ class AttachmentSubmitHandler
//Check if we should assign this attachment to master picture
//this is only possible if the attachment is new (not yet persisted to DB)
if ($options['become_preview_if_empty'] && $attachment->getID() === null && $attachment->isPicture()) {
if ($options['become_preview_if_empty'] && null === $attachment->getID() && $attachment->isPicture()) {
$element = $attachment->getElement();
if ($element instanceof AttachmentContainingDBElement && $element->getMasterPictureAttachment() === null) {
if ($element instanceof AttachmentContainingDBElement && null === $element->getMasterPictureAttachment()) {
$element->setMasterPictureAttachment($attachment);
}
}
@ -187,11 +180,13 @@ class AttachmentSubmitHandler
/**
* Move the given attachment to secure location (or back to public folder) if needed.
* @param Attachment $attachment The attachment for which the file should be moved.
* @param bool $secure_location This value determines, if the attachment is moved to the secure or public folder.
*
* @param Attachment $attachment The attachment for which the file should be moved.
* @param bool $secure_location This value determines, if the attachment is moved to the secure or public folder.
*
* @return Attachment The attachment with the updated filepath
*/
protected function moveFile(Attachment $attachment, bool $secure_location) : Attachment
protected function moveFile(Attachment $attachment, bool $secure_location): Attachment
{
//We can not do anything on builtins or external ressources
if ($attachment->isBuiltIn() || $attachment->isExternal()) {
@ -218,7 +213,7 @@ class AttachmentSubmitHandler
$ext = pathinfo($filename, PATHINFO_EXTENSION);
$new_path = $this->generateAttachmentPath($attachment, $secure_location)
. DIRECTORY_SEPARATOR . $this->generateAttachmentFilename($attachment, $ext);
.\DIRECTORY_SEPARATOR.$this->generateAttachmentFilename($attachment, $ext);
//Move file to new directory
$fs = new Filesystem();
@ -232,12 +227,13 @@ class AttachmentSubmitHandler
}
/**
* Download the URL set in the attachment and save it on the server
* @param Attachment $attachment
* Download the URL set in the attachment and save it on the server.
*
* @param array $options The options from the handleFormSubmit function
*
* @return Attachment The attachment with the new filepath
*/
protected function downloadURL(Attachment $attachment, array $options) : Attachment
protected function downloadURL(Attachment $attachment, array $options): Attachment
{
//Check if we are allowed to download files
if (!$this->allow_attachments_downloads) {
@ -248,7 +244,7 @@ class AttachmentSubmitHandler
$fs = new Filesystem();
$attachment_folder = $this->generateAttachmentPath($attachment, $options['secure_attachment']);
$tmp_path = $attachment_folder . DIRECTORY_SEPARATOR . $this->generateAttachmentFilename($attachment, 'tmp');
$tmp_path = $attachment_folder.\DIRECTORY_SEPARATOR.$this->generateAttachmentFilename($attachment, 'tmp');
try {
$response = $this->httpClient->request('GET', $url, [
@ -256,7 +252,7 @@ class AttachmentSubmitHandler
]);
if (200 !== $response->getStatusCode()) {
throw new AttachmentDownloadException('Status code: ' . $response->getStatusCode());
throw new AttachmentDownloadException('Status code: '.$response->getStatusCode());
}
//Open a temporary file in the attachment folder
@ -271,7 +267,7 @@ class AttachmentSubmitHandler
//File download should be finished here, so determine the new filename and extension
$headers = $response->getHeaders();
//Try to determine an filename
$filename = "";
$filename = '';
//If an content disposition header was set try to extract the filename out of it
if (isset($headers['content-disposition'])) {
@ -281,7 +277,7 @@ class AttachmentSubmitHandler
}
//If we dont know filename yet, try to determine it out of url
if ($filename === "") {
if ('' === $filename) {
$filename = basename(parse_url($url, PHP_URL_PATH));
}
@ -297,14 +293,13 @@ class AttachmentSubmitHandler
}
//Rename the file to its new name and save path to attachment entity
$new_path = $attachment_folder . DIRECTORY_SEPARATOR . $this->generateAttachmentFilename($attachment, $new_ext);
$new_path = $attachment_folder.\DIRECTORY_SEPARATOR.$this->generateAttachmentFilename($attachment, $new_ext);
$fs->rename($tmp_path, $new_path);
//Make our file path relative to %BASE%
$new_path = $this->pathResolver->realPathToPlaceholder($new_path);
//Save the path to the attachment
$attachment->setPath($new_path);
} catch (TransportExceptionInterface $exception) {
throw new AttachmentDownloadException('Transport error!');
}
@ -313,13 +308,15 @@ class AttachmentSubmitHandler
}
/**
* Moves the given uploaded file to a permanent place and saves it into the attachment
* @param Attachment $attachment The attachment in which the file should be saved
* @param UploadedFile $file The file which was uploaded
* @param array $options The options from the handleFormSubmit function
* Moves the given uploaded file to a permanent place and saves it into the attachment.
*
* @param Attachment $attachment The attachment in which the file should be saved
* @param UploadedFile $file The file which was uploaded
* @param array $options The options from the handleFormSubmit function
*
* @return Attachment The attachment with the new filepath
*/
protected function upload(Attachment $attachment, UploadedFile $file, array $options) : Attachment
protected function upload(Attachment $attachment, UploadedFile $file, array $options): Attachment
{
//Move our temporay attachment to its final location
$file_path = $file->move(
@ -336,4 +333,4 @@ class AttachmentSubmitHandler
return $attachment;
}
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony)
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
*
@ -17,16 +17,12 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
namespace App\Services\Attachments;
use App\Entity\Attachments\Attachment;
use App\Services\Attachments\AttachmentManager;
use Liip\ImagineBundle\Service\FilterService;
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\Packages;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
@ -49,7 +45,6 @@ class AttachmentURLGenerator
$this->attachmentHelper = $attachmentHelper;
$this->filterService = $filterService;
//Determine a normalized path to the public folder (assets are relative to this folder)
$this->public_path = $this->pathResolver->parameterToAbsolutePath('public');
}
@ -57,46 +52,46 @@ class AttachmentURLGenerator
/**
* Converts the absolute file path to a version relative to the public folder, that can be passed to asset
* Asset Component functions.
* @param string $absolute_path The absolute path that should be converted.
* @param string|null $public_path The public path to which the relative pathes should be created.
* The path must NOT have a trailing slash!
* If this is set to null, the global public/ folder is used.
*
* @param string $absolute_path The absolute path that should be converted.
* @param string|null $public_path The public path to which the relative pathes should be created.
* The path must NOT have a trailing slash!
* If this is set to null, the global public/ folder is used.
*
* @return string|null The relative version of the string. Null if the absolute path was not a child folder
* of public path
* of public path
*/
public function absolutePathToAssetPath(string $absolute_path, ?string $public_path = null) : ?string
public function absolutePathToAssetPath(string $absolute_path, ?string $public_path = null): ?string
{
if ($public_path === null) {
if (null === $public_path) {
$public_path = $this->public_path;
}
//Our absolute path must begin with public path or we can not use it for asset pathes.
if (strpos($absolute_path, $public_path) !== 0) {
if (0 !== strpos($absolute_path, $public_path)) {
return null;
}
//Return the part relative after public path.
return substr($absolute_path, strlen($public_path) + 1);
return substr($absolute_path, \strlen($public_path) + 1);
}
/**
* Returns a URL under which the attachment file can be viewed.
* @param Attachment $attachment
*
* @return string
*/
public function getViewURL(Attachment $attachment) : string
public function getViewURL(Attachment $attachment): string
{
$absolute_path = $this->attachmentHelper->toAbsoluteFilePath($attachment);
if ($absolute_path === null) {
throw new \RuntimeException(
'The given attachment is external or has no valid file, so no URL can get generated for it!
Use Attachment::getURL() to get the external URL!'
);
if (null === $absolute_path) {
throw new \RuntimeException('The given attachment is external or has no valid file, so no URL can get generated for it!
Use Attachment::getURL() to get the external URL!');
}
$asset_path = $this->absolutePathToAssetPath($absolute_path);
//If path is not relative to public path or marked as secure, serve it via controller
if ($asset_path === null || $attachment->isSecure()) {
if (null === $asset_path || $attachment->isSecure()) {
return $this->urlGenerator->generate('attachment_view', ['id' => $attachment->getID()]);
}
@ -106,11 +101,10 @@ class AttachmentURLGenerator
/**
* Returns a URL to an thumbnail of the attachment file.
* @param Attachment $attachment
* @param string $filter_name
*
* @return string
*/
public function getThumbnailURL(Attachment $attachment, string $filter_name = 'thumbnail_sm') : string
public function getThumbnailURL(Attachment $attachment, string $filter_name = 'thumbnail_sm'): string
{
if (!$attachment->isPicture()) {
throw new \InvalidArgumentException('Thumbnail creation only works for picture attachments!');
@ -121,21 +115,19 @@ class AttachmentURLGenerator
}
$absolute_path = $this->attachmentHelper->toAbsoluteFilePath($attachment);
if ($absolute_path === null) {
throw new \RuntimeException(
'The given attachment is external or has no valid file, so no URL can get generated for it!'
);
if (null === $absolute_path) {
throw new \RuntimeException('The given attachment is external or has no valid file, so no URL can get generated for it!');
}
$asset_path = $this->absolutePathToAssetPath($absolute_path);
//If path is not relative to public path or marked as secure, serve it via controller
if ($asset_path === null || $attachment->isSecure()) {
if (null === $asset_path || $attachment->isSecure()) {
return $this->urlGenerator->generate('attachment_view', ['id' => $attachment->getID()]);
}
//For builtin ressources it is not useful to create a thumbnail
//because the footprints images are small and highly optimized already.
if ($filter_name === 'thumbnail_md' && $attachment->isBuiltIn()) {
if ('thumbnail_md' === $filter_name && $attachment->isBuiltIn()) {
return $this->assets->getUrl($asset_path);
}
@ -144,13 +136,13 @@ class AttachmentURLGenerator
}
/**
* Returns a download link to the file associated with the attachment
* @param Attachment $attachment
* Returns a download link to the file associated with the attachment.
*
* @return string
*/
public function getDownloadURL(Attachment $attachment) : string
public function getDownloadURL(Attachment $attachment): string
{
//Redirect always to download controller, which sets the correct headers for downloading:
return $this->urlGenerator->generate('attachment_download', ['id' => $attachment->getID()]);
}
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony)
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
*
@ -17,21 +17,17 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
namespace App\Services\Attachments;
use App\Entity\Attachments\Attachment;
use App\Services\Attachments\AttachmentPathResolver;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Cache\CacheInterface;
/**
* This service is used to find builtin attachment ressources
* @package App\Services
* This service is used to find builtin attachment ressources.
*/
class BuiltinAttachmentsFinder
{
@ -50,7 +46,7 @@ class BuiltinAttachmentsFinder
'limit' => 15, //Given only 15 entries
//'allowed_extensions' => [], //Filter the filenames. For example ['jpg', 'jpeg'] to only get jpegs.
//'placeholders' => Attachment::BUILTIN_PLACEHOLDER, //By default use all builtin ressources,
'empty_returns_all' => false //Return the whole list of ressources when empty keyword is given
'empty_returns_all' => false, //Return the whole list of ressources when empty keyword is given
]);
}
@ -58,9 +54,10 @@ class BuiltinAttachmentsFinder
* Returns a list of all builtin ressources.
* The array is a list of the relative filenames using the %PLACEHOLDERS%.
* The list contains the files from all configured valid ressoureces.
*
* @return array The list of the ressources, or an empty array if an error happened.
*/
public function getListOfRessources() : array
public function getListOfRessources(): array
{
try {
return $this->cache->get('attachment_builtin_ressources', function () {
@ -73,7 +70,7 @@ class BuiltinAttachmentsFinder
foreach (Attachment::BUILTIN_PLACEHOLDER as $placeholder) {
$tmp = $this->pathResolver->placeholderToRealPath($placeholder);
//Ignore invalid/deactivated placeholders:
if ($tmp !== null) {
if (null !== $tmp) {
$finder->in($tmp);
}
}
@ -93,13 +90,15 @@ class BuiltinAttachmentsFinder
}
/**
* Find all ressources which are matching the given keyword and the specified options
* @param string $keyword The keyword you want to search for.
* @param array $options Here you can specify some options (see configureOptions for list of options)
* Find all ressources which are matching the given keyword and the specified options.
*
* @param string $keyword The keyword you want to search for.
* @param array $options Here you can specify some options (see configureOptions for list of options)
* @param array|null $base_list The list from which should be used as base for filtering.
*
* @return array The list of the results matching the specified keyword and options
*/
public function find(string $keyword, array $options = [], ?array $base_list = []) : array
public function find(string $keyword, array $options = [], ?array $base_list = []): array
{
if (empty($base_list)) {
$base_list = $this->getListOfRessources();
@ -109,14 +108,12 @@ class BuiltinAttachmentsFinder
$this->configureOptions($resolver);
$options = $resolver->resolve($options);
/*
if (empty($options['placeholders'])) {
return [];
} */
if ($keyword === '') {
if ('' === $keyword) {
if ($options['empty_returns_all']) {
$keyword = '.*';
} else {
@ -127,7 +124,7 @@ class BuiltinAttachmentsFinder
$keyword = preg_quote($keyword, '/');
}
/*TODO: Implement placheolder and extension filter */
/*TODO: Implement placheolder and extension filter */
/* if (!empty($options['allowed_extensions'])) {
$keyword .= "\.(";
foreach ($options['allowed_extensions'] as $extension) {
@ -137,9 +134,8 @@ class BuiltinAttachmentsFinder
} */
//Ignore case
$regex = '/.*' . $keyword . '.*/i';
$regex = '/.*'.$keyword.'.*/i';
return preg_grep($regex, $base_list);
}
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony)
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
*
@ -17,7 +17,6 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
namespace App\Services\Attachments;
@ -31,11 +30,9 @@ use Symfony\Contracts\Cache\ItemInterface;
* An servive that helps working with filetype filters (based on the format <input type=file> accept uses.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers for
* more details.
* @package App\Services\Attachments
*/
class FileTypeFilterTools
{
//The file extensions that will be used for the 'video/*', 'image/*', 'audio/*' placeholders
//These file formats can be directly played in common browesers
//Source: https://www.chromium.org/audio-video
@ -53,17 +50,18 @@ class FileTypeFilterTools
$this->cache = $cache;
}
/**
* Check if a filetype filter string is valid.
*
* @param string $filter The filter string that should be validated.
*
* @return bool Returns true, if the string is valid.
*/
public function validateFilterString(string $filter) : bool
public function validateFilterString(string $filter): bool
{
$filter = trim($filter);
//An empty filter is valid (means no filter applied)
if ($filter === '') {
if ('' === $filter) {
return true;
}
@ -73,7 +71,7 @@ class FileTypeFilterTools
$element = trim($element);
if (!preg_match('/^\.\w+$/', $element) // .ext is allowed
&& !preg_match('/^[-\w.]+\/[-\w.]+/', $element) //Explicit MIME type is allowed
&& !in_array($element, static::ALLOWED_MIME_PLACEHOLDERS, false)) { //image/* is allowed
&& !\in_array($element, static::ALLOWED_MIME_PLACEHOLDERS, false)) { //image/* is allowed
return false;
}
}
@ -85,10 +83,12 @@ class FileTypeFilterTools
/**
* Normalize a filter string. All extensions are converted to lowercase, too much whitespaces are removed.
* The filter string is not validated.
*
* @param string $filter The filter string that should be normalized.
*
* @return string The normalized filter string
*/
public function normalizeFilterString(string $filter) : string
public function normalizeFilterString(string $filter): string
{
$filter = trim($filter);
//Replace other separators, with , so we can split it properly
@ -101,75 +101,80 @@ class FileTypeFilterTools
foreach ($elements as $key => &$element) {
$element = trim($element);
//Remove empty elements
if ($element === '') {
if ('' === $element) {
unset($elements[$key]);
}
//Convert *.jpg to .jpg
if (strpos($element, '*.') === 0) {
if (0 === strpos($element, '*.')) {
$element = str_replace('*.', '.', $element);
}
//Convert image to image/*
if ($element === 'image' || $element === 'image/') {
if ('image' === $element || 'image/' === $element) {
$element = 'image/*';
} elseif ($element === 'video' || $element === 'video/') {
} elseif ('video' === $element || 'video/' === $element) {
$element = 'video/*';
} elseif ($element === 'audio' || $element === 'audio/') {
} elseif ('audio' === $element || 'audio/' === $element) {
$element = 'audio/*';
} elseif (!preg_match('/^[-\w.]+\/[-\w.*]+/', $element) && strpos($element, '.') !== 0) {
} elseif (!preg_match('/^[-\w.]+\/[-\w.*]+/', $element) && 0 !== strpos($element, '.')) {
//Convert jpg to .jpg
$element = '.' . $element;
$element = '.'.$element;
}
}
$elements = array_unique($elements);
return implode($elements, ',');
return implode(',', $elements);
}
/**
* Get a list of all file extensions that matches the given filter string
* Get a list of all file extensions that matches the given filter string.
*
* @param string $filter A valid filetype filter string.
*
* @return string[] An array of allowed extensions ['txt', 'csv', 'gif']
*/
public function resolveFileExtensions(string $filter) : array
public function resolveFileExtensions(string $filter): array
{
$filter = trim($filter);
return $this->cache->get('filter_exts_' . md5($filter), function (ItemInterface $item) use ($filter) {
return $this->cache->get('filter_exts_'.md5($filter), function (ItemInterface $item) use ($filter) {
$elements = explode(',', $filter);
$extensions = [];
foreach ($elements as $element) {
$element = trim($element);
if (strpos($element, '.') === 0) {
if (0 === strpos($element, '.')) {
//We found an explicit specified file extension -> add it to list
$extensions[] = substr($element, 1);
} elseif ($element === 'image/*') {
} elseif ('image/*' === $element) {
$extensions = array_merge($extensions, static::IMAGE_EXTS);
} elseif ($element === 'audio/*') {
} elseif ('audio/*' === $element) {
$extensions = array_merge($extensions, static::AUDIO_EXTS);
} elseif ($element === 'image/*') {
} elseif ('image/*' === $element) {
$extensions = array_merge($extensions, static::VIDEO_EXTS);
} elseif (preg_match('/^[-\w.]+\/[-\w.*]+/', $element)) {
$extensions = array_merge($extensions, $this->mimeTypes->getExtensions($element));
}
}
return array_unique($extensions);
});
}
/**
* Check if the given extension matches the filter.
* @param string $filter The filter which should be used for checking.
*
* @param string $filter The filter which should be used for checking.
* @param string $extension The extension that should be checked.
*
* @return bool Returns true, if the extension is allowed with the given filter.
*/
public function isExtensionAllowed(string $filter, string $extension) : bool
public function isExtensionAllowed(string $filter, string $extension): bool
{
$extension = strtolower($extension);
return empty($filter) || in_array($extension, $this->resolveFileExtensions($filter), false);
return empty($filter) || \in_array($extension, $this->resolveFileExtensions($filter), false);
}
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony)
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
*
@ -17,19 +17,13 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
namespace App\Services\Attachments;
use App\Entity\Attachments\Attachment;
use App\Entity\Parts\Part;
use App\Services\Attachments\AttachmentManager;
/**
* @package App\Services\Attachments
*/
class PartPreviewGenerator
{
protected $attachmentHelper;
@ -42,11 +36,13 @@ class PartPreviewGenerator
/**
* Returns a list of attachments that can be used for previewing the part ordered by priority.
* The priority is: Part MasterAttachment -> Footprint MasterAttachment -> Category MasterAttachment
* -> Storelocation Attachment -> MeasurementUnit Attachment -> ManufacturerAttachment
* -> Storelocation Attachment -> MeasurementUnit Attachment -> ManufacturerAttachment.
*
* @param Part $part The part for which the attachments should be determined.
*
* @return Attachment[]
*/
public function getPreviewAttachments(Part $part) : array
public function getPreviewAttachments(Part $part): array
{
$list = [];
@ -56,14 +52,14 @@ class PartPreviewGenerator
$list[] = $attachment;
}
if ($part->getFootprint() !== null) {
if (null !== $part->getFootprint()) {
$attachment = $part->getFootprint()->getMasterPictureAttachment();
if ($this->isAttachmentValidPicture($attachment)) {
$list[] = $attachment;
}
}
if ($part->getCategory() !== null) {
if (null !== $part->getCategory()) {
$attachment = $part->getCategory()->getMasterPictureAttachment();
if ($this->isAttachmentValidPicture($attachment)) {
$list[] = $attachment;
@ -71,7 +67,7 @@ class PartPreviewGenerator
}
foreach ($part->getPartLots() as $lot) {
if ($lot->getStorageLocation() !== null) {
if (null !== $lot->getStorageLocation()) {
$attachment = $lot->getStorageLocation()->getMasterPictureAttachment();
if ($this->isAttachmentValidPicture($attachment)) {
$list[] = $attachment;
@ -79,14 +75,14 @@ class PartPreviewGenerator
}
}
if ($part->getPartUnit() !== null) {
if (null !== $part->getPartUnit()) {
$attachment = $part->getPartUnit()->getMasterPictureAttachment();
if ($this->isAttachmentValidPicture($attachment)) {
$list[] = $attachment;
}
}
if ($part->getManufacturer() !== null) {
if (null !== $part->getManufacturer()) {
$attachment = $part->getManufacturer()->getMasterPictureAttachment();
if ($this->isAttachmentValidPicture($attachment)) {
$list[] = $attachment;
@ -99,10 +95,12 @@ class PartPreviewGenerator
/**
* Determines what attachment should be used for previewing a part (especially in part table).
* The returned attachment is guaranteed to be existing and be a picture.
*
* @param Part $part The part for which the attachment should be determined
*
* @return Attachment|null
*/
public function getTablePreviewAttachment(Part $part) : ?Attachment
public function getTablePreviewAttachment(Part $part): ?Attachment
{
//First of all we check if the master attachment of the part is set (and a picture)
$attachment = $part->getMasterPictureAttachment();
@ -111,7 +109,7 @@ class PartPreviewGenerator
}
//Otherwise check if the part has a footprint with a valid masterattachment
if ($part->getFootprint() !== null) {
if (null !== $part->getFootprint()) {
$attachment = $part->getFootprint()->getMasterPictureAttachment();
if ($this->isAttachmentValidPicture($attachment)) {
return $attachment;
@ -124,13 +122,15 @@ class PartPreviewGenerator
/**
* Checks if a attachment is exising and a valid picture.
*
* @param Attachment|null $attachment The attachment that should be checked.
*
* @return bool True if the attachment is valid.
*/
protected function isAttachmentValidPicture(?Attachment $attachment) : bool
protected function isAttachmentValidPicture(?Attachment $attachment): bool
{
return $attachment !== null
return null !== $attachment
&& $attachment->isPicture()
&& $this->attachmentHelper->isFileExisting($attachment);
}
}
}