[TASK] Create new processor registry 58/27958/9
authorFrans Saris <franssaris@gmail.com>
Fri, 28 Feb 2014 22:34:57 +0000 (23:34 +0100)
committerSteffen Ritter <info@rs-websystems.de>
Tue, 11 Mar 2014 16:46:22 +0000 (17:46 +0100)
File processing might be different for external storages.
Therefore it must be possible to register your own File Processors
and make the registry driver aware.

Resolves: #56775
Releases: 6.2
Change-Id: I187f6f987f7b0bc92989ed38cbef248449a7f59f
Reviewed-on: https://review.typo3.org/27958
Reviewed-by: Steffen Ritter
Tested-by: Steffen Ritter
typo3/sysext/core/Classes/Resource/Index/ExtractorInterface.php
typo3/sysext/core/Classes/Resource/Processing/AbstractProcessingRequest.php [new file with mode: 0644]
typo3/sysext/core/Classes/Resource/Processing/FileBasedConstraintInterface.php [new file with mode: 0644]
typo3/sysext/core/Classes/Resource/Processing/FileProcessorInterface.php [new file with mode: 0644]
typo3/sysext/core/Classes/Resource/Processing/FileProcessorRegistry.php [new file with mode: 0644]
typo3/sysext/core/Classes/Resource/Processing/ImageProcessingRequest.php [new file with mode: 0644]
typo3/sysext/core/Classes/Resource/Processing/ProcessingRequestInterface.php [new file with mode: 0644]
typo3/sysext/core/Classes/Resource/Processing/ProcessingTask.php [new file with mode: 0644]
typo3/sysext/core/Classes/Resource/Processing/ProcessingTaskCustomizationHookInterface.php [new file with mode: 0644]
typo3/sysext/core/Tests/Unit/Resource/Processing/ProcessorRegistryTest.php [new file with mode: 0644]

index 3f83d02..6872212 100644 (file)
@@ -32,41 +32,7 @@ use TYPO3\CMS\Core\Resource;
 /**
  * An Interface for MetaData extractors the FAL Indexer uses
  */
-interface ExtractorInterface {
-
-       /**
-        * Returns an array of supported file types;
-        * An empty array indicates all filetypes
-        *
-        * @return array
-        */
-       public function getFileTypeRestrictions();
-
-
-       /**
-        * Get all supported DriverClasses
-        *
-        * Since some extractors may only work for local files, and other extractors
-        * are especially made for grabbing data from remote.
-        *
-        * Returns array of string with driver names of Drivers which are supported,
-        * If the driver did not register a name, it's the classname.
-        * empty array indicates no restrictions
-        *
-        * @return array
-        */
-       public function getDriverRestrictions();
-
-       /**
-        * Returns the data priority of the extraction Service.
-        * Defines the precedence of Data if several extractors
-        * extracted the same property.
-        *
-        * Should be between 1 and 100, 100 is more important than 1
-        *
-        * @return integer
-        */
-       public function getPriority();
+interface ExtractorInterface extends Resource\Processing\FileBasedConstraintInterface {
 
        /**
         * Returns the execution priority of the extraction Service
@@ -95,5 +61,4 @@ interface ExtractorInterface {
         */
        public function extractMetaData(Resource\File $file, array $previousExtractedData = array());
 
-
 }
diff --git a/typo3/sysext/core/Classes/Resource/Processing/AbstractProcessingRequest.php b/typo3/sysext/core/Classes/Resource/Processing/AbstractProcessingRequest.php
new file mode 100644 (file)
index 0000000..5839628
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Processing;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2014 Frans Saris <franssaris@gmail.com>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the text file GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Abstract class for file ProcessingRequests
+ */
+abstract class AbstractProcessingRequest implements ProcessingRequestInterface {
+
+       /**
+        * Processing Request configuration
+        *
+        * @var array
+        */
+       protected $configuration;
+
+       /**
+        * Constructor
+        *
+        * @param array $configuration
+        */
+       public function __construct(array $configuration = array()) {
+               $this->configuration = $configuration;
+       }
+
+       /**
+        * Return configuration as a flat array
+        *
+        * @return array
+        */
+       public function toArray() {
+               return $this->configuration;
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Resource/Processing/FileBasedConstraintInterface.php b/typo3/sysext/core/Classes/Resource/Processing/FileBasedConstraintInterface.php
new file mode 100644 (file)
index 0000000..c302ff4
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Processing;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2014 Frans Saris <franssaris@gmail.com>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the text file GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Base Interface for file processing task like Extractor and FileProcessor that
+ * can be constrained to certain Drivers or FileTypes.
+ */
+interface FileBasedConstraintInterface {
+
+       /**
+        * Returns an array of supported file types
+        * An empty array indicates all file types
+        *
+        * @return array
+        */
+       public function getFileTypeRestrictions();
+
+       /**
+        * Get all supported DriverTypes
+        *
+        * Since some processors may only work for local files, and other
+        * are especially made for processing files from remote.
+        *
+        * Returns array of strings with driver names of Drivers which are supported,
+        * If the driver did not register a name, it's the class name.
+        * empty array indicates no restrictions
+        *
+        * @return array
+        */
+       public function getDriverRestrictions();
+
+       /**
+        * Returns the data priority of the processing Service.
+        * Defines the precedence if several processors
+        * can handle the same file.
+        *
+        * Should be between 1 and 100, 100 is more important than 1
+        *
+        * @return integer
+        */
+       public function getPriority();
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Resource/Processing/FileProcessorInterface.php b/typo3/sysext/core/Classes/Resource/Processing/FileProcessorInterface.php
new file mode 100644 (file)
index 0000000..12d5494
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Processing;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2014 Frans Saris <franssaris@gmail.com>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the text file GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Core\Resource;
+
+/**
+ * Interface for file processors. All classes capable of processing a file have to implement this interface.
+ */
+interface FileProcessorInterface extends FileBasedConstraintInterface {
+
+       /**
+        * Returns TRUE if this processor can process the given configuration
+        *
+        * @param Resource\FileInterface $file
+        * @param ProcessingRequestInterface $processingRequest
+        * @return boolean
+        */
+       public function canProcess(Resource\FileInterface $file, ProcessingRequestInterface $processingRequest);
+
+       /**
+        * Processes the given request and returns the processing result
+        *
+        * @param Resource\FileInterface $file
+        * @param ProcessingRequestInterface $processingRequest
+        * @return Resource\ProcessedFile
+        */
+       public function process(Resource\FileInterface $file, ProcessingRequestInterface $processingRequest);
+}
diff --git a/typo3/sysext/core/Classes/Resource/Processing/FileProcessorRegistry.php b/typo3/sysext/core/Classes/Resource/Processing/FileProcessorRegistry.php
new file mode 100644 (file)
index 0000000..296a1b9
--- /dev/null
@@ -0,0 +1,182 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Processing;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2014 Frans Saris <franssaris@gmail.com>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the text file GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Registry for File Processors
+ */
+class FileProcessorRegistry implements \TYPO3\CMS\Core\SingletonInterface {
+
+       /**
+        * Registered FileProcessor ClassNames
+        *
+        * @var array
+        */
+       protected $fileProcessors = array();
+
+       /**
+        * Instance Cache for Processors
+        *
+        * @var array FileProcessorInterface[]
+        */
+       protected $instances;
+
+       /**
+        * Returns an instance of this class
+        *
+        * @return FileProcessorRegistry
+        */
+       public static function getInstance() {
+               return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Processing\\FileProcessorRegistry');
+       }
+
+       /**
+        * Constructor
+        */
+       public function __construct() {
+               if (!empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['fileProcessors'])) {
+                       foreach ($GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['fileProcessors'] as $class) {
+                               $this->registerFileProcessorClass($class);
+                       }
+               }
+       }
+
+       /**
+        * Allows to register a FileProcessor
+        *
+        * @param string $className
+        * @throws \InvalidArgumentException
+        */
+       public function registerFileProcessorClass($className) {
+               if (!class_exists($className)) {
+                       throw new \InvalidArgumentException('The Class you are registering is not available', 1393614709);
+               } elseif (!in_array('TYPO3\\CMS\\Core\\Resource\\Processing\\FileProcessorInterface', class_implements($className))) {
+                       throw new \InvalidArgumentException('The Processor needs to implement the FileProcessorInterface', 1393614710);
+               } else {
+                       $this->fileProcessors[] = $className;
+               }
+       }
+
+       /**
+        * Get all registered processors
+        *
+        * @return array FileProcessorInterface[]
+        */
+       public function getFileProcessors() {
+               if ($this->instances === NULL) {
+                       $this->instances = array();
+
+                               // as the result is in reverse order we need to reverse
+                               // the array before processing to keep the items with same
+                               // priority in the same order as they were added to the registry.
+                       $fileProcessors = array_reverse($this->fileProcessors);
+                       foreach ($fileProcessors as $className) {
+                               /** @var FileProcessorInterface $object */
+                               $object = $this->createFileProcessorInstance($className);
+                               $this->instances[] = $object;
+                       }
+
+                       if (count($this->instances) > 1) {
+                               usort($this->instances, array($this, 'compareProcessorPriority'));
+                       }
+               }
+               return $this->instances;
+       }
+
+       /**
+        * Create an instance of a certain FileProcessor class
+        *
+        * @param $className
+        * @return FileProcessorInterface
+        */
+       protected function createFileProcessorInstance($className) {
+               return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance($className);
+       }
+
+       /**
+        * Compare the priority of 2 File Processors
+        * Is used for sorting array of FileProcessor instances by priority
+        * We want the result to be ordered from high to low so a higher
+        * priority comes before a lower.
+        *
+        * @param FileProcessorInterface $processorA
+        * @param FileProcessorInterface $processorB
+        * @return int -1 a > b, 0 a == b, 1 a < b
+        */
+       protected function compareProcessorPriority(FileProcessorInterface $processorA, FileProcessorInterface $processorB) {
+               $return = 0;
+               if ($processorA->getPriority() < $processorB->getPriority()) {
+                       $return = 1;
+               } elseif ($processorA->getPriority() > $processorB->getPriority()) {
+                       $return = -1;
+               }
+               return $return;
+       }
+
+       /**
+        * Get FileProcessors which work for a special driver
+        *
+        * @param string $driverType
+        * @return array FileProcessorInterface[]
+        */
+       public function getFileProcessorsWithDriverSupport($driverType) {
+               $allProcessors = $this->getFileProcessors();
+               $filteredProcessors = array();
+
+               /** @var FileProcessorInterface $processorObject */
+               foreach ($allProcessors as $processorObject) {
+                       if ($processorObject->getDriverRestrictions() === array()) {
+                               $filteredProcessors[] = $processorObject;
+                       } elseif (in_array($driverType, $processorObject->getDriverRestrictions())) {
+                               $filteredProcessors[] = $processorObject;
+                       }
+               }
+               return $filteredProcessors;
+       }
+
+       /**
+        * Get matching file processor with highest priority
+        *
+        * @param \TYPO3\CMS\Core\Resource\FileInterface $file
+        * @param ProcessingRequestInterface $processingRequest
+        * @return NULL|FileProcessorInterface
+        */
+       public function getProcessorFor(\TYPO3\CMS\Core\Resource\FileInterface $file, ProcessingRequestInterface $processingRequest) {
+               $driverType = $file->getStorage()->getDriverType();
+               $matchingFileProcessor = NULL;
+
+               /** @var FileProcessorInterface $fileProcessor */
+               foreach ($this->getFileProcessorsWithDriverSupport($driverType) as $fileProcessor) {
+                       if ($fileProcessor->canProcess($file, $processingRequest)) {
+                               $matchingFileProcessor = $fileProcessor;
+                               break;
+                       }
+               }
+               return $matchingFileProcessor;
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Resource/Processing/ImageProcessingRequest.php b/typo3/sysext/core/Classes/Resource/Processing/ImageProcessingRequest.php
new file mode 100644 (file)
index 0000000..4092ce5
--- /dev/null
@@ -0,0 +1,222 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Processing;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2014 Frans Saris <franssaris@gmail.com>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the text file GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * ImageProcessingRequest configuration container
+ */
+class ImageProcessingRequest extends AbstractProcessingRequest {
+
+       /**
+        * Set width
+        *
+        * @param string $width in pixels add m for max width or c for crop
+        * @return self
+        */
+       public function setWidth($width) {
+               $this->configuration['width'] = $width;
+               return $this;
+       }
+
+       /**
+        * Get width
+        *
+        * @return string
+        */
+       public function getWidth() {
+               $width = NULL;
+               if (isset($this->configuration['width'])) {
+                       $width = $this->configuration['width'];
+               }
+               return $width;
+       }
+
+       /**
+        * Set height
+        *
+        * @param string $height in pixels add m for max height or c for crop
+        * @return self
+        */
+       public function setHeight($height) {
+               $this->configuration['height'] = $height;
+               return $this;
+       }
+
+       /**
+        * Get height
+        *
+        * @return string
+        */
+       public function getHeight() {
+               $height = NULL;
+               if (isset($this->configuration['height'])) {
+                       $height = $this->configuration['height'];
+               }
+               return $height;
+       }
+
+       /**
+        * Set max width
+        *
+        * @param int $maxWidth
+        * @return self
+        */
+       public function setMaxWidth($maxWidth) {
+               $this->configuration['maxWidth'] = (int)$maxWidth;
+               return $this;
+       }
+
+       /**
+        * Get max height
+        *
+        * @return null|int
+        */
+       public function getMaxWidth() {
+               $maxWidth = NULL;
+               if (isset($this->configuration['maxWidth'])) {
+                       $maxWidth = $this->configuration['maxWidth'];
+               }
+               return $maxWidth;
+       }
+
+       /**
+        * Set max height
+        *
+        * @param int $maxHeight
+        * @return self
+        */
+       public function setMaxHeight($maxHeight) {
+               $this->configuration['maxHeight'] = (int)$maxHeight;
+               return $this;
+       }
+
+       /**
+        * Get max height
+        *
+        * @return null|int
+        */
+       public function getMaxHeight() {
+               $maxHeight = NULL;
+               if (isset($this->configuration['maxHeight'])) {
+                       $maxHeight = $this->configuration['maxHeight'];
+               }
+               return $maxHeight;
+       }
+
+       /**
+        * Set frame
+        * Frame refers to which frame-number to select in the image
+        *
+        * @param int $frame
+        * @return self
+        */
+       public function setFrame($frame) {
+               $this->configuration['frame'] = (int)$frame;
+               return $this;
+       }
+
+       /**
+        * Get frame
+        * Frame refers to which frame-number to select in the image
+        *
+        * @return int default 0 when not set
+        */
+       public function getFrame() {
+               $frame = 0;
+               if (isset($this->configuration['frame'])) {
+                       $frame = $this->configuration['frame'];
+               }
+               return $frame;
+       }
+
+       /**
+        * Set useSample
+        * Instructs GifBuilder to use -sample instead of -geometry
+        * when scaling the image
+        *
+        * @param bool $useSample
+        * @return self
+        */
+       public function setUseSample($useSample) {
+               $this->configuration['useSample'] = $useSample;
+               return $this;
+       }
+
+       /**
+        * Get useSample
+        *
+        * @return bool
+        */
+       public function getUseSample() {
+               return !empty($this->configuration['useSample']);
+       }
+
+       /**
+        * Set noScale
+        *
+        * @param bool $noScale
+        * @return self
+        */
+       public function setNoScale($noScale) {
+               $this->configuration['noScale'] = $noScale;
+               return $this;
+       }
+
+       /**
+        * Get noScale
+        *
+        * @return bool
+        */
+       public function getNoScale() {
+               return !empty($this->configuration['noScale']);
+       }
+
+       /**
+        * Set custom ImageMagick configuration
+        *
+        * @param string $customIMConfig Additional ImageMagick parameters
+        * @return self
+        */
+       public function setCustomIMConfig($customIMConfig) {
+               $this->configuration['customIMConfig'] = $customIMConfig;
+               return $this;
+       }
+
+       /**
+        * Get custom ImageMagick configuration
+        *
+        * @return string
+        */
+       public function getCustomIMConfig() {
+               $customIMConfig = '';
+               if (!empty($this->configuration['customIMConfig'])) {
+                       $customIMConfig = $this->configuration['customIMConfig'];
+               }
+               return $customIMConfig;
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Resource/Processing/ProcessingRequestInterface.php b/typo3/sysext/core/Classes/Resource/Processing/ProcessingRequestInterface.php
new file mode 100644 (file)
index 0000000..70e9c55
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Processing;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2014 Frans Saris <franssaris@gmail.com>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the text file GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Interface for Processing Request configuration
+ */
+interface ProcessingRequestInterface {
+
+       /**
+        * Constructor
+        *
+        * @param array $configuration
+        */
+       public function __construct(array $configuration = array());
+
+       /**
+        * Return configuration as a flat array
+        *
+        * @return array
+        */
+       public function toArray();
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Resource/Processing/ProcessingTask.php b/typo3/sysext/core/Classes/Resource/Processing/ProcessingTask.php
new file mode 100644 (file)
index 0000000..8370cf3
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Processing;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2014 Frans Saris <franssaris@gmail.com>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the text file GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * ProcessingTask for processing multiple file processing requests
+ */
+class ProcessingTask {
+
+       /**
+        * @var \TYPO3\CMS\Core\Resource\FileInterface
+        */
+       protected $originalFile;
+
+       /**
+        * @var array ProcessingRequestInterface[]
+        */
+       protected $processingRequests;
+
+       /**
+        * Constructor
+        *
+        * @param \TYPO3\CMS\Core\Resource\FileInterface $originalFile
+        * @param array $processingRequests
+        */
+       public function __construct(\TYPO3\CMS\Core\Resource\FileInterface $originalFile, array $processingRequests) {
+               $this->originalFile = $originalFile;
+
+               $this->callProcessingHook('preProcessingRequests', $processingRequests);
+               $this->processingRequests = $processingRequests;
+               $this->callProcessingHook('postProcessingRequests', $processingRequests);
+       }
+
+       /**
+        * Process all requests
+        *
+        * @return null|\TYPO3\CMS\Core\Resource\ProcessedFile
+        */
+       public function process() {
+               $processedFile = NULL;
+               $fileProcessorRegistry = FileProcessorRegistry::getInstance();
+
+               foreach ($this->processingRequests as $processingRequest) {
+                       $processor = $fileProcessorRegistry->getProcessorFor($processedFile ?: $this->originalFile, $processingRequest);
+                       if ($processor !== NULL) {
+                               $processedFile = $processor->process($processedFile ?: $this->originalFile, $processingRequest);
+                       }
+               }
+
+               return $processedFile;
+       }
+
+       /**
+        * Call processing Hook
+        *
+        * @param string $name name of the hook
+        * @param array $processingRequests processing requests by reference
+        * @throws \UnexpectedValueException
+        */
+       public function callProcessingHook($name, array &$processingRequests) {
+               if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ProcessingTask'][$name])) {
+                       foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ProcessingTask'][$name] as $classRef) {
+                               $hookObject = \TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($classRef);
+                               if (!$hookObject instanceof ProcessingTaskCustomizationHookInterface) {
+                                       throw new \UnexpectedValueException('$hookObject must implement interface TYPO3\\CMS\\Core\\Resource\\Processing\\ProcessingTaskCustomizationHookInterface', 1393686550);
+                               }
+                               $hookObject->manipulateRequests($this->originalFile, $processingRequests);
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Resource/Processing/ProcessingTaskCustomizationHookInterface.php b/typo3/sysext/core/Classes/Resource/Processing/ProcessingTaskCustomizationHookInterface.php
new file mode 100644 (file)
index 0000000..f3a3831
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Processing;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2014 Frans Saris <franssaris@gmail.com>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the text file GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Interface for ProcessingTask manipulateRequests hook
+ */
+interface ProcessingTaskCustomizationHookInterface {
+
+       /**
+        * Function to manipulate the processingRequests
+        *
+        * @param \TYPO3\CMS\Core\Resource\FileInterface $originalFile
+        * @param array $processingRequests
+        * @return void
+        */
+       public function manipulateRequests(\TYPO3\CMS\Core\Resource\FileInterface $originalFile, array &$processingRequests);
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Tests/Unit/Resource/Processing/ProcessorRegistryTest.php b/typo3/sysext/core/Tests/Unit/Resource/Processing/ProcessorRegistryTest.php
new file mode 100644 (file)
index 0000000..2dcccb7
--- /dev/null
@@ -0,0 +1,198 @@
+<?php
+namespace TYPO3\CMS\Core\Tests\Unit\Resource\Processing;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2014 Frans Saris <franssaris@gmail.com>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the text file GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Test cases for ProcessorRegistry
+ */
+class ProcessorRegistryTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
+
+       public function setUp() {
+               parent::setUp();
+               unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['fileProcessors']);
+       }
+
+       /**
+        * Initialize an FileProcessorRegistry and mock createFileProcessorInstance()
+        *
+        * @param array $createdFileProcessorInstances
+        * @return \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Resource\Processing\FileProcessorRegistry
+        */
+       protected function getTestFileProcessorRegistry(array $createdFileProcessorInstances = array()) {
+               $fileProcessorRegistry = $this->getMockBuilder('TYPO3\\CMS\\Core\\Resource\\Processing\\FileProcessorRegistry')
+                       ->setMethods(array('createFileProcessorInstance'))
+                       ->getMock();
+
+               if (count($createdFileProcessorInstances)) {
+                       $fileProcessorRegistry->expects($this->any())
+                               ->method('createFileProcessorInstance')
+                               ->will($this->returnValueMap($createdFileProcessorInstances));
+               }
+
+               return $fileProcessorRegistry;
+       }
+
+       /**
+        * @test
+        */
+       public function registeredFileProcessorClassCanBeRetrieved() {
+               $processorClass = uniqid('myProcessor');
+               $processorObject = $this->getMock('TYPO3\\CMS\\Core\\Resource\\Processing\\FileProcessorInterface', array(), array(), $processorClass);
+
+               $fileProcessorRegistry = $this->getTestFileProcessorRegistry(array(array($processorClass, $processorObject)));
+
+               $fileProcessorRegistry->registerFileProcessorClass($processorClass);
+               $this->assertContains($processorObject, $fileProcessorRegistry->getFileProcessors(), '', FALSE, FALSE);
+       }
+
+       /**
+        * @test
+        */
+       public function registeredLocalProcessorClassCanBeRetrieved() {
+               $processorClass = uniqid('myProcessor');
+               $driverType = 'Local';
+               $processorObject = $this->getMock('TYPO3\\CMS\\Core\\Resource\\Processing\\FileProcessorInterface', array(), array(), $processorClass);
+               $processorObject->expects($this->any())->method('getDriverRestrictions')->will($this->returnValue(array($driverType)));
+
+               $fileProcessorRegistry = $this->getTestFileProcessorRegistry(array(array($processorClass, $processorObject)));
+
+               $fileProcessorRegistry->registerFileProcessorClass($processorClass);
+               $this->assertContains($processorObject, $fileProcessorRegistry->getFileProcessorsWithDriverSupport($driverType), '', FALSE, FALSE);
+       }
+
+       /**
+        * @test
+        */
+       public function registeredLocalProcessorClassDoesNotGetRetrievedWhenAskingForOtherDriverType() {
+               $processorClass = uniqid('myRemoteProcessor');
+               $processorObject = $this->getMock('TYPO3\\CMS\\Core\\Resource\\Processing\\FileProcessorInterface', array(), array(), $processorClass);
+               $processorObject->expects($this->any())->method('getDriverRestrictions')->will($this->returnValue(array('Remote')));
+
+               $fileProcessorRegistry = $this->getTestFileProcessorRegistry(array(array($processorClass, $processorObject)));
+
+               $fileProcessorRegistry->registerFileProcessorClass($processorClass);
+               $this->assertNotContains($processorObject, $fileProcessorRegistry->getFileProcessorsWithDriverSupport('Local'), '', FALSE, FALSE);
+       }
+
+       /**
+        * @test
+        * @expectedException \InvalidArgumentException
+        * @expectedExceptionCode 1393614709
+        */
+       public function registerFileProcessorClassThrowsExceptionIfClassDoesNotExist() {
+               $fileProcessorRegistry = $this->getTestFileProcessorRegistry();
+               $fileProcessorRegistry->registerFileProcessorClass(uniqid());
+       }
+
+       /**
+        * @test
+        * @expectedException \InvalidArgumentException
+        * @expectedExceptionCode 1393614710
+        */
+       public function registerFileProcessorClassThrowsExceptionIfClassDoesNotImplementRightInterface() {
+               $className = __CLASS__;
+               $fileProcessorRegistry = $this->getTestFileProcessorRegistry();
+               $fileProcessorRegistry->registerFileProcessorClass($className);
+       }
+
+       /**
+        * @test
+        */
+       public function processorRegistryIsInitializedWithPreconfiguredFileProcessors() {
+               $processorClass = uniqid('myRemoteProcessor');
+               $processorObject = $this->getMock('TYPO3\\CMS\\Core\\Resource\\Processing\\FileProcessorInterface', array(), array(), $processorClass);
+
+                       // set TYPO3_CONF_VARS
+               $GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['fileProcessors'] = array(
+                       $processorClass
+               );
+
+               $fileProcessorRegistry = $this->getTestFileProcessorRegistry(array(array($processorClass, $processorObject)));
+               $this->assertContains($processorObject, $fileProcessorRegistry->getFileProcessors(), '', FALSE, FALSE);
+       }
+
+       /**
+        * @test
+        */
+       public function registerFileProcessorClassWithHighestPriorityIsFirstInResult() {
+               $processorClass1 = uniqid('myProcessor1');
+               $processorObject1 = $this->getMock('TYPO3\\CMS\\Core\\Resource\\Processing\\FileProcessorInterface', array(), array(), $processorClass1);
+               $processorObject1->expects($this->any())->method('getPriority')->will($this->returnValue(1));
+
+               $processorClass2 = uniqid('myProcessor2');
+               $processorObject2 = $this->getMock('TYPO3\\CMS\\Core\\Resource\\Processing\\FileProcessorInterface', array(), array(), $processorClass2);
+               $processorObject2->expects($this->any())->method('getPriority')->will($this->returnValue(10));
+
+               $processorClass3 = uniqid('myProcessor3');
+               $processorObject3 = $this->getMock('TYPO3\\CMS\\Core\\Resource\\Processing\\FileProcessorInterface', array(), array(), $processorClass3);
+               $processorObject3->expects($this->any())->method('getPriority')->will($this->returnValue(2));
+
+               $createdFileProcessorInstances = array(
+                       array($processorClass1, $processorObject1),
+                       array($processorClass2, $processorObject2),
+                       array($processorClass3, $processorObject3),
+               );
+
+               $fileProcessorRegistry = $this->getTestFileProcessorRegistry($createdFileProcessorInstances);
+               $fileProcessorRegistry->registerFileProcessorClass($processorClass1);
+               $fileProcessorRegistry->registerFileProcessorClass($processorClass2);
+               $fileProcessorRegistry->registerFileProcessorClass($processorClass3);
+
+               $processors = $fileProcessorRegistry->getFileProcessors();
+               $this->assertTrue($processors[0] instanceof $processorClass2);
+               $this->assertTrue($processors[1] instanceof $processorClass3);
+               $this->assertTrue($processors[2] instanceof $processorClass1);
+       }
+
+       /**
+        * @test
+        */
+       public function registeredFileProcessorClassWithSamePriorityAreReturnedInSameOrderAsTheyWereAdded() {
+               $processorClass1 = uniqid('myProcessor1');
+               $processorObject1 = $this->getMock('TYPO3\\CMS\\Core\\Resource\\Processing\\FileProcessorInterface', array(), array(), $processorClass1);
+               $processorObject1->expects($this->any())->method('getPriority')->will($this->returnValue(1));
+
+               $processorClass2 = uniqid('myProcessor2');
+               $processorObject2 = $this->getMock('TYPO3\\CMS\\Core\\Resource\\Processing\\FileProcessorInterface', array(), array(), $processorClass2);
+               $processorObject2->expects($this->any())->method('getPriority')->will($this->returnValue(1));
+
+               $createdFileProcessorInstances = array(
+                       array($processorClass1, $processorObject1),
+                       array($processorClass2, $processorObject2),
+               );
+
+               $fileProcessorRegistry = $this->getTestFileProcessorRegistry($createdFileProcessorInstances);
+               $fileProcessorRegistry->registerFileProcessorClass($processorClass1);
+               $fileProcessorRegistry->registerFileProcessorClass($processorClass2);
+
+               $processors = $fileProcessorRegistry->getFileProcessors();
+               $this->assertTrue($processors[0] instanceof $processorClass1);
+               $this->assertTrue($processors[1] instanceof $processorClass2);
+       }
+}
+