diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigDependencyDeleteFormTrait.php b/core/lib/Drupal/Core/Config/Entity/ConfigDependencyDeleteFormTrait.php index cd28d19..1f2660b 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigDependencyDeleteFormTrait.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigDependencyDeleteFormTrait.php @@ -53,8 +53,7 @@ protected function addDependencyListsToForm(array &$form, $type, array $names, C '#type' => 'details', '#title' => $this->t('Configuration updates'), '#description' => $this->t('The listed configuration will be updated.'), - '#collapsible' => TRUE, - '#collapsed' => TRUE, + '#open' => TRUE, '#access' => FALSE, ); @@ -72,7 +71,7 @@ protected function addDependencyListsToForm(array &$form, $type, array $names, C '#items' => array(), ); } - $form['entity_updates'][$entity_type_id]['#items'][] = $entity->label() ?: $entity->id(); + $form['entity_updates'][$entity_type_id]['#items'][$entity->id()] = $entity->label() ?: $entity->id(); } if (!empty($dependent_entities['update'])) { $form['entity_updates']['#access'] = TRUE; @@ -83,7 +82,7 @@ protected function addDependencyListsToForm(array &$form, $type, array $names, C foreach ($entity_types as $entity_type_id => $label) { $form['entity_updates'][$entity_type_id]['#weight'] = $weight; // Sort the list of entity labels alphabetically. - sort($form['entity_updates'][$entity_type_id]['#items'], SORT_FLAG_CASE); + ksort($form['entity_updates'][$entity_type_id]['#items'], SORT_FLAG_CASE); $weight++; } } @@ -92,8 +91,7 @@ protected function addDependencyListsToForm(array &$form, $type, array $names, C '#type' => 'details', '#title' => $this->t('Configuration deletions'), '#description' => $this->t('The listed configuration will be deleted.'), - '#collapsible' => TRUE, - '#collapsed' => TRUE, + '#open' => TRUE, '#access' => FALSE, ); @@ -110,7 +108,7 @@ protected function addDependencyListsToForm(array &$form, $type, array $names, C '#items' => array(), ); } - $form['entity_deletes'][$entity_type_id]['#items'][] = $entity->label() ?: $entity->id(); + $form['entity_deletes'][$entity_type_id]['#items'][$entity->id()] = $entity->label() ?: $entity->id(); } if (!empty($dependent_entities['delete'])) { $form['entity_deletes']['#access'] = TRUE; @@ -119,10 +117,12 @@ protected function addDependencyListsToForm(array &$form, $type, array $names, C asort($entity_types, SORT_FLAG_CASE); $weight = 0; foreach ($entity_types as $entity_type_id => $label) { - $form['entity_deletes'][$entity_type_id]['#weight'] = $weight; - // Sort the list of entity labels alphabetically. - sort($form['entity_deletes'][$entity_type_id]['#items'], SORT_FLAG_CASE); - $weight++; + if (isset($form['entity_deletes'][$entity_type_id])) { + $form['entity_deletes'][$entity_type_id]['#weight'] = $weight; + // Sort the list of entity labels alphabetically. + ksort($form['entity_deletes'][$entity_type_id]['#items'], SORT_FLAG_CASE); + $weight++; + } } } diff --git a/core/lib/Drupal/Core/Entity/EntityDeleteForm.php b/core/lib/Drupal/Core/Entity/EntityDeleteForm.php index 6d0ef79..5ca89c7 100644 --- a/core/lib/Drupal/Core/Entity/EntityDeleteForm.php +++ b/core/lib/Drupal/Core/Entity/EntityDeleteForm.php @@ -34,7 +34,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { if (!($entity instanceof ConfigEntityInterface)) { return $form; } - $this->addDependencyListsToForm($form, $entity->getConfigDependencyKey(), [$entity->getConfigDependencyName()], $this->getConfigManager(), $this->entityManager); + $this->addDependencyListsToForm($form, $entity->getConfigDependencyKey(), $this->getConfigNamesToDelete($entity), $this->getConfigManager(), $this->entityManager); return $form; } @@ -49,4 +49,17 @@ protected function getConfigManager() { return \Drupal::service('config.manager'); } + /** + * Returns config names to delete for the deletion confirmation form. + * + * @param \Drupal\Core\Config\Entity\ConfigEntityInterface $entity + * The entity being deleted. + * + * @return string[] + * A list of configuration names that will be deleted by this form. + */ + protected function getConfigNamesToDelete(ConfigEntityInterface $entity) { + return [$entity->getConfigDependencyName()]; + } + } diff --git a/core/modules/field/tests/modules/field_test_views/test_views/views.view.test_view_field_delete.yml b/core/modules/field/tests/modules/field_test_views/test_views/views.view.test_view_field_delete.yml new file mode 100644 index 0000000..16e9cb1 --- /dev/null +++ b/core/modules/field/tests/modules/field_test_views/test_views/views.view.test_view_field_delete.yml @@ -0,0 +1,52 @@ +langcode: en +status: true +dependencies: + module: + - node + - user + config: + - field.storage.node.field_test +id: test_view_field_delete +label: test_view_field_delete +module: views +description: '' +tag: default +base_table: node +base_field: nid +core: '8' +display: + default: + display_options: + access: + type: perm + fields: + nid: + field: nid + id: nid + table: node + plugin_id: field + entity_type: node + entity_field: nid + field_test: + id: field_test + table: node__field_test + field: field_test + plugin_id: field + entity_type: node + entity_field: field_test + cache: + type: tag + exposed_form: + type: basic + pager: + type: full + query: + type: views_query + style: + type: default + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: 0 diff --git a/core/modules/field_ui/src/Form/FieldConfigDeleteForm.php b/core/modules/field_ui/src/Form/FieldConfigDeleteForm.php index 2aacd88..cb6e473 100644 --- a/core/modules/field_ui/src/Form/FieldConfigDeleteForm.php +++ b/core/modules/field_ui/src/Form/FieldConfigDeleteForm.php @@ -7,9 +7,11 @@ namespace Drupal\field_ui\Form; +use Drupal\Core\Config\Entity\ConfigEntityInterface; use Drupal\Core\Entity\EntityDeleteForm; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Render\Element; use Drupal\field_ui\FieldUI; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -47,6 +49,44 @@ public static function create(ContainerInterface $container) { /** * {@inheritdoc} */ + public function buildForm(array $form, FormStateInterface $form_state) { + $form = parent::buildForm($form, $form_state); + + // If we are adding the field storage as a dependency to delete, then that + // will list ourself as a dependency. That is confusing, so remove it. + // Recursively also remove the entity type and the whole entity deletions + // details element if nothing else is in there. + if (isset($form['entity_deletes']['field_config']['#items']) && isset($form['entity_deletes']['field_config']['#items'][$this->entity->id()])) { + unset($form['entity_deletes']['field_config']['#items'][$this->entity->id()]); + if (empty($form['entity_deletes']['field_config']['#items'])) { + unset($form['entity_deletes']['field_config']); + if (!Element::children($form['entity_deletes'])) { + $form['entity_deletes']['#access'] = FALSE; + } + } + } + return $form; + } + + /** + * {@inheritdoc} + */ + protected function getConfigNamesToDelete(ConfigEntityInterface $entity) { + /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */ + $field_storage = $entity->getFieldStorageDefinition(); + $config_names = [$entity->getConfigDependencyName()]; + + // If there is only one bundle left for this field storage, it will be + // deleted too, notify the user about dependencies. + if (count($field_storage->getBundles()) <= 1) { + $config_names[] = $field_storage->getConfigDependencyName(); + } + return $config_names; + } + + /** + * {@inheritdoc} + */ public function getCancelUrl() { return FieldUI::getOverviewRouteInfo($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle()); } diff --git a/core/modules/field_ui/src/Tests/FieldUIDeleteTest.php b/core/modules/field_ui/src/Tests/FieldUIDeleteTest.php new file mode 100644 index 0000000..6fe6649 --- /dev/null +++ b/core/modules/field_ui/src/Tests/FieldUIDeleteTest.php @@ -0,0 +1,115 @@ +drupalPlaceBlock('system_breadcrumb_block'); + $this->drupalPlaceBlock('local_tasks_block'); + + // Create a test user. + $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer node fields', 'administer node form display', 'administer node display', 'administer users', 'administer account settings', 'administer user display', 'bypass node access')); + $this->drupalLogin($admin_user); + } + + /** + * Tests that deletion removes field storages and fields as expected. + */ + function testDeleteField() { + $field_label = $this->randomMachineName(); + $field_name_input = 'test'; + $field_name = 'field_test'; + + // Create an additional node type. + $type_name1 = strtolower($this->randomMachineName(8)) . '_test'; + $type1 = $this->drupalCreateContentType(array('name' => $type_name1, 'type' => $type_name1)); + $type_name1 = $type1->id(); + + // Create a new field. + $bundle_path1 = 'admin/structure/types/manage/' . $type_name1; + $this->fieldUIAddNewField($bundle_path1, $field_name_input, $field_label); + + // Create an additional node type. + $type_name2 = strtolower($this->randomMachineName(8)) . '_test'; + $type2 = $this->drupalCreateContentType(array('name' => $type_name2, 'type' => $type_name2)); + $type_name2 = $type2->id(); + + // Add a field to the second node type. + $bundle_path2 = 'admin/structure/types/manage/' . $type_name2; + $this->fieldUIAddExistingField($bundle_path2, $field_name, $field_label); + + \Drupal::service('module_installer')->install(['views']); + ViewTestData::createTestViews(get_class($this), array('field_test_views')); + + // Check the config dependencies of the first field, the field storage must + // not be shown as being deleted yet. + $this->drupalGet("$bundle_path1/fields/node.$type_name1.$field_name/delete"); + $this->assertNoText(t('The listed configuration will be deleted.')); + $this->assertNoText(t('View')); + $this->assertNoText('test_view_field_delete'); + + // Delete the first field. + $this->fieldUIDeleteField($bundle_path1, "node.$type_name1.$field_name", $field_label, $type_name1); + + // Check that the field was deleted. + $this->assertNull(FieldConfig::loadByName('node', $type_name1, $field_name), 'Field was deleted.'); + // Check that the field storage was not deleted + $this->assertNotNull(FieldStorageConfig::loadByName('node', $field_name), 'Field storage was not deleted.'); + + // Check the config dependencies of the first field. + $this->drupalGet("$bundle_path2/fields/node.$type_name2.$field_name/delete"); + $this->assertText(t('The listed configuration will be deleted.')); + $this->assertText(t('View')); + $this->assertText('test_view_field_delete'); + + $xml = $this->cssSelect('#edit-entity-deletes'); + // Remove the wrapping HTML. + $this->assertIdentical(FALSE, strpos($xml[0]->asXml(), $field_label), 'The currently being deleted field is not shown in the entity deletions.'); + + // Delete the second field. + $this->fieldUIDeleteField($bundle_path2, "node.$type_name2.$field_name", $field_label, $type_name2); + + // Check that the field was deleted. + $this->assertNull(FieldConfig::loadByName('node', $type_name2, $field_name), 'Field was deleted.'); + // Check that the field storage was deleted too. + $this->assertNull(FieldStorageConfig::loadByName('node', $field_name), 'Field storage was deleted.'); + } + +}