[BUGFIX] Missing typehint in abstract FAL driver
[Packages/TYPO3.CMS.git] / t3lib / file / Storage.php
index 6ca401c..12c0835 100644 (file)
@@ -146,7 +146,7 @@ class t3lib_file_Storage {
        protected $capabilities;
 
        /**
-        * @var t3lib_SignalSlot_Dispatcher
+        * @var Tx_Extbase_SignalSlot_Dispatcher
         */
        protected $signalSlotDispatcher;
 
@@ -177,6 +177,19 @@ class t3lib_file_Storage {
        protected $processingFolder;
 
        /**
+        * whether this storage is online or offline in this request
+        * @var bool
+        */
+       protected $isOnline = NULL;
+
+       /**
+        * The filters used for the files and folder names.
+        *
+        * @var array
+        */
+       protected $fileAndFolderNameFilters = array();
+
+       /**
         * Constructor for a storage object.
         *
         * @param t3lib_file_Driver_AbstractDriver $driver
@@ -184,18 +197,31 @@ class t3lib_file_Storage {
         */
        public function __construct(t3lib_file_Driver_AbstractDriver $driver, array $storageRecord) {
                $this->storageRecord = $storageRecord;
-               $this->configuration = $this->getFileFactory()->convertFlexFormDataToConfigurationArray(
+               $this->configuration = t3lib_file_Factory::getInstance()->convertFlexFormDataToConfigurationArray(
                        $storageRecord['configuration']
                );
 
                $this->driver = $driver;
                $this->driver->setStorage($this);
+
+               try {
+                       $this->driver->processConfiguration();
+               } catch (t3lib_file_exception_InvalidConfigurationException $e) {
+                               // configuration error
+                               // mark this storage as permanently unusable
+                       $this->markAsPermanentlyOffline();
+               }
+
                $this->driver->initialize();
-               $this->capabilities = ($this->storageRecord['is_browsable'] && $this->driver->hasCapability(self::CAPABILITY_BROWSABLE) ? self::CAPABILITY_BROWSABLE : 0)
-                       + ($this->storageRecord['is_public'] && $this->driver->hasCapability(self::CAPABILITY_PUBLIC) ? self::CAPABILITY_PUBLIC : 0)
-                       + ($this->storageRecord['is_writable'] && $this->driver->hasCapability(self::CAPABILITY_WRITABLE) ? self::CAPABILITY_WRITABLE : 0);
+
+               $this->capabilities = (($this->storageRecord['is_browsable'] && $this->driver->hasCapability(self::CAPABILITY_BROWSABLE)) ? self::CAPABILITY_BROWSABLE : 0)
+                       + (($this->storageRecord['is_public'] && $this->driver->hasCapability(self::CAPABILITY_PUBLIC)) ? self::CAPABILITY_PUBLIC : 0)
+                       + (($this->storageRecord['is_writable'] && $this->driver->hasCapability(self::CAPABILITY_WRITABLE)) ? self::CAPABILITY_WRITABLE : 0);
+               // TODO do not set the "public" capability if no public URIs can be generated
 
                $this->processConfiguration();
+
+               $this->resetFileAndFolderNameFiltersToDefault();
        }
 
        /**
@@ -278,7 +304,10 @@ class t3lib_file_Storage {
         * @param string $identifier
         */
        public function getFolderByIdentifier($identifier) {
-               throw new Exception('Function t3lib_file_Storage::getFolderByIdentifier() has been renamed to just getFolder(). Please fix the metho call.');
+               throw new BadMethodCallException(
+                       'Function t3lib_file_Storage::getFolderByIdentifier() has been renamed to just getFolder(). Please fix the method call.',
+                       1333754514
+               );
        }
 
        /**
@@ -287,7 +316,11 @@ class t3lib_file_Storage {
         * @param string $identifier
         */
        public function getFileByIdentifier($identifier) {
-               throw new Exception('Function t3lib_file_Storage::getFileByIdentifier() has been renamed to just getFileInfoByIdentifier().  Please fix the metho call.');
+               throw new BadMethodCallException(
+                       'Function t3lib_file_Storage::getFileByIdentifier() has been renamed to just getFileInfoByIdentifier(). ' .
+                               'Please fix the method call.',
+                       1333754533
+               );
        }
 
        /**
@@ -339,7 +372,7 @@ class t3lib_file_Storage {
         * @return boolean
         */
        protected function hasCapability($capability) {
-               return $this->capabilities && $capability;
+               return ($this->capabilities & $capability) == $capability;
        }
 
        /**
@@ -370,7 +403,75 @@ class t3lib_file_Storage {
         * @return boolean
         */
        public function isBrowsable() {
-               return $this->hasCapability(self::CAPABILITY_BROWSABLE);
+               return ($this->isOnline() && $this->hasCapability(self::CAPABILITY_BROWSABLE));
+       }
+
+       /**
+        * Returns TRUE if this storage is browsable by a (backend) user of TYPO3.
+        *
+        * @return boolean
+        */
+       public function isOnline() {
+               if ($this->isOnline === NULL) {
+                       if ($this->getUid() === 0) {
+                               $this->isOnline = TRUE;
+                       }
+                               // the storage is not marked as online for a longer time
+                       if ($this->storageRecord['is_online'] == 0) {
+                               $this->isOnline = FALSE;
+                       }
+
+                       if ($this->isOnline !== FALSE) {
+                                       // all files are ALWAYS available in the frontend
+                               if (TYPO3_MODE === 'FE') {
+                                       $this->isOnline = TRUE;
+                               } else {
+                                               // check if the storage is disabled temporary for now
+                                       $registryObject = t3lib_div::makeInstance('t3lib_Registry');
+                                       $offlineUntil = $registryObject->get('core', 'sys_file_storage-' . $this->getUid() . '-offline-until');
+                                       if ($offlineUntil && $offlineUntil > time()) {
+                                               $this->isOnline = FALSE;
+                                       } else {
+                                               $this->isOnline = TRUE;
+                                       }
+                               }
+                       }
+               }
+               return $this->isOnline;
+       }
+
+       /**
+        * blow the "fuse" and mark the storage as offline
+        * can only be modified by an admin
+        * typically this is only done if the configuration is wrong
+        */
+       public function markAsPermanentlyOffline() {
+               if ($this->getUid() > 0) {
+                               // @todo: move this to the storage repository
+                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
+                               'sys_file_storage',
+                               'uid=' . intval($this->getUid()),
+                               array('is_online' => 0)
+                       );
+               }
+               $this->storageRecord['is_online'] = 0;
+               $this->isOnline = FALSE;
+       }
+
+       /**
+        * mark this storage as offline
+        *
+        * non-permanent: this typically happens for remote storages
+        * that are "flaky" and not available all the time
+        * mark this storage as offline for the next 5 minutes
+        *
+        * @return void
+        */
+       public function markAsTemporaryOffline() {
+               $registryObject = t3lib_div::makeInstance('t3lib_Registry');
+               $registryObject->set('core', 'sys_file_storage-' . $this->getUid() . '-offline-until', (time()+60*5));
+               $this->storageRecord['is_online'] = 0;
+               $this->isOnline = FALSE;
        }
 
 
@@ -387,14 +488,25 @@ class t3lib_file_Storage {
         * @return void
         */
        public function injectFileMount($folderIdentifier, $additionalData = array()) {
+
+                       // check for the folder before we add it as a filemount
+               if ($this->driver->folderExists($folderIdentifier) === FALSE) {
+                               // if there is an error, this is important and should be handled
+                               // as otherwise the user would see the whole storage without any restrictions for the filemounts
+                       throw new t3lib_file_exception_FolderDoesNotExistException('Folder for file mount ' . $folderIdentifier . ' does not exist.', 1334427099);
+               }
+
+               $folderObject = $this->driver->getFolder($folderIdentifier);
+
                if (empty($additionalData)) {
                        $additionalData = array(
                                'path' => $folderIdentifier,
                                'title' => $folderIdentifier,
-                               'folder' => $this->getFolder($folderIdentifier)
+                               'folder' => $folderObject
                        );
                } else {
-                       $additionalData['folder'] = $this->getFolder($folderIdentifier);
+                       $additionalData['folder'] = $folderObject;
+
                        if (!isset($additionalData['title'])) {
                                $additionalData['title'] = $folderIdentifier;
                        }
@@ -679,12 +791,12 @@ class t3lib_file_Storage {
         * WARNING: Access to the file may be restricted by further means, e.g.
         * some web-based authentication. You have to take care of this yourself.
         *
-        * @param t3lib_file_FileInterface $fileObject The file object
+        * @param t3lib_file_ResourceInterface $resourceObject The file or folder object
         * @param bool $relativeToCurrentScript Determines whether the URL returned should be relative to the current script, in case it is relative at all (only for the LocalDriver)
         * @return string
         */
-       public function getPublicUrlForFile(t3lib_file_FileInterface $fileObject, $relativeToCurrentScript = FALSE) {
-               return $this->driver->getPublicUrl($fileObject, $relativeToCurrentScript);
+       public function getPublicUrl(t3lib_file_ResourceInterface $resourceObject, $relativeToCurrentScript = FALSE) {
+               return $this->driver->getPublicUrl($resourceObject, $relativeToCurrentScript);
        }
 
        /**
@@ -696,11 +808,13 @@ class t3lib_file_Storage {
         * @return t3lib_file_ProcessedFile
         */
        public function processFile(t3lib_file_FileInterface $fileObject, $context, array $configuration) {
-               $processedFile = $this->getFileFactory()->getProcessedFileObject(
+               $processedFile = t3lib_file_Factory::getInstance()->getProcessedFileObject(
                        $fileObject,
                        $context,
                        $configuration
                );
+                       // set the storage of the processed file
+               $processedFile->setStorage($this);
 
                        // Pre-process the file by an accordant slot
                $this->emitPreFileProcess($processedFile, $fileObject, $context, $configuration);
@@ -732,7 +846,9 @@ class t3lib_file_Storage {
         */
        public function getFileForLocalProcessing(t3lib_file_FileInterface $fileObject, $writable = TRUE) {
                $filePath = $this->driver->getFileForLocalProcessing($fileObject, $writable);
-               touch($filePath, $fileObject->getModificationTime());
+               // @todo: shouldn't this go in the driver? this function is called from the indexing service
+               // @todo: and recursively calls itself over and over again, this is left out for now with getModificationTime()
+               // touch($filePath, $fileObject->getModificationTime());
 
                return $filePath;
        }
@@ -751,11 +867,11 @@ class t3lib_file_Storage {
        /**
         * Get file by identifier
         *
-        * @param string $identifier
-        * @return t3lib_file_FileInterface
+        * @param t3lib_file_FileInterface $identifier
+        * @return array
         */
-       public function getFileInfo($file) {
-               return $this->driver->getFileInfo($file);
+       public function getFileInfo($identifier) {
+               return $this->driver->getFileInfo($identifier);
        }
 
        /**
@@ -770,21 +886,57 @@ class t3lib_file_Storage {
                return $this->driver->getFileInfoByIdentifier($identifier);
        }
 
+       /**
+        * Unsets the file and folder name filters, thus making this storage return unfiltered file lists.
+        *
+        * @return void
+        */
+       public function unsetFileAndFolderNameFilters() {
+               $this->fileAndFolderNameFilters = array();
+       }
+
+       /**
+        * Resets the file and folder name filters to the default values defined in the TYPO3 configuration.
+        *
+        * @return void
+        */
+       public function resetFileAndFolderNameFiltersToDefault() {
+               $this->fileAndFolderNameFilters = $GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['callbackFilterMethods'];
+       }
 
        /**
-        * Returns a list of files in a given path.
+        * Returns the file and folder name filters used by this storage.
+        *
+        * @return array
+        */
+       public function getFileAndFolderNameFilters() {
+               return $this->fileAndFolderNameFilters;
+       }
+
+       public function setFileAndFolderNameFilters(array $filters) {
+               $this->fileAndFolderNameFilters = $filters;
+               return $this;
+       }
+
+       public function addFileAndFolderNameFilter($filter) {
+               $this->fileAndFolderNameFilters[] = $filter;
+       }
+
+       /**
+        * Returns a list of files in a given path, filtered by some custom filter methods.
+        *
+        * @see getUnfilteredFileList(), getFileListWithDefaultFilters()
         *
         * @param string $path The path to list
-        * @param string $pattern The pattern the files have to match
         * @param integer $start The position to start the listing; if not set or 0, start from the beginning
         * @param integer $numberOfItems The number of items to list; if not set, return all items
-        * @param bool $excludeHiddenFiles Set this to TRUE to exclude hidden files (starting with a dot)
+        * @param bool $useFilters If FALSE, the list is returned without any filtering; otherwise, the filters defined for this storage are used.
         * @param bool $loadIndexRecords If set to TRUE, the index records for all files are loaded from the database. This can greatly improve performance of this method, especially with a lot of files.
         * @return array Information about the files found.
         */
        // TODO check if we should use a folder object instead of $path
        // TODO add unit test for $loadIndexRecords
-       public function getFileList($path, $pattern = '', $start = 0, $numberOfItems = 0, $excludeHiddenFiles = TRUE, $loadIndexRecords = TRUE) {
+       public function getFileList($path, $start = 0, $numberOfItems = 0, $useFilters = TRUE, $loadIndexRecords = TRUE) {
                $rows = array();
                if ($loadIndexRecords) {
                        /** @var $repository t3lib_file_Repository_FileRepository */
@@ -792,7 +944,9 @@ class t3lib_file_Storage {
                        $rows = $repository->getFileIndexRecordsForFolder($this->getFolder($path));
                }
 
-               $items = $this->driver->getFileList($path, $pattern, $start, $numberOfItems, $excludeHiddenFiles, $rows);
+               $filters = ($useFilters == TRUE) ? $this->fileAndFolderNameFilters : array();
+
+               $items = $this->driver->getFileList($path, $start, $numberOfItems, $filters, $rows);
                uksort($items, 'strnatcasecmp');
 
                return $items;
@@ -845,7 +999,7 @@ class t3lib_file_Storage {
         * @return integer The number of bytes written to the file
         */
        public function setFileContents(t3lib_file_AbstractFile $file, $contents) {
-                       // TODO does setting file contents require update permission?
+
                        // Check if user is allowed to update
                if (!$this->checkUserActionPermission('update', 'File')) {
                        throw new t3lib_file_exception_InsufficientUserPermissionsException('Updating file "'
@@ -882,7 +1036,7 @@ class t3lib_file_Storage {
         * @return t3lib_file_FileInterface The file object
         */
        public function createFile($fileName, t3lib_file_Folder $targetFolderObject) {
-               if (!$this->checkFolderActionPermission('createFile', $targetFolderObject)) {
+               if (!$this->checkFolderActionPermission('add', $targetFolderObject)) {
                        throw new t3lib_file_exception_InsufficientFolderWritePermissionsException('You are not allowed to create directories on this storage "' . $targetFolderObject->getIdentifier() . '"', 1323059807);
                }
                return $this->driver->createFile($fileName, $targetFolderObject);
@@ -896,7 +1050,7 @@ class t3lib_file_Storage {
         * TODO throw FileInUseException when the file is still used anywhere
         */
        public function deleteFile($fileObject) {
-               if (!$this->checkFileActionPermission('delete', $fileObject)) {
+               if (!$this->checkFileActionPermission('remove', $fileObject)) {
                        throw new t3lib_file_exception_InsufficientFileAccessPermissionsException('You are not allowed to delete the file "' . $fileObject->getIdentifier() . "'", 1319550425);
                }
 
@@ -986,7 +1140,7 @@ class t3lib_file_Storage {
                        // Max upload size (kb) for files.
                $maxUploadFileSize = t3lib_div::getMaxUploadFileSize() * 1024;
                if ($uploadedFileSize >= $maxUploadFileSize) {
-                       throw new t3lib_file_exception_UploadSizeException("The uploaded file exceeds the size-limit of $maxUploadFileSize bytes", 1322110041);
+                       throw new t3lib_file_exception_UploadSizeException('The uploaded file exceeds the size-limit of ' . $maxUploadFileSize . ' bytes', 1322110041);
                }
 
                        // Check if targetFolder is writable
@@ -996,7 +1150,7 @@ class t3lib_file_Storage {
 
                        // Check for a valid file extension
                if (!$this->checkFileExtensionPermission($targetFileName)) {
-                       throw new t3lib_file_exception_IllegalFileExtensionException("Extension of file name is not allowed in \"$targetFileName\"!", 1322120271);
+                       throw new t3lib_file_exception_IllegalFileExtensionException('Extension of file name is not allowed in "' . $targetFileName . '"!', 1322120271);
                }
        }
 
@@ -1118,7 +1272,8 @@ class t3lib_file_Storage {
                        'crdate' => $fileInfo['ctime'],
                        'mime_type' => $fileInfo['mimetype'],
                        'size' => $fileInfo['size'],
-                       'tstamp' => $fileInfo['mtime']
+                       'tstamp' => $fileInfo['mtime'],
+                       'name'  => $fileInfo['name']
                );
                if ($storage !== NULL) {
                        $newProperties['storage'] = $storage->getUid();
@@ -1302,12 +1457,14 @@ class t3lib_file_Storage {
         * @param t3lib_file_Folder $targetParentFolder The target parent folder
         * @param string $newFolderName
         * @param string $conflictMode  How to handle conflicts; one of "overrideExistingFile", "renameNewFolder", "cancel"
+        * @throws t3lib_exception
+        * @throws InvalidArgumentException
         * @return t3lib_file_Folder
         */
        // TODO add tests
        public function moveFolder(t3lib_file_Folder $folderToMove, t3lib_file_Folder $targetParentFolder, $newFolderName = NULL, $conflictMode = 'renameNewFolder') {
                $sourceStorage = $folderToMove->getStorage();
-
+               $returnObject = NULL;
                if (!$targetParentFolder->getStorage() == $this) {
                        throw new InvalidArgumentException('Cannot move a folder into a folder that does not belong to this storage.', 1325777289);
                }
@@ -1327,18 +1484,19 @@ class t3lib_file_Storage {
                        } else {
                                $fileMappings = $this->moveFolderBetweenStorages($folderToMove, $targetParentFolder, $newFolderName);
                        }
-
                                // Update the identifier and storage of all file objects
                        foreach ($fileObjects as $oldIdentifier => $fileObject) {
                                $newIdentifier = $fileMappings[$oldIdentifier];
                                $fileObject->updateProperties(array('storage' => $this, 'identifier' => $newIdentifier));
                        }
+                       $returnObject = $this->getFolder($fileMappings[$folderToMove->getIdentifier()]);
                } catch (t3lib_exception $e) {
                        throw $e;
                        // TODO rollback things that have happened
                }
 
                $this->emitPostFolderMoveSignal($folderToMove, $targetParentFolder, $newFolderName);
+               return $returnObject;
        }
 
        /**
@@ -1368,7 +1526,7 @@ class t3lib_file_Storage {
        public function copyFolder(t3lib_file_Folder $folderToCopy, t3lib_file_Folder $targetParentFolder, $newFolderName = NULL, $conflictMode = 'renameNewFolder') {
                // TODO implement the $conflictMode handling
                // TODO permission checks
-
+               $returnObject = NULL;
                $newFolderName = $newFolderName ? $newFolderName : $folderToCopy->getName();
 
                $this->emitPreFolderCopySignal($folderToCopy, $targetParentFolder, $newFolderName);
@@ -1379,6 +1537,7 @@ class t3lib_file_Storage {
                try {
                        if ($sourceStorage == $this) {
                                $this->driver->copyFolderWithinStorage($folderToCopy, $targetParentFolder, $newFolderName);
+                               $returnObject = $this->getFolder($targetParentFolder->getSubfolder($newFolderName)->getIdentifier());
                        } else {
                                $this->copyFolderBetweenStorages($folderToCopy, $targetParentFolder, $newFolderName);
                        }
@@ -1388,6 +1547,7 @@ class t3lib_file_Storage {
                }
 
                $this->emitPostFolderCopySignal($folderToCopy, $targetParentFolder, $newFolderName);
+               return $returnObject;
        }
 
        /**
@@ -1413,17 +1573,19 @@ class t3lib_file_Storage {
        /**
         * Previously in t3lib_extFileFunc::folder_move()
         *
-        * @throws RuntimeException if an error occurs during renaming
+        *
         * @param t3lib_file_Folder $folderObject
         * @param string $newName
-        * @return bool TRUE if the operation succeeded
+        * @throws Exception
+        * @throws InvalidArgumentException
+        * @return t3lib_file_Folder
         */
        public function renameFolder($folderObject, $newName) {
                // TODO unit tests
                // TODO access checks
-
+               $returnObject = NULL;
                if ($this->driver->folderExistsInFolder($newName, $folderObject)) {
-                       throw new InvalidArgumentException("The folder $newName already exists in folder " . $folderObject->getIdentifier(), 1325418870);
+                       throw new InvalidArgumentException('The folder ' . $newName . ' already exists in folder ' . $folderObject->getIdentifier(), 1325418870);
                }
 
                $this->emitPreFolderRenameSignal($folderObject, $newName);
@@ -1431,29 +1593,31 @@ class t3lib_file_Storage {
                $fileObjects = $this->getAllFileObjectsInFolder($folderObject);
                try {
                        $fileMappings = $this->driver->renameFolder($folderObject, $newName);
-
                                // Update the identifier of all file objects
                        foreach ($fileObjects as $oldIdentifier => $fileObject) {
                                $newIdentifier = $fileMappings[$oldIdentifier];
                                $fileObject->updateProperties(array('identifier' => $newIdentifier));
                        }
+                       $returnObject = $this->getFolder($fileMappings[$folderObject->getIdentifier()]);
                } catch (Exception $e) {
                        throw $e;
                }
-
                $this->emitPostFolderRenameSignal($folderObject, $newName);
+               return $returnObject;
        }
 
        /**
         * Previously in t3lib_extFileFunc::folder_delete()
         *
-        * @param t3lib_file_Folder     $folderObject
+        * @param t3lib_file_Folder    $folderObject
         * @param bool $deleteRecursively
+        * @throws RuntimeException
+        * @throws t3lib_file_exception_InsufficientFileAccessPermissionsException
         * @return bool
         */
        public function deleteFolder($folderObject, $deleteRecursively = FALSE) {
 
-               if (!$this->checkFolderActionPermission('delete', $folderObject)) {
+               if (!$this->checkFolderActionPermission('remove', $folderObject)) {
                        throw new t3lib_file_exception_InsufficientFileAccessPermissionsException('You are not allowed to access the folder "' . $folderObject->getIdentifier() . "'", 1323423953);
                }
 
@@ -1463,30 +1627,44 @@ class t3lib_file_Storage {
 
                $this->emitPreFolderDeleteSignal($folderObject);
 
-               $this->driver->deleteFolder($folderObject, $deleteRecursively);
+               $result = $this->driver->deleteFolder($folderObject, $deleteRecursively);
 
                $this->emitPostFolderDeleteSignal($folderObject);
+
+               return $result;
        }
 
        /**
-        * Returns a list of files in a given path.
+        * Returns a list of folders in a given path.
         *
         * @param string $path The path to list
-        * @param string $pattern The pattern the files have to match
         * @param integer $start The position to start the listing; if not set or 0, start from the beginning
         * @param integer $numberOfItems The number of items to list; if not set, return all items
-        * @param bool $excludeHiddenFolders Set to TRUE to exclude hidden folders (starting with a dot)
+        * @param boolean $useFilters If FALSE, the list is returned without any filtering; otherwise, the filters defined for this storage are used.
         * @return array Information about the folders found.
         */
-       public function getFolderList($path, $pattern = '', $start = 0, $numberOfItems = 0, $excludeHiddenFolders = TRUE) {
-               $items = $this->driver->getFolderList($path, $pattern, $start, $numberOfItems, $excludeHiddenFolders);
+       public function getFolderList($path, $start = 0, $numberOfItems = 0, $useFilters = TRUE) {
+               $filters = ($useFilters === TRUE) ? $this->fileAndFolderNameFilters : array();
+
+               return $this->fetchFolderListFromDriver($path, $start, $numberOfItems, $filters);
+       }
+
+       /**
+        * @param $path
+        * @param int $start
+        * @param int $numberOfItems
+        * @param array $folderFilterCallbacks
+        * @return array
+        */
+       public function fetchFolderListFromDriver($path, $start = 0, $numberOfItems = 0, array $folderFilterCallbacks = array()) {
+               $items = $this->driver->getFolderList($path, $start, $numberOfItems, $folderFilterCallbacks);
 
                        // Exclude the _processed_ folder, so it won't get indexed etc
                $processingFolder = $this->getProcessingFolder();
                if ($processingFolder && $path == '/') {
                        $processedFolderIdentifier = $this->processingFolder->getIdentifier();
                        $processedFolderIdentifier = trim($processedFolderIdentifier, '/');
-                        if (isset($items[$processedFolderIdentifier])) {
+                       if (isset($items[$processedFolderIdentifier])) {
                                unset($items[$processedFolderIdentifier]);
                        }
                }
@@ -1521,20 +1699,35 @@ class t3lib_file_Storage {
         *
         * previously in t3lib_extFileFunc::func_newfolder()
         *
-        * @param string $folderName the new folder name
-        * @param t3lib_file_Folder $parentFolder The parent folder to create the new folder inside of
+        * @param string $folderName The new folder name
+        * @param t3lib_file_Folder $parentFolder (optional) the parent folder to create the new folder inside of. If not given, the root folder is used
         * @return t3lib_file_Folder The new folder object
         */
-       public function createFolder($folderName, t3lib_file_Folder $parentFolder) {
-               if (!$this->checkFolderActionPermission('createFolder', $parentFolder)) {
-                       throw new t3lib_file_exception_InsufficientFolderWritePermissionsException('You are not allowed to create directories on this storage "' . $parentFolder->getIdentifier() . '"', 1323059807);
+       public function createFolder($folderName, t3lib_file_Folder $parentFolder = NULL) {
+               if ($parentFolder === NULL) {
+                       $parentFolder = $this->getRootLevelFolder();
                }
 
                if (!$this->driver->folderExists($parentFolder->getIdentifier())) {
                        throw new InvalidArgumentException('Parent folder "' . $parentFolder->getIdentifier() . '" does not exist.', 1325689164);
                }
 
-               return $this->driver->createFolder($folderName, $parentFolder);
+               if (!$this->checkFolderActionPermission('add', $parentFolder)) {
+                       throw new t3lib_file_exception_InsufficientFolderWritePermissionsException(
+                               'You are not allowed to create directories in the folder "' . $parentFolder->getIdentifier() . '"', 1323059807);
+               }
+
+               $folderParts = t3lib_div::trimExplode('/', $folderName, TRUE);
+               foreach ($folderParts as $folder) {
+                       // TODO check if folder creation succeeded
+                       if ($this->hasFolderInFolder($folder, $parentFolder)) {
+                               $parentFolder = $this->driver->getFolderInFolder($folder, $parentFolder);
+                       } else {
+                               $parentFolder = $this->driver->createFolder($folder, $parentFolder);
+                       }
+               }
+
+               return $parentFolder;
        }
 
        /**
@@ -1551,6 +1744,10 @@ class t3lib_file_Storage {
         * @return t3lib_file_Folder
         */
        public function getFolder($identifier) {
+               if (!$this->driver->folderExists($identifier)) {
+                       throw new t3lib_file_exception_FolderDoesNotExistException('Folder ' . $identifier . ' does not exist.', 1320575630);
+               }
+
                $folderObject = $this->driver->getFolder($identifier);
                if ($this->fileMounts && !$this->isWithinFileMountBoundaries($folderObject)) {
                        throw new t3lib_file_exception_NotInMountPointException('Folder "' . $identifier . '" is not within your mount points.', 1330120649);
@@ -1880,7 +2077,7 @@ class t3lib_file_Storage {
         * @param array $configuration
         */
        protected function emitPreFileProcess(t3lib_file_ProcessedFile $processedFile, t3lib_file_FileInterface $file, $context, array $configuration = array()) {
-               t3lib_SignalSlot_Dispatcher::getInstance()->dispatch(
+               $this->getSignalSlotDispatcher()->dispatch(
                        't3lib_file_Storage',
                        self::SIGNAL_PreFileProcess,
                        array($this, $this->driver, $processedFile, $file, $context, $configuration)
@@ -1896,7 +2093,7 @@ class t3lib_file_Storage {
         * @param array $configuration
         */
        protected function emitPostFileProcess(t3lib_file_ProcessedFile $processedFile, t3lib_file_FileInterface $file, $context, array $configuration = array()) {
-               t3lib_SignalSlot_Dispatcher::getInstance()->dispatch(
+               $this->getSignalSlotDispatcher()->dispatch(
                        't3lib_file_Storage',
                        self::SIGNAL_PostFileProcess,
                        array($this, $this->driver, $processedFile, $file, $context, $configuration)
@@ -1966,16 +2163,27 @@ class t3lib_file_Storage {
        }
 
        /**
-        * @return t3lib_SignalSlot_Dispatcher
+        * Get the SignalSlot dispatcher
+        *
+        * @return Tx_Extbase_SignalSlot_Dispatcher
         */
        protected function getSignalSlotDispatcher() {
                if (!isset($this->signalSlotDispatcher)) {
-                       $this->signalSlotDispatcher = t3lib_div::makeInstance('t3lib_SignalSlot_Dispatcher');
+                       $this->signalSlotDispatcher = $this->getObjectManager()->get('Tx_Extbase_SignalSlot_Dispatcher');
                }
                return $this->signalSlotDispatcher;
        }
 
        /**
+        * Get the ObjectManager
+        *
+        * @return Tx_Extbase_Object_ObjectManager
+        */
+       protected function getObjectManager() {
+               return t3lib_div::makeInstance('Tx_Extbase_Object_ObjectManager');
+       }
+
+       /**
         * @return t3lib_file_Factory
         */
        protected function getFileFactory() {
@@ -2013,23 +2221,27 @@ class t3lib_file_Storage {
 
                        $processingFolder = trim($processingFolder, '/');
 
-                               // @todo: does not resolve deeplinked folders like typo3temp/_processed_
+                               // this way, we also worry about deeplinked folders like typo3temp/_processed_
                        if ($this->driver->folderExists($processingFolder) === FALSE) {
-                               $this->processingFolder = $this->driver->createFolder(
-                                       $processingFolder,
-                                       $this->driver->getRootLevelFolder()
-                               );
-                       } else {
-                               $this->processingFolder = $this->driver->getFolder($processingFolder);
+                               $processingFolderParts = explode('/', $processingFolder);
+                               $parentFolder = $this->driver->getRootLevelFolder();
+
+                               foreach ($processingFolderParts as $folderPart) {
+                                       if (!$this->driver->folderExistsInFolder($folderPart, $parentFolder)) {
+                                               $parentFolder = $this->driver->createFolder(
+                                                       $folderPart,
+                                                       $parentFolder
+                                               );
+                                       } else {
+                                               $parentFolder = $parentFolder->getSubfolder($folderPart);
+                                       }
+                               }
                        }
+                       $this->processingFolder = $this->driver->getFolder($processingFolder);
                }
 
                return $this->processingFolder;
        }
 }
 
-if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/file/Storage.php'])) {
-       include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/file/Storage.php']);
-}
-
 ?>
\ No newline at end of file