diff -u b/core/lib/Drupal/Core/Config/FileStorage.php b/core/lib/Drupal/Core/Config/FileStorage.php --- b/core/lib/Drupal/Core/Config/FileStorage.php +++ b/core/lib/Drupal/Core/Config/FileStorage.php @@ -69,12 +69,12 @@ protected function ensureStorage() { $dir = $this->getCollectionDirectory(); $success = file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); - // Only create .htaccess file in root driectory. + // Only create .htaccess file in root directory. if ($dir == $this->directory) { $success = $success && file_save_htaccess($this->directory, TRUE, TRUE); } if (!$success) { - throw new StorageException("Failed to create config directory {$dir}"); + throw new StorageException('Failed to create config directory ' . $dir); } return $this; } @@ -260,33 +260,60 @@ /** * {@inheritdoc} + */ + public function getAllCollectionNames($directory = '') { + $collections = $this->getAllCollectionNamesHelper($this->directory); + sort($collections); + return $collections; + } + + /** + * Helper function for getAllCollectionNames(). + * + * If the file storage has the following subdirectory structure: + * ./another_collection/one + * ./another_collection/two + * ./collection/sub/one + * ./collection/sub/two + * this function will return: + * @code + * array( + * 'another_collection.one', + * 'another_collection.two', + * 'collection.sub.one', + * 'collection.sub.two', + * ); + * @endcode * * @param string $directory - * (optional) The directory to check for sub directories. This allows this + * The directory to check for sub directories. This allows this * function to be used recursively to discover all the collections in the - * storage. Defaults to an empty string which starts checking from the - * storage's root directory. + * storage. + * + * @return array + * A list of collection names contained within the provided directory. */ - public function getAllCollectionNames($directory = '') { + protected function getAllCollectionNamesHelper($directory) { $collections = array(); - if (empty($directory)) { - $directory = $this->directory; - } foreach (new \DirectoryIterator($directory) as $fileinfo) { if ($fileinfo->isDir() && !$fileinfo->isDot()) { $collection = $fileinfo->getFilename(); - $sub_collections = $this->getAllCollectionNames($directory . '/' . $collection); - if (empty($sub_collections)) { - $collections[] = $collection; - } - else { + // Discover if there are sub directories representing a dotted + // collection name. + $sub_collections = $this->getAllCollectionNamesHelper($directory . '/' . $collection); + if (!empty($sub_collections)) { + // Build up the collection name be concatenating the subdirectory + // names with the current directory name. foreach ($sub_collections as $sub_collection) { $collections[] = $collection . '.' . $sub_collection; } } + else { + // + $collections[] = $collection; + } } } - sort($collections); return $collections; } diff -u b/core/lib/Drupal/Core/Config/StorageComparerInterface.php b/core/lib/Drupal/Core/Config/StorageComparerInterface.php --- b/core/lib/Drupal/Core/Config/StorageComparerInterface.php +++ b/core/lib/Drupal/Core/Config/StorageComparerInterface.php @@ -15,6 +15,10 @@ /** * Gets the configuration source storage. * + * @param string $collection + * (optional) The storage collection to use. Defaults to the + * default collection. + * * @return \Drupal\Core\Config\StorageInterface * Storage object used to read configuration. */ @@ -23,6 +27,10 @@ /** * Gets the configuration target storage. * + * @param string $collection + * (optional) The storage collection to use. Defaults to the + * default collection. + * * @return \Drupal\Core\Config\StorageInterface * Storage object used to write configuration. */ diff -u b/core/lib/Drupal/Core/Config/StorageInterface.php b/core/lib/Drupal/Core/Config/StorageInterface.php --- b/core/lib/Drupal/Core/Config/StorageInterface.php +++ b/core/lib/Drupal/Core/Config/StorageInterface.php @@ -168,18 +168,24 @@ * in partitioned collections. The collection name identifies the current * collection used. * + * Implementations of this method must provide a new instance to avoid side + * effects caused by the fact that Config objects have their storage injected. + * * @param string $collection - * The collection name. If not set then this is normal configuration - * storage. However if set the storage needs to use it to differentiate the - * collections. collections can be nested for example 'language.de'. If this - * is a file system then we could create a language folder with a subfolder - * named de. If this is a database table then there could be a collection - * column which will store the value 'language.de'. + * The collection name. Valid collection names conform to the following + * regex [a-zA-Z_.]. A storage does not need to have a collection set. + * However, if a collection is set, then storage should use it to store + * configuration in a way that allows retrieval of configuration for a + * particular collection. Collections can be nested, for example + * 'language.de'. If this is a file system then we could create a language + * folder with a subfolder named de. If this is a database table then there + * could be a collection column which will store the value 'language.de'. + * Storage nested levels should be consistent. If a collection with the name + * 'language.de' exists then a collections with the names 'language' or + * 'language.en.gb' should not be used. * * @return \Drupal\Core\Config\StorageInterface - * An instance of the storage class. It will be cloned from the original - * class to avoid side effects caused by the fact that Config objects have - * their storage injected. + * An new instance of the storage backend with the collection set. */ public function createCollection($collection); diff -u b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php --- b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php +++ b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php @@ -192,7 +192,7 @@ $this->assertIdentical($data, $this->storage->read($name)); // Create configuration in a new collection. - $new_storage = $this->storage->createCollection('collection.new'); + $new_storage = $this->storage->createCollection('collection.sub.new'); $this->assertEqual(array(), $new_storage->listAll()); $new_storage->write($name, $data); $this->assertIdentical($result, TRUE); @@ -204,7 +204,7 @@ $this->assertIdentical($new_data, $new_storage->read($name)); // Create configuration in another collection. - $another_storage = $this->storage->createCollection('collection.another'); + $another_storage = $this->storage->createCollection('collection.sub.another'); $this->assertEqual(array(), $another_storage->listAll()); $another_storage->write($name, $new_data); $this->assertIdentical($result, TRUE); @@ -222,18 +222,18 @@ $this->assertIdentical($data, $this->storage->read($name)); // Check that the getAllCollectionNames() method works. - $this->assertIdentical(array('alternate', 'collection.another', 'collection.new'), $this->storage->getAllCollectionNames()); + $this->assertIdentical(array('alternate', 'collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames()); // Check that the collections are removed when they are empty. $alt_storage->delete($name); - $this->assertIdentical(array('collection.another', 'collection.new'), $this->storage->getAllCollectionNames()); + $this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames()); // Check that the having an empty collection-less storage does not break // anything. Before deleting check that the previous delete did not affect // data in another collection. $this->assertIdentical($data, $this->storage->read($name)); $this->storage->delete($name); - $this->assertIdentical(array('collection.another', 'collection.new'), $this->storage->getAllCollectionNames()); + $this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames()); } abstract protected function read($name); diff -u b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php --- b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php @@ -6,7 +6,6 @@ use Drupal\Core\Config\CachedStorage; use Drupal\Core\Cache\MemoryBackend; use Drupal\Core\Cache\NullBackend; -use Drupal\Core\Cache\CacheBackendInterface; /** * Tests the interaction of cache and file storage in CachedStorage.