diff --git a/core/core.services.yml b/core/core.services.yml index ac24e17..8ba5160 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -69,7 +69,7 @@ services: factory_method: getActive config.manager: class: Drupal\Core\Config\ConfigManager - arguments: ['@entity.manager', '@config.factory', '@config.typed', '@string_translation'] + arguments: ['@entity.manager', '@config.factory', '@config.typed', '@string_translation', '@config.storage'] config.storage: class: Drupal\Core\Config\CachedStorage arguments: ['@config.cachedstorage.storage', '@cache.config'] diff --git a/core/lib/Drupal/Core/Config/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php index 2bb06b3..b039b8f 100644 --- a/core/lib/Drupal/Core/Config/ConfigInstaller.php +++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Config; use Drupal\Component\Utility\Unicode; +use Drupal\Core\Config\Entity\ConfigDependencyManager; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class ConfigInstaller implements ConfigInstallerInterface { @@ -105,25 +106,43 @@ public function installDefaultConfig($type, $name) { } if (!empty($config_to_install)) { + // Order the configuration to install in the order of dependencies. + $data = $source_storage->readMultiple($config_to_install); + $dependency_manager = new ConfigDependencyManager(); + $sorted_config = $dependency_manager + ->setData($data) + ->sortAll(); + $old_state = $this->configFactory->getOverrideState(); $this->configFactory->setOverrideState(FALSE); - foreach ($config_to_install as $name) { - // Only import new config. - if ($this->activeStorage->exists($name)) { - continue; - } + // Remove configuration that already exists in the active storage. + $sorted_config = array_diff($sorted_config, $this->activeStorage->listAll()); + + foreach ($sorted_config as $name) { $new_config = new Config($name, $this->activeStorage, $this->eventDispatcher, $this->typedConfig); - $data = $source_storage->read($name); - if ($data !== FALSE) { - $new_config->setData($data); + if ($data[$name] !== FALSE) { + $new_config->setData($data[$name]); } if ($entity_type = $this->configManager->getEntityTypeIdByName($name)) { - $this->configManager + $entity_storage = $this->configManager ->getEntityManager() - ->getStorageController($entity_type) - ->create($new_config->get()) - ->save(); + ->getStorageController($entity_type); + // It is possible that secondary writes can occur during configuration + // creation. Updates of such configuration are allowed. + if ($this->activeStorage->exists($name)) { + $id = $entity_storage->getIDFromConfigName($name, $entity_storage->getEntityType()->getConfigPrefix()); + $entity = $entity_storage->load($id); + foreach ($new_config->get() as $property => $value) { + $entity->set($property, $value); + } + $entity->save(); + } + else { + $entity_storage + ->create($new_config->get()) + ->save(); + } } else { $new_config->save(); @@ -131,6 +150,8 @@ public function installDefaultConfig($type, $name) { } $this->configFactory->setOverrideState($old_state); } + // Reset all the static caches and list caches. + $this->configFactory->reset(); } } diff --git a/core/lib/Drupal/Core/Config/ConfigManager.php b/core/lib/Drupal/Core/Config/ConfigManager.php index d52e806..a2c3f20 100644 --- a/core/lib/Drupal/Core/Config/ConfigManager.php +++ b/core/lib/Drupal/Core/Config/ConfigManager.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Config; +use Drupal\Core\Config\Entity\ConfigDependencyManager; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\StringTranslation\TranslationManager; @@ -46,6 +47,13 @@ class ConfigManager implements ConfigManagerInterface { protected $stringTranslation; /** + * The active configuration storage. + * + * @var \Drupal\Core\Config\StorageInterface + */ + protected $activeStorage; + + /** * Creates ConfigManager objects. * * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager @@ -57,11 +65,12 @@ class ConfigManager implements ConfigManagerInterface { * @param \Drupal\Core\StringTranslation\TranslationManager $string_translation * The string translation service. */ - public function __construct(EntityManagerInterface $entity_manager, ConfigFactoryInterface $config_factory, TypedConfigManager $typed_config_manager, TranslationManager $string_translation) { + public function __construct(EntityManagerInterface $entity_manager, ConfigFactoryInterface $config_factory, TypedConfigManager $typed_config_manager, TranslationManager $string_translation, StorageInterface $active_storage) { $this->entityManager = $entity_manager; $this->configFactory = $config_factory; $this->typedConfigManager = $typed_config_manager; $this->stringTranslation = $string_translation; + $this->activeStorage = $active_storage; } /** @@ -125,6 +134,17 @@ public function createSnapshot(StorageInterface $source_storage, StorageInterfac * {@inheritdoc} */ public function uninstall($type, $name) { + // Remove all dependent configuration entities. + $dependent_entities = $this->findConfigEntityDependentsAsEntities($type, array($name)); + + // Reverse the array to that entities are removed in the correct order of + // dependence. For example, this ensures that field instances are removed + // before fields. + foreach (array_reverse($dependent_entities) as $entity) { + $entity->setUninstalling(TRUE); + $entity->delete(); + } + $config_names = $this->configFactory->listAll($name . '.'); foreach ($config_names as $config_name) { $this->configFactory->get($config_name)->delete(); @@ -137,4 +157,55 @@ public function uninstall($type, $name) { } } + /** + * {@inheritdoc} + */ + public function findConfigEntityDependents($type, array $names) { + $dependency_manager = new ConfigDependencyManager(); + // This uses the configuration storage directly to avoid blowing the static + // caches in the configuration factory and the configuration entity system. + // Additionally this ensures that configuration entity dependency discovery + // has no dependencies on the config entity classes. Assume data with UUID + // is a config entity. Only configuration entities can be depended on so we + // can ignore everything else. + $data = array_filter($this->activeStorage->readMultiple($this->activeStorage->listAll()), function($config) { + return isset($config['uuid']); + }); + $dependency_manager->setData($data); + $dependencies = array(); + foreach ($names as $name) { + $dependencies = array_merge($dependencies, $dependency_manager->getDependentEntities($type, $name)); + } + return $dependencies; + } + + /** + * {@inheritdoc} + */ + public function findConfigEntityDependentsAsEntities($type, array $names) { + $dependencies = $this->findConfigEntityDependents($type, $names); + $entities = array(); + $definitions = $this->entityManager->getDefinitions(); + foreach ($dependencies as $config_name => $dependency) { + // Group by entity type so we can use load multiple. + $entity_type_id = $this->getEntityTypeIdByName($config_name); + // It is possible that a non configuration entity will be returned if a + // simple configuration object has a uuid key. This would occur if the + // dependents of the system module are calculated since system.site has + // a uuid key. + if ($entity_type_id) { + $id = substr($config_name, strlen($definitions[$entity_type_id]->getConfigPrefix()) + 1); + $entities[$entity_type_id][] = $id; + } + } + $entities_to_return = array(); + foreach ($entities as $entity_type_id => $entities_to_load) { + $storage_controller = $this->entityManager->getStorageController($entity_type_id); + // Remove the keys since there are potential ID clashes from different + // configuration entity types. + $entities_to_return = array_merge($entities_to_return, array_values($storage_controller->loadMultiple($entities_to_load))); + } + return $entities_to_return; + } + } diff --git a/core/lib/Drupal/Core/Config/ConfigManagerInterface.php b/core/lib/Drupal/Core/Config/ConfigManagerInterface.php index d78b358..b5084fe 100644 --- a/core/lib/Drupal/Core/Config/ConfigManagerInterface.php +++ b/core/lib/Drupal/Core/Config/ConfigManagerInterface.php @@ -68,4 +68,35 @@ public function createSnapshot(StorageInterface $source_storage, StorageInterfac */ public function uninstall($type, $name); + /** + * Finds config entities that are dependent on extensions or entities. + * + * @param string $type + * The type of dependency being checked. Either 'module', 'theme', 'entity'. + * @param array $names + * The specific names to check. If $type equals 'module' or 'theme' then it + * should be a list of module names or theme names. In the case of entity it + * should be a list of full configuration object names. + * + * @return \Drupal\Core\Config\Entity\ConfigEntityDependency[] + * An array of configuration entity dependency objects. + */ + public function findConfigEntityDependents($type, array $names); + + /** + * Finds config entities that are dependent on extensions or entities. + * + * @param string $type + * The type of dependency being checked. Either 'module', 'theme', 'entity'. + * @param array $names + * The specific names to check. If $type equals 'module' or 'theme' then it + * should be a list of module names or theme names. In the case of entity it + * should be a list of full configuration object names. + * + * @return \Drupal\Core\Config\Entity\ConfigEntityInterface[] + * An array of dependencies as configuration entities. + */ + public function findConfigEntityDependentsAsEntities($type, array $names); + + } diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigDependencyManager.php b/core/lib/Drupal/Core/Config/Entity/ConfigDependencyManager.php new file mode 100644 index 0000000..fc76841 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Entity/ConfigDependencyManager.php @@ -0,0 +1,174 @@ +data, function (ConfigEntityDependency $entity) use ($type, $name) { + return $entity->hasDependency($type, $name); + }); + } + + $entities_to_check = array(); + if ($type == 'entity') { + $entities_to_check[] = $name; + } + else { + // If checking module or theme dependencies then discover which entities + // are dependent on the entities that have a direct dependency. + foreach ($dependent_entities as $entity) { + $entities_to_check[] = $entity->getConfigDependencyName(); + } + } + + return array_merge($dependent_entities, $this->graphConfigEntityDependencies($entities_to_check)); + } + + /** + * Sorts the dependencies in order of most dependent last. + * + * @return array + * The list of entities in order of most dependent last otherwise + * alphabetical. + */ + public function sortAll() { + $graph = $this->getGraph(); + // Sort by reverse weight and alphabetically. So the most dependent entities + // are last but entities with the same weight are alphabetically ordered in + // the same way file system reads often are. + uasort($graph, array($this, 'sortGraph')); + return array_keys($graph); + } + + /** + * Sorts the dependency graph by reverse weight and alphabetically. + * + * @param array $a + * First item for comparison. The compared items should be associative + * arrays that include a 'weight' and a 'component' key. + * @param array $b + * Second item for comparison. + * + * @return int + * The comparison result for uasort(). + */ + public function sortGraph(array $a, array $b) { + $weight_cmp = SortArray::sortByKeyInt($a, $b, 'weight') * -1; + + if ($weight_cmp === 0) { + return SortArray::sortByKeyString($a, $b, 'component'); + } + return $weight_cmp; + } + + /** + * Creates a graph of config entity dependencies. + * + * @param array $entities_to_check + * The entities to supply dependencies for. + * + * @return \Drupal\Core\Config\Entity\ConfigEntityDependency[] + * An array of config entity dependency objects that are dependent on the + * supplied entities to check. + */ + protected function graphConfigEntityDependencies($entities_to_check) { + $dependent_entities = array(); + $graph = $this->getGraph(); + + foreach ($entities_to_check as $entity) { + if (isset($graph[$entity]) && !empty($graph[$entity]['reverse_paths'])){ + foreach ($graph[$entity]['reverse_paths'] as $dependency => $value) { + $dependent_entities[$dependency] = $this->data[$dependency]; + } + } + } + return $dependent_entities; + } + + /** + * Gets the dependency graph of all the config entities. + * + * @return array + * The dependency graph of all the config entities. + */ + protected function getGraph() { + if (!isset($this->graph)) { + $graph = array(); + foreach ($this->data as $entity) { + $graph_key = $entity->getConfigDependencyName(); + $graph[$graph_key]['edges'] = array(); + $dependencies = $entity->getDependencies('entity'); + if (!empty($dependencies)) { + foreach ($dependencies as $dependency) { + $graph[$graph_key]['edges'][$dependency] = TRUE; + } + } + } + $graph_object = new Graph($graph); + $this->graph = $graph_object->searchAndSort(); + } + return $this->graph; + } + + /** + * Sets data to calculate dependencies for. + * + * The data is converted into lightweight ConfigEntityDependency objects. + * + * @param array $data + * Configuration data keyed by configuration object name. Typically the + * output of \Drupal\Core\Config\StorageInterface::loadMultiple(). + * + * @return self + */ + public function setData(array $data) { + array_walk($data, function (&$config, $name) { + $config = new ConfigEntityDependency($name, $config); + }); + $this->data = $data; + return $this; + } + +} diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php index 18e0260..e092d88 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php @@ -64,6 +64,20 @@ private $isSyncing = FALSE; /** + * Whether the config is deleted through the uninstall process. + * + * @var bool + */ + private $isUninstalling = FALSE; + + /** + * The configuration entity's dependencies. + * + * @var array + */ + protected $dependencies = array(); + + /** * Overrides Entity::__construct(). */ public function __construct(array $values, $entity_type) { @@ -173,6 +187,20 @@ public function isSyncing() { /** * {@inheritdoc} */ + public function setUninstalling($uninstalling) { + $this->isUninstalling = $uninstalling; + } + + /** + * {@inheritdoc} + */ + public function isUninstalling() { + return $this->isUninstalling; + } + + /** + * {@inheritdoc} + */ public function createDuplicate() { $duplicate = parent::createDuplicate(); @@ -207,6 +235,8 @@ public function getExportProperties() { $name = $property->getName(); $properties[$name] = $this->get($name); } + // Add protected dependencies property. + $properties['dependencies'] = $this->dependencies; return $properties; } @@ -221,7 +251,8 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { if ($this instanceof EntityWithPluginBagInterface) { // Any changes to the plugin configuration must be saved to the entity's // copy as well. - $this->set($this->pluginConfigKey, $this->getPluginBag()->getConfiguration()); + $plugin_bag = $this->getPluginBag(); + $this->set($this->pluginConfigKey, $plugin_bag->getConfiguration()); } // Ensure this entity's UUID does not exist with a different ID, regardless @@ -241,6 +272,29 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { throw new ConfigDuplicateUUIDException(format_string('Attempt to save a configuration entity %id with UUID %uuid when this entity already exists with UUID %original_uuid', array('%id' => $this->id(), '%uuid' => $this->uuid(), '%original_uuid' => $original->uuid()))); } } + // Ensure the correct dependencies are present. + $this->calculateDependencies(); + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + // Dependencies should be recalculated on every save. This ensures stale + // dependencies are never saved. + $this->dependencies = array(); + // @todo When \Drupal\Core\Config\Entity\EntityWithPluginBagInterface moves + // to a trait, switch to class_uses() instead. + if ($this instanceof EntityWithPluginBagInterface) { + // Configuration entities need to depend on the providers of any plugins + // that they store the configuration for. + $plugin_bag = $this->getPluginBag(); + foreach($plugin_bag as $instance) { + $definition = $instance->getPluginDefinition(); + $this->addDependency('module', $definition['provider']); + } + } + return $this->dependencies; } /** @@ -264,4 +318,46 @@ public function url($rel = 'edit-form', $options = array()) { return parent::url($rel, $options); } + /** + * Creates a dependency. + * + * @param string $type + * The type of dependency being checked. Either 'module', 'theme', 'entity'. + * @param string $name + * If $type equals 'module' or 'theme' then it should be the name of the + * module or theme. In the case of entity it should be the full + * configuration object name. + * + * @see \Drupal\Core\Config\Entity\ConfigEntityInterface::getConfigDependencyName() + * + * @return $this + */ + protected function addDependency($type, $name) { + // A config entity is always dependent on it's provider. There is no need to + // explicitly declare the dependency. + // @see \Drupal\Core\Config\Entity\ConfigEntityDependency::hasDependency() + if ($type == 'module' && $this->getEntityType()->getProvider() == $name) { + return $this; + } + if (empty($this->dependencies[$type])) { + $this->dependencies[$type] = array($name); + if (count($this->dependencies) > 1) { + // Ensure a consistent order of type keys. + ksort($this->dependencies); + } + } + elseif (!in_array($name, $this->dependencies[$type])) { + $this->dependencies[$type][] = $name; + // Ensure a consistent order of dependency names. + sort($this->dependencies[$type], SORT_FLAG_CASE); + } + return $this; + } + + /** + * {@inheritdoc} + */ + public function getConfigDependencyName() { + return $this->getEntityType()->getConfigPrefix() . '.' . $this->id(); + } } diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityDependency.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityDependency.php new file mode 100644 index 0000000..0cc191f --- /dev/null +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityDependency.php @@ -0,0 +1,99 @@ +name = $name; + if (isset($values['dependencies'])) { + $this->dependencies = $values['dependencies']; + } + else { + $this->dependencies = array(); + } + } + + /** + * Gets the configuration entity's dependencies of the supplied type. + * + * @param string $type + * The type of dependency to return. Either 'module', 'theme', 'entity'. + * + * @return array + * The list of dependencies of the supplied type. + */ + public function getDependencies($type) { + $dependencies = array(); + if (isset($this->dependencies[$type])) { + $dependencies = $this->dependencies[$type]; + } + if ($type == 'module') { + $dependencies[] = substr($this->name,0, strpos($this->name, '.')); + } + return $dependencies; + } + + /** + * Determines if the entity is dependent on extensions or entities. + * + * @param string $type + * The type of dependency being checked. Either 'module', 'theme', 'entity'. + * @param string $name + * The specific name to check. If $type equals 'module' or 'theme' then it + * should be a module name or theme name. In the case of entity it should be + * the full configuration object name. + * + * @return bool + */ + public function hasDependency($type, $name) { + // A config entity is always dependent on it's provider. + if ($type == 'module' && strpos($this->name, $name . '.') === 0) { + return TRUE; + } + return isset($this->dependencies[$type]) && array_search($name, $this->dependencies[$type]) !== FALSE; + } + + /** + * Gets the configuration entity's configuration dependency name. + * + * @see Drupal\Core\Config\Entity\ConfigEntityInterface::getConfigDependencyName() + * + * @return string + * The configuration dependency name for the entity. + */ + public function getConfigDependencyName() { + return $this->name; + } + +} diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php index 5aa737d..8425b52 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php @@ -92,6 +92,14 @@ public function status(); public function isSyncing(); /** + * Returns whether the configuration entity is deleted through the import + * process. + * + * @return bool + */ + public function isUninstalling(); + + /** * Returns the value of a property. * * @param string $property_name @@ -122,4 +130,20 @@ public function set($property_name, $value); */ public function getExportProperties(); + /** + * Calculates dependencies and stores them in the dependency property. + * + * @return array + * An array of dependencies grouped by type (module, theme, entity). + */ + public function calculateDependencies(); + + /** + * Gets the configuration dependency name. + * + * @return string + * The configuration dependency name. + */ + public function getConfigDependencyName(); + } diff --git a/core/lib/Drupal/Core/Config/Schema/core.data_types.schema.yml b/core/lib/Drupal/Core/Config/Schema/core.data_types.schema.yml index 5395c16..47d3e09 100644 --- a/core/lib/Drupal/Core/Config/Schema/core.data_types.schema.yml +++ b/core/lib/Drupal/Core/Config/Schema/core.data_types.schema.yml @@ -136,3 +136,24 @@ route: sequence: - type: string label: 'Param' + +# Config dependencies. +config_dependency: + type: mapping + label: 'Configuration dependencies' + mapping: + entity: + type: sequence + label: 'Entity dependencies' + sequence: + - type: string + module: + type: sequence + label: 'Module dependencies' + sequence: + - type: string + theme: + type: sequence + label: 'Theme dependencies' + sequence: + - type: string diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 89cd73e..8761a30 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -725,14 +725,16 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) { // Uninstall the module. module_load_install($module); $this->invoke($module, 'uninstall'); + + // Remove all configuration belonging to the module. + \Drupal::service('config.manager')->uninstall('module', $module); + + // Remove the schema. drupal_uninstall_schema($module); // Remove the module's entry from the config. $module_config->clear("enabled.$module")->save(); - // Remove all configuration belonging to the module. - \Drupal::service('config.manager')->uninstall('module', $module); - // Update the module handler to remove the module. // The current ModuleHandler instance is obsolete with the kernel rebuild // below. @@ -824,4 +826,5 @@ public function getModuleDirectories() { } return $dirs; } + } diff --git a/core/modules/aggregator/config/views.view.aggregator_rss_feed.yml b/core/modules/aggregator/config/views.view.aggregator_rss_feed.yml index c15b24a..d22fdcb 100644 --- a/core/modules/aggregator/config/views.view.aggregator_rss_feed.yml +++ b/core/modules/aggregator/config/views.view.aggregator_rss_feed.yml @@ -144,7 +144,10 @@ display: defaults: arguments: true label: 'Aggregator RSS feed' -module: views +module: aggregator id: aggregator_rss_feed tag: aggregator langcode: en +dependencies: + module: + - aggregator diff --git a/core/modules/block/config/schema/block.schema.yml b/core/modules/block/config/schema/block.schema.yml index 1ed3f49..3db3aaa 100644 --- a/core/modules/block/config/schema/block.schema.yml +++ b/core/modules/block/config/schema/block.schema.yml @@ -90,3 +90,6 @@ block.block.*: langcode: type: string label: 'Default language' + dependencies: + type: config_dependency + label: 'Dependencies' diff --git a/core/modules/block/custom_block/config/entity.view_mode.custom_block.full.yml b/core/modules/block/custom_block/config/entity.view_mode.custom_block.full.yml index ba11a62..2987629 100644 --- a/core/modules/block/custom_block/config/entity.view_mode.custom_block.full.yml +++ b/core/modules/block/custom_block/config/entity.view_mode.custom_block.full.yml @@ -3,3 +3,6 @@ label: Full status: false cache: true targetEntityType: custom_block +dependencies: + module: + - custom_block diff --git a/core/modules/block/lib/Drupal/block/Entity/Block.php b/core/modules/block/lib/Drupal/block/Entity/Block.php index cb49cac..20e928b 100644 --- a/core/modules/block/lib/Drupal/block/Entity/Block.php +++ b/core/modules/block/lib/Drupal/block/Entity/Block.php @@ -11,6 +11,7 @@ use Drupal\block\BlockPluginBag; use Drupal\block\BlockInterface; use Drupal\Core\Config\Entity\EntityWithPluginBagInterface; +use Drupal\Core\Entity\EntityStorageControllerInterface; /** * Defines a Block configuration entity class. @@ -165,4 +166,13 @@ public static function sort($a, $b) { return strcmp($a->label(), $b->label()); } + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + $this->addDependency('theme', $this->theme); + return $this->dependencies; + } + } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php index 36ed74a..aff9d70 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php @@ -95,6 +95,7 @@ protected function createTests() { 'weight' => NULL, 'status' => TRUE, 'langcode' => language_default()->id, + 'dependencies' => array('module' => array('block_test'), 'theme' => array('stark')), 'theme' => 'stark', 'region' => '-1', 'plugin' => 'test_html', diff --git a/core/modules/block/tests/Drupal/block/Tests/BlockConfigEntityUnitTest.php b/core/modules/block/tests/Drupal/block/Tests/BlockConfigEntityUnitTest.php new file mode 100644 index 0000000..ff2c614 --- /dev/null +++ b/core/modules/block/tests/Drupal/block/Tests/BlockConfigEntityUnitTest.php @@ -0,0 +1,121 @@ + '', + 'name' => '\Drupal\block\Entity\Block unit test', + 'group' => 'Entity', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->entityTypeId = $this->randomName(); + + $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $this->entityType->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue('block')); + + $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityManager->expects($this->any()) + ->method('getDefinition') + ->with($this->entityTypeId) + ->will($this->returnValue($this->entityType)); + + $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); + + $container = new ContainerBuilder(); + $container->set('entity.manager', $this->entityManager); + $container->set('uuid', $this->uuid); + \Drupal::setContainer($container); + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependencies() { + $values = array('theme' => 'stark'); + // Mock the entity under test so that we can mock the getPluginBag method. + $entity = $this->getMockBuilder('\Drupal\block\Entity\Block') + ->setConstructorArgs(array($values, $this->entityTypeId)) + ->setMethods(array('getPluginBag')) + ->getMock(); + // Create a configurable plugin that would add a dependency. + $instance_id = $this->randomName(); + $instance = new TestConfigurablePlugin(array(), $instance_id, array('provider' => 'test')); + + // Create a plugin bag to contain the instance. + $plugin_bag = $this->getMockBuilder('\Drupal\Core\Plugin\DefaultPluginBag') + ->disableOriginalConstructor() + ->setMethods(array('get')) + ->getMock(); + $plugin_bag->expects($this->atLeastOnce()) + ->method('get') + ->with($instance_id) + ->will($this->returnValue($instance)); + $plugin_bag->addInstanceId($instance_id); + + // Return the mocked plugin bag. + $entity->expects($this->once()) + ->method('getPluginBag') + ->will($this->returnValue($plugin_bag)); + + $dependencies = $entity->calculateDependencies(); + $this->assertContains('test', $dependencies['module']); + $this->assertContains('stark', $dependencies['theme']); + } + +} diff --git a/core/modules/block/tests/modules/block_test/config/block.block.test_block.yml b/core/modules/block/tests/modules/block_test/config/block.block.test_block.yml index 9a8383c..a9eb247 100644 --- a/core/modules/block/tests/modules/block_test/config/block.block.test_block.yml +++ b/core/modules/block/tests/modules/block_test/config/block.block.test_block.yml @@ -18,3 +18,8 @@ visibility: roles: { } node_type: types: { } +dependencies: + module: + - block_test + theme: + - stark diff --git a/core/modules/book/config/entity.view_mode.node.print.yml b/core/modules/book/config/entity.view_mode.node.print.yml index 7854ec1..512d581 100644 --- a/core/modules/book/config/entity.view_mode.node.print.yml +++ b/core/modules/book/config/entity.view_mode.node.print.yml @@ -3,3 +3,6 @@ label: Print status: false cache: true targetEntityType: node +dependencies: + module: + - node diff --git a/core/modules/breakpoint/breakpoint.module b/core/modules/breakpoint/breakpoint.module index 754d968..be06abd 100644 --- a/core/modules/breakpoint/breakpoint.module +++ b/core/modules/breakpoint/breakpoint.module @@ -39,106 +39,6 @@ function breakpoint_help($path, $arg) { } /** - * Implements hook_themes_disabled(). - * - * @param array $theme_list - * An array of theme names. - * - * @see _breakpoint_delete_breakpoints() - * - * @todo: This should be removed if https://drupal.org/node/1813110 is resolved. - */ -function breakpoint_themes_disabled($theme_list) { - _breakpoint_delete_breakpoints($theme_list, Breakpoint::SOURCE_TYPE_THEME); -} - -/** - * Implements hook_modules_uninstalled(). - * - * @param array $modules - * An array of the modules that were uninstalled. - * - * @see _breakpoint_delete_breakpoints() - * - * @todo: This should be removed if https://drupal.org/node/1813110 is resolved. - */ -function breakpoint_modules_uninstalled($modules) { - _breakpoint_delete_breakpoints($modules, Breakpoint::SOURCE_TYPE_MODULE); -} - -/** - * Remove breakpoints from all disabled themes or uninstalled modules. - * - * The source type has to match the original source type, otherwise the group - * will not be deleted. All groups created by the theme or module will be - * deleted as well. - * - * @param array $list - * A list of modules or themes that are disabled. - * @param string $source_type - * Either Breakpoint::SOURCE_TYPE_THEME or Breakpoint::SOURCE_TYPE_MODULE. - */ -function _breakpoint_delete_breakpoints($list, $source_type) { - $ids = \Drupal::configFactory()->listAll('breakpoint.breakpoint_group.' . $source_type . '.'); - $entity_manager = \Drupal::entityManager(); - $entity_type = $entity_manager->getDefinition('breakpoint_group'); - - // Remove the breakpoint.breakpoint part of the breakpoint identifier. - foreach ($ids as &$id) { - $id = ConfigStorageController::getIDFromConfigName($id, $entity_type->getConfigPrefix()); - } - $breakpoint_groups = entity_load_multiple('breakpoint_group', $ids); - - foreach ($breakpoint_groups as $breakpoint_group) { - if ($breakpoint_group->sourceType == $source_type && in_array($breakpoint_group->source, $list)) { - // Delete the automatically created breakpoint group. - $breakpoint_group->delete(); - - // Get all breakpoints defined by this theme/module. - $breakpoint_ids = \Drupal::service('config.storage')->listAll('breakpoint.breakpoint.' . $source_type . '.' . $breakpoint_group->id() . '.'); - $entity_type = $entity_manager->getDefinition('breakpoint'); - - // Remove the breakpoint.breakpoint part of the breakpoint identifier. - foreach ($breakpoint_ids as &$breakpoint_id) { - $breakpoint_id = ConfigStorageController::getIDFromConfigName($breakpoint_id, $entity_type->getConfigPrefix()); - } - $breakpoints = entity_load_multiple('breakpoint', $breakpoint_ids); - - // Make sure we only delete breakpoints defined by this theme/module. - foreach ($breakpoints as $breakpoint) { - if ($breakpoint->sourceType == $source_type && $breakpoint->source == $breakpoint_group->name) { - $breakpoint->delete(); - } - } - } - } - - // Delete groups defined by a module/theme even if that module/theme didn't - // define any breakpoints. - foreach ($ids as $id) { - // Delete all breakpoint groups defined by the theme or module. - _breakpoint_delete_breakpoint_groups($id, $source_type); - } -} - -/** - * Remove breakpoint groups from all disabled themes or uninstalled modules. - * - * @param array $group_id - * Machine readable name of the breakpoint group. - * @param string $source_type - * Either Breakpoint::SOURCE_TYPE_THEME or Breakpoint::SOURCE_TYPE_MODULE. - */ -function _breakpoint_delete_breakpoint_groups($group_id, $source_type) { - $breakpoint_groups = entity_load_multiple('breakpoint_group'); - foreach ($breakpoint_groups as $breakpoint_group) { - if ($breakpoint_group->sourceType == $source_type && $breakpoint_group->source == $group_id) { - $breakpoint_group->delete(); - } - } -} - -/** * Load one breakpoint by its identifier. * * @param int $id diff --git a/core/modules/breakpoint/config/schema/breakpoint.schema.yml b/core/modules/breakpoint/config/schema/breakpoint.schema.yml index ce21948..096fe99 100644 --- a/core/modules/breakpoint/config/schema/breakpoint.schema.yml +++ b/core/modules/breakpoint/config/schema/breakpoint.schema.yml @@ -39,6 +39,9 @@ breakpoint.breakpoint.*.*.*: status: type: boolean label: 'Enabled' + dependencies: + type: config_dependency + label: 'Dependencies' breakpoint.breakpoint_group.*.*.*: type: mapping @@ -74,3 +77,6 @@ breakpoint.breakpoint_group.*.*.*: status: type: boolean label: 'Enabled' + dependencies: + type: config_dependency + label: 'Dependencies' diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroupInterface.php b/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroupInterface.php index 3bb877b..ddf7b41 100644 --- a/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroupInterface.php +++ b/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroupInterface.php @@ -51,7 +51,7 @@ public function addBreakpoints($breakpoints); /** * Gets the array of breakpoints for the breakpoint group. * - * @return array + * @return \Drupal\breakpoint\Entity\Breakpoint[] * The array of breakpoints for the breakpoint group. */ public function getBreakpoints(); diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/Breakpoint.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/Breakpoint.php index 02c1945..eab45d9 100644 --- a/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/Breakpoint.php +++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/Breakpoint.php @@ -14,6 +14,7 @@ use Drupal\breakpoint\InvalidBreakpointSourceException; use Drupal\breakpoint\InvalidBreakpointSourceTypeException; use Drupal\breakpoint\InvalidBreakpointMediaQueryException; +use Drupal\Core\Entity\EntityStorageControllerInterface; /** * Defines the Breakpoint entity. @@ -269,4 +270,20 @@ public static function isValidMediaQuery($media_query) { } throw new InvalidBreakpointMediaQueryException('Media query is empty.'); } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + $this->dependencies = array(); + if ($this->sourceType == static::SOURCE_TYPE_MODULE) { + $this->addDependency('module', $this->source); + } + elseif ($this->sourceType == static::SOURCE_TYPE_THEME) { + $this->addDependency('theme', $this->source); + } + return $this->dependencies; + } + } diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php index 80f10e0..7550e09 100644 --- a/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php +++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php @@ -11,6 +11,7 @@ use Drupal\breakpoint\BreakpointGroupInterface; use Drupal\breakpoint\InvalidBreakpointSourceException; use Drupal\breakpoint\InvalidBreakpointSourceTypeException; +use Drupal\Core\Entity\EntityStorageControllerInterface; /** * Defines the BreakpointGroup entity. @@ -91,7 +92,7 @@ class BreakpointGroup extends ConfigEntityBase implements BreakpointGroupInterfa /** * Overrides Drupal\config\ConfigEntityBase::__construct(). */ - public function __construct(array $values, $entity_type) { + public function __construct(array $values, $entity_type = 'breakpoint_group') { parent::__construct($values, $entity_type); } @@ -207,6 +208,7 @@ public function getExportProperties() { 'sourceType', 'status', 'langcode', + 'dependencies', ); $properties = array(); foreach ($names as $name) { @@ -214,4 +216,25 @@ public function getExportProperties() { } return $properties; } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + + $this->dependencies = array(); + if ($this->sourceType == Breakpoint::SOURCE_TYPE_MODULE) { + $this->addDependency('module', $this->source); + } + elseif ($this->sourceType == Breakpoint::SOURCE_TYPE_THEME) { + $this->addDependency('theme', $this->source); + } + $breakpoints = $this->getBreakpoints(); + foreach ($breakpoints as $breakpoint) { + $this->addDependency('entity', $breakpoint->getConfigDependencyName()); + } + return $this->dependencies; + } + } diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointThemeTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointThemeTest.php index 153df41..5715a8e 100644 --- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointThemeTest.php +++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointThemeTest.php @@ -51,10 +51,6 @@ public function testThemeBreakpoints() { // Verify we can load this breakpoint defined by the theme. $this->verifyBreakpointGroup($breakpoint_group_obj); - - // Disable the test theme and verify the breakpoint group is deleted. - theme_disable(array('breakpoint_test_theme')); - $this->assertFalse(entity_load('breakpoint_group', $breakpoint_group_obj->id()), 'breakpoint_group_load: Loading a deleted breakpoint group returns false.', 'Breakpoint API'); } /** @@ -79,10 +75,6 @@ public function testThemeBreakpointGroup() { // Verify we can load this breakpoint defined by the theme. $this->verifyBreakpointGroup($breakpoint_group_obj); - - // Disable the test theme and verify the breakpoint group is deleted. - theme_disable(array('breakpoint_test_theme')); - $this->assertFalse(entity_load('breakpoint_group', $breakpoint_group_obj->id()), 'breakpoint_group_load: Loading a deleted breakpoint group returns false.', 'Breakpoint API'); } } diff --git a/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointConfigEntityUnitTest.php b/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointConfigEntityUnitTest.php new file mode 100644 index 0000000..e07a39c --- /dev/null +++ b/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointConfigEntityUnitTest.php @@ -0,0 +1,123 @@ + '', + 'name' => '\Drupal\breakpoint\Entity\Breakpoint unit test', + 'group' => 'Entity', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->entityTypeId = $this->randomName(); + + $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $this->entityType->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue('breakpoint')); + + $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityManager->expects($this->any()) + ->method('getDefinition') + ->with($this->entityTypeId) + ->will($this->returnValue($this->entityType)); + + $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); + + $container = new ContainerBuilder(); + $container->set('entity.manager', $this->entityManager); + $container->set('uuid', $this->uuid); + \Drupal::setContainer($container); + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependenciesModule() { + $values = array( + 'source' => 'test_module', + 'sourceType' => Breakpoint::SOURCE_TYPE_MODULE, + ); + $entity = new Breakpoint($values, $this->entityTypeId); + + $dependencies = $entity->calculateDependencies(); + $this->assertArrayNotHasKey('theme', $dependencies); + $this->assertContains('test_module', $dependencies['module']); + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependenciesTheme() { + $values = array( + 'source' => 'test_theme', + 'sourceType' => Breakpoint::SOURCE_TYPE_THEME, + ); + $entity = new Breakpoint($values, $this->entityTypeId); + + $dependencies = $entity->calculateDependencies(); + $this->assertArrayNotHasKey('module', $dependencies); + $this->assertContains('test_theme', $dependencies['theme']); + } + +} diff --git a/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointGroupConfigEntityUnitTest.php b/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointGroupConfigEntityUnitTest.php new file mode 100644 index 0000000..154e8e8 --- /dev/null +++ b/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointGroupConfigEntityUnitTest.php @@ -0,0 +1,158 @@ + '', + 'name' => '\Drupal\breakpoint\Entity\BreakpointGroup unit test', + 'group' => 'Entity', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->entityTypeId = $this->randomName(); + + $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $this->entityType->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue('breakpoint')); + + $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityManager->expects($this->any()) + ->method('getDefinition') + ->with($this->entityTypeId) + ->will($this->returnValue($this->entityType)); + + $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); + + $container = new ContainerBuilder(); + $container->set('entity.manager', $this->entityManager); + $container->set('uuid', $this->uuid); + \Drupal::setContainer($container); + } + + /** + * Sets up the entity to test. + */ + public function setUpEntity($values) { + // Mocking the entity under test because the class contains calls to + // procedural code. + $this->entity = $this->getMockBuilder('\Drupal\breakpoint\Entity\BreakpointGroup') + ->setConstructorArgs(array($values, $this->entityTypeId)) + ->setMethods(array('getBreakpoints')) + ->getMock(); + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependenciesModule() { + $this->setUpEntity( + array( + 'source' => 'test_module', + 'sourceType' => Breakpoint::SOURCE_TYPE_MODULE, + ) + ); + $breakpoint = $this->getMockBuilder('\Drupal\breakpoint\Entity\Breakpoint') + ->disableOriginalConstructor()->getMock(); + $breakpoint->expects($this->once()) + ->method('getConfigDependencyName') + ->will($this->returnValue('breakpoint.breakpoint.test')); + + $this->entity->expects($this->once()) + ->method('getBreakpoints') + ->will($this->returnValue(array($breakpoint))); + + $dependencies = $this->entity->calculateDependencies(); + $this->assertArrayNotHasKey('theme', $dependencies); + $this->assertContains('test_module', $dependencies['module']); + $this->assertContains('breakpoint.breakpoint.test', $dependencies['entity']); + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependenciesTheme() { + $this->setUpEntity( + array( + 'source' => 'test_theme', + 'sourceType' => Breakpoint::SOURCE_TYPE_THEME, + ) + ); + + $breakpoint = $this->getMockBuilder('\Drupal\breakpoint\Entity\Breakpoint') + ->disableOriginalConstructor()->getMock(); + $breakpoint->expects($this->once()) + ->method('getConfigDependencyName') + ->will($this->returnValue('breakpoint.breakpoint.test')); + + $this->entity->expects($this->once()) + ->method('getBreakpoints') + ->will($this->returnValue(array($breakpoint))); + + $dependencies = $this->entity->calculateDependencies(); + $this->assertArrayNotHasKey('module', $dependencies); + $this->assertContains('test_theme', $dependencies['theme']); + $this->assertContains('breakpoint.breakpoint.test', $dependencies['entity']); + } + +} diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.php index 7a6f3cc..55346ff 100644 --- a/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.php +++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.php @@ -124,12 +124,10 @@ function testGetJSSettings() { // Change the allowed HTML tags; the "allowedContent" and "format_tags" // settings for CKEditor should automatically be updated as well. - $format = entity_load('filter_format', 'filtered_html'); + $format = $editor->getFilterFormat(); $format->filters('filter_html')->settings['allowed_html'] .= '
 

'; $format->save(); - // $editor is a Text Editor object that has a statically cached FilterFormat - // which is now outdated. Therefore, reload it. - $editor = entity_load('editor', $editor->id()); + $expected_config['allowedContent']['pre'] = array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE); $expected_config['allowedContent']['h3'] = array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE); $expected_config['format_tags'] = 'p;h3;h4;h5;h6;pre'; @@ -138,9 +136,7 @@ function testGetJSSettings() { // Disable the filter_html filter: allow *all *tags. $format->setFilterConfig('filter_html', array('status' => 0)); $format->save(); - // $editor is a Text Editor object that has a statically cached FilterFormat - // which is now outdated. Therefore, reload it. - $editor = entity_load('editor', $editor->id()); + $expected_config['allowedContent'] = TRUE; $expected_config['format_tags'] = 'p;h1;h2;h3;h4;h5;h6;pre'; $this->assertIdentical($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for customized configuration.'); @@ -174,9 +170,7 @@ function testGetJSSettings() { ), )); $format->save(); - // $editor is a Text Editor object that has a statically cached FilterFormat - // which is now outdated. Therefore, reload it. - $editor = entity_load('editor', $editor->id()); + $expected_config['allowedContent'] = array( 'p' => array( 'attributes' => TRUE, diff --git a/core/modules/comment/config/entity.view_mode.comment.full.yml b/core/modules/comment/config/entity.view_mode.comment.full.yml index fd623d4..c30d77d 100644 --- a/core/modules/comment/config/entity.view_mode.comment.full.yml +++ b/core/modules/comment/config/entity.view_mode.comment.full.yml @@ -3,3 +3,6 @@ label: 'Full comment' status: false cache: true targetEntityType: comment +dependencies: + module: + - comment diff --git a/core/modules/comment/config/system.action.comment_publish_action.yml b/core/modules/comment/config/system.action.comment_publish_action.yml index ae9d93a..e1e12ad 100644 --- a/core/modules/comment/config/system.action.comment_publish_action.yml +++ b/core/modules/comment/config/system.action.comment_publish_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: comment plugin: comment_publish_action +dependencies: + module: + - comment diff --git a/core/modules/comment/config/system.action.comment_save_action.yml b/core/modules/comment/config/system.action.comment_save_action.yml index e98a92a..8c605b0 100644 --- a/core/modules/comment/config/system.action.comment_save_action.yml +++ b/core/modules/comment/config/system.action.comment_save_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: comment plugin: comment_save_action +dependencies: + module: + - comment diff --git a/core/modules/comment/config/system.action.comment_unpublish_action.yml b/core/modules/comment/config/system.action.comment_unpublish_action.yml index 61b8e24..5e8e169 100644 --- a/core/modules/comment/config/system.action.comment_unpublish_action.yml +++ b/core/modules/comment/config/system.action.comment_unpublish_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: comment plugin: comment_unpublish_action +dependencies: + module: + - comment diff --git a/core/modules/comment/config/views.view.comments_recent.yml b/core/modules/comment/config/views.view.comments_recent.yml index dc16d8d..15993f8 100644 --- a/core/modules/comment/config/views.view.comments_recent.yml +++ b/core/modules/comment/config/views.view.comments_recent.yml @@ -234,3 +234,6 @@ module: views id: comments_recent tag: default langcode: en +dependencies: + module: + - comment diff --git a/core/modules/comment/lib/Drupal/comment/Tests/Views/CommentTestBase.php b/core/modules/comment/lib/Drupal/comment/Tests/Views/CommentTestBase.php index 32ffa1f..43c987f 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/Views/CommentTestBase.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/Views/CommentTestBase.php @@ -40,6 +40,7 @@ function setUp() { $this->account2 = $this->drupalCreateUser(); $this->drupalLogin($this->account); + $this->drupalCreateContentType(array('type' => 'page', 'name' => t('Basic page'))); $this->container->get('comment.manager')->addDefaultField('node', 'page'); $this->node_user_posted = $this->drupalCreateNode(); diff --git a/core/modules/comment/lib/Drupal/comment/Tests/Views/WizardTest.php b/core/modules/comment/lib/Drupal/comment/Tests/Views/WizardTest.php index 9a39f2f..00f3ccc 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/Views/WizardTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/Views/WizardTest.php @@ -33,6 +33,11 @@ public static function getInfo() { ); } + public function setUp() { + parent::setUp(); + $this->drupalCreateContentType(array('type' => 'page', 'name' => t('Basic page'))); + } + /** * Tests adding a view of comments. */ diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigDependencyTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigDependencyTest.php new file mode 100644 index 0000000..3c2a21c --- /dev/null +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigDependencyTest.php @@ -0,0 +1,169 @@ + 'Configuration dependency tests', + 'description' => 'Tests for configuration dependencies.', + 'group' => 'Configuration', + ); + } + + /** + * Tests that calculating dependencies for system module. + */ + public function testNonEntity() { + $this->installConfig(array('system')); + $config_manager = \Drupal::service('config.manager'); + $dependents = $config_manager->findConfigEntityDependents('module', array('system')); + $this->assertTrue(isset($dependents['system.site']), 'Simple configuration system.site has a UUID key even though it is not a configuration entity and therefore is found when looking for dependencies of the System module.'); + // Ensure that calling + // \Drupal\Core\Config\ConfigManager::findConfigEntityDependentsAsEntities() + // does not try to load system.site as an entity. + $config_manager->findConfigEntityDependentsAsEntities('module', array('system')); + } + + /** + * Tests creating dependencies on configuration entities. + */ + public function testDependencyMangement() { + $config_manager = \Drupal::service('config.manager'); + $storage = $this->container->get('entity.manager')->getStorageController('config_test'); + // Test dependencies between modules. + $entity1 = $storage->create( + array( + 'id' => 'entity1', + 'test_dependencies' => array( + 'module' => array('node', 'config_test') + ) + ) + ); + $entity1->save(); + + $dependents = $config_manager->findConfigEntityDependents('module', array('node')); + $this->assertTrue(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 has a dependency on the Node module.'); + $dependents = $config_manager->findConfigEntityDependents('module', array('config_test')); + $this->assertTrue(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 has a dependency on the config_test module.'); + $dependents = $config_manager->findConfigEntityDependents('module', array('views')); + $this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the Views module.'); + // Ensure that the provider of the config entity is not actually written to + // the dependencies array. + $raw_config = \Drupal::config('config_test.dynamic.entity1'); + $this->assertTrue(array_search('config_test', $raw_config->get('dependencies.module')) === FALSE, 'Module that the provides the configuration entity is not written to the dependencies array as this is implicit.'); + $this->assertTrue(array_search('node', $raw_config->get('dependencies.module')) !== FALSE, 'Node module is written to the dependencies array as this has to be explicit.'); + + // Create additional entities to test dependencies on config entities. + $entity2 = $storage->create(array('id' => 'entity2', 'test_dependencies' => array('entity' => array($entity1->getConfigDependencyName())))); + $entity2->save(); + $entity3 = $storage->create(array('id' => 'entity3', 'test_dependencies' => array('entity' => array($entity2->getConfigDependencyName())))); + $entity3->save(); + $entity4 = $storage->create(array('id' => 'entity4', 'test_dependencies' => array('entity' => array($entity3->getConfigDependencyName())))); + $entity4->save(); + + // Test getting $entity1's dependencies as configuration dependency objects. + $dependents = $config_manager->findConfigEntityDependents('entity', array($entity1->getConfigDependencyName())); + $this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on itself.'); + $this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity1 has a dependency on config_test.dynamic.entity1.'); + $this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity1 has a dependency on config_test.dynamic.entity1.'); + $this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity1 has a dependency on config_test.dynamic.entity1.'); + + // Test getting $entity2's dependencies as entities. + $dependents = $config_manager->findConfigEntityDependentsAsEntities('entity', array($entity2->getConfigDependencyName())); + $dependent_ids = $this->getDependentIds($dependents); + $this->assertFalse(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity2 does not have a dependency on config_test.dynamic.entity1.'); + $this->assertFalse(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 does not have a dependency on itself.'); + $this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity2 has a dependency on config_test.dynamic.entity2.'); + $this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity2 has a dependency on config_test.dynamic.entity2.'); + + // Test getting node module's dependencies as configuration dependency + // objects. + $dependents = $config_manager->findConfigEntityDependents('module', array('node')); + $this->assertTrue(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 has a dependency on the Node module.'); + $this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on the Node module.'); + $this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the Node module.'); + $this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the Node module.'); + + // Test getting node module's dependencies as configuration dependency + // objects after making $entity3 also dependent on node. + $entity1->test_dependencies = array(); + $entity1->save(); + $entity3->test_dependencies['module'] = array('node'); + $entity3->save(); + $dependents = $config_manager->findConfigEntityDependents('module', array('node')); + $this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the Node module.'); + $this->assertFalse(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 does not have a dependency on the Node module.'); + $this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the Node module.'); + $this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the Node module.'); + + // Create an configuration entity of a different type with the same ID as + // one of the entities already created. + $alt_storage = $this->container->get('entity.manager')->getStorageController('config_query_test'); + $alt_storage->create(array('id' => 'entity1', 'test_dependencies' => array('entity' => array($entity1->getConfigDependencyName()))))->save(); + $alt_storage->create(array('id' => 'entity2', 'test_dependencies' => array('module' => array('views'))))->save(); + + $dependents = $config_manager->findConfigEntityDependentsAsEntities('entity', array($entity1->getConfigDependencyName())); + $dependent_ids = $this->getDependentIds($dependents); + $this->assertFalse(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 does not have a dependency on itself.'); + $this->assertTrue(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 has a dependency on config_test.dynamic.entity1.'); + $this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on config_test.dynamic.entity1.'); + $this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on config_test.dynamic.entity1.'); + $this->assertTrue(in_array('config_query_test:entity1', $dependent_ids), 'config_query_test.dynamic.entity1 has a dependency on config_test.dynamic.entity1.'); + $this->assertFalse(in_array('config_query_test:entity2', $dependent_ids), 'config_query_test.dynamic.entity2 does not have a dependency on config_test.dynamic.entity1.'); + + $dependents = $config_manager->findConfigEntityDependentsAsEntities('module', array('node', 'views')); + $dependent_ids = $this->getDependentIds($dependents); + $this->assertFalse(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 does not have a dependency on Views or Node.'); + $this->assertFalse(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 does not have a dependency on Views or Node.'); + $this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on Views or Node.'); + $this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on Views or Node.'); + $this->assertFalse(in_array('config_query_test:entity1', $dependent_ids), 'config_test.query.entity1 does not have a dependency on Views or Node.'); + $this->assertTrue(in_array('config_query_test:entity2', $dependent_ids), 'config_test.query.entity2 has a dependency on Views or Node.'); + + $dependents = $config_manager->findConfigEntityDependentsAsEntities('module', array('config_test')); + $dependent_ids = $this->getDependentIds($dependents); + $this->assertTrue(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 has a dependency on config_test module.'); + $this->assertTrue(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 has a dependency on config_test module.'); + $this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on config_test module.'); + $this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on config_test module.'); + $this->assertTrue(in_array('config_query_test:entity1', $dependent_ids), 'config_test.query.entity1 has a dependency on config_test module.'); + $this->assertTrue(in_array('config_query_test:entity2', $dependent_ids), 'config_test.query.entity2 has a dependency on config_test module.'); + + } + + /** + * Gets a list of identifiers from an array of configuration entities. + * + * @param \Drupal\Core\Config\Entity\ConfigEntityInterface[] $dependents + * An array of configuration entities. + * + * @return array + * An array with values of entity_type_id:ID + */ + protected function getDependentIds(array $dependents) { + $dependent_ids = array(); + foreach($dependents as $dependent) { + $dependent_ids[] = $dependent->getEntityTypeId() . ':' . $dependent->id(); + } + return $dependent_ids; + } +} diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php index 89413d2..51f9182 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php @@ -55,9 +55,11 @@ function testImport() { 'label' => 'New', 'weight' => 0, 'style' => '', + 'test_dependencies' => array(), 'status' => TRUE, 'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651', 'langcode' => language_default()->id, + 'dependencies' => array(), 'protected_property' => '', ); $staging->write($dynamic_name, $original_dynamic_data); diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php index b597aa7..b01ddab 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php @@ -166,9 +166,11 @@ function testNew() { 'label' => 'New', 'weight' => 0, 'style' => '', + 'test_dependencies' => array(), 'status' => TRUE, 'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651', 'langcode' => language_default()->id, + 'dependencies' => array(), 'protected_property' => '', ); $staging->write($dynamic_name, $original_dynamic_data); diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigOtherModuleTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigOtherModuleTest.php index 90915fc..3342808 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigOtherModuleTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigOtherModuleTest.php @@ -93,4 +93,16 @@ public function testInstallConfigEnityModuleFirst() { $this->assertTrue(entity_load('config_test', 'other_module', TRUE), 'Default configuration provided by config_other_module_config has been installed.'); } + /** + * Tests uninstalling node removes views which are dependent on them. + */ + public function testUninstall() { + $this->moduleHandler->install(array('views')); + $this->assertTrue(entity_load('view', 'frontpage', TRUE) === NULL, 'After installing Views, frontpage view which is dependant on the Node and Views modules does not exist.'); + $this->moduleHandler->install(array('node')); + $this->assertTrue(entity_load('view', 'frontpage', TRUE) !== NULL, 'After installing Node, frontpage view which is dependant on the Node and Views modules exists.'); + $this->moduleHandler->uninstall(array('node')); + $this->assertTrue(entity_load('view', 'frontpage', TRUE) === NULL, 'After uninstalling Node, frontpage view which is dependant on the Node and Views modules does not exist.'); + } + } diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/Entity/ConfigTest.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/Entity/ConfigTest.php index 029a7ac..f3ce085 100644 --- a/core/modules/config/tests/config_test/lib/Drupal/config_test/Entity/ConfigTest.php +++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/Entity/ConfigTest.php @@ -70,6 +70,13 @@ class ConfigTest extends ConfigEntityBase implements ConfigTestInterface { public $style; /** + * Test dependencies. + * + * @var array; + */ + public $test_dependencies = array(); + + /** * A protected property of the configuration entity. * * @var string @@ -98,4 +105,16 @@ public static function sort($a, $b) { return parent::sort($a, $b); } + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + foreach ($this->test_dependencies as $type => $deps) { + foreach ($deps as $dep) { + $this->addDependency($type, $dep); + } + } + } + } diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/TestInstallStorage.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/TestInstallStorage.php index 954036e..cefb13f 100644 --- a/core/modules/config/tests/config_test/lib/Drupal/config_test/TestInstallStorage.php +++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/TestInstallStorage.php @@ -25,6 +25,8 @@ protected function getAllFolders() { if (!isset($this->folders)) { // @todo Refactor getComponentNames() to use the extension list directly. $listing = new ExtensionDiscovery(); + // Test all profiles. + $listing->setProfileDirectories(array()); $this->folders = $this->getComponentNames('profile', array_keys($listing->scan('profile'))); $this->folders += $this->getComponentNames('module', array_keys($listing->scan('module'))); $this->folders += $this->getComponentNames('theme', array_keys($listing->scan('theme'))); diff --git a/core/modules/editor/config/schema/editor.schema.yml b/core/modules/editor/config/schema/editor.schema.yml index 086abf6..136ecaf 100644 --- a/core/modules/editor/config/schema/editor.schema.yml +++ b/core/modules/editor/config/schema/editor.schema.yml @@ -44,3 +44,6 @@ editor.editor.*: langcode: type: string label: 'Default language' + dependencies: + type: config_dependency + label: 'Dependencies' diff --git a/core/modules/editor/lib/Drupal/editor/Entity/Editor.php b/core/modules/editor/lib/Drupal/editor/Entity/Editor.php index c58d2fa..0a36b43 100644 --- a/core/modules/editor/lib/Drupal/editor/Entity/Editor.php +++ b/core/modules/editor/lib/Drupal/editor/Entity/Editor.php @@ -60,6 +60,11 @@ class Editor extends ConfigEntityBase implements EditorInterface { protected $filterFormat; /** + * @var \Drupal\Component\Plugin\PluginManagerInterface + */ + protected $editorPluginManager; + + /** * {@inheritdoc} */ public function id() { @@ -72,8 +77,7 @@ public function id() { public function __construct(array $values, $entity_type) { parent::__construct($values, $entity_type); - $manager = \Drupal::service('plugin.manager.editor'); - $plugin = $manager->createInstance($this->editor); + $plugin = $this->editorPluginManager()->createInstance($this->editor); // Initialize settings, merging module-provided defaults. $default_settings = $plugin->getDefaultSettings(); @@ -85,6 +89,20 @@ public function __construct(array $values, $entity_type) { /** * {@inheritdoc} */ + public function calculateDependencies() { + parent::calculateDependencies(); + // Create a dependency of the linked filter. + $this->addDependency('entity', $this->getFilterFormat()->getConfigDependencyName()); + // @todo use EntityWithPluginBagInterface so configuration between config + // entity and dependency on provider is managed automatically. + $definition = $this->editorPluginManager()->createInstance($this->editor)->getPluginDefinition(); + $this->addDependency('module', $definition['provider']); + return $this->dependencies; + } + + /** + * {@inheritdoc} + */ public function getFilterFormat() { if (!$this->filterFormat) { $this->filterFormat = \Drupal::entityManager()->getStorageController('filter_format')->load($this->format); @@ -92,4 +110,17 @@ public function getFilterFormat() { return $this->filterFormat; } + /** + * Returns the editor plugin manager. + * + * @return \Drupal\Component\Plugin\PluginManagerInterface + */ + protected function editorPluginManager() { + if (!$this->editorPluginManager) { + $this->editorPluginManager = \Drupal::service('plugin.manager.editor'); + } + + return $this->editorPluginManager; + } + } diff --git a/core/modules/editor/tests/Drupal/editor/Tests/EditorConfigEntityUnitTest.php b/core/modules/editor/tests/Drupal/editor/Tests/EditorConfigEntityUnitTest.php new file mode 100644 index 0000000..2760e93 --- /dev/null +++ b/core/modules/editor/tests/Drupal/editor/Tests/EditorConfigEntityUnitTest.php @@ -0,0 +1,171 @@ + '', + 'name' => '\Drupal\editor\Entity\Editor unit test', + 'group' => 'Entity', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->editorId = $this->randomName(); + $this->entityTypeId = $this->randomName(); + + $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $this->entityType->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue('editor')); + + $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityManager->expects($this->any()) + ->method('getDefinition') + ->with($this->entityTypeId) + ->will($this->returnValue($this->entityType)); + + $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); + + $this->editorPluginManager = $this->getMockBuilder('Drupal\editor\Plugin\EditorManager') + ->disableOriginalConstructor() + ->getMock(); + + $this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface'); + $this->moduleHandler->expects($this->once()) + ->method('invokeAll') + ->with('editor_default_settings', array($this->editorId)) + ->will($this->returnValue(array())); + + $this->moduleHandler->expects($this->once()) + ->method('alter') + ->with('editor_default_settings', array(), $this->editorId) + ->will($this->returnValue(array())); + + $container = new ContainerBuilder(); + $container->set('entity.manager', $this->entityManager); + $container->set('uuid', $this->uuid); + $container->set('plugin.manager.editor', $this->editorPluginManager); + $container->set('module_handler', $this->moduleHandler); + \Drupal::setContainer($container); + + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependencies() { + $format_id = 'filter.format.test'; + $values = array('editor' => $this->editorId, 'format' => $format_id); + + $plugin = $this->getMockBuilder('Drupal\editor\Plugin\EditorPluginInterface') + ->disableOriginalConstructor() + ->getMock(); + $plugin->expects($this->once()) + ->method('getPluginDefinition') + ->will($this->returnValue(array('provider' => 'test_module'))); + $plugin->expects($this->once()) + ->method('getDefaultSettings') + ->will($this->returnValue(array())); + + $this->editorPluginManager->expects($this->any()) + ->method('createInstance') + ->with($this->editorId) + ->will($this->returnValue($plugin)); + + $entity = new Editor($values, $this->entityTypeId); + + $filter_format = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityInterface'); + $filter_format->expects($this->once()) + ->method('getConfigDependencyName') + ->will($this->returnValue('filter.format.test')); + + $storage = $this->getMock('Drupal\Core\Entity\EntityStorageControllerInterface'); + $storage->expects($this->once()) + ->method('load') + ->with($format_id) + ->will($this->returnValue($filter_format)); + + $this->entityManager->expects($this->once()) + ->method('getStorageController') + ->with('filter_format') + ->will($this->returnValue($storage)); + + $dependencies = $entity->calculateDependencies(); + $this->assertContains('test_module', $dependencies['module']); + $this->assertContains('filter.format.test', $dependencies['entity']); + } + +} diff --git a/core/modules/entity/config/schema/entity.schema.yml b/core/modules/entity/config/schema/entity.schema.yml index 6334cb4..24a684e 100644 --- a/core/modules/entity/config/schema/entity.schema.yml +++ b/core/modules/entity/config/schema/entity.schema.yml @@ -25,6 +25,9 @@ entity.view_mode.*.*: langcode: type: string label: 'Default language' + dependencies: + type: config_dependency + label: 'Dependencies' entity.form_mode.*.*: type: mapping @@ -51,6 +54,9 @@ entity.form_mode.*.*: langcode: type: string label: 'Default language' + dependencies: + type: config_dependency + label: 'Dependencies' # Overview configuration information for view mode or form mode displays. entity.view_display.*.*.*: @@ -86,6 +92,9 @@ entity.view_display.*.*.*: sequence: - type: boolean label: 'Value' + dependencies: + type: config_dependency + label: 'Dependencies' # Overview configuration information for form mode displays. entity.form_display.*.*.*: @@ -115,6 +124,9 @@ entity.form_display.*.*.*: status: type: boolean label: 'Enabled' + dependencies: + type: config_dependency + label: 'Dependencies' # Default schema for entity display field with undefined type. entity_view_display.field.*: diff --git a/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php index 150b73b..2b361d0 100644 --- a/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php +++ b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php @@ -11,6 +11,7 @@ use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Entity\Display\EntityDisplayInterface; +use Drupal\field\Field; /** * Provides a common base class for entity view and form displays. @@ -143,6 +144,40 @@ public function id() { public function preSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) { // Sort elements by weight before saving. uasort($this->content, 'Drupal\Component\Utility\SortArray::sortByWeightElement'); + parent::preSave($storage_controller, $update); + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + $target_entity_type = \Drupal::entityManager()->getDefinition($this->targetEntityType); + + $bundle_entity_type_id = $target_entity_type->getBundleEntityType(); + if ($bundle_entity_type_id != 'bundle') { + // If the target entity type uses bundles the depend on the bundle entity. + $bundle_entity = \Drupal::entityManager()->getStorageController($bundle_entity_type_id)->load($this->bundle); + $this->addDependency('entity', $bundle_entity->getConfigDependencyName()); + } + // Create dependencies on both hidden and visible fields. + $fields = $this->content + $this->hidden; + foreach ($fields as $field_name => $component) { + $field_instance = Field::fieldInfo()->getInstance($this->targetEntityType, $this->bundle, $field_name); + if ($field_instance) { + $this->addDependency('entity', $field_instance->getConfigDependencyName()); + } + if (isset($component['type'])) { + $definition = $this->pluginManager->getDefinition($component['type']); + $this->addDependency('module', $definition['provider']); + } + } + // Depend on configured modes. + if ($this->mode != 'default') { + $mode_entity = \Drupal::entityManager()->getStorageController($this->displayContext . '_mode')->load($target_entity_type->id() . '.' . $this->mode); + $this->addDependency('entity', $mode_entity->getConfigDependencyName()); + } + return $this->dependencies; } /** @@ -168,6 +203,7 @@ public function getExportProperties() { 'content', 'hidden', 'status', + 'dependencies' ); $properties = array(); foreach ($names as $name) { diff --git a/core/modules/entity/lib/Drupal/entity/EntityDisplayModeBase.php b/core/modules/entity/lib/Drupal/entity/EntityDisplayModeBase.php index 6705f03..29ddcfb 100644 --- a/core/modules/entity/lib/Drupal/entity/EntityDisplayModeBase.php +++ b/core/modules/entity/lib/Drupal/entity/EntityDisplayModeBase.php @@ -75,4 +75,14 @@ public function getTargetType() { return $this->targetEntityType; } + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + $target_entity_type = \Drupal::entityManager()->getDefinition($this->targetEntityType); + $this->addDependency('module', $target_entity_type->getProvider()); + return $this->dependencies; + } + } diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php index b78131b..09d023d 100644 --- a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php +++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php @@ -14,7 +14,7 @@ */ class EntityDisplayTest extends DrupalUnitTestBase { - public static $modules = array('entity', 'field', 'entity_test', 'user', 'text'); + public static $modules = array('entity', 'field', 'entity_test', 'user', 'text', 'entity_test'); public static function getInfo() { return array( @@ -76,9 +76,12 @@ public function testEntityDisplayCRUD() { // Check that CreateCopy() creates a new component that can be correclty // saved. + entity_create('view_mode', array('id' => $display->targetEntityType .'.other_view_mode', 'targetEntityType' => $display->targetEntityType))->save(); $new_display = $display->createCopy('other_view_mode'); $new_display->save(); $new_display = entity_load('entity_view_display', $new_display->id()); + $dependencies = $new_display->calculateDependencies(); + $this->assertEqual(array('entity' => array('entity.view_mode.entity_test.other_view_mode')), $dependencies); $this->assertEqual($new_display->targetEntityType, $display->targetEntityType); $this->assertEqual($new_display->bundle, $display->bundle); $this->assertEqual($new_display->mode, 'other_view_mode'); @@ -188,16 +191,10 @@ public function testFieldComponent() { $this->assertEqual($formatter->getPluginId(), 'field_test_multiple'); $this->assertFalse(isset($formatter->randomValue)); - // Check that specifying an unknown formatter (e.g. case of a disabled - // module) gets stored as is in the display, but results in the default - // formatter being used. - $display->setComponent($field_name, array( - 'type' => 'unknown_formatter', - )); - $options = $display->getComponent($field_name); - $this->assertEqual($options['type'], 'unknown_formatter'); - $formatter = $display->getRenderer($field_name); - $this->assertEqual($formatter->getPluginId(), $default_formatter); + // Check that the display has dependencies on the field and the module that + // provides the formatter. + $dependencies = $display->calculateDependencies(); + $this->assertEqual(array('entity' => array('field.instance.entity_test.entity_test.test_field'), 'module' => array('field_test')), $dependencies); } /** @@ -287,6 +284,20 @@ public function testRenameDeleteBundle() { $this->assertEqual('article_rename', $new_form_display->bundle); $this->assertEqual('node.article_rename.default', $new_form_display->id); + $expected_dependencies = array( + 'entity' => array('field.instance.node.article_rename.body', 'node.type.article_rename'), + 'module' => array('Core', 'text') + ); + // Check that the display has dependencies on the bundle, fields and the + // modules that provide the formatters. + $dependencies = $new_display->calculateDependencies(); + $this->assertEqual($expected_dependencies, $dependencies); + + // Check that the form display has dependencies on the bundle, fields and + // the modules that provide the formatters. + $dependencies = $new_form_display->calculateDependencies(); + $this->assertEqual($expected_dependencies, $dependencies); + // Delete the bundle. $type->delete(); $display = entity_load('entity_view_display', 'node.article_rename.default'); @@ -317,6 +328,7 @@ public function testDeleteFieldInstance() { $instance->save(); // Create default and teaser entity display. + entity_create('view_mode', array('id' => 'entity_test.teaser', 'targetEntityType' => 'entity_test'))->save(); entity_create('entity_view_display', array( 'targetEntityType' => 'entity_test', 'bundle' => 'entity_test', diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php index 9dad54d..eb0477f 100644 --- a/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php +++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php @@ -199,6 +199,7 @@ public function testDeleteFieldInstance() { $instance->save(); // Create default and compact entity display. + entity_create('form_mode', array('id' => 'entity_test.compact', 'targetEntityType' => 'entity_test'))->save(); entity_create('entity_form_display', array( 'targetEntityType' => 'entity_test', 'bundle' => 'entity_test', diff --git a/core/modules/field/config/schema/field.schema.yml b/core/modules/field/config/schema/field.schema.yml index e1fe2c0..0db3eb6 100644 --- a/core/modules/field/config/schema/field.schema.yml +++ b/core/modules/field/config/schema/field.schema.yml @@ -56,6 +56,9 @@ field.field.*.*: sequence: - type: string label: 'Column' + dependencies: + type: config_dependency + label: 'Dependencies' field.instance.*.*.*: type: mapping @@ -104,6 +107,9 @@ field.instance.*.*.*: field_type: type: string label: 'Field type' + dependencies: + type: config_dependency + label: 'Dependencies' entity_form_display.field.hidden: type: entity_field_form_display_base diff --git a/core/modules/field/lib/Drupal/field/Entity/FieldConfig.php b/core/modules/field/lib/Drupal/field/Entity/FieldConfig.php index e3aaea4..61f9035 100644 --- a/core/modules/field/lib/Drupal/field/Entity/FieldConfig.php +++ b/core/modules/field/lib/Drupal/field/Entity/FieldConfig.php @@ -252,6 +252,7 @@ public function getExportProperties() { 'cardinality', 'translatable', 'indexes', + 'dependencies', ); $properties = array(); foreach ($names as $name) { @@ -273,11 +274,13 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { unset($this->schema); if ($this->isNew()) { - return $this->preSaveNew($storage_controller); + $this->preSaveNew($storage_controller); } else { - return $this->preSaveUpdated($storage_controller); + $this->preSaveUpdated($storage_controller); } + // Ensure the correct dependencies are present. + $this->calculateDependencies(); } /** @@ -328,6 +331,16 @@ protected function preSaveNew(EntityStorageControllerInterface $storage_controll } /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + // Ensure the field is dependent on the providing module. + $this->addDependency('module', $this->module); + return $this->dependencies; + } + + /** * Prepares saving an updated field definition. * * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller @@ -415,6 +428,7 @@ public static function preDelete(EntityStorageControllerInterface $storage_contr $deleted_fields[$field->uuid] = $config; } } + $state->set('field.field.deleted', $deleted_fields); } diff --git a/core/modules/field/lib/Drupal/field/Entity/FieldInstanceConfig.php b/core/modules/field/lib/Drupal/field/Entity/FieldInstanceConfig.php index 34b36f7..7775c15 100644 --- a/core/modules/field/lib/Drupal/field/Entity/FieldInstanceConfig.php +++ b/core/modules/field/lib/Drupal/field/Entity/FieldInstanceConfig.php @@ -12,6 +12,7 @@ use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\Core\Field\FieldDefinition; use Drupal\Core\Field\TypedData\FieldItemDataDefinition; +use Drupal\field\Field; use Drupal\field\FieldException; use Drupal\field\FieldInstanceConfigInterface; @@ -235,7 +236,7 @@ public function __construct(array $values, $entity_type = 'field_instance_config // 'uuid' entry is present too, so that leftover 'field_uuid' entries // present in config files imported as "default module config" are ignored. if (isset($values['field_uuid']) && isset($values['uuid'])) { - $field = field_info_field_by_id($values['field_uuid']); + $field = Field::fieldInfo()->getFieldById($values['field_uuid']); if (!$field) { throw new FieldException(format_string('Attempt to create an instance of unknown field @uuid', array('@uuid' => $values['field_uuid']))); } @@ -245,7 +246,7 @@ public function __construct(array $values, $entity_type = 'field_instance_config // easier DX on creation of new instances (either through programmatic // creation / or through import of default config files). elseif (isset($values['field_name']) && isset($values['entity_type'])) { - $field = field_info_field($values['entity_type'], $values['field_name']); + $field = Field::fieldInfo()->getField($values['entity_type'], $values['field_name']); if (!$field) { throw new FieldException(format_string('Attempt to create an instance of field @field_name that does not exist on entity type @entity_type.', array('@field_name' => $values['field_name'], '@entity_type' => $values['entity_type']))); } @@ -304,6 +305,7 @@ public function getExportProperties() { 'default_value', 'default_value_function', 'settings', + 'dependencies', ); $properties = array(); foreach ($names as $name) { @@ -353,6 +355,18 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { // Notify the entity storage controller. $entity_manager->getStorageController($this->entity_type)->onInstanceUpdate($this); } + // Ensure the correct dependencies are present. + $this->calculateDependencies(); + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + // Manage dependencies. + $this->addDependency('entity', $this->field->getConfigDependencyName()); + return $this->dependencies; } /** @@ -409,7 +423,7 @@ public static function postDelete(EntityStorageControllerInterface $storage_cont $fields_to_delete = array(); foreach ($instances as $instance) { $field = $instance->getField(); - if (!$instance->deleted && empty($instance->noFieldDelete) && count($field->getBundles()) == 0) { + if (!$instance->deleted && empty($instance->noFieldDelete) && !$instance->isUninstalling() && count($field->getBundles()) == 0) { // Key by field UUID to avoid deleting the same field twice. $fields_to_delete[$instance->field_uuid] = $field; } diff --git a/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php b/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php index 90c8e56..88001f0 100644 --- a/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php @@ -104,6 +104,7 @@ function setUp() { ->setComponent($this->field_name, $this->display_options['default']) ->save(); // Create a display for the teaser view mode. + entity_create('view_mode', array('id' => 'entity_test.teaser', 'targetEntityType' => 'entity_test'))->save(); entity_get_display($instance['entity_type'], $instance['bundle'], 'teaser') ->setComponent($this->field_name, $this->display_options['teaser']) ->save(); diff --git a/core/modules/field/tests/Drupal/field/Tests/FieldConfigEntityUnitTest.php b/core/modules/field/tests/Drupal/field/Tests/FieldConfigEntityUnitTest.php new file mode 100644 index 0000000..59f3359 --- /dev/null +++ b/core/modules/field/tests/Drupal/field/Tests/FieldConfigEntityUnitTest.php @@ -0,0 +1,97 @@ + '', + 'name' => '\Drupal\field\Entity\FieldConfig unit test', + 'group' => 'Entity', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->entityTypeId = $this->randomName(); + + $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $this->entityType->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue('entity')); + + $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityManager->expects($this->any()) + ->method('getDefinition') + ->with($this->entityTypeId) + ->will($this->returnValue($this->entityType)); + + $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); + + $container = new ContainerBuilder(); + $container->set('entity.manager', $this->entityManager); + $container->set('uuid', $this->uuid); + \Drupal::setContainer($container); + + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependencies() { + $values = array('name' => 'test_field', 'type' => 'test_field_type', 'entity_type' => 'test_entity_type', 'module' => 'test_module'); + $entity = new FieldConfig($values, $this->entityTypeId); + $dependencies = $entity->calculateDependencies(); + $this->assertContains('test_module', $dependencies['module']); + } + +} diff --git a/core/modules/field/tests/Drupal/field/Tests/FieldInstanceConfigEntityUnitTest.php b/core/modules/field/tests/Drupal/field/Tests/FieldInstanceConfigEntityUnitTest.php new file mode 100644 index 0000000..9bde1ec --- /dev/null +++ b/core/modules/field/tests/Drupal/field/Tests/FieldInstanceConfigEntityUnitTest.php @@ -0,0 +1,120 @@ + '', + 'name' => '\Drupal\field\Entity\FieldInstanceConfig unit test', + 'group' => 'Entity', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->entityTypeId = $this->randomName(); + + $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $this->entityType->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue('entity')); + + $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityManager->expects($this->any()) + ->method('getDefinition') + ->with($this->entityTypeId) + ->will($this->returnValue($this->entityType)); + + $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); + + $this->fieldInfo = $this->getMockBuilder('\Drupal\field\FieldInfo') + ->disableOriginalConstructor() + ->getMock(); + + $container = new ContainerBuilder(); + $container->set('entity.manager', $this->entityManager); + $container->set('uuid', $this->uuid); + $container->set('field.info', $this->fieldInfo); + \Drupal::setContainer($container); + + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependencies() { + $field = $this->getMock('\Drupal\field\FieldConfigInterface'); + // The field name property is public and accessed this way in the field + // instance config entity constructor. + $field->name = 'test_field'; + $field->expects($this->once()) + ->method('getConfigDependencyName') + ->will($this->returnValue('field.field.test_entity_type.test_field')); + $this->fieldInfo->expects($this->any()) + ->method('getField') + ->with('test_entity_type', 'test_field') + ->will($this->returnValue($field)); + $values = array('field_name' => 'test_field', 'entity_type' => 'test_entity_type', $this->entityTypeId, 'bundle' => 'test_bundle'); + $entity = new FieldInstanceConfig($values, $this->entityTypeId); + $dependencies = $entity->calculateDependencies(); + $this->assertContains('field.field.test_entity_type.test_field', $dependencies['entity']); + } + +} diff --git a/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import.yml b/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import.yml index 20b5662..28036f9 100644 --- a/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import.yml +++ b/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import.yml @@ -12,3 +12,6 @@ translatable: false indexes: format: - format +dependencies: + module: + - text diff --git a/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import_2.yml b/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import_2.yml index a94a715..4c2042b 100644 --- a/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import_2.yml +++ b/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import_2.yml @@ -12,3 +12,6 @@ translatable: false indexes: format: - format +dependencies: + module: + - text diff --git a/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging.yml b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging.yml index 127a97a..f4b6e1a 100644 --- a/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging.yml +++ b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging.yml @@ -13,3 +13,6 @@ translatable: '0' indexes: format: - format +dependencies: + module: + - text diff --git a/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging_2.yml b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging_2.yml index 017e9db..8bb4272 100644 --- a/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging_2.yml +++ b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging_2.yml @@ -13,3 +13,6 @@ translatable: '0' indexes: format: - format +dependencies: + module: + - text diff --git a/core/modules/file/config/views.view.files.yml b/core/modules/file/config/views.view.files.yml index ea17b1e..cc7d114 100644 --- a/core/modules/file/config/views.view.files.yml +++ b/core/modules/file/config/views.view.files.yml @@ -1050,3 +1050,6 @@ module: file id: files tag: default langcode: en +dependencies: + module: + - file diff --git a/core/modules/forum/config/entity.form_display.taxonomy_term.forums.default.yml b/core/modules/forum/config/entity.form_display.taxonomy_term.forums.default.yml index 91ea8ec..ff9133f 100644 --- a/core/modules/forum/config/entity.form_display.taxonomy_term.forums.default.yml +++ b/core/modules/forum/config/entity.form_display.taxonomy_term.forums.default.yml @@ -8,3 +8,6 @@ content: weight: -5 description: weight: 0 +dependencies: + entity: + - taxonomy.vocabulary.forums diff --git a/core/modules/forum/config/entity.view_display.taxonomy_term.forums.default.yml b/core/modules/forum/config/entity.view_display.taxonomy_term.forums.default.yml index 4e3733a..cefa753 100644 --- a/core/modules/forum/config/entity.view_display.taxonomy_term.forums.default.yml +++ b/core/modules/forum/config/entity.view_display.taxonomy_term.forums.default.yml @@ -8,3 +8,6 @@ content: weight: 0 hidden: { } status: true +dependencies: + entity: + - taxonomy.vocabulary.forums diff --git a/core/modules/forum/config/field.field.forum.forum_container.yml b/core/modules/forum/config/field.field.forum.forum_container.yml index 4c20672..af5ebf2 100644 --- a/core/modules/forum/config/field.field.forum.forum_container.yml +++ b/core/modules/forum/config/field.field.forum.forum_container.yml @@ -14,3 +14,6 @@ locked: true cardinality: 1 translatable: false indexes: { } +dependencies: + module: + - options diff --git a/core/modules/forum/config/field.instance.taxonomy_term.forums.forum_container.yml b/core/modules/forum/config/field.instance.taxonomy_term.forums.forum_container.yml index 66feae9..e91a93b 100644 --- a/core/modules/forum/config/field.instance.taxonomy_term.forums.forum_container.yml +++ b/core/modules/forum/config/field.instance.taxonomy_term.forums.forum_container.yml @@ -13,3 +13,6 @@ default_value: default_value_function: '' settings: { } field_type: list_boolean +dependencies: + entity: + - field.field.forum.forum_container diff --git a/core/modules/forum/config/rdf.mapping.node.forum.yml b/core/modules/forum/config/rdf.mapping.node.forum.yml index fc47f15..7db68c7 100644 --- a/core/modules/forum/config/rdf.mapping.node.forum.yml +++ b/core/modules/forum/config/rdf.mapping.node.forum.yml @@ -31,3 +31,8 @@ fieldMappings: callable: 'Drupal\rdf\SchemaOrgDataConverter::interactionCount' arguments: interaction_type: 'UserComments' +dependencies: + entity: + - node.type.forum + module: + - node diff --git a/core/modules/forum/config/rdf.mapping.taxonomy_term.forums.yml b/core/modules/forum/config/rdf.mapping.taxonomy_term.forums.yml index aaaf536..be766eb 100644 --- a/core/modules/forum/config/rdf.mapping.taxonomy_term.forums.yml +++ b/core/modules/forum/config/rdf.mapping.taxonomy_term.forums.yml @@ -11,3 +11,8 @@ fieldMappings: description: properties: - 'schema:description' +dependencies: + entity: + - taxonomy.vocabulary.forums + module: + - taxonomy diff --git a/core/modules/language/lib/Drupal/language/Entity/Language.php b/core/modules/language/lib/Drupal/language/Entity/Language.php index c7ad236..e168aa5 100644 --- a/core/modules/language/lib/Drupal/language/Entity/Language.php +++ b/core/modules/language/lib/Drupal/language/Entity/Language.php @@ -96,7 +96,7 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) { $default_language = \Drupal::service('language.default')->get(); foreach ($entities as $entity) { - if ($entity->id() == $default_language->id) { + if ($entity->id() == $default_language->id && !$entity->isUninstalling()) { throw new DeleteDefaultLanguageException('Can not delete the default language'); } } diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php index f7c64bf..c4c5be2 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php @@ -131,7 +131,7 @@ function testConfigTranslation() { // Enable the image module. $this->drupalPostForm('admin/modules', array('modules[Field types][image][enable]' => "1"), t('Save configuration')); - $this->resetAll(); + $this->rebuildContainer(); $string = $this->storage->findString(array('source' => 'Medium (220x220)', 'context' => '', 'type' => 'configuration')); $this->assertTrue($string, 'Configuration strings have been created upon installation.'); diff --git a/core/modules/node/config/entity.view_mode.node.full.yml b/core/modules/node/config/entity.view_mode.node.full.yml index fce4bf4..3a8654a 100644 --- a/core/modules/node/config/entity.view_mode.node.full.yml +++ b/core/modules/node/config/entity.view_mode.node.full.yml @@ -3,3 +3,6 @@ label: 'Full content' status: false cache: true targetEntityType: node +dependencies: + module: + - node diff --git a/core/modules/node/config/entity.view_mode.node.rss.yml b/core/modules/node/config/entity.view_mode.node.rss.yml index 3e7414c..6a79ca5 100644 --- a/core/modules/node/config/entity.view_mode.node.rss.yml +++ b/core/modules/node/config/entity.view_mode.node.rss.yml @@ -3,3 +3,6 @@ label: RSS status: false cache: true targetEntityType: node +dependencies: + module: + - node diff --git a/core/modules/search/config/entity.view_mode.node.search_index.yml b/core/modules/node/config/entity.view_mode.node.search_index.yml similarity index 72% rename from core/modules/search/config/entity.view_mode.node.search_index.yml rename to core/modules/node/config/entity.view_mode.node.search_index.yml index af5fd59..54385d9 100644 --- a/core/modules/search/config/entity.view_mode.node.search_index.yml +++ b/core/modules/node/config/entity.view_mode.node.search_index.yml @@ -3,3 +3,6 @@ label: 'Search index' status: false cache: true targetEntityType: node +dependencies: + module: + - node diff --git a/core/modules/search/config/entity.view_mode.node.search_result.yml b/core/modules/node/config/entity.view_mode.node.search_result.yml similarity index 73% rename from core/modules/search/config/entity.view_mode.node.search_result.yml rename to core/modules/node/config/entity.view_mode.node.search_result.yml index 178a11c..9d5c913 100644 --- a/core/modules/search/config/entity.view_mode.node.search_result.yml +++ b/core/modules/node/config/entity.view_mode.node.search_result.yml @@ -3,3 +3,6 @@ label: 'Search result' status: false cache: true targetEntityType: node +dependencies: + module: + - node diff --git a/core/modules/node/config/entity.view_mode.node.teaser.yml b/core/modules/node/config/entity.view_mode.node.teaser.yml index 875d61f..be00b8d 100644 --- a/core/modules/node/config/entity.view_mode.node.teaser.yml +++ b/core/modules/node/config/entity.view_mode.node.teaser.yml @@ -3,3 +3,6 @@ label: Teaser status: true cache: true targetEntityType: node +dependencies: + module: + - node diff --git a/core/modules/node/config/search.page.node_search.yml b/core/modules/node/config/search.page.node_search.yml index e4ea66e..76456e5 100644 --- a/core/modules/node/config/search.page.node_search.yml +++ b/core/modules/node/config/search.page.node_search.yml @@ -7,3 +7,6 @@ weight: -10 plugin: node_search configuration: rankings: { } +dependencies: + module: + - node \ No newline at end of file diff --git a/core/modules/node/config/system.action.node_delete_action.yml b/core/modules/node/config/system.action.node_delete_action.yml index deceb87..af4ce28 100644 --- a/core/modules/node/config/system.action.node_delete_action.yml +++ b/core/modules/node/config/system.action.node_delete_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: node plugin: node_delete_action +dependencies: + module: + - node diff --git a/core/modules/node/config/system.action.node_make_sticky_action.yml b/core/modules/node/config/system.action.node_make_sticky_action.yml index 104f92c..04276c0 100644 --- a/core/modules/node/config/system.action.node_make_sticky_action.yml +++ b/core/modules/node/config/system.action.node_make_sticky_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: node plugin: node_make_sticky_action +dependencies: + module: + - node diff --git a/core/modules/node/config/system.action.node_make_unsticky_action.yml b/core/modules/node/config/system.action.node_make_unsticky_action.yml index 2207e7d..03a99c5 100644 --- a/core/modules/node/config/system.action.node_make_unsticky_action.yml +++ b/core/modules/node/config/system.action.node_make_unsticky_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: node plugin: node_make_unsticky_action +dependencies: + module: + - node diff --git a/core/modules/node/config/system.action.node_save_action.yml b/core/modules/node/config/system.action.node_save_action.yml index 0864be5..4dc2073 100644 --- a/core/modules/node/config/system.action.node_save_action.yml +++ b/core/modules/node/config/system.action.node_save_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: node plugin: node_save_action +dependencies: + module: + - node diff --git a/core/modules/node/config/system.action.node_unpromote_action.yml b/core/modules/node/config/system.action.node_unpromote_action.yml index 2b9cc1f..21c0ae8 100644 --- a/core/modules/node/config/system.action.node_unpromote_action.yml +++ b/core/modules/node/config/system.action.node_unpromote_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: node plugin: node_unpromote_action +dependencies: + module: + - node diff --git a/core/modules/node/config/system.action.node_unpublish_action.yml b/core/modules/node/config/system.action.node_unpublish_action.yml index 1b70c4f..2e01fae 100644 --- a/core/modules/node/config/system.action.node_unpublish_action.yml +++ b/core/modules/node/config/system.action.node_unpublish_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: node plugin: node_unpublish_action +dependencies: + module: + - node diff --git a/core/modules/views/config/views.view.archive.yml b/core/modules/node/config/views.view.archive.yml similarity index 99% rename from core/modules/views/config/views.view.archive.yml rename to core/modules/node/config/views.view.archive.yml index d64c243..1151b1a 100644 --- a/core/modules/views/config/views.view.archive.yml +++ b/core/modules/node/config/views.view.archive.yml @@ -171,3 +171,6 @@ module: node id: archive tag: default langcode: en +dependencies: + module: + - node diff --git a/core/modules/node/config/views.view.content.yml b/core/modules/node/config/views.view.content.yml index cb0ec4f..074c5b5 100644 --- a/core/modules/node/config/views.view.content.yml +++ b/core/modules/node/config/views.view.content.yml @@ -349,6 +349,7 @@ display: hide_alter_empty: true link_to_node: false comments: false + optional: true plugin_id: history_user_timestamp provider: history filters: @@ -558,3 +559,7 @@ module: node id: content tag: default langcode: en +dependencies: + module: + - node + - user diff --git a/core/modules/node/config/views.view.content_recent.yml b/core/modules/node/config/views.view.content_recent.yml index 45edb2f..09993b7 100644 --- a/core/modules/node/config/views.view.content_recent.yml +++ b/core/modules/node/config/views.view.content_recent.yml @@ -207,6 +207,7 @@ display: hide_alter_empty: true link_to_node: false comments: false + optional: true plugin_id: history_user_timestamp provider: history name: @@ -462,7 +463,11 @@ display: link_url: admin/content block_category: 'Lists (Views)' label: 'Recent content' -module: views +module: node id: content_recent tag: default langcode: en +dependencies: + module: + - node + - user diff --git a/core/modules/node/config/views.view.frontpage.yml b/core/modules/node/config/views.view.frontpage.yml index 9efe139..a6928ab 100644 --- a/core/modules/node/config/views.view.frontpage.yml +++ b/core/modules/node/config/views.view.frontpage.yml @@ -238,3 +238,6 @@ module: node id: frontpage tag: default langcode: en +dependencies: + module: + - node diff --git a/core/modules/views/config/views.view.glossary.yml b/core/modules/node/config/views.view.glossary.yml similarity index 99% rename from core/modules/views/config/views.view.glossary.yml rename to core/modules/node/config/views.view.glossary.yml index 882012e..0edc991 100644 --- a/core/modules/views/config/views.view.glossary.yml +++ b/core/modules/node/config/views.view.glossary.yml @@ -379,3 +379,7 @@ module: node id: glossary tag: default langcode: en +dependencies: + module: + - node + - user diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessPagerTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessPagerTest.php index 74cd9c7..ee9f7cd 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessPagerTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessPagerTest.php @@ -35,6 +35,7 @@ public function setUp() { parent::setUp(); node_access_rebuild(); + $this->drupalCreateContentType(array('type' => 'page', 'name' => t('Basic page'))); $this->container->get('comment.manager')->addDefaultField('node', 'page'); $this->web_user = $this->drupalCreateUser(array('access content', 'access comments', 'node test view')); } diff --git a/core/modules/node/node.module b/core/modules/node/node.module index c97851e..7603db3 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -446,12 +446,18 @@ function node_add_body_field(NodeTypeInterface $type, $label = 'Body') { 'type' => 'text_default', )) ->save(); - entity_get_display('node', $type->type, 'teaser') - ->setComponent('body', array( - 'label' => 'hidden', - 'type' => 'text_summary_or_trimmed', - )) - ->save(); + + // The teaser view mode is created by the Standard profile and therefore + // might not exist. + $view_modes = entity_get_view_modes('node'); + if (isset($view_modes['teaser'])) { + entity_get_display('node', $type->type, 'teaser') + ->setComponent('body', array( + 'label' => 'hidden', + 'type' => 'text_summary_or_trimmed', + )) + ->save(); + } } return $instance; diff --git a/core/modules/rdf/config/schema/rdf.schema.yml b/core/modules/rdf/config/schema/rdf.schema.yml index 570d602..b743ce3 100644 --- a/core/modules/rdf/config/schema/rdf.schema.yml +++ b/core/modules/rdf/config/schema/rdf.schema.yml @@ -26,3 +26,6 @@ rdf.mapping.*.*: label: 'Field mappings' sequence: - type: rdf_field_mapping + dependencies: + type: config_dependency + label: 'Dependencies' diff --git a/core/modules/rdf/lib/Drupal/rdf/Entity/RdfMapping.php b/core/modules/rdf/lib/Drupal/rdf/Entity/RdfMapping.php index 2256dd4..e1abd09 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Entity/RdfMapping.php +++ b/core/modules/rdf/lib/Drupal/rdf/Entity/RdfMapping.php @@ -149,12 +149,32 @@ public function getExportProperties() { foreach ($names as $name) { $properties[$name] = $this->get($name); } + if (!empty($this->dependencies)) { + // Add protected dependencies property if set. + $properties['dependencies'] = $this->dependencies; + } return $properties; } /** * {@inheritdoc} */ + public function calculateDependencies() { + parent::calculateDependencies(); + $entity_type = \Drupal::entityManager()->getDefinition($this->targetEntityType); + $this->addDependency('module', $entity_type->getProvider()); + $bundle_entity_type_id = $entity_type->getBundleEntityType(); + if ($bundle_entity_type_id != 'bundle') { + // If the target entity type uses bundles the depend on the bundle entity. + $bundle_entity = \Drupal::entityManager()->getStorageController($bundle_entity_type_id)->load($this->bundle); + $this->addDependency('entity', $bundle_entity->getConfigDependencyName()); + } + return $this->dependencies; + } + + /** + * {@inheritdoc} + */ public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) { parent::postSave($storage_controller, $update); diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/Field/EmailFieldRdfaTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/Field/EmailFieldRdfaTest.php index 32e060b..4a3550d 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/Field/EmailFieldRdfaTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/Field/EmailFieldRdfaTest.php @@ -53,7 +53,7 @@ public function setUp() { */ public function testAllFormatters() { // Test the plain formatter. - $this->assertFormatterRdfa('text_plain', 'http://schema.org/email', $this->testValue); + $this->assertFormatterRdfa('string', 'http://schema.org/email', $this->testValue); // Test the mailto formatter. $this->assertFormatterRdfa('email_mailto', 'http://schema.org/email', $this->testValue); } diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaAttributesTest.php index 03500bd..726e78b 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaAttributesTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaAttributesTest.php @@ -135,9 +135,8 @@ function testRel() { * The data to pass into the datatype callback, if specified. */ protected function _testAttributes($expected_attributes, $field_mapping, $data = NULL) { - $this->saveMapping($field_mapping); - $mapping = rdf_get_mapping('node', 'article') + ->setFieldMapping('field_test', $field_mapping) ->getPreparedFieldMapping('field_test'); $attributes = rdf_rdfa_attributes($mapping, $data); ksort($expected_attributes); @@ -145,15 +144,4 @@ protected function _testAttributes($expected_attributes, $field_mapping, $data = $this->assertEqual($expected_attributes, $attributes); } - /** - * Helper function to save mapping using config system. - * - * @param array $field_mapping - * The field mapping. - */ - protected function saveMapping($field_mapping) { - rdf_get_mapping('node', 'article') - ->setFieldMapping('field_test', $field_mapping) - ->save(); - } } diff --git a/core/modules/rdf/tests/Drupal/rdf/Tests/RdfMappingConfigEntityUnitTest.php b/core/modules/rdf/tests/Drupal/rdf/Tests/RdfMappingConfigEntityUnitTest.php new file mode 100644 index 0000000..1572d9c --- /dev/null +++ b/core/modules/rdf/tests/Drupal/rdf/Tests/RdfMappingConfigEntityUnitTest.php @@ -0,0 +1,162 @@ + '', + 'name' => '\Drupal\field\Entity\RdfMapping unit test', + 'group' => 'Entity', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->entityTypeId = $this->randomName(); + + $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $this->entityType->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue('entity')); + + $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + + $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); + + $container = new ContainerBuilder(); + $container->set('entity.manager', $this->entityManager); + $container->set('uuid', $this->uuid); + \Drupal::setContainer($container); + + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependencies() { + $target_entity_type_id = $this->randomName(16); + + $target_entity_type = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $target_entity_type->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue('test_module')); + $values = array('targetEntityType' => $target_entity_type_id); + $target_entity_type->expects($this->any()) + ->method('getBundleEntityType') + ->will($this->returnValue('bundle')); + + $this->entityManager->expects($this->at(0)) + ->method('getDefinition') + ->with($target_entity_type_id) + ->will($this->returnValue($target_entity_type)); + $this->entityManager->expects($this->at(1)) + ->method('getDefinition') + ->with($this->entityTypeId) + ->will($this->returnValue($this->entityType)); + + $entity = new RdfMapping($values, $this->entityTypeId); + $dependencies = $entity->calculateDependencies(); + $this->assertArrayNotHasKey('entity', $dependencies); + $this->assertContains('test_module', $dependencies['module']); + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependenciesWithEntityBundle() { + $target_entity_type_id = $this->randomName(16); + $target_entity_type = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $target_entity_type->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue('test_module')); + $bundle_id = $this->randomName(10); + $values = array('targetEntityType' => $target_entity_type_id , 'bundle' => $bundle_id); + + $bundle_entity_type_id = $this->randomName(17); + $bundle_entity = $this->getMock('\Drupal\Core\Config\Entity\ConfigEntityInterface'); + $bundle_entity + ->expects($this->once()) + ->method('getConfigDependencyName') + ->will($this->returnValue('test_module.type.' . $bundle_id)); + + $target_entity_type->expects($this->any()) + ->method('getBundleEntityType') + ->will($this->returnValue($bundle_entity_type_id)); + + $this->entityManager->expects($this->at(0)) + ->method('getDefinition') + ->with($target_entity_type_id) + ->will($this->returnValue($target_entity_type)); + $this->entityManager->expects($this->at(1)) + ->method('getDefinition') + ->with($this->entityTypeId) + ->will($this->returnValue($this->entityType)); + + $storage = $this->getMock('Drupal\Core\Entity\EntityStorageControllerInterface'); + $storage->expects($this->once()) + ->method('load') + ->with($bundle_id) + ->will($this->returnValue($bundle_entity)); + + $this->entityManager->expects($this->once()) + ->method('getStorageController') + ->with($bundle_entity_type_id) + ->will($this->returnValue($storage)); + + $entity = new RdfMapping($values, $this->entityTypeId); + $dependencies = $entity->calculateDependencies(); + $this->assertContains('test_module.type.' . $bundle_id, $dependencies['entity']); + $this->assertContains('test_module', $dependencies['module']); + } + +} diff --git a/core/modules/responsive_image/config/schema/responsive_image.schema.yml b/core/modules/responsive_image/config/schema/responsive_image.schema.yml index 55a6290..7abda42 100644 --- a/core/modules/responsive_image/config/schema/responsive_image.schema.yml +++ b/core/modules/responsive_image/config/schema/responsive_image.schema.yml @@ -37,6 +37,9 @@ responsive_image.mappings.*: langcode: type: string label: 'Default language' + dependencies: + type: config_dependency + label: 'Dependencies' entity_view_display.field.responsive_image: type: entity_field_view_display_base diff --git a/core/modules/responsive_image/lib/Drupal/responsive_image/Entity/ResponsiveImageMapping.php b/core/modules/responsive_image/lib/Drupal/responsive_image/Entity/ResponsiveImageMapping.php index 19b8bcb..80c327e 100644 --- a/core/modules/responsive_image/lib/Drupal/responsive_image/Entity/ResponsiveImageMapping.php +++ b/core/modules/responsive_image/lib/Drupal/responsive_image/Entity/ResponsiveImageMapping.php @@ -78,6 +78,21 @@ public function __construct(array $values, $entity_type) { } /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + if (isset($this->breakpointGroup)) { + // @todo Implement getExportProperties() so we do not have reload the + // entity since this property is changed in + // \Drupal\responsive_image\Entity\ResponsiveImageMapping::save(). + $breakpoint_group = \Drupal::entityManager()->getStorageController('breakpoint_group')->load($this->breakpointGroup); + $this->addDependency('entity', $breakpoint_group->getConfigDependencyName()); + } + return $this->dependencies; + } + + /** * Overrides Drupal\Core\Entity::save(). */ public function save() { diff --git a/core/modules/responsive_image/tests/Drupal/responsive_image/Tests/PictureMappingEntityTest.php b/core/modules/responsive_image/tests/Drupal/responsive_image/Tests/PictureMappingEntityTest.php new file mode 100644 index 0000000..f14c2fe --- /dev/null +++ b/core/modules/responsive_image/tests/Drupal/responsive_image/Tests/PictureMappingEntityTest.php @@ -0,0 +1,137 @@ + '', + 'name' => '\Drupal\responsive_image\Entity\ResponsiveImageMapping unit test', + 'group' => 'Picture', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->entityTypeId = $this->randomName(); + $this->provider = $this->randomName(); + $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $this->entityType->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue($this->provider)); + + $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityManager->expects($this->any()) + ->method('getDefinition') + ->with($this->entityTypeId) + ->will($this->returnValue($this->entityType)); + + $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); + + $this->breakpointGroupId = $this->randomName(9); + $this->breakpointGroup = $this->getMock('Drupal\breakpoint\Entity\BreakpointGroup', array(), array(array('id' => $this->breakpointGroupId))); + + $this->breakpointGroupStorageController = $this->getMock('\Drupal\Core\Config\Entity\ConfigStorageControllerInterface'); + $this->breakpointGroupStorageController + ->expects($this->any()) + ->method('load') + ->with($this->breakpointGroupId) + ->will($this->returnValue($this->breakpointGroup)); + + $this->entityManager->expects($this->any()) + ->method('getStorageController') + ->will($this->returnValue($this->breakpointGroupStorageController)); + + $container = new ContainerBuilder(); + $container->set('entity.manager', $this->entityManager); + $container->set('uuid', $this->uuid); + \Drupal::setContainer($container); + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependencies() { + $picture_mapping = new ResponsiveImageMapping(array(), $this->entityTypeId); + // Set the breakpoint group after creating the entity to avoid the calls + // in the constructor. + $picture_mapping->breakpointGroup = $this->breakpointGroupId; + $this->breakpointGroup->expects($this->once()) + ->method('getConfigDependencyName') + ->will($this->returnValue('breakpoint.breakpoint_group.' . $this->breakpointGroupId)); + + $dependencies = $picture_mapping->calculateDependencies(); + $this->assertContains('breakpoint.breakpoint_group.' . $this->breakpointGroupId, $dependencies['entity']); + } + +} diff --git a/core/modules/search/config/schema/search.schema.yml b/core/modules/search/config/schema/search.schema.yml index 68e6877..2d9d39e 100644 --- a/core/modules/search/config/schema/search.schema.yml +++ b/core/modules/search/config/schema/search.schema.yml @@ -94,3 +94,6 @@ search.page.*: label: 'Plugin' configuration: type: search.plugin.[%parent.plugin] + dependencies: + type: config_dependency + label: 'Dependencies' diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml index 6b82e60..f8d0143 100644 --- a/core/modules/system/config/schema/system.schema.yml +++ b/core/modules/system/config/schema/system.schema.yml @@ -337,6 +337,9 @@ system.action.*: label: 'Plugin' configuration: type: action.configuration.[plugin] + dependencies: + type: config_dependency + label: 'Dependencies' system.file: type: mapping diff --git a/core/modules/system/lib/Drupal/system/Form/ModulesUninstallConfirmForm.php b/core/modules/system/lib/Drupal/system/Form/ModulesUninstallConfirmForm.php index 738dda2..70fe125 100644 --- a/core/modules/system/lib/Drupal/system/Form/ModulesUninstallConfirmForm.php +++ b/core/modules/system/lib/Drupal/system/Form/ModulesUninstallConfirmForm.php @@ -7,6 +7,8 @@ namespace Drupal\system\Form; +use Drupal\Core\Config\ConfigManagerInterface; +use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Form\ConfirmFormBase; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -33,6 +35,20 @@ class ModulesUninstallConfirmForm extends ConfirmFormBase { protected $keyValueExpirable; /** + * The configuration manager. + * + * @var \Drupal\Core\Config\ConfigManagerInterface + */ + protected $configManager; + + /** + * The entity manager. + * + * @var \Drupal\Core\Entity\EntityManagerInterface + */ + protected $entityManager; + + /** * An array of modules to uninstall. * * @var array @@ -46,10 +62,16 @@ class ModulesUninstallConfirmForm extends ConfirmFormBase { * The module handler. * @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value_expirable * The key value expirable factory. + * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager + * The configuration manager. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. */ - public function __construct(ModuleHandlerInterface $module_handler, KeyValueStoreExpirableInterface $key_value_expirable) { + public function __construct(ModuleHandlerInterface $module_handler, KeyValueStoreExpirableInterface $key_value_expirable, ConfigManagerInterface $config_manager, EntityManagerInterface $entity_manager) { $this->moduleHandler = $module_handler; $this->keyValueExpirable = $key_value_expirable; + $this->configManager = $config_manager; + $this->entityManager = $entity_manager; } /** @@ -58,7 +80,9 @@ public function __construct(ModuleHandlerInterface $module_handler, KeyValueStor public static function create(ContainerInterface $container) { return new static( $container->get('module_handler'), - $container->get('keyvalue.expirable')->get('modules_uninstall') + $container->get('keyvalue.expirable')->get('modules_uninstall'), + $container->get('config.manager'), + $container->get('entity.manager') ); } @@ -121,6 +145,47 @@ public function buildForm(array $form, array &$form_state) { }, $this->modules), ); + $form['entities'] = array( + '#type' => 'details', + '#title' => $this->t('Configuration deletions'), + '#description' => $this->t('The listed configuration will be deleted.'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#access' => FALSE, + ); + + // Get the dependent entities. + $entity_types = array(); + $dependent_entities = $this->configManager->findConfigEntityDependentsAsEntities('module', $this->modules); + foreach ($dependent_entities as $entity) { + $entity_type_id = $entity->getEntityTypeId(); + if (!isset($form['entities'][$entity_type_id])) { + $entity_type = $this->entityManager->getDefinition($entity_type_id); + // Store the ID and label to sort the entity types and entities later. + $label = $entity_type->getLabel(); + $entity_types[$entity_type_id] = $label; + $form['entities'][$entity_type_id] = array( + '#theme' => 'item_list', + '#title' => $label, + '#items' => array(), + ); + } + $form['entities'][$entity_type_id]['#items'][] = $entity->label(); + } + if (!empty($dependent_entities)) { + $form['entities']['#access'] = TRUE; + + // Add a weight key to the entity type sections. + asort($entity_types, SORT_FLAG_CASE); + $weight = 0; + foreach ($entity_types as $entity_type_id => $label) { + $form['entities'][$entity_type_id]['#weight'] = $weight; + // Sort the list of entity labels alphabetically. + sort($form['entities'][$entity_type_id]['#items'], SORT_FLAG_CASE); + $weight++; + } + } + return parent::buildForm($form, $form_state); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Ajax/MultiFormTest.php b/core/modules/system/lib/Drupal/system/Tests/Ajax/MultiFormTest.php index 93b3b28..5eb13e5 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Ajax/MultiFormTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Ajax/MultiFormTest.php @@ -48,7 +48,7 @@ function setUp() { 'bundle' => 'page', ))->save(); entity_get_form_display('node', 'page', 'default') - ->setComponent($field_name, array('type' => 'text_default')) + ->setComponent($field_name, array('type' => 'text_textfield')) ->save(); // Login a user who can create 'page' nodes. diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php index b7dd9ad..458d8de 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php @@ -79,8 +79,9 @@ protected function assertHookMessageOrder($messages) { */ public function testBlockHooks() { $entity = entity_create('block', array( - 'id' => 'stark.test_html', + 'id' => 'stark_test_html', 'plugin' => 'test_html', + 'theme' => 'stark', )); $this->assertHookMessageOrder(array( @@ -134,6 +135,10 @@ public function testBlockHooks() { public function testCommentHooks() { $account = $this->createUser(); $this->enableModules(array('entity', 'filter')); + entity_create('node_type', array( + 'type' => 'article', + 'name' => 'Article', + ))->save(); $this->container->get('comment.manager')->addDefaultField('node', 'article', 'comment', CommentItemInterface::OPEN); $node = entity_create('node', array( diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/RebuildTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/RebuildTest.php index 478db04..1f91e80 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Form/RebuildTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Form/RebuildTest.php @@ -86,7 +86,7 @@ function testPreserveFormActionAfterAJAX() { ); entity_create('field_instance_config', $instance)->save(); entity_get_form_display('node', 'page', 'default') - ->setComponent($field_name, array('type' => 'text_test')) + ->setComponent($field_name, array('type' => 'text_textfield')) ->save(); // Log in a user who can create 'page' nodes. diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php index b644c19..12ee30c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php @@ -193,6 +193,6 @@ function assertLogMessage($type, $message, $variables = array(), $severity = WAT ->countQuery() ->execute() ->fetchField(); - $this->assertTrue($count > 0, format_string('watchdog table contains @count rows for @message', array('@count' => $count, '@message' => $message))); + $this->assertTrue($count > 0, format_string('watchdog table contains @count rows for @message', array('@count' => $count, '@message' => format_string($message, $variables)))); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php index 2f6443e..bbb4399 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Module; +use Drupal\Component\Utility\String; use Drupal\simpletest\WebTestBase; /** @@ -19,7 +20,7 @@ class UninstallTest extends WebTestBase { * * @var array */ - public static $modules = array('module_test', 'user'); + public static $modules = array('module_test', 'user', 'views', 'node'); public static function getInfo() { return array( @@ -42,12 +43,44 @@ function testUserPermsUninstalled() { } /** - * Tests the Uninstall page. + * Tests the Uninstall page and Uninstall confirmation page. */ function testUninstallPage() { $account = $this->drupalCreateUser(array('administer modules')); $this->drupalLogin($account); $this->drupalGet('admin/modules/uninstall'); $this->assertTitle(t('Uninstall') . ' | Drupal'); + + // Uninstall module_test. + $edit = array(); + $edit['uninstall[module_test]'] = TRUE; + $this->drupalPostForm('admin/modules/uninstall', $edit, t('Uninstall')); + $this->assertNoText(\Drupal::translation()->translate('Configuration deletions'), 'No configuration deletions listed on the module install confirmation page.'); + $this->drupalPostForm(NULL, NULL, t('Uninstall')); + $this->assertText(t('The selected modules have been uninstalled.'), 'Modules status has been updated.'); + + // Uninstall node testing that the configuration that will be deleted is + // listed. + $node_dependencies = \Drupal::service('config.manager')->findConfigEntityDependentsAsEntities('module', array('node')); + $edit = array(); + $edit['uninstall[node]'] = TRUE; + $this->drupalPostForm('admin/modules/uninstall', $edit, t('Uninstall')); + $this->assertText(\Drupal::translation()->translate('Configuration deletions'), 'Configuration deletions listed on the module install confirmation page.'); + + $entity_types = array(); + foreach ($node_dependencies as $entity) { + $label = $entity->label(); + $this->assertText($label, String::format('The entity label "!label" found.', array('!label' => $label))); + $entity_types[] = $entity->getEntityTypeId(); + } + $entity_types = array_unique($entity_types); + foreach ($entity_types as $entity_type_id) { + $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id); + // Add h3's since the entity type label is often repeated in the entity + // labels. + $this->assertRaw('

' . $entity_type->getLabel() . '

'); + } + $this->drupalPostForm(NULL, NULL, t('Uninstall')); + $this->assertText(t('The selected modules have been uninstalled.'), 'Modules status has been updated.'); } } diff --git a/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test.full.yml b/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test.full.yml index 412942c..321b372 100644 --- a/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test.full.yml +++ b/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test.full.yml @@ -3,3 +3,6 @@ label: Full status: false cache: true targetEntityType: entity_test +dependencies: + module: + - entity_test diff --git a/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test.test.yml b/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test.test.yml index baeed36..37534a3 100644 --- a/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test.test.yml +++ b/core/modules/system/tests/modules/entity_test/config/entity.view_mode.entity_test.test.yml @@ -3,3 +3,6 @@ label: Test status: false cache: false targetEntityType: entity_test +dependencies: + module: + - entity_test diff --git a/core/modules/system/tests/modules/entity_test/entity_test.install b/core/modules/system/tests/modules/entity_test/entity_test.install index 3df5396..9e48029 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.install +++ b/core/modules/system/tests/modules/entity_test/entity_test.install @@ -32,7 +32,7 @@ function entity_test_install() { ))->save(); entity_get_form_display($entity_type, $entity_type, 'default') - ->setComponent('field_test_text', array('type' => 'text_text')) + ->setComponent('field_test_text', array('type' => 'text_textfield')) ->save(); } } diff --git a/core/modules/taxonomy/config/entity.view_mode.taxonomy_term.full.yml b/core/modules/taxonomy/config/entity.view_mode.taxonomy_term.full.yml index fb7b59c..50b1854 100644 --- a/core/modules/taxonomy/config/entity.view_mode.taxonomy_term.full.yml +++ b/core/modules/taxonomy/config/entity.view_mode.taxonomy_term.full.yml @@ -3,3 +3,6 @@ label: 'Taxonomy term page' status: false cache: true targetEntityType: taxonomy_term +dependencies: + module: + - taxonomy diff --git a/core/modules/views/config/views.view.taxonomy_term.yml b/core/modules/taxonomy/config/views.view.taxonomy_term.yml similarity index 99% rename from core/modules/views/config/views.view.taxonomy_term.yml rename to core/modules/taxonomy/config/views.view.taxonomy_term.yml index beac139..10ce059 100644 --- a/core/modules/views/config/views.view.taxonomy_term.yml +++ b/core/modules/taxonomy/config/views.view.taxonomy_term.yml @@ -246,3 +246,7 @@ module: taxonomy id: taxonomy_term tag: default langcode: en +dependencies: + module: + - node + - taxonomy diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php index d1ff5d2..8e37920 100644 --- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php +++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php @@ -64,7 +64,7 @@ function setUp() { 'text_processing' => FALSE, ); - $this->formatter_type = 'text_plain'; + $this->formatter_type = 'string'; $this->formatter_settings = array(); $this->field = entity_create('field_config', array( diff --git a/core/modules/tour/config/schema/tour.schema.yml b/core/modules/tour/config/schema/tour.schema.yml index 9ac1082..b225fcd 100644 --- a/core/modules/tour/config/schema/tour.schema.yml +++ b/core/modules/tour/config/schema/tour.schema.yml @@ -34,6 +34,9 @@ tour.tour.*: sequence: - type: tour.tip.[plugin] label: 'Tour tip' + dependencies: + type: config_dependency + label: 'Dependencies' tour.tip: type: mapping diff --git a/core/modules/tour/lib/Drupal/tour/Entity/Tour.php b/core/modules/tour/lib/Drupal/tour/Entity/Tour.php index f581488..f3aeebe 100644 --- a/core/modules/tour/lib/Drupal/tour/Entity/Tour.php +++ b/core/modules/tour/lib/Drupal/tour/Entity/Tour.php @@ -8,6 +8,7 @@ namespace Drupal\tour\Entity; use Drupal\Core\Config\Entity\ConfigEntityBase; +use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\tour\TipsBag; use Drupal\tour\TourInterface; @@ -168,4 +169,19 @@ public function resetKeyedRoutes() { unset($this->keyedRoutes); } + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + + foreach($this->tipsBag as $instance) { + $definition = $instance->getPluginDefinition(); + $this->addDependency('module', $definition['provider']); + } + + $this->addDependency('module', $this->module); + return $this->dependencies; + } + } diff --git a/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php b/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php index 00e049c..d5a4be1 100644 --- a/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php +++ b/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php @@ -124,10 +124,11 @@ public function testTourFunctionality() { language_save(new Language(array('id' => 'en'))); // Programmatically create a tour for use through the remainder of the test. - entity_create('tour', array( + $tour = entity_create('tour', array( 'id' => 'tour-entity-create-test-en', 'label' => 'Tour test english', 'langcode' => 'en', + 'module' => 'system', 'routes' => array( array('route_name' => 'tour_test.1'), ), @@ -142,8 +143,24 @@ public function testTourFunctionality() { 'data-id' => 'tour-code-test-1', ), ), + 'tour-code-test-2' => array( + 'id' => 'tour-code-test-2', + 'plugin' => 'image', + 'label' => 'The awesome image', + 'url' => 'http://local/image.png', + 'weight' => 1, + 'attributes' => array( + 'data-id' => 'tour-code-test-2' + ), + ), ), - ))->save(); + )); + $tour->save(); + + // Ensure that a tour entity has the expected dependencies based on plugin + // providers and the module named in the configuration entity. + $dependencies = $tour->calculateDependencies(); + $this->assertEqual($dependencies['module'], array('system', 'tour_test')); $this->drupalGet('tour-test-1'); diff --git a/core/modules/tracker/lib/Drupal/tracker/Tests/TrackerNodeAccessTest.php b/core/modules/tracker/lib/Drupal/tracker/Tests/TrackerNodeAccessTest.php index 207becc..c50eed2 100644 --- a/core/modules/tracker/lib/Drupal/tracker/Tests/TrackerNodeAccessTest.php +++ b/core/modules/tracker/lib/Drupal/tracker/Tests/TrackerNodeAccessTest.php @@ -33,6 +33,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); node_access_rebuild(); + $this->drupalCreateContentType(array('type' => 'page')); $this->container->get('comment.manager')->addDefaultField('node', 'page', 'comment', CommentItemInterface::OPEN); \Drupal::state()->set('node_access_test.private', TRUE); } diff --git a/core/modules/user/config/entity.form_mode.user.register.yml b/core/modules/user/config/entity.form_mode.user.register.yml index c720f19..06919cd 100644 --- a/core/modules/user/config/entity.form_mode.user.register.yml +++ b/core/modules/user/config/entity.form_mode.user.register.yml @@ -2,3 +2,6 @@ id: user.register label: Register status: true targetEntityType: user +dependencies: + module: + - user diff --git a/core/modules/user/config/entity.view_mode.user.compact.yml b/core/modules/user/config/entity.view_mode.user.compact.yml index 4522e10..6e0b13d 100644 --- a/core/modules/user/config/entity.view_mode.user.compact.yml +++ b/core/modules/user/config/entity.view_mode.user.compact.yml @@ -3,3 +3,6 @@ label: Compact status: true cache: true targetEntityType: user +dependencies: + module: + - user diff --git a/core/modules/user/config/entity.view_mode.user.full.yml b/core/modules/user/config/entity.view_mode.user.full.yml index a282b0c..2f0f250 100644 --- a/core/modules/user/config/entity.view_mode.user.full.yml +++ b/core/modules/user/config/entity.view_mode.user.full.yml @@ -3,3 +3,6 @@ label: 'User account' status: false cache: true targetEntityType: user +dependencies: + module: + - user diff --git a/core/modules/user/config/rdf.mapping.user.user.yml b/core/modules/user/config/rdf.mapping.user.user.yml index ad082a9..f052a1a 100644 --- a/core/modules/user/config/rdf.mapping.user.user.yml +++ b/core/modules/user/config/rdf.mapping.user.user.yml @@ -7,3 +7,6 @@ fieldMappings: name: properties: - 'schema:name' +dependencies: + module: + - user diff --git a/core/modules/user/config/search.page.user_search.yml b/core/modules/user/config/search.page.user_search.yml index bb130c2..50b0113 100644 --- a/core/modules/user/config/search.page.user_search.yml +++ b/core/modules/user/config/search.page.user_search.yml @@ -5,3 +5,6 @@ langcode: en path: user plugin: user_search configuration: { } +dependencies: + module: + - user diff --git a/core/modules/user/config/system.action.user_block_user_action.yml b/core/modules/user/config/system.action.user_block_user_action.yml index 57b69e9..d7e15c6 100644 --- a/core/modules/user/config/system.action.user_block_user_action.yml +++ b/core/modules/user/config/system.action.user_block_user_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: user plugin: user_block_user_action +dependencies: + module: + - user diff --git a/core/modules/user/config/system.action.user_cancel_user_action.yml b/core/modules/user/config/system.action.user_cancel_user_action.yml index 875ecee..bcc03ed 100644 --- a/core/modules/user/config/system.action.user_cancel_user_action.yml +++ b/core/modules/user/config/system.action.user_cancel_user_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: user plugin: user_cancel_user_action +dependencies: + module: + - user diff --git a/core/modules/user/config/system.action.user_unblock_user_action.yml b/core/modules/user/config/system.action.user_unblock_user_action.yml index 749e61b..0d313ea 100644 --- a/core/modules/user/config/system.action.user_unblock_user_action.yml +++ b/core/modules/user/config/system.action.user_unblock_user_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: user plugin: user_unblock_user_action +dependencies: + module: + - user diff --git a/core/modules/user/config/views.view.user_admin_people.yml b/core/modules/user/config/views.view.user_admin_people.yml index 849697a..89d1e4e 100644 --- a/core/modules/user/config/views.view.user_admin_people.yml +++ b/core/modules/user/config/views.view.user_admin_people.yml @@ -952,7 +952,10 @@ display: defaults: show_admin_links: false label: People -module: views +module: user id: user_admin_people tag: default langcode: und +dependencies: + module: + - user diff --git a/core/modules/user/config/views.view.who_s_new.yml b/core/modules/user/config/views.view.who_s_new.yml index 4653796..f5bee58 100644 --- a/core/modules/user/config/views.view.who_s_new.yml +++ b/core/modules/user/config/views.view.who_s_new.yml @@ -167,7 +167,10 @@ display: relationships: { } arguments: { } label: 'Who''s new' -module: views +module: user id: who_s_new tag: default langcode: en +dependencies: + module: + - user diff --git a/core/modules/user/config/views.view.who_s_online.yml b/core/modules/user/config/views.view.who_s_online.yml index 040e2f9..efe344f 100644 --- a/core/modules/user/config/views.view.who_s_online.yml +++ b/core/modules/user/config/views.view.who_s_online.yml @@ -198,7 +198,10 @@ display: block_description: 'Who''s online' display_description: 'A list of users that are currently logged in.' label: 'Who''s online block' -module: views +module: user id: who_s_online tag: default langcode: en +dependencies: + module: + - user diff --git a/core/modules/views/config/schema/views.schema.yml b/core/modules/views/config/schema/views.schema.yml index a9a7eaf..933d839 100644 --- a/core/modules/views/config/schema/views.schema.yml +++ b/core/modules/views/config/schema/views.schema.yml @@ -117,3 +117,6 @@ views.view.*: label: 'Position' display_options: type: views.display.[%parent.display_plugin] + dependencies: + type: config_dependency + label: 'Dependencies' diff --git a/core/modules/views/lib/Drupal/views/Entity/View.php b/core/modules/views/lib/Drupal/views/Entity/View.php index 8eea4c1..fc82619 100644 --- a/core/modules/views/lib/Drupal/views/Entity/View.php +++ b/core/modules/views/lib/Drupal/views/Entity/View.php @@ -22,7 +22,7 @@ * id = "view", * label = @Translation("View"), * controllers = { - * "storage" = "Drupal\views\ViewStorageController", + * "storage" = "Drupal\Core\Config\Entity\ConfigStorageController", * "access" = "Drupal\views\ViewAccessController" * }, * admin_permission = "administer views", @@ -258,6 +258,7 @@ public function getExportProperties() { 'tag', 'uuid', 'langcode', + 'dependencies', ); $properties = array(); foreach ($names as $name) { @@ -269,6 +270,39 @@ public function getExportProperties() { /** * {@inheritdoc} */ + public function calculateDependencies() { + parent::calculateDependencies(); + + // Ensure that the view is dependant on the module that implements the view. + $this->addDependency('module', $this->module); + // Ensure that the view is dependant on the module that provides the schema + // for the base table. + $schema = drupal_get_schema($this->base_table); + if ($this->module != $schema['module']) { + $this->addDependency('module', $schema['module']); + } + + $handler_types = array(); + foreach (ViewExecutable::viewsHandlerTypes() as $type) { + $handler_types[] = $type['plural']; + } + foreach ($this->get('display') as $display) { + foreach ($handler_types as $handler_type) { + if (!empty($display['display_options'][$handler_type])) { + foreach ($display['display_options'][$handler_type] as $handler) { + if (isset($handler['provider']) && empty($handler['optional'])) { + $this->addDependency('module', $handler['provider']); + } + } + } + } + } + return $this->dependencies; + } + + /** + * {@inheritdoc} + */ public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) { parent::postSave($storage_controller, $update); diff --git a/core/modules/views/lib/Drupal/views/Tests/Entity/FieldEntityTest.php b/core/modules/views/lib/Drupal/views/Tests/Entity/FieldEntityTest.php index d828383..9d9a03a 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Entity/FieldEntityTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Entity/FieldEntityTest.php @@ -46,6 +46,7 @@ public function testGetEntity() { $account = entity_create('user', array('name' => $this->randomName(), 'bundle' => 'user')); $account->save(); + $this->drupalCreateContentType(array('type' => 'page')); $this->container->get('comment.manager')->addDefaultField('node', 'page'); // Force a flush of the in-memory storage. $this->container->get('views.views_data')->clear(); diff --git a/core/modules/views/lib/Drupal/views/Tests/Entity/FieldEntityTest.php b/core/modules/views/lib/Drupal/views/Tests/Entity/ViewEntityDependenciesTest.php similarity index 54% copy from core/modules/views/lib/Drupal/views/Tests/Entity/FieldEntityTest.php copy to core/modules/views/lib/Drupal/views/Tests/Entity/ViewEntityDependenciesTest.php index d828383..082f352 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Entity/FieldEntityTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Entity/ViewEntityDependenciesTest.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Tests\Entity\FieldEntityTest. + * Contains \Drupal\views\Tests\Entity\ViewEntityDependenciesTest. */ namespace Drupal\views\Tests\Entity; @@ -11,9 +11,9 @@ use Drupal\views\Views; /** - * Tests the field plugin base integration with the entity system. + * Tests \Drupal\views\Entity\View::calculateDependencies(). */ -class FieldEntityTest extends ViewTestBase { +class ViewEntityDependenciesTest extends ViewTestBase { /** * Views used by this test. @@ -31,21 +31,21 @@ class FieldEntityTest extends ViewTestBase { public static function getInfo() { return array( - 'name' => 'Field: Entity Api Integration', - 'description' => 'Tests the field plugin base integration with the entity system.', + 'name' => 'View dependencies test', + 'description' => 'Tests the calculation of dependencies for views.', 'group' => 'Views module integration', ); } /** - * Tests the getEntity method. + * Tests the calculateDependencies method. */ - public function testGetEntity() { + public function testCalculateDependencies() { // The view is a view of comments, their nodes and their authors, so there // are three layers of entities. - $account = entity_create('user', array('name' => $this->randomName(), 'bundle' => 'user')); $account->save(); + $this->drupalCreateContentType(array('type' => 'page')); $this->container->get('comment.manager')->addDefaultField('node', 'page'); // Force a flush of the in-memory storage. $this->container->get('views.views_data')->clear(); @@ -61,18 +61,16 @@ public function testGetEntity() { $comment->save(); $view = Views::getView('test_field_get_entity'); - $this->executeView($view); - $row = $view->result[0]; - // Tests entities on the base level. - $entity = $view->field['cid']->getEntity($row); - $this->assertEqual($entity->id(), $comment->id(), 'Make sure the right comment entity got loaded.'); - // Tests entities as relationship on first level. - $entity = $view->field['nid']->getEntity($row); - $this->assertEqual($entity->id(), $node->id(), 'Make sure the right node entity got loaded.'); - // Tests entities as relationships on second level. - $entity = $view->field['uid']->getEntity($row); - $this->assertEqual($entity->id(), $account->id(), 'Make sure the right user entity got loaded.'); + $expected_dependencies = array( + 'module' => array( + 'comment', + 'node', + 'user', + ) + ); + $dependencies = $view->calculateDependencies(); + $this->assertEqual($expected_dependencies, $dependencies); } } diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php index 5f73592..a7dc428 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php @@ -53,6 +53,7 @@ public static function getInfo() { * Tests most of the handlers. */ public function testHandlers() { + $this->drupalCreateContentType(array('type' => 'article')); $this->container->get('comment.manager')->addDefaultField('node', 'article'); $object_types = array_keys(ViewExecutable::viewsHandlerTypes()); diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerTest.php index fc5eeed..5c145fa 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerTest.php @@ -41,6 +41,7 @@ public static function getInfo() { protected function setUp() { parent::setUp(); + $this->drupalCreateContentType(array('type' => 'page')); $this->container->get('comment.manager')->addDefaultField('node', 'page'); $this->enableViewsTestModule(); } diff --git a/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php b/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php index 79bce4b..f43edbc 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php @@ -152,6 +152,7 @@ public function customErrorHandler($error_level, $message, $filename, $line, $co */ public function testLoadFunctions() { $this->enableModules(array('node')); + $this->installConfig(array('node')); $controller = $this->container->get('entity.manager')->getStorageController('view'); // Test views_view_is_enabled/disabled. diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php index 0d7f19e..6c97e99 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php @@ -88,6 +88,10 @@ protected function setUpFixtures() { $this->installSchema('node', array('node', 'node_field_data')); $this->installSchema('comment', array('comment', 'comment_entity_statistics')); $this->installConfig(array('field')); + entity_create('node_type', array( + 'type' => 'page', + 'name' => 'Page', + ))->save(); $this->container->get('comment.manager')->addDefaultField('node', 'page'); parent::setUpFixtures(); diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php index d0cea34..e9d0d50 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php @@ -8,7 +8,7 @@ namespace Drupal\views\Tests; use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\views\ViewStorageController; +use Drupal\Core\Config\Entity\ConfigStorageController; use Drupal\views\Entity\View; use Drupal\views\Plugin\views\display\Page; use Drupal\views\Plugin\views\display\DefaultDisplay; @@ -16,10 +16,10 @@ use Drupal\views\Views; /** - * Tests the functionality of View and ViewStorageController. + * Tests the functionality of View and ConfigStorageController. * * @see \Drupal\views\Entity\View - * @see \Drupal\views\ViewStorageController + * @see \Drupal\Core\Config\Entity\ConfigStorageController */ class ViewStorageTest extends ViewUnitTestBase { @@ -50,7 +50,7 @@ class ViewStorageTest extends ViewUnitTestBase { /** * The configuration entity storage controller. * - * @var \Drupal\views\ViewStorageController + * @var \Drupal\Core\Config\Entity\ConfigStorageController */ protected $controller; @@ -81,7 +81,7 @@ function testConfigurationEntityCRUD() { $this->assertTrue($this->entityType instanceof EntityTypeInterface, 'The View info array is loaded.'); // Confirm we have the correct controller class. - $this->assertTrue($this->controller instanceof ViewStorageController, 'The correct controller is loaded.'); + $this->assertTrue($this->controller instanceof ConfigStorageController, 'The correct controller is loaded.'); // CRUD tests. $this->loadTests(); diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php index 3761ed8..af8f86d 100644 --- a/core/modules/views/lib/Drupal/views/ViewExecutable.php +++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php @@ -2239,4 +2239,16 @@ public function hasFormElements() { return FALSE; } + /** + * Calculates dependencies for the view. + * + * @see \Drupal\views\Entity\View::calculateDependencies() + * + * @return array + * An array of dependencies grouped by type (module, theme, entity). + */ + public function calculateDependencies() { + return $this->storage->calculateDependencies(); + } + } diff --git a/core/modules/views/lib/Drupal/views/ViewStorageController.php b/core/modules/views/lib/Drupal/views/ViewStorageController.php deleted file mode 100644 index a418fb1..0000000 --- a/core/modules/views/lib/Drupal/views/ViewStorageController.php +++ /dev/null @@ -1,33 +0,0 @@ -moduleExists($entity->get('module'))) { - return TRUE; - } - return FALSE; - }); - } - -} diff --git a/core/modules/views/tests/Drupal/views/Tests/EventSubscriber/RouteSubscriberTest.php b/core/modules/views/tests/Drupal/views/Tests/EventSubscriber/RouteSubscriberTest.php index e5d6c69..cccd842 100644 --- a/core/modules/views/tests/Drupal/views/Tests/EventSubscriber/RouteSubscriberTest.php +++ b/core/modules/views/tests/Drupal/views/Tests/EventSubscriber/RouteSubscriberTest.php @@ -61,7 +61,7 @@ public static function getInfo() { protected function setUp() { $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); - $this->viewStorageController = $this->getMockBuilder('\Drupal\views\ViewStorageController') + $this->viewStorageController = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigStorageController') ->disableOriginalConstructor() ->getMock(); $this->entityManager->expects($this->any()) diff --git a/core/modules/views/tests/Drupal/views/Tests/Plugin/Block/ViewsBlockTest.php b/core/modules/views/tests/Drupal/views/Tests/Plugin/Block/ViewsBlockTest.php index 15d4d6e..51254b5 100644 --- a/core/modules/views/tests/Drupal/views/Tests/Plugin/Block/ViewsBlockTest.php +++ b/core/modules/views/tests/Drupal/views/Tests/Plugin/Block/ViewsBlockTest.php @@ -101,7 +101,7 @@ protected function setUp() { ->with($this->view) ->will($this->returnValue($this->executable)); - $this->storageController = $this->getMockBuilder('Drupal\views\ViewStorageController') + $this->storageController = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigStorageController') ->disableOriginalConstructor() ->getMock(); diff --git a/core/modules/views/tests/Drupal/views/Tests/Routing/ViewPageControllerTest.php b/core/modules/views/tests/Drupal/views/Tests/Routing/ViewPageControllerTest.php index 2cdd6e1..0daf925 100644 --- a/core/modules/views/tests/Drupal/views/Tests/Routing/ViewPageControllerTest.php +++ b/core/modules/views/tests/Drupal/views/Tests/Routing/ViewPageControllerTest.php @@ -53,7 +53,7 @@ public static function getInfo() { } protected function setUp() { - $this->storageController = $this->getMockBuilder('Drupal\views\ViewStorageController') + $this->storageController = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigStorageController') ->disableOriginalConstructor() ->getMock(); $this->executableFactory = $this->getMockBuilder('Drupal\views\ViewExecutableFactory') diff --git a/core/modules/views/tests/Drupal/views/Tests/ViewsTest.php b/core/modules/views/tests/Drupal/views/Tests/ViewsTest.php index 9466c0b..1ebf244 100644 --- a/core/modules/views/tests/Drupal/views/Tests/ViewsTest.php +++ b/core/modules/views/tests/Drupal/views/Tests/ViewsTest.php @@ -35,7 +35,7 @@ protected function setUp() { $this->view = new View(array('id' => 'test_view'), 'view'); - $view_storage_controller = $this->getMockBuilder('Drupal\views\ViewStorageController') + $view_storage_controller = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigStorageController') ->disableOriginalConstructor() ->getMock(); $view_storage_controller->expects($this->once()) diff --git a/core/modules/views_ui/config/tour.tour.views-ui.yml b/core/modules/views_ui/config/tour.tour.views-ui.yml index 76c9302..8695a75 100644 --- a/core/modules/views_ui/config/tour.tour.views-ui.yml +++ b/core/modules/views_ui/config/tour.tour.views-ui.yml @@ -87,3 +87,6 @@ tips: location: left attributes: data-id: views-display-extra-actions +dependencies: + module: + - views_ui diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php index f959d0d..fe82cfb 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php @@ -149,6 +149,13 @@ class ViewUI implements ViewStorageInterface { private $isSyncing = FALSE; /** + * Whether the config is being deleted through the uninstall process. + * + * @var bool + */ + private $isUninstalling = FALSE; + + /** * Constructs a View UI object. * * @param \Drupal\views\ViewStorageInterface $storage @@ -207,11 +214,25 @@ public function setSyncing($syncing) { /** * {@inheritdoc} */ + public function setUninstalling($isUninstalling) { + $this->isUninstalling = $isUninstalling; + } + + /** + * {@inheritdoc} + */ public function isSyncing() { return $this->isSyncing; } /** + * {@inheritdoc} + */ + public function isUninstalling() { + return $this->isUninstalling; + } + + /** * Basic submit handler applicable to all 'standard' forms. * * This submit handler determines whether the user wants the submitted changes @@ -1179,4 +1200,16 @@ public function hasLinkTemplate($key) { return $this->storage->hasLinkTemplate($key); } + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + } + + /** + * {@inheritdoc} + */ + public function getConfigDependencyName() { + } + } diff --git a/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php b/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php index 534f824..62a1586 100644 --- a/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php +++ b/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php @@ -30,7 +30,7 @@ public static function getInfo() { * @see \Drupal\views_ui\ViewListController::getDisplaysList(). */ public function testBuildRowEntityList() { - $storage_controller = $this->getMockBuilder('Drupal\views\ViewStorageController') + $storage_controller = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigStorageController') ->disableOriginalConstructor() ->getMock(); $display_manager = $this->getMockBuilder('\Drupal\views\Plugin\ViewsPluginManager') diff --git a/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewUIObjectTest.php b/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewUIObjectTest.php index b8256a5..7cbf7b9 100644 --- a/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewUIObjectTest.php +++ b/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewUIObjectTest.php @@ -47,7 +47,11 @@ public function testEntityDecoration() { // EntityInterface::isNew() is missing from the list of methods, because it // calls id(), which breaks the ->expect($this->once()) call. Call it later. // EntityInterface::isSyncing() is only called during syncing process. - if ($reflection_method->getName() != 'isNew' && $reflection_method->getName() != 'isSyncing') { + // EntityInterface::isUninstalling() is only called during uninstallation + // process. ConfigEntityInterface::getConfigDependencyName() and + // ConfigEntityInterface::calculateDependencies() are only used for + // dependency management. + if (!in_array($reflection_method->getName(), ['isNew', 'isSyncing', 'isUninstalling', 'getConfigDependencyName', 'calculateDependencies'])) { if (count($reflection_method->getParameters()) == 0) { $method_args[$reflection_method->getName()] = array(); } diff --git a/core/profiles/minimal/config/block.block.stark_admin.yml b/core/profiles/minimal/config/block.block.stark_admin.yml index f3c1735..68bee15 100644 --- a/core/profiles/minimal/config/block.block.stark_admin.yml +++ b/core/profiles/minimal/config/block.block.stark_admin.yml @@ -18,3 +18,8 @@ visibility: roles: { } node_type: types: { } +dependencies: + module: + - system + theme: + - stark diff --git a/core/profiles/minimal/config/block.block.stark_login.yml b/core/profiles/minimal/config/block.block.stark_login.yml index 4ca8266..fb1301f 100644 --- a/core/profiles/minimal/config/block.block.stark_login.yml +++ b/core/profiles/minimal/config/block.block.stark_login.yml @@ -18,3 +18,8 @@ visibility: roles: { } node_type: types: { } +dependencies: + module: + - user + theme: + - stark diff --git a/core/profiles/minimal/config/block.block.stark_tools.yml b/core/profiles/minimal/config/block.block.stark_tools.yml index a452643..9d53bc3 100644 --- a/core/profiles/minimal/config/block.block.stark_tools.yml +++ b/core/profiles/minimal/config/block.block.stark_tools.yml @@ -18,3 +18,8 @@ visibility: roles: { } node_type: types: { } +dependencies: + module: + - system + theme: + - stark diff --git a/core/profiles/standard/config/block.block.bartik_breadcrumbs.yml b/core/profiles/standard/config/block.block.bartik_breadcrumbs.yml index 3465368..b8c9eb0 100644 --- a/core/profiles/standard/config/block.block.bartik_breadcrumbs.yml +++ b/core/profiles/standard/config/block.block.bartik_breadcrumbs.yml @@ -20,3 +20,8 @@ visibility: types: article: '0' page: '0' +dependencies: + module: + - system + theme: + - bartik diff --git a/core/profiles/standard/config/block.block.bartik_content.yml b/core/profiles/standard/config/block.block.bartik_content.yml index 8146a54..f7362e6 100644 --- a/core/profiles/standard/config/block.block.bartik_content.yml +++ b/core/profiles/standard/config/block.block.bartik_content.yml @@ -20,3 +20,8 @@ visibility: types: article: '0' page: '0' +dependencies: + module: + - system + theme: + - bartik diff --git a/core/profiles/standard/config/block.block.bartik_footer.yml b/core/profiles/standard/config/block.block.bartik_footer.yml index 76f9508..07bdba5 100644 --- a/core/profiles/standard/config/block.block.bartik_footer.yml +++ b/core/profiles/standard/config/block.block.bartik_footer.yml @@ -20,3 +20,8 @@ visibility: types: article: '0' page: '0' +dependencies: + module: + - system + theme: + - bartik diff --git a/core/profiles/standard/config/block.block.bartik_help.yml b/core/profiles/standard/config/block.block.bartik_help.yml index 5806db7..6ee1371 100644 --- a/core/profiles/standard/config/block.block.bartik_help.yml +++ b/core/profiles/standard/config/block.block.bartik_help.yml @@ -20,3 +20,8 @@ visibility: types: article: '0' page: '0' +dependencies: + module: + - system + theme: + - bartik diff --git a/core/profiles/standard/config/block.block.bartik_login.yml b/core/profiles/standard/config/block.block.bartik_login.yml index 7fc17cb..235fa04 100644 --- a/core/profiles/standard/config/block.block.bartik_login.yml +++ b/core/profiles/standard/config/block.block.bartik_login.yml @@ -20,3 +20,8 @@ visibility: types: article: '0' page: '0' +dependencies: + module: + - user + theme: + - bartik diff --git a/core/profiles/standard/config/block.block.bartik_powered.yml b/core/profiles/standard/config/block.block.bartik_powered.yml index 0e3ebab..a0257a2 100644 --- a/core/profiles/standard/config/block.block.bartik_powered.yml +++ b/core/profiles/standard/config/block.block.bartik_powered.yml @@ -20,3 +20,8 @@ visibility: types: article: '0' page: '0' +dependencies: + module: + - system + theme: + - bartik diff --git a/core/profiles/standard/config/block.block.bartik_search.yml b/core/profiles/standard/config/block.block.bartik_search.yml index f68adbc..dc39361 100644 --- a/core/profiles/standard/config/block.block.bartik_search.yml +++ b/core/profiles/standard/config/block.block.bartik_search.yml @@ -20,3 +20,8 @@ visibility: types: article: '0' page: '0' +dependencies: + module: + - search + theme: + - bartik diff --git a/core/profiles/standard/config/block.block.bartik_tools.yml b/core/profiles/standard/config/block.block.bartik_tools.yml index 257990d..cfe490d 100644 --- a/core/profiles/standard/config/block.block.bartik_tools.yml +++ b/core/profiles/standard/config/block.block.bartik_tools.yml @@ -20,3 +20,8 @@ visibility: types: article: '0' page: '0' +dependencies: + module: + - system + theme: + - bartik diff --git a/core/profiles/standard/config/block.block.seven_breadcrumbs.yml b/core/profiles/standard/config/block.block.seven_breadcrumbs.yml index b56f633..b48f4b4 100644 --- a/core/profiles/standard/config/block.block.seven_breadcrumbs.yml +++ b/core/profiles/standard/config/block.block.seven_breadcrumbs.yml @@ -20,3 +20,8 @@ visibility: types: article: '0' page: '0' +dependencies: + module: + - system + theme: + - seven diff --git a/core/profiles/standard/config/block.block.seven_content.yml b/core/profiles/standard/config/block.block.seven_content.yml index 23a6568..1f44d0e 100644 --- a/core/profiles/standard/config/block.block.seven_content.yml +++ b/core/profiles/standard/config/block.block.seven_content.yml @@ -20,3 +20,8 @@ visibility: types: article: '0' page: '0' +dependencies: + module: + - system + theme: + - seven diff --git a/core/profiles/standard/config/block.block.seven_help.yml b/core/profiles/standard/config/block.block.seven_help.yml index 77918e5..2f3121f 100644 --- a/core/profiles/standard/config/block.block.seven_help.yml +++ b/core/profiles/standard/config/block.block.seven_help.yml @@ -20,3 +20,8 @@ visibility: types: article: '0' page: '0' +dependencies: + module: + - system + theme: + - seven diff --git a/core/profiles/standard/config/block.block.seven_login.yml b/core/profiles/standard/config/block.block.seven_login.yml index 239bd36..8eae9f4 100644 --- a/core/profiles/standard/config/block.block.seven_login.yml +++ b/core/profiles/standard/config/block.block.seven_login.yml @@ -20,3 +20,8 @@ visibility: types: article: '0' page: '0' +dependencies: + module: + - user + theme: + - seven diff --git a/core/profiles/standard/config/editor.editor.basic_html.yml b/core/profiles/standard/config/editor.editor.basic_html.yml index 155c5fb..fdf6e96 100644 --- a/core/profiles/standard/config/editor.editor.basic_html.yml +++ b/core/profiles/standard/config/editor.editor.basic_html.yml @@ -41,3 +41,8 @@ image_upload: height: 0 status: true langcode: en +dependencies: + entity: + - filter.format.basic_html + module: + - ckeditor diff --git a/core/profiles/standard/config/editor.editor.full_html.yml b/core/profiles/standard/config/editor.editor.full_html.yml index cd17f03..66df850 100644 --- a/core/profiles/standard/config/editor.editor.full_html.yml +++ b/core/profiles/standard/config/editor.editor.full_html.yml @@ -53,3 +53,8 @@ image_upload: height: 0 status: true langcode: en +dependencies: + entity: + - filter.format.full_html + module: + - ckeditor diff --git a/core/profiles/standard/config/entity.form_display.node.article.default.yml b/core/profiles/standard/config/entity.form_display.node.article.default.yml index ff157a9..32fcba1 100644 --- a/core/profiles/standard/config/entity.form_display.node.article.default.yml +++ b/core/profiles/standard/config/entity.form_display.node.article.default.yml @@ -2,25 +2,45 @@ id: node.article.default targetEntityType: node bundle: article mode: default -status: true content: + title: + type: text_textfield + weight: 0 + settings: + size: 60 + placeholder: '' body: type: text_textarea_with_summary - weight: 0 + weight: 1 settings: rows: 9 summary_rows: 3 placeholder: '' + comment: + type: comment_default + weight: 2 + settings: { } field_tags: type: taxonomy_autocomplete - weight: -4 - settings: - size: 60 - autocomplete_route_name: taxonomy.autocomplete - placeholder: '' + weight: 3 + settings: { } field_image: type: image_image + weight: 4 settings: progress_indicator: throbber preview_image_style: thumbnail - weight: -1 +hidden: { } +status: true +dependencies: + entity: + - field.instance.node.article.body + - field.instance.node.article.comment + - field.instance.node.article.field_image + - field.instance.node.article.field_tags + - node.type.article + module: + - comment + - image + - taxonomy + - text diff --git a/core/profiles/standard/config/entity.form_display.user.user.default.yml b/core/profiles/standard/config/entity.form_display.user.user.default.yml index 13dc334..a9c9197 100644 --- a/core/profiles/standard/config/entity.form_display.user.user.default.yml +++ b/core/profiles/standard/config/entity.form_display.user.user.default.yml @@ -10,3 +10,6 @@ content: preview_image_style: thumbnail weight: -1 status: true +dependencies: + module: + - image diff --git a/core/profiles/standard/config/entity.view_display.node.article.default.yml b/core/profiles/standard/config/entity.view_display.node.article.default.yml index 6d76c36..c4f2c85 100644 --- a/core/profiles/standard/config/entity.view_display.node.article.default.yml +++ b/core/profiles/standard/config/entity.view_display.node.article.default.yml @@ -21,3 +21,14 @@ content: image_style: large image_link: '' weight: -1 +dependencies: + entity: + - field.instance.node.article.body + - field.instance.node.article.field_image + - field.instance.node.article.field_tags + - node.type.article + module: + - comment + - image + - taxonomy + - text diff --git a/core/profiles/standard/config/entity.view_display.node.article.teaser.yml b/core/profiles/standard/config/entity.view_display.node.article.teaser.yml index 75bf6d0..a88fc06 100644 --- a/core/profiles/standard/config/entity.view_display.node.article.teaser.yml +++ b/core/profiles/standard/config/entity.view_display.node.article.teaser.yml @@ -22,3 +22,14 @@ content: image_style: medium image_link: content weight: -1 +dependencies: + entity: + - entity.view_mode.node.teaser + - field.instance.node.article.body + - field.instance.node.article.field_image + - field.instance.node.article.field_tags + - node.type.article + module: + - image + - taxonomy + - text diff --git a/core/profiles/standard/config/entity.view_display.user.user.compact.yml b/core/profiles/standard/config/entity.view_display.user.user.compact.yml index f9e85b3..1c7e4e9 100644 --- a/core/profiles/standard/config/entity.view_display.user.user.compact.yml +++ b/core/profiles/standard/config/entity.view_display.user.user.compact.yml @@ -13,3 +13,8 @@ content: hidden: member_for: true status: true +dependencies: + entity: + - entity.view_mode.user.compact + module: + - image diff --git a/core/profiles/standard/config/entity.view_display.user.user.default.yml b/core/profiles/standard/config/entity.view_display.user.user.default.yml index fe9e3eb..f39700b 100644 --- a/core/profiles/standard/config/entity.view_display.user.user.default.yml +++ b/core/profiles/standard/config/entity.view_display.user.user.default.yml @@ -11,3 +11,6 @@ content: image_link: content weight: 0 status: true +dependencies: + module: + - image diff --git a/core/profiles/standard/config/field.field.node.field_image.yml b/core/profiles/standard/config/field.field.node.field_image.yml index b397ecd..a0b1966 100644 --- a/core/profiles/standard/config/field.field.node.field_image.yml +++ b/core/profiles/standard/config/field.field.node.field_image.yml @@ -32,3 +32,6 @@ indexes: - target_id status: true langcode: und +dependencies: + module: + - image diff --git a/core/profiles/standard/config/field.field.node.field_tags.yml b/core/profiles/standard/config/field.field.node.field_tags.yml index 55eac77..7018e87 100644 --- a/core/profiles/standard/config/field.field.node.field_tags.yml +++ b/core/profiles/standard/config/field.field.node.field_tags.yml @@ -16,3 +16,6 @@ indexes: - target_id status: true langcode: und +dependencies: + module: + - taxonomy diff --git a/core/profiles/standard/config/field.field.user.user_picture.yml b/core/profiles/standard/config/field.field.user.user_picture.yml index ecbe2c4..7becb75 100644 --- a/core/profiles/standard/config/field.field.user.user_picture.yml +++ b/core/profiles/standard/config/field.field.user.user_picture.yml @@ -32,3 +32,6 @@ translatable: false indexes: target_id: - target_id +dependencies: + module: + - image diff --git a/core/profiles/standard/config/field.instance.node.article.field_image.yml b/core/profiles/standard/config/field.instance.node.article.field_image.yml index 69d8593..11cbbd4 100644 --- a/core/profiles/standard/config/field.instance.node.article.field_image.yml +++ b/core/profiles/standard/config/field.instance.node.article.field_image.yml @@ -26,3 +26,6 @@ settings: status: true langcode: und field_type: image +dependencies: + entity: + - field.field.node.field_image diff --git a/core/profiles/standard/config/field.instance.node.article.field_tags.yml b/core/profiles/standard/config/field.instance.node.article.field_tags.yml index ed4e166..ed175f7 100644 --- a/core/profiles/standard/config/field.instance.node.article.field_tags.yml +++ b/core/profiles/standard/config/field.instance.node.article.field_tags.yml @@ -10,3 +10,6 @@ default_value_function: '' settings: { } status: true langcode: und +dependencies: + entity: + - field.field.node.field_tags diff --git a/core/profiles/standard/config/field.instance.user.user.user_picture.yml b/core/profiles/standard/config/field.instance.user.user.user_picture.yml index f31efe6..6469300 100644 --- a/core/profiles/standard/config/field.instance.user.user.user_picture.yml +++ b/core/profiles/standard/config/field.instance.user.user.user_picture.yml @@ -26,3 +26,6 @@ settings: alt_field_required: false title_field_required: false field_type: image +dependencies: + entity: + - field.field.user.user_picture diff --git a/core/profiles/standard/config/rdf.mapping.comment.node__comment.yml b/core/profiles/standard/config/rdf.mapping.comment.node__comment.yml index 42d8f34..70f25b1 100644 --- a/core/profiles/standard/config/rdf.mapping.comment.node__comment.yml +++ b/core/profiles/standard/config/rdf.mapping.comment.node__comment.yml @@ -24,3 +24,6 @@ fieldMappings: properties: - 'schema:author' mapping_type: 'rel' +dependencies: + module: + - comment diff --git a/core/profiles/standard/config/rdf.mapping.node.article.yml b/core/profiles/standard/config/rdf.mapping.node.article.yml index a87edaf..10147e7 100644 --- a/core/profiles/standard/config/rdf.mapping.node.article.yml +++ b/core/profiles/standard/config/rdf.mapping.node.article.yml @@ -40,3 +40,8 @@ fieldMappings: field_tags: properties: - 'schema:about' +dependencies: + entity: + - node.type.article + module: + - node diff --git a/core/profiles/standard/config/rdf.mapping.node.page.yml b/core/profiles/standard/config/rdf.mapping.node.page.yml index 967449c..8a5d377 100644 --- a/core/profiles/standard/config/rdf.mapping.node.page.yml +++ b/core/profiles/standard/config/rdf.mapping.node.page.yml @@ -31,3 +31,8 @@ fieldMappings: callable: 'Drupal\rdf\SchemaOrgDataConverter::interactionCount' arguments: interaction_type: 'UserComments' +dependencies: + entity: + - node.type.page + module: + - node diff --git a/core/profiles/standard/config/rdf.mapping.taxonomy_term.tags.yml b/core/profiles/standard/config/rdf.mapping.taxonomy_term.tags.yml index 051795c..60bf174 100644 --- a/core/profiles/standard/config/rdf.mapping.taxonomy_term.tags.yml +++ b/core/profiles/standard/config/rdf.mapping.taxonomy_term.tags.yml @@ -10,3 +10,8 @@ fieldMappings: description: properties: - 'schema:description' +dependencies: + entity: + - taxonomy.vocabulary.tags + module: + - taxonomy diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigDependencyManagerTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigDependencyManagerTest.php new file mode 100644 index 0000000..489fa28 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigDependencyManagerTest.php @@ -0,0 +1,44 @@ + 'ConfigDependencyManager test', + 'description' => 'Tests the ConfigDependencyManager class.', + 'group' => 'Configuration' + ); + } + + public function testNoConfiguration() { + $dep_manger = new ConfigDependencyManager(); + $this->assertEmpty($dep_manger->getDependentEntities('entity', 'config_test.dynamic.entity_id:745b0ce0-aece-42dd-a800-ade5b8455e84')); + } + + public function testNoConfigEntities() { + $dep_manger = new ConfigDependencyManager(); + $dep_manger->setData(array( + 'simple.config' => array( + 'key' => 'value', + ), + )); + $this->assertEmpty($dep_manger->getDependentEntities('entity', 'config_test.dynamic.entity_id:745b0ce0-aece-42dd-a800-ade5b8455e84')); + + // Configuration is always dependent on its provider. + $dependencies = $dep_manger->getDependentEntities('module', 'simple'); + $this->assertArrayHasKey('simple.config', $dependencies); + $this->assertCount(1, $dependencies); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php new file mode 100644 index 0000000..ef6fa61 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php @@ -0,0 +1,206 @@ + '', + 'name' => '\Drupal\Core\Config\Entity\ConfigEntityBase unit test', + 'group' => 'Entity', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $values = array(); + $this->entityTypeId = $this->randomName(); + $this->provider = $this->randomName(); + $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $this->entityType->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue($this->provider)); + + $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityManager->expects($this->any()) + ->method('getDefinition') + ->with($this->entityTypeId) + ->will($this->returnValue($this->entityType)); + + $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); + + $container = new ContainerBuilder(); + $container->set('entity.manager', $this->entityManager); + $container->set('uuid', $this->uuid); + \Drupal::setContainer($container); + + $this->entity = $this->getMockForAbstractClass('\Drupal\Core\Config\Entity\ConfigEntityBase', array($values, $this->entityTypeId)); + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependencies() { + // Calculating dependencies will reset the dependencies array. + $this->entity->set('dependencies', array('module' => array('node'))); + $this->assertEmpty($this->entity->calculateDependencies()); + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependenciesWithPluginBag() { + $values = array(); + $this->entity = $this->getMockBuilder('\Drupal\Tests\Core\Config\Entity\Fixtures\ConfigEntityBaseWithPluginBag') + ->setConstructorArgs(array($values, $this->entityTypeId)) + ->setMethods(array('getPluginBag')) + ->getMock(); + + // Create a configurable plugin that would add a dependency. + $instance_id = $this->randomName(); + $instance = new TestConfigurablePlugin(array(), $instance_id, array('provider' => 'test')); + + // Create a plugin bag to contain the instance. + $pluginBag = $this->getMockBuilder('\Drupal\Core\Plugin\DefaultPluginBag') + ->disableOriginalConstructor() + ->setMethods(array('get')) + ->getMock(); + $pluginBag->expects($this->atLeastOnce()) + ->method('get') + ->with($instance_id) + ->will($this->returnValue($instance)); + $pluginBag->addInstanceId($instance_id); + + // Return the mocked plugin bag. + $this->entity->expects($this->once()) + ->method('getPluginBag') + ->will($this->returnValue($pluginBag)); + + $dependencies = $this->entity->calculateDependencies(); + $this->assertContains('test', $dependencies['module']); + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependenciesWithPluginBagSameProviderAsEntityType() { + $values = array(); + $this->entity = $this->getMockBuilder('\Drupal\Tests\Core\Config\Entity\Fixtures\ConfigEntityBaseWithPluginBag') + ->setConstructorArgs(array($values, $this->entityTypeId)) + ->setMethods(array('getPluginBag')) + ->getMock(); + + // Create a configurable plugin that will not add a dependency since it is + // provider matches the provider of the entity type. + $instance_id = $this->randomName(); + $instance = new TestConfigurablePlugin(array(), $instance_id, array('provider' => $this->provider)); + + // Create a plugin bag to contain the instance. + $pluginBag = $this->getMockBuilder('\Drupal\Core\Plugin\DefaultPluginBag') + ->disableOriginalConstructor() + ->setMethods(array('get')) + ->getMock(); + $pluginBag->expects($this->atLeastOnce()) + ->method('get') + ->with($instance_id) + ->will($this->returnValue($instance)); + $pluginBag->addInstanceId($instance_id); + + // Return the mocked plugin bag. + $this->entity->expects($this->once()) + ->method('getPluginBag') + ->will($this->returnValue($pluginBag)); + + $this->assertEmpty($this->entity->calculateDependencies()); + } + +} + +class TestConfigurablePlugin extends PluginBase implements ConfigurablePluginInterface { + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + return $this->configuration; + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + $this->configuration = $configuration; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return array(); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityDependencyTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityDependencyTest.php new file mode 100644 index 0000000..a653730 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityDependencyTest.php @@ -0,0 +1,61 @@ + 'ConfigEntityDependency test', + 'description' => 'Tests the ConfigEntityDependency class.', + 'group' => 'Configuration' + ); + } + + public function testEmptyDependencies() { + $dep = new ConfigEntityDependency('config_test.dynamic.entity_id', array()); + + $this->assertEquals('config_test.dynamic.entity_id', $dep->getConfigDependencyName()); + $this->assertEquals(array(), $dep->getDependencies('theme')); + $this->assertEquals(array(), $dep->getDependencies('entity')); + $this->assertEquals(array('config_test'), $dep->getDependencies('module')); + $this->assertTrue($dep->hasDependency('module', 'config_test')); + $this->assertFalse($dep->hasDependency('module', 'views')); + } + + public function testWithDependencies() { + $values = array( + 'uuid' => '60db47f4-54fb-4c86-a439-5769fbda4bd1', + 'dependencies' => array( + 'module' => array( + 'node', + 'views' + ), + 'entity' => array( + 'config_test.dynamic.entity_id:745b0ce0-aece-42dd-a800-ade5b8455e84', + ), + ), + ); + $dep = new ConfigEntityDependency('config_test.dynamic.entity_id', $values); + + $this->assertEquals(array(), $dep->getDependencies('theme')); + $this->assertEquals(array('config_test.dynamic.entity_id:745b0ce0-aece-42dd-a800-ade5b8455e84'), $dep->getDependencies('entity')); + $this->assertEquals(array('node', 'views', 'config_test'), $dep->getDependencies('module')); + $this->assertTrue($dep->hasDependency('module', 'config_test')); + $this->assertTrue($dep->hasDependency('module', 'views')); + $this->assertTrue($dep->hasDependency('module', 'node')); + $this->assertFalse($dep->hasDependency('module', 'block')); + $this->assertTrue($dep->hasDependency('entity', 'config_test.dynamic.entity_id:745b0ce0-aece-42dd-a800-ade5b8455e84')); + $this->assertFalse($dep->hasDependency('entity', 'config_test.dynamic.another_id:7dfa5cb7-2248-4d52-8c00-cd8e02d1e78e')); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/EntityDisplayModeBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/EntityDisplayModeBaseUnitTest.php new file mode 100644 index 0000000..314e044 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Config/Entity/EntityDisplayModeBaseUnitTest.php @@ -0,0 +1,120 @@ + '', + 'name' => '\Drupal\entity\EntityDisplayModeBase unit test', + 'group' => 'Entity', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->entityType = $this->randomName(); + + $this->entityInfo = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $this->entityInfo->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue('entity')); + + $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + + $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); + + $container = new ContainerBuilder(); + $container->set('entity.manager', $this->entityManager); + $container->set('uuid', $this->uuid); + \Drupal::setContainer($container); + + } + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependencies() { + $target_entity_type_id = $this->randomName(16); + + $target_entity_type = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); + $target_entity_type->expects($this->any()) + ->method('getProvider') + ->will($this->returnValue('test_module')); + $values = array('targetEntityType' => $target_entity_type_id); + + $this->entityManager->expects($this->at(0)) + ->method('getDefinition') + ->with($target_entity_type_id) + ->will($this->returnValue($target_entity_type)); + $this->entityManager->expects($this->at(1)) + ->method('getDefinition') + ->with($this->entityType) + ->will($this->returnValue($this->entityInfo)); + + $this->entity = $this->getMockBuilder('\Drupal\entity\EntityDisplayModeBase') + ->setConstructorArgs(array($values, $this->entityType)) + ->setMethods(array('getFilterFormat')) + ->getMock(); + + $dependencies = $this->entity->calculateDependencies(); + $this->assertContains('test_module', $dependencies['module']); + + } + +} diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/Fixtures/ConfigEntityBaseWithPluginBag.php b/core/tests/Drupal/Tests/Core/Config/Entity/Fixtures/ConfigEntityBaseWithPluginBag.php new file mode 100644 index 0000000..85a7f10 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Config/Entity/Fixtures/ConfigEntityBaseWithPluginBag.php @@ -0,0 +1,20 @@ +