Commit c2125b05 authored by catch's avatar catch

Issue #2282519 by alexpott, cilefen: Fixed Add content dependency information...

Issue #2282519 by alexpott, cilefen: Fixed Add content dependency information to configuration entities.
parent 0faa5ce3
......@@ -211,9 +211,14 @@ route:
config_dependencies_base:
type: mapping
mapping:
entity:
config:
type: sequence
label: 'Entity dependencies'
label: 'Configuration entity dependencies'
sequence:
- type: string
content:
type: sequence
label: 'Content entity dependencies'
sequence:
- type: string
module:
......
......@@ -47,18 +47,19 @@ public function defaultConfiguration();
* dependencies listing the specified roles.
*
* @return array
* An array of dependencies grouped by type (module, theme, entity). For
* example:
* An array of dependencies grouped by type (config, content, module,
* theme). For example:
* @code
* array(
* 'entity' => array('user.role.anonymous', 'user.role.authenticated'),
* 'config' => array('user.role.anonymous', 'user.role.authenticated'),
* 'content' => array('node:article:f0a189e6-55fb-47fb-8005-5bef81c44d6d'),
* 'module' => array('node', 'user'),
* 'theme' => array('seven'),
* );
* @endcode
*
* @see \Drupal\Core\Config\Entity\ConfigDependencyManager
* @see \Drupal\Core\Config\Entity\ConfigEntityInterface::getConfigDependencyName()
* @see \Drupal\Core\Entity\EntityInterface::getConfigDependencyName()
*/
public function calculateDependencies();
......
......@@ -192,14 +192,14 @@ public function uninstall($type, $name) {
// Work out if any of the entity's dependencies are going to be affected
// by the uninstall.
$affected_dependencies = array(
'entity' => array(),
'config' => array(),
'module' => array(),
'theme' => array(),
);
if (isset($entity_dependencies['entity'])) {
if (isset($entity_dependencies['config'])) {
foreach ($extension_dependent_entities as $extension_dependent_entity) {
if (in_array($extension_dependent_entity->getConfigDependencyName(), $entity_dependencies['entity'])) {
$affected_dependencies['entity'][] = $extension_dependent_entity;
if (in_array($extension_dependent_entity->getConfigDependencyName(), $entity_dependencies['config'])) {
$affected_dependencies['config'][] = $extension_dependent_entity;
}
}
}
......
......@@ -86,7 +86,8 @@ 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'.
* The type of dependency being checked. Either 'module', 'theme', 'config'
* or 'content'.
* @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
......@@ -101,7 +102,8 @@ 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'.
* The type of dependency being checked. Either 'module', 'theme', 'config'
* or 'content'.
* @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
......
......@@ -22,9 +22,13 @@
* The configuration dependency value is structured like this:
* <code>
* array(
* 'entity => array(
* 'config => array(
* // An array of configuration entity object names. Recalculated on save.
* ),
* 'content => array(
* // An array of content entity configuration dependency names. The default
* // format is "ENTITY_TYPE_ID:BUNDLE:UUID". Recalculated on save.
* ),
* 'module' => array(
* // An array of module names. Recalculated on save.
* ),
......@@ -35,7 +39,8 @@
* // An array of configuration dependencies that the config entity is
* // ensured to have regardless of the details of the configuration. These
* // dependencies are not recalculated on save.
* 'entity' => array(),
* 'config' => array(),
* 'content' => array(),
* 'module' => array(),
* 'theme' => array(),
* ),
......@@ -110,12 +115,12 @@
* module dependency in the sub-module only.
*
* @see \Drupal\Core\Config\Entity\ConfigEntityInterface::calculateDependencies()
* @see \Drupal\Core\Config\Entity\ConfigEntityInterface::getConfigDependencyName()
* @see \Drupal\Core\Config\Entity\ConfigEntityInterface::onDependencyRemoval()
* @see \Drupal\Core\Config\Entity\ConfigEntityBase::addDependency()
* @see \Drupal\Core\Config\ConfigInstallerInterface::installDefaultConfig()
* @see \Drupal\Core\Config\ConfigManagerInterface::uninstall()
* @see \Drupal\Core\Config\Entity\ConfigEntityDependency
* @see \Drupal\Core\Entity\EntityInterface::getConfigDependencyName()
* @see \Drupal\Core\Plugin\PluginDependencyTrait
*/
class ConfigDependencyManager {
......@@ -138,7 +143,8 @@ class ConfigDependencyManager {
* Gets dependencies.
*
* @param string $type
* The type of dependency being checked. Either 'module', 'theme', 'entity'.
* The type of dependency being checked. Either 'module', 'theme', 'config'
* or 'content'.
* @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
......@@ -151,17 +157,17 @@ public function getDependentEntities($type, $name) {
$dependent_entities = array();
$entities_to_check = array();
if ($type == 'entity') {
if ($type == 'config') {
$entities_to_check[] = $name;
}
else {
if ($type == 'module' || $type == 'theme') {
if ($type == 'module' || $type == 'theme' || $type == 'content') {
$dependent_entities = array_filter($this->data, function (ConfigEntityDependency $entity) use ($type, $name) {
return $entity->hasDependency($type, $name);
});
}
// If checking module or theme dependencies then discover which entities
// are dependent on the entities that have a direct dependency.
// If checking content, module, or theme dependencies, discover which
// entities are dependent on the entities that have a direct dependency.
foreach ($dependent_entities as $entity) {
$entities_to_check[] = $entity->getConfigDependencyName();
}
......@@ -243,7 +249,7 @@ protected function getGraph() {
foreach ($this->data as $entity) {
$graph_key = $entity->getConfigDependencyName();
$graph[$graph_key]['edges'] = array();
$dependencies = $entity->getDependencies('entity');
$dependencies = $entity->getDependencies('config');
if (!empty($dependencies)) {
foreach ($dependencies as $dependency) {
$graph[$graph_key]['edges'][$dependency] = TRUE;
......
......@@ -50,7 +50,8 @@ public function __construct($name, $values = array()) {
* Gets the configuration entity's dependencies of the supplied type.
*
* @param string $type
* The type of dependency to return. Either 'module', 'theme', 'entity'.
* The type of dependency to return. Either 'module', 'theme', 'config' or
* 'content'.
*
* @return array
* The list of dependencies of the supplied type.
......@@ -70,7 +71,8 @@ public function getDependencies($type) {
* Determines if the entity is dependent on extensions or entities.
*
* @param string $type
* The type of dependency being checked. Either 'module', 'theme', 'entity'.
* The type of dependency being checked. Either 'module', 'theme', 'config'
* or 'content'.
* @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
......@@ -89,7 +91,7 @@ public function hasDependency($type, $name) {
/**
* Gets the configuration entity's configuration dependency name.
*
* @see Drupal\Core\Config\Entity\ConfigEntityInterface::getConfigDependencyName()
* @see \Drupal\Core\Entity\EntityInterface::getConfigDependencyName()
*
* @return string
* The configuration dependency name for the entity.
......
......@@ -151,16 +151,6 @@ public function set($property_name, $value);
*/
public function calculateDependencies();
/**
* Gets the configuration dependency name.
*
* @return string
* The configuration dependency name.
*
* @see \Drupal\Core\Config\Entity\ConfigDependencyManager
*/
public function getConfigDependencyName();
/**
* Informs the entity that entities it depends on will be deleted.
*
......
......@@ -28,7 +28,7 @@ trait DependencyTrait {
* If $type is 'module' or 'theme', the name of the module or theme. If
* $type is 'entity', the full configuration object name.
*
* @see \Drupal\Core\Config\Entity\ConfigEntityInterface::getConfigDependencyName()
* @see \Drupal\Core\Entity\EntityInterface::getConfigDependencyName()
*
* @return $this
*/
......
......@@ -561,4 +561,11 @@ public function __sleep() {
return $this->traitSleep();
}
/**
* {@inheritdoc}
*/
public function getConfigDependencyName() {
return $this->getEntityTypeId() . ':' . $this->bundle() . ':' . $this->uuid();
}
}
......@@ -162,7 +162,7 @@ public function calculateDependencies() {
// If the target entity type uses entities to manage its bundles then
// depend on the bundle entity.
$bundle_entity = \Drupal::entityManager()->getStorage($bundle_entity_type_id)->load($this->bundle);
$this->addDependency('entity', $bundle_entity->getConfigDependencyName());
$this->addDependency('config', $bundle_entity->getConfigDependencyName());
}
else {
// Depend on the provider of the entity type.
......@@ -173,7 +173,7 @@ public function calculateDependencies() {
foreach ($fields as $field_name => $component) {
$field = FieldConfig::loadByName($this->targetEntityType, $this->bundle, $field_name);
if ($field) {
$this->addDependency('entity', $field->getConfigDependencyName());
$this->addDependency('config', $field->getConfigDependencyName());
}
// Create a dependency on the module that provides the formatter or
// widget.
......@@ -190,7 +190,7 @@ public function calculateDependencies() {
// Depend on configured modes.
if ($this->mode != 'default') {
$mode_entity = \Drupal::entityManager()->getStorage('entity_' . $this->displayContext . '_mode')->load($target_entity_type->id() . '.' . $this->mode);
$this->addDependency('entity', $mode_entity->getConfigDependencyName());
$this->addDependency('config', $mode_entity->getConfigDependencyName());
}
return $this->dependencies;
}
......@@ -387,7 +387,7 @@ private function fieldHasDisplayOptions(FieldDefinitionInterface $definition) {
*/
public function onDependencyRemoval(array $dependencies) {
$changed = FALSE;
foreach ($dependencies['entity'] as $entity) {
foreach ($dependencies['config'] as $entity) {
if ($entity instanceof FieldConfigInterface) {
// Remove components for fields that are being deleted.
$this->removeComponent($entity->getName());
......
......@@ -404,4 +404,18 @@ public function getTypedData();
*/
public function getCacheTags();
/**
* Gets the configuration dependency name.
*
* Configuration entities can depend on content and configuration entities.
* They store an array of content and config dependency names in their
* "dependencies" key.
*
* @return string
* The configuration dependency name.
*
* @see \Drupal\Core\Config\Entity\ConfigDependencyManager
*/
public function getConfigDependencyName();
}
......@@ -233,7 +233,7 @@ public function calculateDependencies() {
// If the target entity type uses entities to manage its bundles then
// depend on the bundle entity.
$bundle_entity = $this->entityManager()->getStorage($bundle_entity_type_id)->load($this->bundle);
$this->addDependency('entity', $bundle_entity->getConfigDependencyName());
$this->addDependency('config', $bundle_entity->getConfigDependencyName());
}
return $this->dependencies;
}
......
......@@ -16,7 +16,7 @@ hidden:
feed_icon: true
status: true
dependencies:
entity:
config:
- core.entity_view_mode.aggregator_feed.summary
module:
- aggregator
......@@ -13,7 +13,7 @@ hidden:
description: true
status: true
dependencies:
entity:
config:
- core.entity_view_mode.aggregator_item.summary
module:
- aggregator
......
......@@ -18,9 +18,13 @@ class BlockContent extends DeriverBase {
*/
public function getDerivativeDefinitions($base_plugin_definition) {
$block_contents = entity_load_multiple('block_content');
/** @var $block_content \Drupal\block_content\Entity\BlockContent */
foreach ($block_contents as $block_content) {
$this->derivatives[$block_content->uuid()] = $base_plugin_definition;
$this->derivatives[$block_content->uuid()]['admin_label'] = $block_content->label();
$this->derivatives[$block_content->uuid()]['config_dependencies']['content'] = array(
$block_content->getConfigDependencyName()
);
}
return parent::getDerivativeDefinitions($base_plugin_definition);
}
......
......@@ -7,6 +7,7 @@
namespace Drupal\block_content\Tests;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Database\Database;
use Drupal\block_content\Entity\BlockContent;
......@@ -210,4 +211,25 @@ public function testBlockDelete() {
$this->assertNoText('This will also remove');
}
/**
* Test that placed content blocks create a dependency in the block placement.
*/
public function testConfigDependencies() {
$block = $this->createBlockContent();
// Place the block.
$block_placement_id = Unicode::strtolower($block->label());
$instance = array(
'id' => $block_placement_id,
'settings[label]' => $block->label(),
'region' => 'sidebar_first',
);
$block = entity_load('block_content', 1);
$url = 'admin/structure/block/add/block_content:' . $block->uuid() . '/' . \Drupal::config('system.theme')->get('default');
$this->drupalPostForm($url, $instance, t('Save block'));
$dependencies = \Drupal::service('config.manager')->findConfigEntityDependentsAsEntities('content', array($block->getConfigDependencyName()));
$block_placement = reset($dependencies);
$this->assertEqual($block_placement_id, $block_placement->id(), "The block placement config entity has a dependency on the block content entity.");
}
}
......@@ -2,7 +2,7 @@
langcode: en
status: true
dependencies:
entity:
config:
- node.type.book
id: node.book.promote
field_name: promote
......
......@@ -68,22 +68,22 @@ public function testDependencyMangement() {
$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', 'dependencies' => array('enforced' => array('entity' => array($entity1->getConfigDependencyName())))));
$entity2 = $storage->create(array('id' => 'entity2', 'dependencies' => array('enforced' => array('config' => array($entity1->getConfigDependencyName())))));
$entity2->save();
$entity3 = $storage->create(array('id' => 'entity3', 'dependencies' => array('enforced' => array('entity' => array($entity2->getConfigDependencyName())))));
$entity3 = $storage->create(array('id' => 'entity3', 'dependencies' => array('enforced' => array('config' => array($entity2->getConfigDependencyName())))));
$entity3->save();
$entity4 = $storage->create(array('id' => 'entity4', 'dependencies' => array('enforced' => array('entity' => array($entity3->getConfigDependencyName())))));
$entity4 = $storage->create(array('id' => 'entity4', 'dependencies' => array('enforced' => array('config' => array($entity3->getConfigDependencyName())))));
$entity4->save();
// Test getting $entity1's dependencies as configuration dependency objects.
$dependents = $config_manager->findConfigEntityDependents('entity', array($entity1->getConfigDependencyName()));
$dependents = $config_manager->findConfigEntityDependents('config', 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.entity2 has a dependency on config_test.dynamic.entity1.');
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on config_test.dynamic.entity1.');
$this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on config_test.dynamic.entity1.');
// Test getting $entity2's dependencies as entities.
$dependents = $config_manager->findConfigEntityDependentsAsEntities('entity', array($entity2->getConfigDependencyName()));
$dependents = $config_manager->findConfigEntityDependentsAsEntities('config', array($entity2->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 config_test.dynamic.entity1.');
$this->assertFalse(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 does not have a dependency on itself.');
......@@ -102,20 +102,28 @@ public function testDependencyMangement() {
// objects after making $entity3 also dependent on node module but $entity1
// no longer depend on node module.
$entity1->setEnforcedDependencies([])->save();
$entity3->setEnforcedDependencies(['module' => ['node'], 'entity' => [$entity2->getConfigDependencyName()]])->save();
$entity3->setEnforcedDependencies(['module' => ['node'], 'config' => [$entity2->getConfigDependencyName()]])->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.');
// Test dependency on a fake content entity.
$entity2->setEnforcedDependencies(['config' => [$entity1->getConfigDependencyName()], 'content' => ['node:page:uuid']])->save();;
$dependents = $config_manager->findConfigEntityDependents('content', array('node:page:uuid'));
$this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the content entity.');
$this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on the content entity.');
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the content entity (via entity2).');
$this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the content entity (via entity3).');
// Create a configuration entity of a different type with the same ID as one
// of the entities already created.
$alt_storage = $this->container->get('entity.manager')->getStorage('config_query_test');
$alt_storage->create(array('id' => 'entity1', 'dependencies' => array('enforced' => array('entity' => array($entity1->getConfigDependencyName())))))->save();
$alt_storage->create(array('id' => 'entity1', 'dependencies' => array('enforced' => array('config' => array($entity1->getConfigDependencyName())))))->save();
$alt_storage->create(array('id' => 'entity2', 'dependencies' => array('enforced' => array('module' => array('views')))))->save();
$dependents = $config_manager->findConfigEntityDependentsAsEntities('entity', array($entity1->getConfigDependencyName()));
$dependents = $config_manager->findConfigEntityDependentsAsEntities('config', 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.');
......@@ -169,7 +177,7 @@ public function testConfigEntityUninstall() {
'id' => 'entity2',
'dependencies' => array(
'enforced' => array(
'entity' => array($entity1->getConfigDependencyName()),
'config' => array($entity1->getConfigDependencyName()),
),
),
)
......@@ -197,7 +205,7 @@ public function testConfigEntityUninstall() {
'id' => 'entity2',
'dependencies' => array(
'enforced' => array(
'entity' => array($entity1->getConfigDependencyName()),
'config' => array($entity1->getConfigDependencyName()),
),
),
)
......@@ -212,7 +220,7 @@ public function testConfigEntityUninstall() {
$this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
$entity2 = $storage->load('entity2');
$this->assertTrue($entity2, 'Entity 2 not deleted');
$this->assertEqual($entity2->calculateDependencies()['entity'], array(), 'Entity 2 dependencies updated to remove dependency on Entity1.');
$this->assertEqual($entity2->calculateDependencies()['config'], array(), 'Entity 2 dependencies updated to remove dependency on Entity1.');
}
/**
......
......@@ -375,7 +375,7 @@ function testImportErrorLog() {
'status' => TRUE,
// Add a dependency on primary, to ensure that is synced first.
'dependencies' => array(
'entity' => array($name_primary),
'config' => array($name_primary),
),
'id' => 'secondary',
'label' => 'Secondary Sync',
......
......@@ -226,7 +226,7 @@ function testSecondaryWritePrimaryFirst() {
'uuid' => $uuid->generate(),
// Add a dependency on primary, to ensure that is synced first.
'dependencies' => array(
'entity' => array($name_primary),
'config' => array($name_primary),
)
);
$staging->write($name_secondary, $values_secondary);
......@@ -265,7 +265,7 @@ function testSecondaryWriteSecondaryFirst() {
'uuid' => $uuid->generate(),
// Add a dependency on secondary, so that is synced first.
'dependencies' => array(
'entity' => array($name_secondary),
'config' => array($name_secondary),
)
);
$staging->write($name_primary, $values_primary);
......@@ -323,7 +323,7 @@ function testSecondaryUpdateDeletedDeleterFirst() {
'uuid' => $uuid->generate(),
// Add a dependency on deleter, to make sure that is synced first.
'dependencies' => array(
'entity' => array($name_deleter),
'config' => array($name_deleter),
)
);
$storage->write($name_deletee, $values_deletee);
......@@ -339,7 +339,7 @@ function testSecondaryUpdateDeletedDeleterFirst() {
// Add a dependency on deleter, to make sure that is synced first. This
// will also be synced after the deletee due to alphabetical ordering.
'dependencies' => array(
'entity' => array($name_deleter),
'config' => array($name_deleter),
)
);
$storage->write($name_other, $values_other);
......@@ -395,7 +395,7 @@ function testSecondaryUpdateDeletedDeleteeFirst() {
'uuid' => $uuid->generate(),
// Add a dependency on deletee, to make sure that is synced first.
'dependencies' => array(
'entity' => array($name_deletee),
'config' => array($name_deletee),
),
);
$storage->write($name_deleter, $values_deleter);
......@@ -444,7 +444,7 @@ function testSecondaryDeletedDeleteeSecond() {
'uuid' => $uuid->generate(),
// Add a dependency on deletee, to make sure this delete is synced first.
'dependencies' => array(
'entity' => array($name_deletee),
'config' => array($name_deletee),
),
);
$storage->write($name_deleter, $values_deleter);
......
......@@ -123,12 +123,12 @@ public static function postDelete(EntityStorageInterface $storage, array $entiti
public function onDependencyRemoval(array $dependencies) {
$changed = FALSE;
$fix_deps = \Drupal::state()->get('config_test.fix_dependencies', array());
foreach ($dependencies['entity'] as $entity) {
foreach ($dependencies['config'] as $entity) {
if (in_array($entity->getConfigDependencyName(), $fix_deps)) {
$key = array_search($entity->getConfigDependencyName(), $this->dependencies['enforced']['entity']);
$key = array_search($entity->getConfigDependencyName(), $this->dependencies['enforced']['config']);
if ($key !== FALSE) {
$changed = TRUE;
unset($this->dependencies['enforced']['entity'][$key]);
unset($this->dependencies['enforced']['config'][$key]);
}
}
}
......
......@@ -101,7 +101,7 @@ public function label() {
public function calculateDependencies() {
parent::calculateDependencies();
// Create a dependency on the associated FilterFormat.
$this->addDependency('entity', $this->getFilterFormat()->getConfigDependencyName());
$this->addDependency('config', $this->getFilterFormat()->getConfigDependencyName());
// @todo use EntityWithPluginCollectionInterface so configuration between
// config entity and dependency on provider is managed automatically.
$definition = $this->editorPluginManager()->createInstance($this->editor)->getPluginDefinition();
......
......@@ -152,7 +152,7 @@ public function testCalculateDependencies() {
$dependencies = $entity->calculateDependencies();
$this->assertContains('test_module', $dependencies['module']);
$this->assertContains('filter.format.test', $dependencies['entity']);
$this->assertContains('filter.format.test', $dependencies['config']);
}
}