From d82fb479cf61b08fedac55d64c615d5d33f5bdc8 Mon Sep 17 00:00:00 2001 From: Benjamin Perche Date: Wed, 2 Jul 2014 14:00:49 +0200 Subject: [PATCH] =?UTF-8?q?Define=20archive=20builders=20and=20formatters?= =?UTF-8?q?=20=09nouveau=20fichier:=20core/lib/Thelia/Core/FileFormat/Arch?= =?UTF-8?q?ive/AbstractArchiveBuilder.php=20=09nouveau=20fichier:=20core/l?= =?UTF-8?q?ib/Thelia/Core/FileFormat/Archive/ArchiveBuilder/ZipArchiveBuil?= =?UTF-8?q?der.php=20=09nouveau=20fichier:=20core/lib/Thelia/Core/FileForm?= =?UTF-8?q?at/Archive/ArchiveBuilder/ZipArchiveException.php=20=09nouveau?= =?UTF-8?q?=20fichier:=20core/lib/Thelia/Core/FileFormat/Archive/ArchiveBu?= =?UTF-8?q?ilderInterface.php=20=09nouveau=20fichier:=20core/lib/Thelia/Co?= =?UTF-8?q?re/FileFormat/Archive/ArchiveBuilderManager.php=20=09nouveau=20?= =?UTF-8?q?fichier:=20core/lib/Thelia/Core/FileFormat/FormatInterface.php?= =?UTF-8?q?=20=09nouveau=20fichier:=20core/lib/Thelia/Core/FileFormat/Form?= =?UTF-8?q?atter/AbstractFormatter.php=20=09nouveau=20fichier:=20core/lib/?= =?UTF-8?q?Thelia/Core/FileFormat/Formatter/Exception/BadFormattedStringEx?= =?UTF-8?q?ception.php=20=09nouveau=20fichier:=20core/lib/Thelia/Core/File?= =?UTF-8?q?Format/Formatter/FormatterManager.php=20=09nouveau=20fichier:?= =?UTF-8?q?=20core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/Test?= =?UTF-8?q?Resources/bad=5Fformatted.zip=20=09nouveau=20fichier:=20core/li?= =?UTF-8?q?b/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/TestResources/?= =?UTF-8?q?test=5Ffile=20=09nouveau=20fichier:=20core/lib/Thelia/Tests/Fil?= =?UTF-8?q?eFormat/Archive/ArchiveBuilder/TestResources/well=5Fformatted.z?= =?UTF-8?q?ip=20=09nouveau=20fichier:=20core/lib/Thelia/Tests/FileFormat/A?= =?UTF-8?q?rchive/ArchiveBuilder/ZipArchiveBuilderTest.php=20=09nouveau=20?= =?UTF-8?q?fichier:=20core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuil?= =?UTF-8?q?derManagerTest.php=20=09nouveau=20fichier:=20core/lib/Thelia/Te?= =?UTF-8?q?sts/FileFormat/Formatter/FormatterManagerTest.php=20=09nouveau?= =?UTF-8?q?=20fichier:=20core/lib/Thelia/Tests/Tools/FakeFileDownloader.ph?= =?UTF-8?q?p=20=09nouveau=20fichier:=20core/lib/Thelia/Tests/Tools/FileDow?= =?UTF-8?q?nloaderTest.php=20=09nouveau=20fichier:=20core/lib/Thelia/Tools?= =?UTF-8?q?/FileDownload/FileDownloader.php=20=09nouveau=20fichier:=20core?= =?UTF-8?q?/lib/Thelia/Tools/FileDownload/FileDownloaderAwareTrait.php=20?= =?UTF-8?q?=09nouveau=20fichier:=20core/lib/Thelia/Tools/FileDownload/File?= =?UTF-8?q?DownloaderInterface.php=20=09modifi=C3=A9:=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20core/lib/Thelia/Tools/URL.php?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Archive/AbstractArchiveBuilder.php | 25 + .../ArchiveBuilder/ZipArchiveBuilder.php | 608 ++++++++++++ .../ArchiveBuilder/ZipArchiveException.php | 23 + .../Archive/ArchiveBuilderInterface.php | 90 ++ .../Archive/ArchiveBuilderManager.php | 76 ++ .../Core/FileFormat/FormatInterface.php | 58 ++ .../Formatter/AbstractFormatter.php | 41 + .../Exception/BadFormattedStringException.php | 23 + .../FileFormat/Formatter/FormatterManager.php | 69 ++ .../TestResources/bad_formatted.zip | 0 .../ArchiveBuilder/TestResources/test_file | 0 .../TestResources/well_formatted.zip | 0 .../ArchiveBuilder/ZipArchiveBuilderTest.php | 344 +++++++ .../Archive/ArchiveBuilderManagerTest.php | 75 ++ .../Formatter/FormatterManagerTest.php | 74 ++ .../Thelia/Tests/Tools/FakeFileDownloader.php | 44 + .../Thelia/Tests/Tools/FileDownloaderTest.php | 63 ++ .../Thelia/Tests/Tools/FileManagerTest.php | 900 ++++++++++++++++++ .../Tools/FileDownload/FileDownloader.php | 142 +++ .../FileDownload/FileDownloaderAwareTrait.php | 47 + .../FileDownload/FileDownloaderInterface.php | 44 + core/lib/Thelia/Tools/URL.php | 8 + 22 files changed, 2754 insertions(+) create mode 100644 core/lib/Thelia/Core/FileFormat/Archive/AbstractArchiveBuilder.php create mode 100644 core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilder/ZipArchiveBuilder.php create mode 100644 core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilder/ZipArchiveException.php create mode 100644 core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilderInterface.php create mode 100644 core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilderManager.php create mode 100644 core/lib/Thelia/Core/FileFormat/FormatInterface.php create mode 100644 core/lib/Thelia/Core/FileFormat/Formatter/AbstractFormatter.php create mode 100644 core/lib/Thelia/Core/FileFormat/Formatter/Exception/BadFormattedStringException.php create mode 100644 core/lib/Thelia/Core/FileFormat/Formatter/FormatterManager.php create mode 100644 core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/TestResources/bad_formatted.zip create mode 100644 core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/TestResources/test_file create mode 100644 core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/TestResources/well_formatted.zip create mode 100644 core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/ZipArchiveBuilderTest.php create mode 100644 core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilderManagerTest.php create mode 100644 core/lib/Thelia/Tests/FileFormat/Formatter/FormatterManagerTest.php create mode 100644 core/lib/Thelia/Tests/Tools/FakeFileDownloader.php create mode 100644 core/lib/Thelia/Tests/Tools/FileDownloaderTest.php create mode 100644 core/lib/Thelia/Tests/Tools/FileManagerTest.php create mode 100644 core/lib/Thelia/Tools/FileDownload/FileDownloader.php create mode 100644 core/lib/Thelia/Tools/FileDownload/FileDownloaderAwareTrait.php create mode 100644 core/lib/Thelia/Tools/FileDownload/FileDownloaderInterface.php diff --git a/core/lib/Thelia/Core/FileFormat/Archive/AbstractArchiveBuilder.php b/core/lib/Thelia/Core/FileFormat/Archive/AbstractArchiveBuilder.php new file mode 100644 index 000000000..66613ba45 --- /dev/null +++ b/core/lib/Thelia/Core/FileFormat/Archive/AbstractArchiveBuilder.php @@ -0,0 +1,25 @@ + + */ +abstract class AbstractArchiveBuilder implements FormatInterface, ArchiveBuilderInterface +{ + use FileDownloaderAwareTrait; +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilder/ZipArchiveBuilder.php b/core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilder/ZipArchiveBuilder.php new file mode 100644 index 000000000..f38b1c3fe --- /dev/null +++ b/core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilder/ZipArchiveBuilder.php @@ -0,0 +1,608 @@ + + * + * This class is a driver defined by AbstractArchiveBuilder, + * it's goal is to manage Zip archives. + * + * You can create a new archive by creating a new instance, + * or load an existing zip with the static method loadArchive. + */ +class ZipArchiveBuilder extends AbstractArchiveBuilder +{ + const TEMP_DIRECTORY_NAME = "archive_builder"; + + /** + * @var \ZipArchive + */ + protected $zip; + + /** + * @var string This is the absolute path to the zip file in cache + */ + protected $zip_cache_file; + + /** + * @var string This is the path of the cache + */ + protected $cache_dir; + + /** + * @var \Thelia\Log\Tlog + */ + protected $logger; + + /** + * @var Translator + */ + protected $translator; + + public function __construct() + { + $this->zip = new \ZipArchive(); + + $this->logger = Tlog::getNewInstance(); + + $this->translator = Translator::getInstance(); + } + + /** + * On the destruction of the class, + * remove the temporary file. + */ + function __destruct() + { + if ($this->zip instanceof \ZipArchive) { + @$this->zip->close(); + + if (file_exists($this->zip_cache_file)) { + unlink($this->zip_cache_file); + } + } + } + + /** + * @param string $filePath It is the path to access the file. + * @param string $directoryInArchive This is the directory where it will be stored in the archive + * @param null|string $name The name of the file in the archive. if it null or empty, it keeps the same name + * @param bool $isOnline + * @return $this + * @throws \Thelia\Exception\FileNotFoundException + * @throws \Thelia\Exception\FileNotReadableException + * + * This methods adds a file in the archive. + * If the file is local, $isOnline must be false, + * If the file online, $filePath must be an URL. + */ + public function addFile($filePath, $directoryInArchive = null, $name = null, $isOnline = false) + { + /** + * Add empty directory if it doesn't exist + */ + if (empty($directoryInArchive) || preg_match("#^\/+$#", $directoryInArchive)) { + $directoryInArchive = ""; + } + + if(!empty($directoryInArchive) && $directoryInArchive != "/") { + $directoryInArchive = $this->getDirectoryPath($directoryInArchive); + + if (!$this->zip->addEmptyDir($directoryInArchive)) { + throw new \ErrorException( + $this->translator->trans( + "The directory %dir has not been created in the archive", + [ + "%dir" => $directoryInArchive + ] + ) + ); + } + } + + if ($isOnline) { + $fileDownloadCache = $this->cache_dir . DS . "download"; + + $this->getFileDownloader() + ->download($filePath, $fileDownloadCache) + ; + + $filePath = $fileDownloadCache; + } else { + if (!file_exists($filePath)) { + $this->throwFileNotFound($filePath); + } else if (!is_readable($filePath)) { + throw new FileNotReadableException( + $this->translator + ->trans( + "The file %file is not readable", + [ + "%file" => $filePath, + ] + ) + ); + } + } + + if (empty($name)) { + $name = basename($filePath); + } + + $destination = $directoryInArchive . $name; + + if (!$this->zip->addFile($filePath,$destination)) { + $translatedErrorMessage = $this->translator->trans( + "An error occurred while adding this file to the archive: %file", + [ + "%file" => $filePath + ] + ); + + $this->logger->error($translatedErrorMessage); + + throw new \ErrorException($translatedErrorMessage); + } + + $this->commit(); + + return $this; + } + + /** + * @param $pathInArchive + * @return $this + * @throws \Thelia\Exception\FileNotFoundException + * @throws \ErrorException + * + * This method deletes a file in the archive + */ + public function deleteFile($pathInArchive) + { + $pathInArchive = $this->getFilePath($pathInArchive); + + if (!$this->hasFile($pathInArchive)) { + $this->throwFileNotFound($pathInArchive); + } + + $deleted = $this->zip->deleteName($pathInArchive); + + if (!$deleted) { + throw new \ErrorException( + $this->translator->trans( + "The file %file has not been deleted", + [ + "%file" => $pathInArchive, + ] + ) + ); + } + + return $this; + } + + /** + * @return \Thelia\Core\HttpFoundation\Response + * + * This method return an instance of a Response with the archive as content. + */ + public function buildArchiveResponse() + { + $this->zip->comment = "Generated by Thelia v" . Thelia::THELIA_VERSION; + + $this->commit(); + + if (!file_exists($this->zip_cache_file)) { + $this->throwFileNotFound($this->zip_cache_file); + } + + if (!is_readable($this->zip_cache_file)) { + throw new FileNotReadableException( + $this->translator->trans( + "The cache file %file is not readable", + [ + "%file" => $this->zip_cache_file + ] + ) + ); + } + + $content = file_get_contents($this->zip_cache_file); + + $this->zip->close(); + + return new Response( + $content, + 200, + [ + "Content-Type" => $this->getMimeType() + ] + ); + } + + /** + * @param string $pathToArchive + * @param string $environment + * @param bool $isOnline + * @param FileDownloaderInterface $fileDownloader + * @return $this + * @throws \Thelia\Exception\FileNotFoundException + * @throws \Thelia\Exception\HttpUrlException + * + * Loads an archive + */ + public static function loadArchive( + $pathToArchive, + $environment, + $isOnline = false, + FileDownloaderInterface $fileDownloader = null + ) { + /** @var ZipArchiveBuilder $instance */ + $instance = new static(); + + $instance->setEnvironment($environment); + $zip = $instance->getRawZipArchive(); + $zip->close(); + + if ($fileDownloader !== null) { + $instance->setFileDownloader($fileDownloader); + } + + if ($isOnline) { + /** + * It's an online file + */ + $instance->getFileDownloader() + ->download($pathToArchive, $instance->getZipCacheFile()) + ; + } else { + /** + * It's a local file + */ + if (!is_file($pathToArchive) || !is_readable($pathToArchive)) { + $instance->throwFileNotFound($pathToArchive); + } + + if (!copy($pathToArchive, $instance->getZipCacheFile())) { + $translatedErrorMessage = $instance->getTranslator()->trans( + "An unknown error happend while copying %prev to %dest", + [ + "%prev" => $pathToArchive, + "%dest" => $instance->getZipCacheFile(), + ] + ); + + $instance->getLogger() + ->error($translatedErrorMessage) + ; + + throw new \ErrorException($translatedErrorMessage); + } + } + + if (true !== $return = $zip->open($instance->getZipCacheFile())) { + throw new ZipArchiveException( + $instance->getZipErrorMessage($return) + ); + } + + return $instance; + } + + /** + * @param $pathToFile + * @return bool + * + * Checks if the archive has a file + */ + public function hasFile($pathToFile) + { + return $this->zip + ->locateName($this->getFilePath($pathToFile)) !== false + ; + } + + /** + * @param string $directory + * @return bool + * + * Checks if the link $directory exists and if it's not a file. + */ + public function hasDirectory($directory) + { + $link = $this->zip->locateName($this->getDirectoryPath($directory)); + + return $link !== false; + } + + /** + * @param string $environment + * @return $this + * + * Sets the execution environment of the Kernel, + * used to know which cache is used. + */ + public function setEnvironment($environment) + { + $theliaCacheDir = THELIA_CACHE_DIR . $environment . DS; + + if (!is_writable($theliaCacheDir)) { + throw new \ErrorException( + $this->translator->trans( + "The cache directory \"%env\" is not writable", + [ + "%env" => $environment + ] + ) + ); + } + + $archiveBuilderCacheDir = $this->cache_dir = $theliaCacheDir . static::TEMP_DIRECTORY_NAME; + + if (!is_dir($archiveBuilderCacheDir) && !mkdir($archiveBuilderCacheDir, 0755)) { + throw new \ErrorException( + $this->translator->trans( + "Error while creating the directory \"%directory\"", + [ + "%directory" => static::TEMP_DIRECTORY_NAME + ] + ) + ); + } + + $cacheFileName = md5 (uniqid()); + + $cacheFile = $archiveBuilderCacheDir . DS . $cacheFileName; + $cacheFile .= "." . $this->getExtension(); + + if (file_exists($cacheFile)) { + unlink($cacheFile); + } + + $opening = $this->zip->open( + $cacheFile, + \ZipArchive::CREATE + ); + + if($opening !== true) { + throw new \ErrorException( + $this->translator->trans( + "Unknown" + ) + ); + } + + $this->zip_cache_file = $cacheFile; + + return $this; + } + + /** + * @param $errorCode + * @return string + * + * Give the error message of a \ZipArchive error code + */ + public function getZipErrorMessage($errorCode) + { + switch ($errorCode) { + case \ZipArchive::ER_EXISTS: + $message = "The archive already exists"; + break; + + case \ZipArchive::ER_INCONS: + $message = "The archive is inconsistent"; + break; + + case \ZipArchive::ER_INVAL: + $message = "Invalid argument"; + break; + + case \ZipArchive::ER_MEMORY: + $message = "Memory error"; + break; + + case \ZipArchive::ER_NOENT: + $message = "The file doesn't exist"; + break; + + case \ZipArchive::ER_NOZIP: + $message = "The file is not a zip archive"; + break; + + case \ZipArchive::ER_OPEN: + $message = "The file could not be open"; + break; + + case \ZipArchive::ER_READ: + $message = "The file could not be read"; + break; + + case \ZipArchive::ER_SEEK: + $message = "Position error"; + break; + + default: + $message = "Unknown error on the ZIP archive"; + break; + } + + $zipMessageHead = $this->translator->trans( + "Zip Error" + ); + + $message = $this->translator->trans( + "[%zip_head] " . $message, + [ + "%zip_head" => $zipMessageHead + ] + ); + + return $message; + } + + public function commit() + { + $this->zip->close(); + $result = $this->zip->open($this->getZipCacheFile()); + + if ($result !== true) { + throw new \ErrorException( + $this->translator->trans( + "The changes could on the Zip Archive not be commited" + ) + ); + } + + return $this; + } + + /** + * @param string $initialString + * @return string + * + * Gives a valid file path for \ZipArchive + */ + public function getFilePath($initialString) + { + /** + * Remove the / at the beginning and the end. + */ + $initialString = trim($initialString, "/"); + + /** + * Remove the double, triple, ... slashes + */ + $initialString = preg_replace("#\/{2,}#", "/", $initialString); + + if (preg_match("#\/?[^\/]+\/[^/]+\/?#", $initialString)) { + $initialString = "/" . $initialString; + } + return $initialString; + } + + /** + * @param string $initialString + * @return string + * + * Gives a valid directory path for \ZipArchive + */ + public function getDirectoryPath($initialString) + { + $initialString = $this->getFilePath($initialString); + + if ($initialString[0] !== "/") { + $initialString = "/" . $initialString; + } + + return $initialString . "/"; + } + + public function throwFileNotFound($file) + { + + throw new FileNotFoundException( + $this->getTranslator() + ->trans( + "The file %file is missing or is not readable", + [ + "%file" => $file, + ] + ) + ); + } + + /** + * @return string + * + * This method must return a string, the name of the format. + * + * example: + * return "XML"; + */ + public function getName() + { + return "ZIP"; + } + + /** + * @return string + * + * This method must return a string, the extension of the file format, without the ".". + * The string should be lowercase. + * + * example: + * return "xml"; + */ + public function getExtension() + { + return "zip"; + } + + /** + * @return string + * + * This method must return a string, the mime type of the file format. + * + * example: + * return "application/json"; + */ + public function getMimeType() + { + return "application/zip"; + } + + /** + * @return Tlog + */ + public function getLogger() + { + return $this->logger; + } + + /** + * @return Translator + */ + public function getTranslator() + { + return $this->translator; + } + + /** + * @return \ZipArchive + */ + public function getRawZipArchive() + { + return $this->zip; + } + + public function getZipCacheFile() + { + return $this->zip_cache_file; + } + + public function getCacheDir() + { + return $this->cache_dir; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilder/ZipArchiveException.php b/core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilder/ZipArchiveException.php new file mode 100644 index 000000000..c85fce339 --- /dev/null +++ b/core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilder/ZipArchiveException.php @@ -0,0 +1,23 @@ + + */ +class ZipArchiveException extends \ErrorException +{ + +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilderInterface.php b/core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilderInterface.php new file mode 100644 index 000000000..c779722a6 --- /dev/null +++ b/core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilderInterface.php @@ -0,0 +1,90 @@ + + * + * This interface defines the methods that an archive creator must have. + */ +interface ArchiveBuilderInterface +{ + /** + * @param string $filePath It is the path to access the file. + * @param string $directoryInArchive This is the directory where it will be stored in the archive + * @param null|string $name The name of the file in the archive. if it null or empty, it keeps the same name + * @param bool $isOnline + * @return $this + * @throws \Thelia\Exception\FileNotFoundException + * @throws \Thelia\Exception\FileNotReadableException + * + * This methods adds a file in the archive. + * If the file is local, $isOnline must be false, + * If the file online, $filePath must be an URL. + */ + public function addFile($filePath, $directoryInArchive = "/", $name = null, $isOnline = false); + + /** + * @param $pathInArchive + * @return $this + * @throws \Thelia\Exception\FileNotFoundException + * + * This method deletes a file in the archive + */ + public function deleteFile($pathInArchive); + + /** + * @return \Thelia\Core\HttpFoundation\Response + * + * This method return an instance of a Response with the archive as content. + */ + public function buildArchiveResponse(); + + /** + * @param string $pathToArchive + * @param bool $isOnline + * @return $this + * @throws \Thelia\Exception\FileNotFoundException + * @throws \Thelia\Exception\HttpUrlException + * + * Loads an archive + */ + public static function loadArchive($pathToArchive, $environment, $isOnline = false); + + /** + * @param $pathToFile + * @return bool + * + * Checks if the archive has a file + */ + public function hasFile($pathToFile); + + /** + * @param string $directory + * @return bool + * + * Check if the archive has a directory + */ + public function hasDirectory($directory); + + /** + * @param string $environment + * @return $this + * + * Sets the execution environment of the Kernel, + * used to know which cache is used. + */ + public function setEnvironment($environment); +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilderManager.php b/core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilderManager.php new file mode 100644 index 000000000..7957fca4f --- /dev/null +++ b/core/lib/Thelia/Core/FileFormat/Archive/ArchiveBuilderManager.php @@ -0,0 +1,76 @@ + + */ +class ArchiveBuilderManager +{ + protected $archiveCreators = array(); + + protected $environment; + + public function __construct($environment) + { + $this->environment = $environment; + } + /** + * @param AbstractArchiveBuilder $archiveCreator + * @return $this + */ + public function add(AbstractArchiveBuilder $archiveCreator) + { + if (null !== $archiveCreator) { + $archiveCreator->setEnvironment($this->environment); + + $this->archiveCreators[$archiveCreator->getName()] = $archiveCreator; + } + + return $this; + } + + /** + * @param $name + * @return $this + * @throws \OutOfBoundsException + */ + public function delete($name) + { + if (!array_key_exists($name, $this->archiveCreators)) { + throw new \OutOfBoundsException( + Translator::getInstance()->trans( + "The archive creator %name doesn't exist", + [ + "%name" => $name + ] + ) + ); + } + + unset($this->archiveCreators[$name]); + + return $this; + } + + /** + * @return array[AbstractArchiveBuilder] + */ + public function getAll() + { + return $this->archiveCreators; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/FileFormat/FormatInterface.php b/core/lib/Thelia/Core/FileFormat/FormatInterface.php new file mode 100644 index 000000000..cd10a0cb0 --- /dev/null +++ b/core/lib/Thelia/Core/FileFormat/FormatInterface.php @@ -0,0 +1,58 @@ + + * + * This interface defines what a formatter must have: + * - A name ( example: XML, JSON, yaml ) + * - An extension ( example: xml, json, yml ) + * - A mime type ( example: application/xml, application/json, ... ) + */ +interface FormatInterface +{ + + /** + * @return string + * + * This method must return a string, the name of the format. + * + * example: + * return "XML"; + */ + public function getName(); + + /** + * @return string + * + * This method must return a string, the extension of the file format, without the ".". + * The string should be lowercase. + * + * example: + * return "xml"; + */ + public function getExtension(); + + /** + * @return string + * + * This method must return a string, the mime type of the file format. + * + * example: + * return "application/json"; + */ + public function getMimeType(); +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/FileFormat/Formatter/AbstractFormatter.php b/core/lib/Thelia/Core/FileFormat/Formatter/AbstractFormatter.php new file mode 100644 index 000000000..f991535c8 --- /dev/null +++ b/core/lib/Thelia/Core/FileFormat/Formatter/AbstractFormatter.php @@ -0,0 +1,41 @@ + + */ +abstract class AbstractFormatter implements FormatInterface +{ + /** + * @param array $data + * @return mixed + * + * Encodes an array to the desired format. + * $data array only contains array and scalar data. + */ + abstract public function encode(array $data); + + /** + * @param $data + * @return array + * @throws \Thelia\Core\FileFormat\Formatter\Exception\BadFormattedStringException + * + * this method must do exactly the opposite of encode and return + * an array composed of array and scalar data. + */ + abstract public function decode($data); +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/FileFormat/Formatter/Exception/BadFormattedStringException.php b/core/lib/Thelia/Core/FileFormat/Formatter/Exception/BadFormattedStringException.php new file mode 100644 index 000000000..6d763a01b --- /dev/null +++ b/core/lib/Thelia/Core/FileFormat/Formatter/Exception/BadFormattedStringException.php @@ -0,0 +1,23 @@ + + */ +class BadFormattedStringException extends \ErrorException +{ + +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/FileFormat/Formatter/FormatterManager.php b/core/lib/Thelia/Core/FileFormat/Formatter/FormatterManager.php new file mode 100644 index 000000000..cc8aad8e4 --- /dev/null +++ b/core/lib/Thelia/Core/FileFormat/Formatter/FormatterManager.php @@ -0,0 +1,69 @@ + + */ +class FormatterManager +{ + + protected $formatters = array(); + + /** + * @param $archiveCreator + * @return $this + */ + public function add(AbstractFormatter $formatter) + { + if (null !== $formatter) { + $this->formatters[$formatter->getName()] = $formatter; + } + + return $this; + } + + /** + * @param $name + * @return $this + * @throws \OutOfBoundsException + */ + public function delete($name) + { + if (!array_key_exists($name, $this->formatters)) { + throw new \OutOfBoundsException( + Translator::getInstance()->trans( + "The formatter %name doesn't exist", + [ + "%name" => $name + ] + ) + ); + } + + unset($this->formatters[$name]); + + return $this; + } + + /** + * @return array[AbstractFormatter] + */ + public function getAll() + { + return $this->formatters; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/TestResources/bad_formatted.zip b/core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/TestResources/bad_formatted.zip new file mode 100644 index 000000000..e69de29bb diff --git a/core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/TestResources/test_file b/core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/TestResources/test_file new file mode 100644 index 000000000..e69de29bb diff --git a/core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/TestResources/well_formatted.zip b/core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/TestResources/well_formatted.zip new file mode 100644 index 000000000..e69de29bb diff --git a/core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/ZipArchiveBuilderTest.php b/core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/ZipArchiveBuilderTest.php new file mode 100644 index 000000000..68c5fbfad --- /dev/null +++ b/core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilder/ZipArchiveBuilderTest.php @@ -0,0 +1,344 @@ + + */ +class ZipArchiveBuilderTest extends \PHPUnit_Framework_TestCase +{ + /** @var ZipArchiveBuilder */ + protected $zip; + + /** @var ZipArchiveBuilder */ + protected $loadedZip; + + public function setUp() + { + new Translator( + new Container() + ); + + Tlog::getNewInstance(); + + $this->zip = new ZipArchiveBuilder(); + + $this->loadedZip = $this->zip->loadArchive( + __DIR__ . DS . "TestResources/well_formatted.zip", + "dev" + ); + } + + /** + * This method formats a path to be compatible with \ZipArchive + * + * + */ + public function testGetFilePath() + { + $this->assertEquals( + "foo", + $this->zip->getFilePath("foo") + ); + + $this->assertEquals( + "foo", + $this->zip->getFilePath("/foo") + ); + + $this->assertEquals( + "foo", + $this->zip->getFilePath("foo/") + ); + + $this->assertEquals( + "foo", + $this->zip->getFilePath("/foo/") + ); + + $this->assertEquals( + "/foo/bar", + $this->zip->getFilePath("foo/bar") + ); + + $this->assertEquals( + "/foo/bar", + $this->zip->getFilePath("/foo/bar") + ); + + $this->assertEquals( + "/foo/bar", + $this->zip->getFilePath("/foo//bar/") + ); + + $this->assertEquals( + "/foo/bar", + $this->zip->getFilePath("/foo/bar/") + ); + + $this->assertEquals( + "/foo/bar/baz", + $this->zip->getFilePath("foo/bar/baz") + ); + + $this->assertEquals( + "/foo/bar/baz", + $this->zip->getFilePath("//foo/bar///baz/") + ); + } + + public function testGetDirectoryPath() + { + $this->assertEquals( + "/foo/", + $this->zip->getDirectoryPath("foo") + ); + + $this->assertEquals( + "/foo/", + $this->zip->getDirectoryPath("/foo") + ); + + $this->assertEquals( + "/foo/", + $this->zip->getDirectoryPath("foo/") + ); + + $this->assertEquals( + "/foo/", + $this->zip->getDirectoryPath("/foo/") + ); + + $this->assertEquals( + "/foo/bar/", + $this->zip->getDirectoryPath("foo/bar") + ); + + $this->assertEquals( + "/foo/bar/", + $this->zip->getDirectoryPath("/foo/bar") + ); + + $this->assertEquals( + "/foo/bar/", + $this->zip->getDirectoryPath("/foo//bar/") + ); + + $this->assertEquals( + "/foo/bar/", + $this->zip->getDirectoryPath("/foo/bar/") + ); + + $this->assertEquals( + "/foo/bar/baz/", + $this->zip->getDirectoryPath("foo/bar/baz") + ); + + $this->assertEquals( + "/foo/bar/baz/", + $this->zip->getDirectoryPath("//foo/bar///baz/") + ); + } + + public function testLoadValidZip() + { + $loadedZip = $this->zip->loadArchive( + __DIR__ . DS . "TestResources/well_formatted.zip", + "dev" + ); + + $this->assertInstanceOf( + get_class($this->loadedZip), + $loadedZip + ); + } + + /** + * @expectedException \Thelia\Core\FileFormat\Archive\ArchiveBuilder\ZipArchiveException + * @expectedExceptionMessage [Zip Error] The file is not a zip archive + */ + public function testLoadNotValidZip() + { + $this->zip->loadArchive( + __DIR__ . DS . "TestResources/bad_formatted.zip", + "dev" + ); + } + + /** + * @expectedException \Thelia\Exception\FileNotFoundException + */ + public function testLoadNotExistingFile() + { + $this->zip->loadArchive( + __DIR__ . DS . "TestResources/this_file_doesn_t_exist.zip", + "dev" + ); + } + + public function testLoadOnlineAvailableAndValidFile() + { + $this->zip->loadArchive( + __DIR__ . DS . "TestResources/well_formatted.zip", + "dev", + true, + FakeFileDownloader::getInstance() + ); + } + + /** + * @expectedException \Thelia\Core\FileFormat\Archive\ArchiveBuilder\ZipArchiveException + * @expectedExceptionMessage [Zip Error] The file is not a zip archive + */ + public function testLoadOnlineAvailableAndNotValidFile() + { + $this->zip->loadArchive( + __DIR__ . DS . "TestResources/bad_formatted.zip", + "dev", + true, + FakeFileDownloader::getInstance() + ); + } + + /** + * @expectedException \Thelia\Exception\FileNotFoundException + */ + public function testLoadOnlineNotExistingFile() + { + $this->zip->loadArchive( + __DIR__ . DS . "TestResources/this_file_doesn_t_exist.zip", + "dev", + true, + FakeFileDownloader::getInstance() + ); + } + + public function testHasFile() + { + $this->assertTrue( + $this->loadedZip->hasFile("LICENSE.txt") + ); + + $this->assertFalse( + $this->loadedZip->hasFile("foo") + ); + + $this->assertFalse( + $this->loadedZip->hasFile("LICENSE.TXT") + ); + } + + public function testDeleteFile() + { + $this->assertInstanceOf( + get_class($this->loadedZip), + $this->loadedZip->deleteFile("LICENSE.txt") + ); + } + + /** + * @expectedException \Thelia\Exception\FileNotFoundException + */ + public function testDeleteNotExistingFile() + { + $this->loadedZip->deleteFile("foo"); + } + + public function testAddExistingFile() + { + $this->assertInstanceOf( + get_class($this->loadedZip), + $this->loadedZip->addFile( + __DIR__ . DS . "TestResources/test_file", + "/f/f/" + ) + ); + + /** + * Show that even weird paths are correctly interpreted + */ + $this->assertTrue( + $this->loadedZip->hasFile("///f//f/test_file/") + ); + } + + public function testAddExistingFileInNewDirectory() + { + $this->assertInstanceOf( + get_class($this->loadedZip), + $this->loadedZip->addFile( + __DIR__ . DS . "TestResources/test_file", + "testDir" + ) + ); + + /** + * You can create and check the directory and files + * without giving the initial and final slashes + */ + $this->assertTrue( + $this->loadedZip->hasDirectory("testDir") + ); + + $this->assertTrue( + $this->loadedZip->hasDirectory("/testDir") + ); + + $this->assertTrue( + $this->loadedZip->hasDirectory("testDir/") + ); + + $this->assertTrue( + $this->loadedZip->hasDirectory("/testDir/") + ); + + $this->assertTrue( + $this->loadedZip->hasFile("testDir/test_file") + ); + + $this->assertTrue( + $this->loadedZip->hasFile("/testDir/test_file") + ); + + $this->assertTrue( + $this->loadedZip->hasFile("testDir/test_file/") + ); + + $this->assertTrue( + $this->loadedZip->hasFile("/testDir/test_file/") + ); + } + + public function testBuildArchiveResponse() + { + $loadedArchiveResponse = $this->loadedZip + ->buildArchiveResponse() + ; + + $loadedArchiveResponseContent = $loadedArchiveResponse->getContent(); + + $content = file_get_contents(__DIR__ . DS . "TestResources/well_formatted.zip"); + + $this->assertEquals( + $content, + $loadedArchiveResponseContent + ); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilderManagerTest.php b/core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilderManagerTest.php new file mode 100644 index 000000000..c01b35a45 --- /dev/null +++ b/core/lib/Thelia/Tests/FileFormat/Archive/ArchiveBuilderManagerTest.php @@ -0,0 +1,75 @@ + + */ +class ArchiveBuilderManagerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ArchiveBuilderManager + */ + protected $manager; + + public function setUp() + { + new Translator( + new Container() + ); + $this->manager = new ArchiveBuilderManager("dev"); + } + + public function testAddArchiveBuilder() + { + /** @var AbstractArchiveBuilder $instance */ + $instance = $this->getMock("Thelia\\Core\\FileFormat\\Archive\\AbstractArchiveBuilder"); + + $this->manager->add($instance); + + $archiveBuilders = $this->manager->getAll(); + + $this->assertTrue( + array_key_exists($instance->getName(), $archiveBuilders) + ); + } + + public function testDeleteArchiveBuilder() + { + /** @var AbstractArchiveBuilder $instance */ + $instance = $this->getMock("Thelia\\Core\\FileFormat\\Archive\\AbstractArchiveBuilder"); + + $this->manager->add($instance); + + $this->manager->delete($instance->getName()); + + $this->assertTrue( + count($this->manager->getAll()) === 0 + ); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testDeleteNotExistingArchiveBuilder() + { + $this->manager->delete("foo"); + } + +} \ No newline at end of file diff --git a/core/lib/Thelia/Tests/FileFormat/Formatter/FormatterManagerTest.php b/core/lib/Thelia/Tests/FileFormat/Formatter/FormatterManagerTest.php new file mode 100644 index 000000000..8759c2fca --- /dev/null +++ b/core/lib/Thelia/Tests/FileFormat/Formatter/FormatterManagerTest.php @@ -0,0 +1,74 @@ + + */ +class FormatterManagerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var FormatterManager + */ + protected $manager; + + public function setUp() + { + new Translator( + new Container() + ); + $this->manager = new FormatterManager(); + } + + public function testAddFormatter() + { + /** @var AbstractFormatter $instance */ + $instance = $this->getMock("Thelia\\Core\\FileFormat\\Formatter\\AbstractFormatter"); + + $this->manager->add($instance); + + $archiveBuilders = $this->manager->getAll(); + + $this->assertTrue( + array_key_exists($instance->getName(), $archiveBuilders) + ); + } + + public function testDeleteFormatter() + { + /** @var AbstractFormatter $instance */ + $instance = $this->getMock("Thelia\\Core\\FileFormat\\Formatter\\AbstractFormatter"); + + $this->manager->add($instance); + + $this->manager->delete($instance->getName()); + + $this->assertTrue( + count($this->manager->getAll()) === 0 + ); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testDeleteNotExistingFormatter() + { + $this->manager->delete("foo"); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Tests/Tools/FakeFileDownloader.php b/core/lib/Thelia/Tests/Tools/FakeFileDownloader.php new file mode 100644 index 000000000..851036062 --- /dev/null +++ b/core/lib/Thelia/Tests/Tools/FakeFileDownloader.php @@ -0,0 +1,44 @@ + + */ +class FakeFileDownloader extends FileDownloader +{ + /** + * @param string $url + * @param string $pathToStore + * @throws \Thelia\Exception\FileNotFoundException + * @throws \ErrorException + * @throws \HttpUrlException + * + * Downloads the file $url in $pathToStore + */ + public function download($url, $pathToStore) + { + if (!file_exists($url) || !is_readable($url)) { + throw new FileNotFoundException(); + } + + if (!copy($url, $pathToStore)) { + throw new \ErrorException(); + } + } + +} \ No newline at end of file diff --git a/core/lib/Thelia/Tests/Tools/FileDownloaderTest.php b/core/lib/Thelia/Tests/Tools/FileDownloaderTest.php new file mode 100644 index 000000000..3ebc3e459 --- /dev/null +++ b/core/lib/Thelia/Tests/Tools/FileDownloaderTest.php @@ -0,0 +1,63 @@ + + */ +class FileDownloaderTest extends \PHPUnit_Framework_TestCase +{ + /** @var FileDownloader */ + protected $downloader; + + public function setUp() + { + $logger = Tlog::getNewInstance(); + $translator = new Translator( + new Container() + ); + + $this->downloader = new FileDownloader( + $logger, + $translator + ); + } + + /** + * @expectedException \Thelia\Exception\HttpUrlException + * @expectedExceptionMessage Tried to download a file, but the URL was not valid: foo + */ + public function testFileDownloadInvalidURL() + { + $this->downloader->download("foo", "bar"); + } + + /** + * @expectedException \Thelia\Exception\FileNotFoundException + */ + public function testFileDownloadNonExistingFile() + { + $this->downloader->download("https://github.com/foo/bar/baz", "baz"); + } + + public function testFileDownloadSuccess() + { + $this->downloader->download("https://github.com/thelia/thelia", "php://temp"); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Tests/Tools/FileManagerTest.php b/core/lib/Thelia/Tests/Tools/FileManagerTest.php new file mode 100644 index 000000000..29e4f2966 --- /dev/null +++ b/core/lib/Thelia/Tests/Tools/FileManagerTest.php @@ -0,0 +1,900 @@ +markTestIncomplete( + 'This test has not been implemented yet : Mock issue' + ); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue('translated')); + + $stubRequest = $this->getMockBuilder('\Thelia\Core\HttpFoundation\Request') + ->disableOriginalConstructor() + ->getMock(); + + $stubSecurity = $this->getMockBuilder('\Thelia\Core\Security\SecurityContext') + ->disableOriginalConstructor() + ->getMock(); + $stubSecurity->expects($this->any()) + ->method('getAdminUser') + ->will($this->returnValue(new Admin())); + + // Create a map of arguments to return values. + $map = array( + array('thelia.translator', $stubTranslator), + array('request', $stubRequest), + array('thelia.securityContext', $stubSecurity) + ); + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + $stubContainer->expects($this->any()) + ->method('get') + ->will($this->returnValueMap($map)); + + $stubProductImage = $this->getMockBuilder('\Thelia\Model\ProductImage') + ->disableOriginalConstructor() + ->getMock(); + $stubProductImage->expects($this->any()) + ->method('getUploadDir') + ->will($this->returnValue(THELIA_LOCAL_DIR . 'media/images/product')); + $stubProductImage->expects($this->any()) + ->method('getId') + ->will($this->returnValue(42)); + $stubProductImage->expects($this->any()) + ->method('setFile') + ->will($this->returnValue(true)); + $stubProductImage->expects($this->any()) + ->method('save') + ->will($this->returnValue(0)); + + $stubUploadedFile = $this->getMockBuilder('\Symfony\Component\HttpFoundation\File\UploadedFile') + ->disableOriginalConstructor() + ->getMock(); + $stubUploadedFile->expects($this->any()) + ->method('getClientOriginalName') + ->will($this->returnValue('goodName')); + $stubUploadedFile->expects($this->any()) + ->method('getClientOriginalExtension') + ->will($this->returnValue('png')); + $stubUploadedFile->expects($this->any()) + ->method('move') + ->will($this->returnValue($stubUploadedFile)); + + $fileManager = new FileManager($stubContainer); + + $newUploadedFiles = array(); + + $actual = $fileManager->copyUploadedFile(24, FileManager::TYPE_PRODUCT, $stubProductImage, $stubUploadedFile, $newUploadedFiles, FileManager::FILE_TYPE_IMAGES); + + $this->assertCount(1, $actual); + }*/ + + /** + * @covers Thelia\Tools\FileManager::copyUploadedFile + * @expectedException \Thelia\Exception\ImageException + */ + /*public function testCopyUploadedFileExceptionImageException() + { + $this->markTestIncomplete( + 'This test has not been implemented yet : Mock issue' + ); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue('translated')); + + $stubRequest = $this->getMockBuilder('\Thelia\Core\HttpFoundation\Request') + ->disableOriginalConstructor() + ->getMock(); + + $stubSecurity = $this->getMockBuilder('\Thelia\Core\Security\SecurityContext') + ->disableOriginalConstructor() + ->getMock(); + $stubSecurity->expects($this->any()) + ->method('getAdminUser') + ->will($this->returnValue(new Admin())); + + // Create a map of arguments to return values. + $map = array( + array('thelia.translator', $stubTranslator), + array('request', $stubRequest), + array('thelia.securityContext', $stubSecurity) + ); + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + $stubContainer->expects($this->any()) + ->method('get') + ->will($this->returnValueMap($map)); + + $stubProductImage = $this->getMockBuilder('\Thelia\Model\ProductImage') + ->disableOriginalConstructor() + ->getMock(); + $stubProductImage->expects($this->any()) + ->method('getUploadDir') + ->will($this->returnValue(THELIA_LOCAL_DIR . 'media/images/product')); + $stubProductImage->expects($this->any()) + ->method('getId') + ->will($this->returnValue(42)); + $stubProductImage->expects($this->any()) + ->method('setFile') + ->will($this->returnValue(true)); + $stubProductImage->expects($this->any()) + ->method('save') + ->will($this->returnValue(0)); + + $stubUploadedFile = $this->getMockBuilder('\Symfony\Component\HttpFoundation\File\UploadedFile') + ->disableOriginalConstructor() + ->getMock(); + $stubUploadedFile->expects($this->any()) + ->method('getClientOriginalName') + ->will($this->returnValue('goodName')); + $stubUploadedFile->expects($this->any()) + ->method('getClientOriginalExtension') + ->will($this->returnValue('png')); + $stubUploadedFile->expects($this->any()) + ->method('move') + ->will($this->returnValue($stubUploadedFile)); + + $fileManager = new FileManager($stubContainer); + + $newUploadedFiles = array(); + + $actual = $fileManager->copyUploadedFile(24, FileManager::TYPE_PRODUCT, $stubProductImage, $stubUploadedFile, $newUploadedFiles, FileManager::FILE_TYPE_DOCUMENTS); + + }*/ + + /** + * @covers Thelia\Tools\FileManager::saveImage + */ + public function testSaveImageProductImage() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $stubProductImage = $this->getMockBuilder('\Thelia\Model\ProductImage') + ->disableOriginalConstructor() + ->getMock(); + $stubProductImage->expects($this->any()) + ->method('save') + ->will($this->returnValue(10)); + $stubProductImage->expects($this->any()) + ->method('getFile') + ->will($this->returnValue('file')); + + $fileManager = new FileManager(); + + $event = new ImageCreateOrUpdateEvent(FileManager::TYPE_PRODUCT, 24); + + $expected = 10; + $actual = $fileManager->saveImage($event, $stubProductImage); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::saveDocument + */ + public function testSaveDocumentProductDocument() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $stubProductDocument = $this->getMockBuilder('\Thelia\Model\ProductDocument') + ->disableOriginalConstructor() + ->getMock(); + $stubProductDocument->expects($this->any()) + ->method('save') + ->will($this->returnValue(10)); + $stubProductDocument->expects($this->any()) + ->method('getFile') + ->will($this->returnValue('file')); + + $fileManager = new FileManager(); + + $event = new DocumentCreateOrUpdateEvent(FileManager::TYPE_PRODUCT, 24); + + $expected = 10; + $actual = $fileManager->saveDocument($event, $stubProductDocument); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::saveImage + */ + public function testSaveImageCategoryImage() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $stubCategoryImage = $this->getMockBuilder('\Thelia\Model\CategoryImage') + ->disableOriginalConstructor() + ->getMock(); + $stubCategoryImage->expects($this->any()) + ->method('save') + ->will($this->returnValue(10)); + $stubCategoryImage->expects($this->any()) + ->method('getFile') + ->will($this->returnValue('file')); + + $fileManager = new FileManager(); + + $event = new ImageCreateOrUpdateEvent(FileManager::TYPE_CATEGORY, 24); + + $expected = 10; + $actual = $fileManager->saveImage($event, $stubCategoryImage); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::saveDocument + */ + public function testSaveDocumentCategoryDocument() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $stubCategoryDocument = $this->getMockBuilder('\Thelia\Model\CategoryDocument') + ->disableOriginalConstructor() + ->getMock(); + $stubCategoryDocument->expects($this->any()) + ->method('save') + ->will($this->returnValue(10)); + $stubCategoryDocument->expects($this->any()) + ->method('getFile') + ->will($this->returnValue('file')); + + $fileManager = new FileManager(); + + $event = new DocumentCreateOrUpdateEvent(FileManager::TYPE_CATEGORY, 24); + + $expected = 10; + $actual = $fileManager->saveDocument($event, $stubCategoryDocument); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::saveImage + */ + public function testSaveImageFolderImage() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $stubFolderImage = $this->getMockBuilder('\Thelia\Model\FolderImage') + ->disableOriginalConstructor() + ->getMock(); + $stubFolderImage->expects($this->any()) + ->method('save') + ->will($this->returnValue(10)); + $stubFolderImage->expects($this->any()) + ->method('getFile') + ->will($this->returnValue('file')); + + $fileManager = new FileManager(); + + $event = new ImageCreateOrUpdateEvent(FileManager::TYPE_FOLDER, 24); + + $expected = 10; + $actual = $fileManager->saveImage($event, $stubFolderImage); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::saveDocument + */ + public function testSaveDocumentFolderDocument() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $stubFolderDocument = $this->getMockBuilder('\Thelia\Model\FolderDocument') + ->disableOriginalConstructor() + ->getMock(); + $stubFolderDocument->expects($this->any()) + ->method('save') + ->will($this->returnValue(10)); + $stubFolderDocument->expects($this->any()) + ->method('getFile') + ->will($this->returnValue('file')); + + $fileManager = new FileManager(); + + $event = new DocumentCreateOrUpdateEvent(FileManager::TYPE_FOLDER, 24); + + $expected = 10; + $actual = $fileManager->saveDocument($event, $stubFolderDocument); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::saveImage + */ + public function testSaveImageContentImage() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $stubContentImage = $this->getMockBuilder('\Thelia\Model\ContentImage') + ->disableOriginalConstructor() + ->getMock(); + $stubContentImage->expects($this->any()) + ->method('save') + ->will($this->returnValue(10)); + $stubContentImage->expects($this->any()) + ->method('getFile') + ->will($this->returnValue('file')); + + $fileManager = new FileManager(); + + $event = new ImageCreateOrUpdateEvent(FileManager::TYPE_CONTENT, 24); + + $expected = 10; + $actual = $fileManager->saveImage($event, $stubContentImage); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::saveDocument + */ + public function testSaveDocumentContentDocument() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $stubContentDocument = $this->getMockBuilder('\Thelia\Model\ContentDocument') + ->disableOriginalConstructor() + ->getMock(); + $stubContentDocument->expects($this->any()) + ->method('save') + ->will($this->returnValue(10)); + $stubContentDocument->expects($this->any()) + ->method('getFile') + ->will($this->returnValue('file')); + + $fileManager = new FileManager(); + + $event = new DocumentCreateOrUpdateEvent(FileManager::TYPE_CONTENT, 24); + + $expected = 10; + $actual = $fileManager->saveDocument($event, $stubContentDocument); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::saveImage + * @expectedException \Thelia\Exception\ImageException + */ + public function testSaveImageExceptionImageException() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + $fileManager = new FileManager(); + + $stubProductImage = $this->getMockBuilder('\Thelia\Model\ProductImage') + ->disableOriginalConstructor() + ->getMock(); + $stubProductImage->expects($this->any()) + ->method('save') + ->will($this->returnValue(10)); + $stubProductImage->expects($this->any()) + ->method('getFile') + ->will($this->returnValue('file')); + + $event = new ImageCreateOrUpdateEvent('bad', 24); + + $fileManager->saveImage($event, $stubProductImage); + } + + /** + * @covers Thelia\Tools\FileManager::saveDocument + * @expectedException \Thelia\Model\Exception\InvalidArgumentException + */ + public function testSaveDocumentExceptionDocumentException() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + $fileManager = new FileManager(); + + $stubProductDocument = $this->getMockBuilder('\Thelia\Model\ProductDocument') + ->disableOriginalConstructor() + ->getMock(); + $stubProductDocument->expects($this->any()) + ->method('save') + ->will($this->returnValue(10)); + $stubProductDocument->expects($this->any()) + ->method('getFile') + ->will($this->returnValue('file')); + + $event = new DocumentCreateOrUpdateEvent('bad', 24); + + $fileManager->saveDocument($event, $stubProductDocument); + } + + /** + * @covers Thelia\Tools\FileManager::saveImage + * @expectedException \Thelia\Exception\ImageException + */ + public function testSaveImageExceptionImageException2() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + $fileManager = new FileManager(); + + $stubProductImage = $this->getMockBuilder('\Thelia\Model\ProductImage') + ->disableOriginalConstructor() + ->getMock(); + $stubProductImage->expects($this->any()) + ->method('save') + ->will($this->returnValue(0)); + $stubProductImage->expects($this->any()) + ->method('getFile') + ->will($this->returnValue('file')); + + $event = new ImageCreateOrUpdateEvent(FileManager::TYPE_PRODUCT, 24); + + $fileManager->saveImage($event, $stubProductImage); + } + + /** + * @covers Thelia\Tools\FileManager::saveDocument + * @expectedException \Thelia\Model\Exception\InvalidArgumentException + */ + public function testSaveDocumentExceptionDocumentException2() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + $fileManager = new FileManager(); + + $stubProductDocument = $this->getMockBuilder('\Thelia\Model\ProductDocument') + ->disableOriginalConstructor() + ->getMock(); + $stubProductDocument->expects($this->any()) + ->method('save') + ->will($this->returnValue(0)); + $stubProductDocument->expects($this->any()) + ->method('getFile') + ->will($this->returnValue('file')); + + $event = new DocumentCreateOrUpdateEvent(FileManager::TYPE_PRODUCT, 24); + + $fileManager->saveDocument($event, $stubProductDocument); + } + + /** + * @covers Thelia\Tools\FileManager::sanitizeFileName + */ + public function testSanitizeFileName() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $fileManager = new FileManager(); + $badFileName = 'a/ze\érà~çè§^"$*+-_°)(&é<>@#ty2/[\/:*?"<>|]/fi?.fUPPERile.exel../e*'; + + $expected = 'azer-_ty2fi.fupperile.exel..e'; + $actual = $fileManager->sanitizeFileName($badFileName); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::getImageModel + */ + public function testGetImageModel() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $fileManager = new FileManager(); + $actual = $fileManager->getImageModel(FileManager::TYPE_PRODUCT); + $this->assertInstanceOf('\Thelia\Model\ProductImage', $actual); + $actual = $fileManager->getImageModel(FileManager::TYPE_CATEGORY); + $this->assertInstanceOf('\Thelia\Model\CategoryImage', $actual); + $actual = $fileManager->getImageModel(FileManager::TYPE_CONTENT); + $this->assertInstanceOf('\Thelia\Model\ContentImage', $actual); + $actual = $fileManager->getImageModel(FileManager::TYPE_FOLDER); + $this->assertInstanceOf('\Thelia\Model\FolderImage', $actual); + $actual = $fileManager->getImageModel('bad'); + $this->assertNull($actual); + } + + /** + * @covers Thelia\Tools\FileManager::getDocumentModel + */ + public function testGetDocumentModel() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $fileManager = new FileManager(); + $actual = $fileManager->getDocumentModel(FileManager::TYPE_PRODUCT); + $this->assertInstanceOf('\Thelia\Model\ProductDocument', $actual); + $actual = $fileManager->getDocumentModel(FileManager::TYPE_CATEGORY); + $this->assertInstanceOf('\Thelia\Model\CategoryDocument', $actual); + $actual = $fileManager->getDocumentModel(FileManager::TYPE_CONTENT); + $this->assertInstanceOf('\Thelia\Model\ContentDocument', $actual); + $actual = $fileManager->getDocumentModel(FileManager::TYPE_FOLDER); + $this->assertInstanceOf('\Thelia\Model\FolderDocument', $actual); + $actual = $fileManager->getDocumentModel('bad'); + $this->assertNull($actual); + } + + /** + * @covers Thelia\Tools\FileManager::getImageModelQuery + */ + public function testGetImageModelQuery() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $fileManager = new FileManager(); + $actual = $fileManager->getImageModelQuery(FileManager::TYPE_PRODUCT); + $this->assertInstanceOf('\Thelia\Model\ProductImageQuery', $actual); + $actual = $fileManager->getImageModelQuery(FileManager::TYPE_CATEGORY); + $this->assertInstanceOf('\Thelia\Model\CategoryImageQuery', $actual); + $actual = $fileManager->getImageModelQuery(FileManager::TYPE_CONTENT); + $this->assertInstanceOf('\Thelia\Model\ContentImageQuery', $actual); + $actual = $fileManager->getImageModelQuery(FileManager::TYPE_FOLDER); + $this->assertInstanceOf('\Thelia\Model\FolderImageQuery', $actual); + $actual = $fileManager->getImageModelQuery('bad'); + $this->assertNull($actual); + } + + /** + * @covers Thelia\Tools\FileManager::getDocumentModelQuery + */ + public function testGetDocumentModelQuery() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $fileManager = new FileManager(); + $actual = $fileManager->getDocumentModelQuery(FileManager::TYPE_PRODUCT); + $this->assertInstanceOf('\Thelia\Model\ProductDocumentQuery', $actual); + $actual = $fileManager->getDocumentModelQuery(FileManager::TYPE_CATEGORY); + $this->assertInstanceOf('\Thelia\Model\CategoryDocumentQuery', $actual); + $actual = $fileManager->getDocumentModelQuery(FileManager::TYPE_CONTENT); + $this->assertInstanceOf('\Thelia\Model\ContentDocumentQuery', $actual); + $actual = $fileManager->getDocumentModelQuery(FileManager::TYPE_FOLDER); + $this->assertInstanceOf('\Thelia\Model\FolderDocumentQuery', $actual); + $actual = $fileManager->getDocumentModelQuery('bad'); + $this->assertNull($actual); + } + + /** + * @covers Thelia\Tools\FileManager::getParentFileModel + */ + public function testGetParentFileModel() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $fileManager = new FileManager(); + $actual = $fileManager->getParentFileModel(FileManager::TYPE_PRODUCT, ProductQuery::create()->findOne()->getId()); + $this->assertInstanceOf('\Thelia\Model\Product', $actual); + $actual = $fileManager->getParentFileModel(FileManager::TYPE_CATEGORY, CategoryQuery::create()->findOne()->getId()); + $this->assertInstanceOf('\Thelia\Model\Category', $actual); + $actual = $fileManager->getParentFileModel(FileManager::TYPE_CONTENT, ContentQuery::create()->findOne()->getId()); + $this->assertInstanceOf('\Thelia\Model\Content', $actual); + $actual = $fileManager->getParentFileModel(FileManager::TYPE_FOLDER, FolderQuery::create()->findOne()->getId()); + $this->assertInstanceOf('\Thelia\Model\Folder', $actual, 1); + $actual = $fileManager->getParentFileModel('bad', 1); + $this->assertNull($actual); + } + + /** + * @covers Thelia\Tools\FileManager::getImageForm + */ + /* public function testGetImageForm() + { + // Mock issue + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + }*/ + /** + * @covers Thelia\Tools\FileManager::getDocumentForm + */ + /* public function testGetDocumentForm() + { + // Mock issue + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + }*/ + + /** + * @covers Thelia\Tools\FileManager::getUploadDir + */ + public function testGetUploadDir() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $fileManager = new FileManager(); + + $actual = $fileManager->getUploadDir(FileManager::TYPE_PRODUCT, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals(THELIA_LOCAL_DIR . 'media/images/product', $actual); + $actual = $fileManager->getUploadDir(FileManager::TYPE_CATEGORY, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals(THELIA_LOCAL_DIR . 'media/images/category', $actual); + $actual = $fileManager->getUploadDir(FileManager::TYPE_CONTENT, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals(THELIA_LOCAL_DIR . 'media/images/content', $actual); + $actual = $fileManager->getUploadDir(FileManager::TYPE_FOLDER, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals(THELIA_LOCAL_DIR . 'media/images/folder', $actual); + $actual = $fileManager->getUploadDir('bad', FileManager::FILE_TYPE_IMAGES); + $this->assertEquals(false, $actual); + + $actual = $fileManager->getUploadDir(FileManager::TYPE_PRODUCT, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals(THELIA_LOCAL_DIR . 'media/documents/product', $actual); + $actual = $fileManager->getUploadDir(FileManager::TYPE_CATEGORY, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals(THELIA_LOCAL_DIR . 'media/documents/category', $actual); + $actual = $fileManager->getUploadDir(FileManager::TYPE_CONTENT, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals(THELIA_LOCAL_DIR . 'media/documents/content', $actual); + $actual = $fileManager->getUploadDir(FileManager::TYPE_FOLDER, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals(THELIA_LOCAL_DIR . 'media/documents/folder', $actual); + $actual = $fileManager->getUploadDir('bad', FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals(false, $actual); + + $actual = $fileManager->getUploadDir(FileManager::TYPE_FOLDER, 'bad'); + $this->assertEquals(false, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::getRedirectionUrl + */ + public function testGetRedirectionUrl() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $fileManager = new FileManager(); + + $actual = $fileManager->getRedirectionUrl(FileManager::TYPE_PRODUCT, 1, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals('/admin/products/update?product_id=1¤t_tab=images', $actual); + $actual = $fileManager->getRedirectionUrl(FileManager::TYPE_CATEGORY, 1, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals('/admin/categories/update?category_id=1¤t_tab=images', $actual); + $actual = $fileManager->getRedirectionUrl(FileManager::TYPE_CONTENT, 1, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals('/admin/content/update/1?current_tab=images', $actual); + $actual = $fileManager->getRedirectionUrl(FileManager::TYPE_FOLDER, 1, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals('/admin/folders/update/1?current_tab=images', $actual); + $actual = $fileManager->getRedirectionUrl('bad', 1, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals(false, $actual); + + $actual = $fileManager->getRedirectionUrl(FileManager::TYPE_PRODUCT, 1, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals('/admin/products/update?product_id=1¤t_tab=documents', $actual); + $actual = $fileManager->getRedirectionUrl(FileManager::TYPE_CATEGORY, 1, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals('/admin/categories/update?category_id=1¤t_tab=documents', $actual); + $actual = $fileManager->getRedirectionUrl(FileManager::TYPE_CONTENT, 1, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals('/admin/content/update/1?current_tab=documents', $actual); + $actual = $fileManager->getRedirectionUrl(FileManager::TYPE_FOLDER, 1, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals('/admin/folders/update/1?current_tab=documents', $actual); + $actual = $fileManager->getRedirectionUrl('bad', 1, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals(false, $actual); + + $actual = $fileManager->getRedirectionUrl(FileManager::TYPE_FOLDER, 1, 'bad'); + $this->assertEquals(false, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::getFormId + */ + public function testGetFormId() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $fileManager = new FileManager(); + + $actual = $fileManager->getFormId(FileManager::TYPE_PRODUCT, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals('thelia.admin.product.image.modification', $actual); + $actual = $fileManager->getFormId(FileManager::TYPE_CATEGORY, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals('thelia.admin.category.image.modification', $actual); + $actual = $fileManager->getFormId(FileManager::TYPE_CONTENT, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals('thelia.admin.content.image.modification', $actual); + $actual = $fileManager->getFormId(FileManager::TYPE_FOLDER, FileManager::FILE_TYPE_IMAGES); + $this->assertEquals('thelia.admin.folder.image.modification', $actual); + $actual = $fileManager->getFormId('bad', FileManager::FILE_TYPE_IMAGES); + $this->assertEquals(false, $actual); + + $actual = $fileManager->getFormId(FileManager::TYPE_PRODUCT, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals('thelia.admin.product.document.modification', $actual); + $actual = $fileManager->getFormId(FileManager::TYPE_CATEGORY, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals('thelia.admin.category.document.modification', $actual); + $actual = $fileManager->getFormId(FileManager::TYPE_CONTENT, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals('thelia.admin.content.document.modification', $actual); + $actual = $fileManager->getFormId(FileManager::TYPE_FOLDER, FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals('thelia.admin.folder.document.modification', $actual); + $actual = $fileManager->getFormId('bad', FileManager::FILE_TYPE_DOCUMENTS); + $this->assertEquals(false, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::renameFile + */ + public function testRenameFile() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $stubUploadedFile = $this->getMockBuilder('\Symfony\Component\HttpFoundation\File\UploadedFile') + ->disableOriginalConstructor() + ->getMock(); + $stubUploadedFile->expects($this->any()) + ->method('getClientOriginalExtension') + ->will($this->returnValue('yml')); + $stubUploadedFile->expects($this->any()) + ->method('getClientOriginalName') + ->will($this->returnValue('or1-g_n?al*/&é"filen@me#')); + + $fileManager = new FileManager(); + + $expected = 'or1-g_nalfilenme-1.yml'; + $actual = $fileManager->renameFile(1, $stubUploadedFile); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::renameFile + */ + public function testRenameFileWithoutExtension() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $stubUploadedFile = $this->getMockBuilder('\Symfony\Component\HttpFoundation\File\UploadedFile') + ->disableOriginalConstructor() + ->getMock(); + $stubUploadedFile->expects($this->any()) + ->method('getClientOriginalExtension') + ->will($this->returnValue('')); + $stubUploadedFile->expects($this->any()) + ->method('getClientOriginalName') + ->will($this->returnValue('or1-g_n?al*/&é"filen@me#')); + + $fileManager = new FileManager(); + + $expected = 'or1-g_nalfilenme-1'; + $actual = $fileManager->renameFile(1, $stubUploadedFile); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::isImage + */ + public function testIsImage() + { + $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $fileManager = new FileManager(); + + $actual = $fileManager->isImage('image/jpeg'); + $this->assertTrue($actual); + $actual = $fileManager->isImage('image/png'); + $this->assertTrue($actual); + $actual = $fileManager->isImage('image/gif'); + $this->assertTrue($actual); + + $actual = $fileManager->isImage('bad'); + $this->assertFalse($actual); + $actual = $fileManager->isImage('image/jpg'); + $this->assertFalse($actual); + $actual = $fileManager->isImage('application/x-msdownload'); + $this->assertFalse($actual); + $actual = $fileManager->isImage('application/x-sh'); + $this->assertFalse($actual); + + } + + /** + * @covers Thelia\Tools\FileManager::getAvailableTypes + */ + public function testGetAvailableTypes() + { + $expected = array( + FileManager::TYPE_CATEGORY, + FileManager::TYPE_CONTENT, + FileManager::TYPE_FOLDER, + FileManager::TYPE_PRODUCT, + FileManager::TYPE_MODULE, + ); + $actual = FileManager::getAvailableTypes(); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Tools\FileManager::adminLogAppend + */ + /* public function testAdminLogAppend() + { + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + }*/ + + /** + * @covers Thelia\Tools\FileManager::deleteFile + */ + /* public function testDeleteFile() + { + // @todo see http://tech.vg.no/2011/03/09/mocking-the-file-system-using-phpunit-and-vfsstream/ + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + }*/ +} diff --git a/core/lib/Thelia/Tools/FileDownload/FileDownloader.php b/core/lib/Thelia/Tools/FileDownload/FileDownloader.php new file mode 100644 index 000000000..10eb1ef88 --- /dev/null +++ b/core/lib/Thelia/Tools/FileDownload/FileDownloader.php @@ -0,0 +1,142 @@ + + */ +class FileDownloader implements FileDownloaderInterface +{ + /** @var LoggerInterface */ + protected $logger; + + /** @var Translator */ + protected $translator; + + public function __construct(LoggerInterface $logger, Translator $translator) + { + $this->logger = $logger; + + $this->translator = $translator; + } + + public static function getInstance() + { + return new static(Tlog::getInstance(), TheliaTranslator::getInstance()); + } + + /** + * @param string $url + * @param string $pathToStore + * @throws \Thelia\Exception\FileNotFoundException + * @throws \ErrorException + * @throws \HttpUrlException + * + * Downloads the file $url in $pathToStore + */ + public function download($url, $pathToStore) + { + if (!URL::checkUrl($url)) { + /** + * The URL is not valid + */ + throw new HttpUrlException( + $this->translator->trans( + "Tried to download a file, but the URL was not valid: %url", + [ + "%url" => $url + ] + ) + ); + } + + /** + * Try to get the file if it is online + */ + $con = curl_init($url); + curl_setopt($con, CURLOPT_RETURNTRANSFER, true); + + $response = curl_exec($con); + $errno = curl_errno($con); + $curlErrorMessage = curl_error($con); + + $httpCode = curl_getinfo($con, CURLINFO_HTTP_CODE); + + curl_close($con); + + if (false === $response || $errno !== 0 || + ($httpCode != "200" && $httpCode != "204") + ) { + /** + * The server is down ? The file doesn't exist ? Anything else ? + */ + $errorMessage = $this->translator->trans( + "cURL errno %errno, http code %http_code on link \"%path\": %error", + [ + "%errno" => $errno, + "%path" => $url, + "%error" => $curlErrorMessage, + "%http_code" => $httpCode, + ] + ); + + $this->logger + ->error($errorMessage) + ; + + throw new FileNotFoundException($errorMessage); + } + + /** + * Inform that you've downloaded a file + */ + $this->logger + ->info( + $this->translator->trans( + "The file %path has been successfully downloaded", + [ + "%path" => $url + ] + ) + ) + ; + + /** + * Then try to write it on the disk + */ + $file = @fopen($pathToStore, "w"); + + if($file === false) { + $translatedErrorMessage = $this->translator->trans( + "Failed to open a writing stream on the file: %file", + [ + "%file" => $pathToStore + ] + ); + + $this->logger->error($translatedErrorMessage); + throw new \ErrorException($translatedErrorMessage); + } + + fputs($file, $response); + fclose($file); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Tools/FileDownload/FileDownloaderAwareTrait.php b/core/lib/Thelia/Tools/FileDownload/FileDownloaderAwareTrait.php new file mode 100644 index 000000000..e9b99d73d --- /dev/null +++ b/core/lib/Thelia/Tools/FileDownload/FileDownloaderAwareTrait.php @@ -0,0 +1,47 @@ + + */ +trait FileDownloaderAwareTrait +{ + /** @var FileDownloaderInterface */ + protected $fileDownloader; + + /** + * @return FileDownloaderInterface + */ + public function getFileDownloader() + { + if (!$this->fileDownloader instanceof FileDownloaderInterface) { + $this->fileDownloader = FileDownloader::getInstance(); + } + + return $this->fileDownloader; + } + + /** + * @param FileDownloaderInterface $fileDownloader + * @return $this + */ + public function setFileDownloader(FileDownloaderInterface $fileDownloader) + { + $this->fileDownloader = $fileDownloader; + + return $this; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Tools/FileDownload/FileDownloaderInterface.php b/core/lib/Thelia/Tools/FileDownload/FileDownloaderInterface.php new file mode 100644 index 000000000..bdfebb5f9 --- /dev/null +++ b/core/lib/Thelia/Tools/FileDownload/FileDownloaderInterface.php @@ -0,0 +1,44 @@ + + */ +interface FileDownloaderInterface +{ + /** + * @param string $url + * @param string $pathToStore + * @throws \Thelia\Exception\FileNotFoundException + * @throws \ErrorException + * @throws \HttpUrlException + * + * Downloads the file $url in $pathToStore + */ + public function download($url, $pathToStore); + + public function __construct(LoggerInterface $logger, Translator $translator); + + /** + * @return $this + * + * Returns an hydrated instance + */ + public static function getInstance(); +} \ No newline at end of file diff --git a/core/lib/Thelia/Tools/URL.php b/core/lib/Thelia/Tools/URL.php index 65a03ed65..569b15780 100644 --- a/core/lib/Thelia/Tools/URL.php +++ b/core/lib/Thelia/Tools/URL.php @@ -13,6 +13,7 @@ namespace Thelia\Tools; use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Validator\Constraints\UrlValidator; use Thelia\Model\ConfigQuery; use Thelia\Rewriting\RewritingResolver; use Thelia\Rewriting\RewritingRetriever; @@ -313,4 +314,11 @@ class URL strtolower($clean) : $clean; } + + public function checkUrl($url, array $protocols = ["http", "https"]) + { + $pattern = sprintf(UrlValidator::PATTERN, implode('|', $protocols)); + + return (bool) preg_match($pattern, $url); + } }