diff --git a/core/modules/field/src/Entity/FieldStorageConfig.php b/core/modules/field/src/Entity/FieldStorageConfig.php index 5fff1aa..a3a1a2d 100644 --- a/core/modules/field/src/Entity/FieldStorageConfig.php +++ b/core/modules/field/src/Entity/FieldStorageConfig.php @@ -23,6 +23,7 @@ * id = "field_storage_config", * label = @Translation("Field storage"), * handlers = { + * "access" = "Drupal\field\FieldStorageConfigAccessControlHandler", * "storage" = "Drupal\field\FieldStorageConfigStorage" * }, * config_prefix = "storage", @@ -136,8 +137,8 @@ class FieldStorageConfig extends ConfigEntityBase implements FieldStorageConfigI * * If TRUE, some actions not available though the UI (but are still possible * through direct API manipulation): - * - field settings cannot be changed, - * - new fields cannot be created + * - field and storage settings can not be changed, + * - new fields cannot be created, * - existing fields cannot be deleted. * Defaults to FALSE. * @@ -794,9 +795,9 @@ public static function loadByName($entity_type_id, $field_name) { */ public function isDeletable() { // The field storage is not deleted, is configured to be removed when there - // are no fields, the field storage has no bundles, and field storages are + // are no fields, the field storage has no bundles, and field storage are // not in the process of being deleted. - return !$this->deleted && !$this->persist_with_no_fields && count($this->getBundles()) == 0 && !static::$inDeletion; + return !$this->deleted && !$this->isLocked() && !$this->persist_with_no_fields && count($this->getBundles()) == 0 && !static::$inDeletion; } /** diff --git a/core/modules/field/src/FieldStorageConfigAccessControlHandler.php b/core/modules/field/src/FieldStorageConfigAccessControlHandler.php new file mode 100644 index 0000000..253a54d --- /dev/null +++ b/core/modules/field/src/FieldStorageConfigAccessControlHandler.php @@ -0,0 +1,38 @@ +getTargetEntityTypeId() . ' fields'); + /** @var \Drupal\field\FieldStorageConfigInterface $entity */ + if ($operation == 'delete'&& !$entity->isDeletable()) { + $result = AccessResult::forbidden(); + } + elseif ($operation == 'update'&& $entity->isLocked()) { + $result = AccessResult::forbidden(); + } + return $result->addCacheableDependency($entity); + } + +} diff --git a/core/modules/field_ui/src/FieldConfigListBuilder.php b/core/modules/field_ui/src/FieldConfigListBuilder.php index d3feac1..ecc2d53 100644 --- a/core/modules/field_ui/src/FieldConfigListBuilder.php +++ b/core/modules/field_ui/src/FieldConfigListBuilder.php @@ -148,8 +148,9 @@ public function buildRow(EntityInterface $field_config) { $row['data'] = $row['data'] + parent::buildRow($field_config); if ($field_storage->isLocked()) { - $row['data']['operations'] = array('data' => array('#markup' => $this->t('Locked'))); - $row['class'][] = 'menu-disabled'; + // Remove the 'delete' and 'storage settings' links when the field is + // locked. + $row['data']['operations']['data']['#links'] = array_diff_key($row['data']['operations']['data']['#links'], array_flip(['delete', 'storage-settings'])); } return $row; diff --git a/core/modules/field_ui/src/Form/FieldConfigEditForm.php b/core/modules/field_ui/src/Form/FieldConfigEditForm.php index 153b8c0..6c6fe58 100644 --- a/core/modules/field_ui/src/Form/FieldConfigEditForm.php +++ b/core/modules/field_ui/src/Form/FieldConfigEditForm.php @@ -45,13 +45,6 @@ public function form(array $form, FormStateInterface $form_state) { )); $form['#title'] = $form_title; - if ($field_storage->isLocked()) { - $form['locked'] = array( - '#markup' => $this->t('The field %field is locked and cannot be edited.', array('%field' => $this->entity->getLabel())), - ); - return $form; - } - // Build the configurable field values. $form['label'] = array( '#type' => 'textfield', diff --git a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php index adafcc7..dcb3593 100644 --- a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php +++ b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php @@ -62,6 +62,17 @@ public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); $field_label = $form_state->get('field_config')->label(); + + // Forbid access to the form if the field is locked. + $field_storage = $form_state->get('field_config')->getFieldStorageDefinition(); + if ($field_storage->isLocked()) { + $form['locked'] = array( + '#markup' => $this->t('The field %field is locked and cannot be edited.', array('%field' => $field_label)), + ); + + return $form; + } + $form['#title'] = $field_label; $form['#prefix'] = '

' . $this->t('These settings apply to the %field field everywhere it is used. These settings impact the way that data is stored in the database and cannot be changed once data has been created.', array('%field' => $field_label)) . '

'; diff --git a/core/modules/field_ui/src/Routing/RouteSubscriber.php b/core/modules/field_ui/src/Routing/RouteSubscriber.php index 8f1b8f1..b03537b 100644 --- a/core/modules/field_ui/src/Routing/RouteSubscriber.php +++ b/core/modules/field_ui/src/Routing/RouteSubscriber.php @@ -79,7 +79,7 @@ protected function alterRoutes(RouteCollection $collection) { $route = new Route( "$path/fields/{field_config}/storage", array('_entity_form' => 'field_storage_config.edit') + $defaults, - array('_permission' => 'administer ' . $entity_type_id . ' fields'), + array('_entity_access' => 'field_storage_config.update'), $options ); $collection->add("entity.field_config.{$entity_type_id}_storage_edit_form", $route); diff --git a/core/modules/field_ui/src/Tests/ManageFieldsTest.php b/core/modules/field_ui/src/Tests/ManageFieldsTest.php index 6872bcf..28e9b33 100644 --- a/core/modules/field_ui/src/Tests/ManageFieldsTest.php +++ b/core/modules/field_ui/src/Tests/ManageFieldsTest.php @@ -525,16 +525,32 @@ function testLockedField() { )) ->save(); - // Check that the links for edit and delete are not present. $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields'); - $locked = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field_name)); - $this->assertTrue(in_array('Locked', $locked), 'Field is marked as Locked in the UI'); - $edit_link = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field_name)); - $this->assertFalse(in_array('edit', $edit_link), 'Edit option for locked field is not present the UI'); - $delete_link = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field_name)); - $this->assertFalse(in_array('delete', $delete_link), 'Delete option for locked field is not present the UI'); - $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/node.' . $this->contentType . '.' . $field_name . '/delete'); + // Check that the link for edit is present. + $edit_link_path = 'admin/structure/types/manage/' . $this->contentType . '/fields/node.' . $this->contentType . '.' . $field_name; + $edit_link = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[contains(@href, :path)]', array(':path' => $edit_link_path)); + $this->assertEqual(count($edit_link), 1, 'Edit option for locked field is present in the UI'); + + // Check that the links for delete and storage settings are not present. + $delete_link_path = $edit_link_path . '/delete'; + $delete_link = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[contains(@href, :path)]', array(':path' => $delete_link_path)); + $this->assertEqual(count($delete_link), 0, 'Delete option for locked field is not present in the UI'); + $storage_link_path = $edit_link_path . '/storage'; + $storage_link = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[contains(@href, :path)]', array(':path' => $storage_link_path)); + $this->assertEqual(count($storage_link), 0, 'Storage option for locked field is not present in the UI'); + + $this->drupalGet($edit_link_path); + $this->assertResponse(200); + // Make sure the label field is available even when the field is locked. + $this->assertField('label', 'The label field is available for the locked field.'); + $this->drupalGet($delete_link_path); $this->assertResponse(403); + $this->drupalGet($storage_link_path); + $this->assertResponse(200); + // Make sure the cardinality field is not available once the field is + // locked. + $this->assertNoField('cardinality', 'The cardinality field is not available for the locked field.'); + $this->assertRaw(SafeMarkup::format('The field %field_name is locked and cannot be edited.', array('%field_name' => $field_name))); } /**