diff --git a/.travis.yml b/.travis.yml index 808de2a..a894e02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,16 +3,6 @@ language: php matrix: fast_finish: true include: - - php: 5.5 - env: TYPO3_VERSION=^7 - - php: 5.6 - env: TYPO3_VERSION=^7 - - php: 7.0 - env: TYPO3_VERSION=^7 - - php: 7.1 - env: TYPO3_VERSION=^7 - - php: 7.2 - env: TYPO3_VERSION=^7 - php: 7.0 env: TYPO3_VERSION=^8 - php: 7.1 @@ -21,6 +11,12 @@ matrix: env: TYPO3_VERSION=^8 - php: 7.2 env: TYPO3_VERSION=^9 + - php: 7.3 + env: TYPO3_VERSION=^9 + - php: 7.2 + env: TYPO3_VERSION=^10 + - php: 7.3 + env: TYPO3_VERSION=^10 sudo: false diff --git a/CHANGELOG.md b/CHANGELOG.md index cc18a6e..bd3e92f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +### 1.5.0 +* **[FEATURE]** Add support for TYPO3 10.0 - Thanks to @achimfritz, @davidsteeb and @bmack from @b13 GmbH +* **[CHANGE]** Drop support for TYPO3 7.6 +* **[CHANGE]** Rename TypoScript setup file +* **[CHANGE]** Add compression errors and optimize flash messages + +### 1.4.0 +* **[FEATURE]** Add option to exclude specific folders from compression - Thanks to @achimfritz, @davidsteeb and @bmack from @b13 GmbH + ### 1.3.0 * **[FEATURE]** Add support for TYPO3 9.5 * **[CHANGE]** Drop support of non composer installation diff --git a/Classes/Command/CompressImagesCommand.php b/Classes/Command/CompressImagesCommand.php new file mode 100644 index 0000000..de764f2 --- /dev/null +++ b/Classes/Command/CompressImagesCommand.php @@ -0,0 +1,150 @@ +setName('compressImages:compress') + ->setDescription('compress uncompressed images') + ->addArgument( + 'limit', + InputArgument::OPTIONAL, + 'limit of files to compress', + self::DEFAULT_LIMIT_TO_PROCESS + ); + } + + /** + * @throws \TYPO3\CMS\Extbase\Object\Exception + */ + protected function initializeDependencies(): void + { + $this->objectManager = GeneralUtility::makeInstance(ObjectManager::class); + $this->fileStorageRepository = $this->objectManager->get(FileStorageRepository::class); + $this->fileRepository = $this->objectManager->get(FileRepository::class); + $this->resourceFactory = $this->objectManager->get(ResourceFactory::class); + $this->compressImageService = $this->objectManager->get(CompressImageService::class); + } + + /** + * Executes the command for adding the lock file + * + * @param InputInterface $input + * @param OutputInterface $output + */ + protected function execute(InputInterface $input, OutputInterface $output): void + { + $limit = (int)$input->getArgument('limit'); + $this->initializeDependencies(); + $settings = $this->getTypoScriptConfiguration(); + /** @var FileStorage $fileStorage */ + foreach ($this->fileStorageRepository->findAll() as $fileStorage) { + $excludeFolders = GeneralUtility::trimExplode(',', (string)$settings['excludeFolders'], true); + $files = $this->fileRepository->findAllNonCompressedInStorageWithLimit($fileStorage, $limit, $excludeFolders); + $this->compressImages($files); + $this->clearPageCache(); + } + } + + /** + * @param QueryResultInterface $files + * @return void + * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException + * @throws \TYPO3\CMS\Extbase\Configuration\Exception\InvalidConfigurationTypeException + * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException + * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException + */ + protected function compressImages(QueryResultInterface $files): void + { + /** @var FileDeletionAspect $fileDeletionAspect */ + $fileDeletionAspect = GeneralUtility::makeInstance(FileDeletionAspect::class); + /** @var \Schmitzal\Tinyimg\Domain\Model\File $file */ + foreach ($files as $file) { + if ($file instanceof \Schmitzal\Tinyimg\Domain\Model\File) { + $file = $this->resourceFactory->getFileObject($file->getUid()); + $this->compressImageService->initializeCompression($file); + $fileDeletionAspect->cleanupProcessedFilesPostFileReplace($file, ''); + } + } + } + + /** + * Remove all processed files, so they get generated again after being compressed + * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheGroupException + */ + protected function clearPageCache(): void + { + /** @var CacheManager $cacheManager */ + $cacheManager = GeneralUtility::makeInstance(CacheManager::class); + $cacheManager->flushCachesInGroup('pages'); + } + + /** + * @return array + * @throws \TYPO3\CMS\Extbase\Configuration\Exception\InvalidConfigurationTypeException + */ + protected function getTypoScriptConfiguration(): array + { + /** @var ConfigurationManager $configurationManager */ + $configurationManager = $this->objectManager->get(ConfigurationManager::class); + + return (array)$configurationManager->getConfiguration( + ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, + 'tinyimg' + ); + } +} diff --git a/Classes/Command/CompressImagesCommandController.php b/Classes/Command/CompressImagesCommandController.php deleted file mode 100644 index a7647bd..0000000 --- a/Classes/Command/CompressImagesCommandController.php +++ /dev/null @@ -1,94 +0,0 @@ -fileStorageRepository->findAll() as $fileStorage) { - $files = $this->fileRepository->findAllNonCompressedInStorageWithLimit($fileStorage, 100); - - $this->compressImages($files); - - $this->clearProcessedFiles(); - } - } - - /** - * @param QueryResultInterface $files - * @return void - * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException - * @throws \TYPO3\CMS\Extbase\Configuration\Exception\InvalidConfigurationTypeException - * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException - * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException - */ - protected function compressImages(QueryResultInterface $files) - { - /** @var \Schmitzal\Tinyimg\Domain\Model\File $file */ - foreach ($files as $file) { - if ($file instanceof \Schmitzal\Tinyimg\Domain\Model\File) { - $file = $this->resourceFactory->getFileObject($file->getUid()); - if (filesize(GeneralUtility::getFileAbsFileName($file->getPublicUrl())) > 0) { - $this->compressImageService->initializeCompression($file); - } - } - } - } - - /** - * Remove all processed files, so they get generated again after being compressed - * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheGroupException - */ - protected function clearProcessedFiles() - { - /** @var ProcessedFileRepository $repository */ - $repository = GeneralUtility::makeInstance(ProcessedFileRepository::class); - /** @var CacheManager $cacheManager */ - $cacheManager = GeneralUtility::makeInstance(CacheManager::class); - - $repository->removeAll(); - $cacheManager->flushCachesInGroup('pages'); - } -} diff --git a/Classes/Domain/Model/File.php b/Classes/Domain/Model/File.php index 23d716f..b27e9a4 100644 --- a/Classes/Domain/Model/File.php +++ b/Classes/Domain/Model/File.php @@ -17,10 +17,15 @@ class File extends \TYPO3\CMS\Extbase\Domain\Model\File */ protected $compressed = false; + /** + * @var string + */ + protected $compressError = ''; + /** * @return int */ - public function getStorage() + public function getStorage(): int { return $this->storage; } @@ -28,7 +33,7 @@ public function getStorage() /** * @param int $storage */ - public function setStorage($storage) + public function setStorage(int $storage): void { $this->storage = $storage; } @@ -36,7 +41,7 @@ public function setStorage($storage) /** * @return bool */ - public function isCompressed() + public function isCompressed(): bool { return $this->compressed; } @@ -44,8 +49,32 @@ public function isCompressed() /** * @param bool $compressed */ - public function setCompressed($compressed) + public function setCompressed(bool $compressed): void { $this->compressed = $compressed; } + + /** + * @return string + */ + public function getCompressError(): string + { + return $this->compressError; + } + + /** + * @param string $compressError + */ + public function setCompressError(string $compressError): void + { + $this->compressError = $compressError; + } + + /** + * @return void + */ + public function resetCompressError(): void + { + $this->setCompressError(''); + } } diff --git a/Classes/Domain/Model/FileStorage.php b/Classes/Domain/Model/FileStorage.php index 5ae546e..8864019 100644 --- a/Classes/Domain/Model/FileStorage.php +++ b/Classes/Domain/Model/FileStorage.php @@ -41,7 +41,7 @@ class FileStorage extends AbstractEntity /** * @return string */ - public function getName() + public function getName(): string { return $this->name; } @@ -49,7 +49,7 @@ public function getName() /** * @param string $name */ - public function setName($name) + public function setName(string $name) { $this->name = $name; } @@ -57,7 +57,7 @@ public function setName($name) /** * @return string */ - public function getDescription() + public function getDescription(): string { return $this->description; } @@ -65,7 +65,7 @@ public function getDescription() /** * @param string $description */ - public function setDescription($description) + public function setDescription(string $description) { $this->description = $description; } @@ -73,7 +73,7 @@ public function setDescription($description) /** * @return bool */ - public function isDefault() + public function isDefault(): bool { return $this->default; } @@ -81,7 +81,7 @@ public function isDefault() /** * @param bool $default */ - public function setDefault($default) + public function setDefault(bool $default) { $this->default = $default; } @@ -89,7 +89,7 @@ public function setDefault($default) /** * @return bool */ - public function isBrowsable() + public function isBrowsable(): bool { return $this->browsable; } @@ -97,7 +97,7 @@ public function isBrowsable() /** * @param bool $browsable */ - public function setBrowsable($browsable) + public function setBrowsable(bool $browsable) { $this->browsable = $browsable; } @@ -105,7 +105,7 @@ public function setBrowsable($browsable) /** * @return bool */ - public function isPublic() + public function isPublic(): bool { return $this->public; } @@ -113,7 +113,7 @@ public function isPublic() /** * @param bool $public */ - public function setPublic($public) + public function setPublic(bool $public) { $this->public = $public; } @@ -121,7 +121,7 @@ public function setPublic($public) /** * @return bool */ - public function isWritable() + public function isWritable(): bool { return $this->writable; } @@ -129,7 +129,7 @@ public function isWritable() /** * @param bool $writable */ - public function setWritable($writable) + public function setWritable(bool $writable) { $this->writable = $writable; } @@ -137,7 +137,7 @@ public function setWritable($writable) /** * @return bool */ - public function isOnline() + public function isOnline(): bool { return $this->online; } @@ -145,7 +145,7 @@ public function isOnline() /** * @param bool $online */ - public function setOnline($online) + public function setOnline(bool $online) { $this->online = $online; } diff --git a/Classes/Domain/Repository/FileRepository.php b/Classes/Domain/Repository/FileRepository.php index 00f38b4..6594dac 100644 --- a/Classes/Domain/Repository/FileRepository.php +++ b/Classes/Domain/Repository/FileRepository.php @@ -1,8 +1,11 @@ getQuerySettings()->setRespectStoragePage(false); @@ -24,19 +27,32 @@ public function createQuery() /** * @param FileStorage $storage * @param int $limit + * @param array $excludeFolders * @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException */ - public function findAllNonCompressedInStorageWithLimit(FileStorage $storage, $limit = 100) + public function findAllNonCompressedInStorageWithLimit(FileStorage $storage, $limit = 100, $excludeFolders = []): QueryResultInterface { $query = $this->createQuery(); + + $excludeFoldersConstraints = []; + foreach ($excludeFolders as $excludeFolder) { + $excludeFoldersConstraints[] = $query->logicalNot($query->like('identifier', $excludeFolder . '%')); + } + $query->matching( - $query->logicalAnd([ - $query->equals('storage', $storage), - $query->equals('compressed', false), - $query->equals('missing', false), - $query->in('extension', ['png', 'jpg', 'jpeg']) - ]) + $query->logicalAnd( + array_merge( + [ + $query->equals('storage', $storage), + $query->equals('compressed', false), + $query->equals('missing', false), + $query->equals('compress_error', ''), + $query->in('mime_type', ['image/png', 'image/jpeg']) + ], + $excludeFoldersConstraints + ) + ) ); $query->setLimit($limit); diff --git a/Classes/Domain/Repository/FileStorageRepository.php b/Classes/Domain/Repository/FileStorageRepository.php index 4235a6e..5ce15a3 100644 --- a/Classes/Domain/Repository/FileStorageRepository.php +++ b/Classes/Domain/Repository/FileStorageRepository.php @@ -2,6 +2,7 @@ namespace Schmitzal\Tinyimg\Domain\Repository; use TYPO3\CMS\Extbase\Persistence\Repository; +use TYPO3\CMS\Extbase\Persistence\QueryInterface; /** * Class FileStorageRepository @@ -12,7 +13,7 @@ class FileStorageRepository extends Repository /** * Do not respect storage pid for domain records */ - public function createQuery() + public function createQuery(): QueryInterface { $query = parent::createQuery(); $query->getQuerySettings()->setRespectStoragePage(false); diff --git a/Classes/Service/CompressImageService.php b/Classes/Service/CompressImageService.php index 8ebb614..ebff346 100644 --- a/Classes/Service/CompressImageService.php +++ b/Classes/Service/CompressImageService.php @@ -1,15 +1,23 @@ objectManager = $objectManager; + } + + /** + * @param FileRepository $fileRepository + */ + public function injectFileRepository(FileRepository $fileRepository): void + { + $this->fileRepository = $fileRepository; + } + + /** + * @param PersistenceManager $persistenceManager + */ + public function injectPersistenceManager(PersistenceManager $persistenceManager): void + { + $this->persistenceManager = $persistenceManager; + } + /** * CompressImageService constructor. * @throws \TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationExtensionNotConfiguredException * @throws \TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException */ - public function initAction() + public function initAction(): void { - $this->extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['tinyimg']); + if (version_compare(TYPO3_version, '9', '>')) { + $this->extConf = $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['tinyimg']; + } else { + // @extensionScannerIgnoreLine + $this->extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['tinyimg']); + } if (ExtensionManagementUtility::isLoaded('aus_driver_amazon_s3')) { $this->initCdn(); } + + \Tinify\setKey($this->getApiKey()); + $this->settings = $this->getTypoScriptConfiguration(); + } + + /** + * @return string + */ + protected function getPublicPath(): string + { + if (version_compare(TYPO3_version, '9', '>')) { + return Environment::getPublicPath() . '/'; + } else { + // @extensionScannerIgnoreLine + return PATH_site; + } } /** * initialize the CDN */ - public function initCdn() + public function initCdn(): void { /** @var S3Client client */ $this->client = S3Client::factory(array( @@ -84,26 +136,78 @@ public function initCdn() * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException */ - public function initializeCompression($file) + public function initializeCompression($file): void { $this->initAction(); - \Tinify\setKey($this->getApiKey()); - $this->settings = $this->getTypoScriptConfiguration(); + if ($this->isFileInExcludeFolder($file)) { + return; + } - if ((int)$this->settings['debug'] === 0 && - in_array(strtolower($file->getExtension()), ['png', 'jpg', 'jpeg'], true)) { - if ($this->checkForAmazonCdn($file)) { - $this->pushToTinyPngAndStoreToCdn($file); - } else { - $publicUrl = PATH_site . $file->getPublicUrl(); - $source = \Tinify\fromFile($publicUrl); - $source->toFile($publicUrl); - $this->setCompressedForCurrentFile($file); + if (!in_array(strtolower($file->getMimeType()), ['image/png', 'image/jpeg'], true)) { + return; + } + + if ((int)$this->settings['debug'] === 0) { + try { + $this->assureFileExists($file); + $originalFileSize = $file->getSize(); + if ($this->checkForAmazonCdn($file)) { + $fileSize = $this->pushToTinyPngAndStoreToCdn($file); + } else { + $publicUrl = $this->getPublicPath() . urldecode($file->getPublicUrl()); + $source = \Tinify\fromFile($publicUrl); + $source->toFile($publicUrl); + $fileSize = $this->setCompressedForCurrentFile($file); + } + if ((int)$fileSize !== 0) { + $percentageSaved = (int)(100 - ((100 / $originalFileSize) * $fileSize)); + $this->addMessageToFlashMessageQueue('success', [0 => (string)$percentageSaved . '%'], FlashMessage::INFO); + } + $this->updateFileInformation($file); + } catch (\Exception $e) { + $this->saveError($file, $e); + $this->addMessageToFlashMessageQueue('compressionFailed', [0 => $e->getMessage()], FlashMessage::WARNING); } + } else { + $this->addMessageToFlashMessageQueue('debugMode', [], FlashMessage::INFO); + } + } + + /** + * @param File $file + * @throws \Exception + */ + protected function assureFileExists(File $file): void + { + $absFileName = GeneralUtility::getFileAbsFileName(urldecode($file->getPublicUrl())); + if (file_exists($absFileName) === false) { + throw new \Exception('file not exists: ' . $absFileName, 1575270381); + } + if ((int)filesize($absFileName) === 0) { + throw new \Exception('filesize is 0: ' . $absFileName, 1575270380); } + } + + - $this->updateFileInformation($file); + /** + * @param File $file + * @return bool + */ + protected function isFileInExcludeFolder(File $file): bool + { + if (!empty($this->settings['excludeFolders'])) { + $excludeFolders = GeneralUtility::trimExplode(',', $this->settings['excludeFolders'], true); + $identifier = $file->getIdentifier(); + foreach ($excludeFolders as $excludeFolder) { + if (strpos($identifier, $excludeFolder) === 0) { + $this->addMessageToFlashMessageQueue('folderExcluded', [0 => $excludeFolder], FlashMessage::INFO); + return true; + } + } + } + return false; } /** @@ -114,11 +218,11 @@ public function initializeCompression($file) * @param File $file * @return bool */ - public function checkForAmazonCdn($file) + public function checkForAmazonCdn(File $file): bool { return ExtensionManagementUtility::isLoaded('aus_driver_amazon_s3') && - $this->getUseCdn() && - $this->checkIfFolderIsCdn($file); + $this->getUseCdn() && + $this->checkIfFolderIsCdn($file); } /** @@ -129,15 +233,17 @@ public function checkForAmazonCdn($file) * Deletes old temp file. * * @param File $file + * @return int + * @throws \Exception */ - public function pushToTinyPngAndStoreToCdn($file) + public function pushToTinyPngAndStoreToCdn(File $file): int { // get the image // no PATH_site as file will be provided by absolute URL of the bucket or the CDN $publicUrl = $file->getPublicUrl(); // get the temp file and prefix with current time - $tempFile = PATH_site . 'typo3temp' . DIRECTORY_SEPARATOR . time() .'_'. $this->getCdnFileName($publicUrl); + $tempFile = $this->getPublicPath() . 'typo3temp' . DIRECTORY_SEPARATOR . time() . '_' . $this->getCdnFileName($publicUrl); $source = \Tinify\fromFile($publicUrl); @@ -145,18 +251,16 @@ public function pushToTinyPngAndStoreToCdn($file) $source->toFile($tempFile); // upload to CDN - try { - $this->client->putObject([ - 'Bucket' => $this->extConf['bucket'], - 'Key' => $file->getIdentifier(), - 'SourceFile' => $tempFile - ]); - } catch (S3Exception $e) { - throw new S3Exception($e->getMessage()); - } - + $splFileObject = new \SplFileObject($tempFile); + $fileSize = $splFileObject->getSize(); + $this->client->putObject([ + 'Bucket' => $this->extConf['bucket'], + 'Key' => $file->getIdentifier(), + 'SourceFile' => $tempFile + ]); // remove temp file GeneralUtility::unlink_tempfile($tempFile); + return (int)$fileSize; } /** @@ -165,7 +269,7 @@ public function pushToTinyPngAndStoreToCdn($file) * @param File $file * @return boolean */ - public function checkIfFolderIsCdn($file) + public function checkIfFolderIsCdn($file): bool { // if this is string, then we know, that there is already a file in the folder // In this case you have to check if the object in the bucket exists @@ -180,40 +284,40 @@ public function checkIfFolderIsCdn($file) } /** - * @param $file string + * @param string $fileName * @return string */ - public function getCdnFileName($file) + public function getCdnFileName(string $fileName): string { - return preg_replace('/^.*\/(.*)$/', '$1', $file); + return preg_replace('/^.*\/(.*)$/', '$1', $fileName); } /** * @return string */ - protected function getApiKey() + protected function getApiKey(): string { - return $this->extConf['apiKey']; + return (string)$this->extConf['apiKey']; } /** * @return boolean */ - protected function getUseCdn() + protected function getUseCdn(): bool { - return $this->extConf['useCdn']; + return (bool)$this->extConf['useCdn']; } /** * @return array * @throws \TYPO3\CMS\Extbase\Configuration\Exception\InvalidConfigurationTypeException */ - protected function getTypoScriptConfiguration() + protected function getTypoScriptConfiguration(): array { /** @var ConfigurationManager $configurationManager */ $configurationManager = $this->objectManager->get(ConfigurationManager::class); - return $configurationManager->getConfiguration( + return (array)$configurationManager->getConfiguration( ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, 'tinyimg' ); @@ -222,7 +326,7 @@ protected function getTypoScriptConfiguration() /** * @param File $file */ - protected function updateFileInformation($file) + protected function updateFileInformation(File $file): void { /** @var Indexer $fileIndexer */ $fileIndexer = $this->objectManager->get(Indexer::class, $file->getStorage()); @@ -233,13 +337,78 @@ protected function updateFileInformation($file) * @param File $file * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException + * @return int */ - protected function setCompressedForCurrentFile(File $file) + protected function setCompressedForCurrentFile(File $file): ?int { /** @var \Schmitzal\Tinyimg\Domain\Model\File $extbaseFileObject */ $extbaseFileObject = $this->fileRepository->findByUid($file->getUid()); $extbaseFileObject->setCompressed(true); + $extbaseFileObject->resetCompressError(); + $this->fileRepository->update($extbaseFileObject); + $this->persistenceManager->persistAll(); + try { + $splFileObject = new \SplFileObject(GeneralUtility::getFileAbsFileName($file->getPublicUrl())); + return (int)$splFileObject->getSize(); + } catch (\Exception $e) { + return null; + } + } + + /** + * @return bool + */ + protected function isCli(): bool + { + if (version_compare(TYPO3_version, '9', '>')) { + return Environment::isCli(); + } else { + return php_sapi_name() === 'cli'; + } + } + + /** + * @param string $key + * @param array $replaceMarkers + * @param int $severity + * @throws \TYPO3\CMS\Core\Exception + */ + protected function addMessageToFlashMessageQueue($key, array $replaceMarkers = [], $severity = FlashMessage::ERROR): void + { + if ($this->isCli()) { + return; + } + + $localizationUtility = GeneralUtility::makeInstance(LocalizationUtility::class); + $message = $localizationUtility->translate( + 'LLL:EXT:tinyimg/Resources/Private/Language/locallang.xlf:flashMessage.message.' . $key, + null, + $replaceMarkers + ); + $flashMessage = GeneralUtility::makeInstance( + FlashMessage::class, + $message, + $localizationUtility->translate('LLL:EXT:tinyimg/Resources/Private/Language/locallang.xlf:flashMessage.title'), + $severity, + true + ); + + $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class); + $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier(); + $defaultFlashMessageQueue->enqueue($flashMessage); + } + + /** + * @param File $file + * @param \Exception $e + */ + protected function saveError(File $file, \Exception $e) + { + /** @var \Schmitzal\Tinyimg\Domain\Model\File $extbaseFileObject */ + $extbaseFileObject = $this->fileRepository->findByUid($file->getUid()); + $extbaseFileObject->setCompressed(false); + $extbaseFileObject->setCompressError($e->getCode() . ' : ' . $e->getMessage()); $this->fileRepository->update($extbaseFileObject); $this->persistenceManager->persistAll(); } diff --git a/Configuration/Commands.php b/Configuration/Commands.php new file mode 100644 index 0000000..5fc92bd --- /dev/null +++ b/Configuration/Commands.php @@ -0,0 +1,7 @@ + [ + 'class' => \Schmitzal\Tinyimg\Command\CompressImagesCommand::class + ] +]; diff --git a/Configuration/Extbase/Persistence/Classes.php b/Configuration/Extbase/Persistence/Classes.php new file mode 100644 index 0000000..6f0dcf7 --- /dev/null +++ b/Configuration/Extbase/Persistence/Classes.php @@ -0,0 +1,10 @@ + [ + 'tableName' => 'sys_file_storage' + ], + \Schmitzal\Tinyimg\Domain\Model\File::class => [ + 'tableName' => 'sys_file' + ] +]; diff --git a/Configuration/TCA/Overrides/sys_file.php b/Configuration/TCA/Overrides/sys_file.php index be43bb4..b804062 100644 --- a/Configuration/TCA/Overrides/sys_file.php +++ b/Configuration/TCA/Overrides/sys_file.php @@ -3,12 +3,23 @@ $sysFileColumns = [ 'compressed' => [ 'exclude' => true, - 'label' => 'sys_file.compressed', + 'label' => 'Compressed', 'config' => [ 'type' => 'check', 'default' => 0 ] + ], + 'compress_error' => [ + 'exclude' => true, + 'label' => 'Compression Error', + 'config' => [ + 'type' => 'text', + 'default' => '' + ] ] ]; -\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('sys_file', $sysFileColumns); \ No newline at end of file +\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('sys_file', $sysFileColumns); +\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes('sys_file', 'compress_error', '', ''); + +$GLOBALS['TCA']['sys_file']['interface']['showRecordFieldList'] .= ',compressed,compress_error'; diff --git a/Configuration/TypoScript/setup.ts b/Configuration/TypoScript/setup.ts deleted file mode 100644 index 375388c..0000000 --- a/Configuration/TypoScript/setup.ts +++ /dev/null @@ -1,5 +0,0 @@ -module.tx_tinyimg.settings.debug = 0 - -[applicationContext = Development] -module.tx_tinyimg.settings.debug = 1 -[global] diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript new file mode 100644 index 0000000..fa7011c --- /dev/null +++ b/Configuration/TypoScript/setup.typoscript @@ -0,0 +1,16 @@ +module.tx_filelist.view { + templateRootPaths.0 = EXT:tinyimg/Resources/Private/Templates/ +} + +module.tx_tinyimg.settings { + debug = 0 + excludeFolders = +} + +[applicationContext == "Development"] + module.tx_tinyimg.settings.debug = 1 +[global] +[like(applicationContext, "Development*")] + module.tx_tinyimg.settings.debug = 1 +[global] + diff --git a/README.md b/README.md index d4d1c88..28d7529 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,12 @@ Make sure to have an updated index. TYPO3 comes with an index updater as a sched Also be aware that the tinify API is limited to 500 free compressions (see note above). So on huge websites it will be reached quickly. +## TypoScript reference +| Setting | Type | Default | Description | +|----------------|--------|---------|------------------------------------------------------------------------------------------------------------------------------------| +| debug | bool | 0 | Enable or disable debugging mode. Stops extension from compressing images. Use in development mode to avoid waisting compressions. | +| excludeFolders | string | empty | Comma-separated list of folders which should be excluded from compression. Relative from storage (e.g. fileadmin), starting with "/" (e.g. "/user_upload,/folder_under_fileadmin") | + ## Contribution Bugs and feature requests are welcome. Feel free to create an [issue](https://github.com/schmitzal/tinyimg/issues) and i'll have a look at it as soon as possible. diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 2f92290..eb53306 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -6,6 +6,21 @@ Image is getting compressed... + + Folder excluded from compression ("%s") + + + running in debugMode, no compression + + + compression failed with message "%s" + + + Tinyimg saved you about %s of your file size! + + + Compression + - \ No newline at end of file + diff --git a/Resources/Public/Icons/Extension.svg b/Resources/Public/Icons/Extension.svg new file mode 100644 index 0000000..5131065 --- /dev/null +++ b/Resources/Public/Icons/Extension.svg @@ -0,0 +1,17 @@ + + + + +EXT:tinyimg + + + + + diff --git a/Resources/Public/JavaScript/ExtendedUpload.js b/Resources/Public/JavaScript/ExtendedUpload.js index c480878..70ac000 100644 --- a/Resources/Public/JavaScript/ExtendedUpload.js +++ b/Resources/Public/JavaScript/ExtendedUpload.js @@ -1,7 +1,6 @@ define([ - 'jquery', - 'TYPO3/CMS/Backend/Notification' -], function ($, Notification) { + 'jquery' +], function ($) { $(document).ready(function () { $('.t3js-drag-uploader-trigger').on('updateProgress', function (event, me, percentage) { if (percentage === '100%' && me.file.type.match(/image\/(jpg|jpeg|png)$/i)) { @@ -11,10 +10,8 @@ define([ } }).on('uploadSuccess', function (event, me, data) { if (me.file.type.match(/image\/(jpg|jpeg|png)$/i)) { - var percentageSaved = Math.round(100 - ((data.upload[0].size / me.file.size) * 100)); me.$row.removeClass('compressing'); - Notification.info('Compression', 'Tinyimg saved you about ' + percentageSaved + '% of your file size!', 10); } }); }); -}); \ No newline at end of file +}); diff --git a/composer.json b/composer.json index 6a939d7..307df90 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "issues": "https://github.com/schmitzal/tinyimg/issues" }, "require": { - "typo3/cms-core": "^7.6.0 || ^8.7.0 || ^9.5.0", + "typo3/cms-core": "^8.0 || ^9.0 || ^10.0", "tinify/tinify": "^1.5" }, "require-dev": { diff --git a/ext_emconf.php b/ext_emconf.php index 60625dc..1ba10c0 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -6,12 +6,12 @@ 'author' => 'Alessandro Schmitz', 'author_email' => 'alessandro.schmitz@interlutions.de', 'author_company' => 'Interlutions GmbH', - 'version' => '1.3.0', + 'version' => '1.5.0', 'state' => 'beta', 'clearCacheOnLoad' => true, 'constraints' => [ 'depends' => [ - 'typo3' => '7.6.0-9.5.99', + 'typo3' => '8.7.0-10.99.99' ], 'conflicts' => [], 'suggests' => [], diff --git a/ext_icon.svg b/ext_icon.svg deleted file mode 100644 index fdaba9d..0000000 --- a/ext_icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ext_localconf.php b/ext_localconf.php index 01527ed..2be17fd 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -23,7 +23,4 @@ true ); - // Register command - $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['extbase']['commandControllers'][$_EXTKEY] = - \Schmitzal\Tinyimg\Command\CompressImagesCommandController::class; -} \ No newline at end of file +} diff --git a/ext_tables.sql b/ext_tables.sql index 1e664f7..5874e18 100644 --- a/ext_tables.sql +++ b/ext_tables.sql @@ -3,4 +3,5 @@ # CREATE TABLE sys_file ( compressed SMALLINT(5) UNSIGNED DEFAULT '0' NOT NULL, -); \ No newline at end of file + compress_error text DEFAULT '' NOT NULL +); diff --git a/ext_typoscript_setup.txt b/ext_typoscript_setup.txt index 7806bc9..71639a5 100644 --- a/ext_typoscript_setup.txt +++ b/ext_typoscript_setup.txt @@ -1,3 +1,4 @@ +# this is required for TYPO3 < v10, for v10 Configuration/Extbase is used config.tx_extbase { persistence { classes { @@ -14,7 +15,3 @@ config.tx_extbase { } } } - -module.tx_filelist.view { - templateRootPaths.0 = EXT:tinyimg/Resources/Private/Templates/ -}