From e542fac68f84d56b7ce711593a4d58fc6a7fd37b Mon Sep 17 00:00:00 2001
From: Frans Saris <franssaris@gmail.com>
Date: Sun, 2 Feb 2014 11:04:44 +0100
Subject: [PATCH] [TASK] Add FileCollectionRegistry

The classes belonging to the FileCollection types are hardwired
in ResourceFactory::createCollectionObject(), therefore you can
not use your own types.

This change introduces a Registry API that makes it possible to
register your own FileCollection type + class and has a generic
way to add the new type to TCA[sys_file_collection].

How to use:
- Register type in ext_localconf.php
  $register->registerFileCollectionClass(full_class_name, your_type);
- Add type to TCA in Configuration/TCA/sys_file_collection.php
  $register->addTypeToTCA(your_type, label, needed_fields);
  return $GLOBALS['TCA']['sys_file_collection'];

Resolves: #53910
Documentation: #56032
Releases: 6.2
Change-Id: Id6cb1c7a59f741b28fee7bdfef32890f34a072a5
Reviewed-on: https://review.typo3.org/27257
Reviewed-by: Markus Klein
Tested-by: Markus Klein
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
---
 .../Collection/FileCollectionRegistry.php     | 146 ++++++++++++++
 .../core/Classes/Resource/ResourceFactory.php |  19 +-
 .../Configuration/DefaultConfiguration.php    |   5 +
 .../Collection/FileCollectionRegistryTest.php | 188 ++++++++++++++++++
 4 files changed, 344 insertions(+), 14 deletions(-)
 create mode 100644 typo3/sysext/core/Classes/Resource/Collection/FileCollectionRegistry.php
 create mode 100644 typo3/sysext/core/Tests/Unit/Resource/Collection/FileCollectionRegistryTest.php

diff --git a/typo3/sysext/core/Classes/Resource/Collection/FileCollectionRegistry.php b/typo3/sysext/core/Classes/Resource/Collection/FileCollectionRegistry.php
new file mode 100644
index 000000000000..5a39b8967b0f
--- /dev/null
+++ b/typo3/sysext/core/Classes/Resource/Collection/FileCollectionRegistry.php
@@ -0,0 +1,146 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Collection;
+
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2013 - 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 FileCollection classes
+ */
+class FileCollectionRegistry implements \TYPO3\CMS\Core\SingletonInterface {
+
+	/**
+	 * Registered FileCollection types
+	 *
+	 * @var array
+	 */
+	protected $types = array();
+
+	/**
+	 * Constructor
+	 */
+	public function __construct() {
+		foreach ($GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['registeredCollections'] as $type => $class) {
+			$this->registerFileCollectionClass($class, $type);
+		}
+	}
+
+	/**
+	 * Register a (new) FileCollection type
+	 *
+	 * @param string $className
+	 * @param string $type FileCollection type max length 30 chars (db field restriction)
+	 * @param bool $override existing FileCollection type
+	 * @return bool TRUE if registration succeeded
+	 * @throws \InvalidArgumentException
+	 */
+	public function registerFileCollectionClass($className, $type, $override = FALSE) {
+
+		if (strlen($type) > 30) {
+			throw new \InvalidArgumentException('FileCollection type can have a max string length of 30 bytes', 1391295611);
+		}
+
+		if (!class_exists($className)) {
+			throw new \InvalidArgumentException('Class ' . $className . ' does not exist.', 1391295613);
+		}
+
+		if (!in_array('TYPO3\\CMS\\Core\\Resource\\Collection\\AbstractFileCollection', class_parents($className), TRUE)) {
+			throw new \InvalidArgumentException('FileCollection ' . $className . ' needs to extend the AbstractFileCollection.', 1391295633);
+		}
+
+		if (isset($this->types[$type])) {
+			// Return immediately without changing configuration
+			if ($this->types[$type] === $className) {
+				return TRUE;
+			} elseif (!$override) {
+				throw new \InvalidArgumentException('FileCollections ' . $type . ' is already registered.', 1391295643);
+			}
+		}
+
+		$this->types[$type] = $className;
+		return TRUE;
+	}
+
+	/**
+	 * Add the type to the TCA of sys_file_collection
+	 *
+	 * @param string $type
+	 * @param string $label
+	 * @param string $availableFields comma separated list of fields to show
+	 * @param array $additionalColumns Additional columns configuration
+	 * @return array adjusted TCA for sys_file_collection
+	 */
+	public function addTypeToTCA($type, $label, $availableFields, array $additionalColumns = array()) {
+
+		$GLOBALS['TCA']['sys_file_collection']['types'][$type] = array(
+			'showitem' => 'sys_language_uid;;;;1-1-1, l10n_parent, l10n_diffsource, title;;1, type, ' . $availableFields
+		);
+
+		// search for existing type when found override label
+		$typeFound = FALSE;
+		foreach ($GLOBALS['TCA']['sys_file_collection']['columns']['type']['config']['items'] as $key => $item) {
+			if ($item[1] === $type) {
+				$typeFound = TRUE;
+				$GLOBALS['TCA']['sys_file_collection']['columns']['type']['config']['items'][$key][0] = $label;
+			}
+		}
+		if (!$typeFound) {
+			$GLOBALS['TCA']['sys_file_collection']['columns']['type']['config']['items'][] = array(
+				0 => $label,
+				1 => $type
+			);
+		}
+		if ($additionalColumns !== array()) {
+			\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['TCA']['sys_file_collection']['columns'], $additionalColumns);
+		}
+		return $GLOBALS['TCA']['sys_file_collection'];
+	}
+
+	/**
+	 * Returns a class name for a given type
+	 *
+	 * @param string $type
+	 * @return string The class name
+	 * @throws \InvalidArgumentException
+	 */
+	public function getFileCollectionClass($type) {
+		if (!isset($this->types[$type])) {
+			throw new \InvalidArgumentException('Desired FileCollection type "' . $type . '" is not in the list of available FileCollections.', 1391295644);
+		}
+		return $this->types[$type];
+	}
+
+	/**
+	 * Checks if the given FileCollection type exists
+	 *
+	 * @param string $type Type of the FileCollection
+	 * @return boolean TRUE if the FileCollection exists, FALSE otherwise
+	 */
+	public function fileCollectionTypeExists($type) {
+		return isset($this->types[$type]);
+	}
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Resource/ResourceFactory.php b/typo3/sysext/core/Classes/Resource/ResourceFactory.php
index c75478f1488b..0d5a1f6b90e4 100644
--- a/typo3/sysext/core/Classes/Resource/ResourceFactory.php
+++ b/typo3/sysext/core/Classes/Resource/ResourceFactory.php
@@ -296,20 +296,11 @@ class ResourceFactory implements \TYPO3\CMS\Core\SingletonInterface {
 	 * @return Collection\AbstractFileCollection
 	 */
 	public function createCollectionObject(array $collectionData) {
-		switch ($collectionData['type']) {
-			case 'static':
-				$collection = Collection\StaticFileCollection::create($collectionData);
-				break;
-			case 'folder':
-				$collection = Collection\FolderBasedFileCollection::create($collectionData);
-				break;
-			case 'category':
-				$collection = Collection\CategoryBasedFileCollection::create($collectionData);
-				break;
-			default:
-				$collection = NULL;
-		}
-		return $collection;
+		/** @var $registry Collection\FileCollectionRegistry */
+		$registry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Collection\\FileCollectionRegistry');
+		$class = $registry->getFileCollectionClass($collectionData['type']);
+
+		return $class::create($collectionData);
 	}
 
 	/**
diff --git a/typo3/sysext/core/Configuration/DefaultConfiguration.php b/typo3/sysext/core/Configuration/DefaultConfiguration.php
index a9b829dabb67..49bd89bc851d 100644
--- a/typo3/sysext/core/Configuration/DefaultConfiguration.php
+++ b/typo3/sysext/core/Configuration/DefaultConfiguration.php
@@ -247,6 +247,11 @@ return array(
 			'processingTaskTypes' => array(
 				'Image.Preview' => 'TYPO3\\CMS\\Core\\Resource\\Processing\\ImagePreviewTask',
 				'Image.CropScaleMask' => 'TYPO3\\CMS\\Core\\Resource\\Processing\\ImageCropScaleMaskTask'
+			),
+			'registeredCollections' => array(
+				'static' => 'TYPO3\\CMS\\Core\\Resource\\Collection\\StaticFileCollection',
+				'folder' => 'TYPO3\\CMS\\Core\\Resource\\Collection\\FolderBasedFileCollection',
+				'category' => 'TYPO3\\CMS\\Core\\Resource\\Collection\\CategoryBasedFileCollection',
 			)
 		),
 		'isInitialInstallationInProgress' => FALSE,		// Boolean: If TRUE, the installation is 'in progress'. This value is handled within the install tool step installer internally.
diff --git a/typo3/sysext/core/Tests/Unit/Resource/Collection/FileCollectionRegistryTest.php b/typo3/sysext/core/Tests/Unit/Resource/Collection/FileCollectionRegistryTest.php
new file mode 100644
index 000000000000..7ccee569dc1e
--- /dev/null
+++ b/typo3/sysext/core/Tests/Unit/Resource/Collection/FileCollectionRegistryTest.php
@@ -0,0 +1,188 @@
+<?php
+namespace TYPO3\CMS\Core\Tests\Unit\Resource\Collection;
+
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2013 - 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 FileCollectionRegistry
+ */
+class FileCollectionRegistryTest extends \TYPO3\CMS\Core\Tests\Unit\Resource\BaseTestCase {
+
+	/**
+	 * @var \TYPO3\CMS\Core\Resource\Collection\FileCollectionRegistry
+	 */
+	protected $testSubject;
+
+	public function setUp() {
+		$this->initializeTestSubject();
+	}
+
+	protected function initializeTestSubject() {
+		$this->testSubject = new \TYPO3\CMS\Core\Resource\Collection\FileCollectionRegistry();
+	}
+
+	/**
+	 * @test
+	 */
+	public function registeredFileCollectionClassesCanBeRetrieved() {
+		$className = get_class($this->getMockForAbstractClass('TYPO3\\CMS\\Core\\Resource\\Collection\\AbstractFileCollection'));
+		$this->testSubject->registerFileCollectionClass($className, 'foobar');
+		$returnedClassName = $this->testSubject->getFileCollectionClass('foobar');
+		$this->assertEquals($className, $returnedClassName);
+	}
+
+	/**
+	 * @test
+	 * @expectedException InvalidArgumentException
+	 * @expectedExceptionCode 1391295613
+	 */
+	public function registerFileCollectionClassThrowsExceptionIfClassDoesNotExist() {
+		$this->testSubject->registerFileCollectionClass(uniqid(), uniqid());
+	}
+
+	/**
+	 * @test
+	 * @expectedException InvalidArgumentException
+	 * @expectedExceptionCode 1391295611
+	 */
+	public function registerFileCollectionClassThrowsExceptionIfTypeIsTooLong() {
+		$className = get_class($this->getMockForAbstractClass('TYPO3\\CMS\\Core\\Resource\\Collection\\AbstractFileCollection'));
+		$type = str_pad('', 40);
+		$this->testSubject->registerFileCollectionClass($className, $type);
+	}
+
+	/**
+	 * @test
+	 * @expectedException InvalidArgumentException
+	 * @expectedExceptionCode 1391295643
+	 */
+	public function registerFileCollectionClassThrowsExceptionIfTypeIsAlreadyRegistered() {
+		$className = get_class($this->getMockForAbstractClass('TYPO3\\CMS\\Core\\Resource\\Collection\\AbstractFileCollection'));
+		$className2 = get_class($this->getMockForAbstractClass('TYPO3\\CMS\\Core\\Resource\\Collection\\StaticFileCollection'));
+		$this->testSubject->registerFileCollectionClass($className, 'foobar');
+		$this->testSubject->registerFileCollectionClass($className2, 'foobar');
+	}
+
+	/**
+	 * @test
+	 */
+	public function registerFileCollectionClassOverridesExistingRegisteredFileCollectionClass() {
+		$className = get_class($this->getMockForAbstractClass('TYPO3\\CMS\\Core\\Resource\\Collection\\AbstractFileCollection'));
+		$className2 = get_class($this->getMockForAbstractClass('TYPO3\\CMS\\Core\\Resource\\Collection\\StaticFileCollection'));
+		$this->testSubject->registerFileCollectionClass($className, 'foobar');
+		$this->testSubject->registerFileCollectionClass($className2, 'foobar', TRUE);
+	}
+
+	/**
+	 * @test
+	 * @expectedException InvalidArgumentException
+	 * @expectedExceptionCode 1391295644
+	 */
+	public function getFileCollectionClassThrowsExceptionIfClassIsNotRegistered() {
+		$this->testSubject->getFileCollectionClass(uniqid());
+	}
+
+	/**
+	 * @test
+	 */
+	public function getFileCollectionClassAcceptsClassNameIfClassIsRegistered() {
+		$className = get_class($this->getMockForAbstractClass('TYPO3\\CMS\\Core\\Resource\\Collection\\AbstractFileCollection'));
+		$this->testSubject->registerFileCollectionClass($className, 'foobar');
+		$this->assertEquals($className, $this->testSubject->getFileCollectionClass('foobar'));
+	}
+
+	/**
+	 * @test
+	 */
+	public function fileCollectionRegistryIsInitializedWithPreconfiguredFileCollections() {
+		$className = get_class($this->getMockForAbstractClass('TYPO3\\CMS\\Core\\Resource\\Collection\\AbstractFileCollection'));
+		$type = uniqid();
+		$GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['registeredCollections'] = array(
+			$type => $className
+		);
+		$this->initializeTestSubject();
+		$this->assertEquals($className, $this->testSubject->getFileCollectionClass($type));
+	}
+
+	/**
+	 * @test
+	 */
+	public function fileCollectionExistsReturnsTrueForAllExistingFileCollections() {
+		$className = get_class($this->getMockForAbstractClass('TYPO3\\CMS\\Core\\Resource\\Collection\\AbstractFileCollection'));
+		$type = 'foo';
+		$GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['registeredCollections'] = array(
+			$type => $className
+		);
+		$this->initializeTestSubject();
+		$this->assertTrue($this->testSubject->fileCollectionTypeExists($type));
+		$this->assertFalse($this->testSubject->fileCollectionTypeExists('bar'));
+	}
+
+	/**
+	 * @test
+	 */
+	public function fileCollectionExistsReturnsFalseIfFileCollectionDoesNotExist() {
+		$GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['registeredFileCollections'] = array();
+		$this->initializeTestSubject();
+		$this->assertFalse($this->testSubject->fileCollectionTypeExists(uniqid()));
+	}
+
+	/**
+	 * @test
+	 */
+	public function addNewTypeToTCA() {
+
+		// Create a TCA fixture for sys_file_collection
+		$GLOBALS['TCA']['sys_file_collection'] = array(
+			'types' => array(
+				'typeB' => array('showitem' => 'fieldA, fieldB, fieldC;labelC;paletteC;specialC, fieldD'),
+			),
+			'columns' => array(
+				'type' => array(
+					'config' => array(
+						'items' => array('Type A', 'typeA'),
+						'items' => array('Type B', 'typeB')
+					)
+				)
+			)
+		);
+
+		$type = 'my_type';
+		$label = 'The Label';
+
+		$this->testSubject->addTypeToTCA($type, $label, 'something');
+
+		// check type
+		$this->assertEquals('sys_language_uid;;;;1-1-1, l10n_parent, l10n_diffsource, title;;1, type, something', $GLOBALS['TCA']['sys_file_collection']['types']['my_type']['showitem']);
+
+		$indexOfNewType = count($GLOBALS['TCA']['sys_file_collection']['columns']['type']['config']['items']) - 1;
+
+		// check if columns.type.item exist
+		$this->assertEquals($type, $GLOBALS['TCA']['sys_file_collection']['columns']['type']['config']['items'][$indexOfNewType][1]);
+		$this->assertEquals($label, $GLOBALS['TCA']['sys_file_collection']['columns']['type']['config']['items'][$indexOfNewType][0]);
+	}
+}
\ No newline at end of file
-- 
GitLab