Issue #2172843 by fgm, marthinal, pfrenssen, amateescu, andypost, Wim Leers, stefan.r, pwieck, deepak_123: Remove ability to update entity bundle machine names

8.0.x
Nathaniel Catchpole 2015-10-01 19:43:05 +01:00
parent 10626d87a3
commit 03bd7fc75b
28 changed files with 126 additions and 676 deletions

View File

@ -7,8 +7,7 @@
namespace Drupal\Core\Config\Entity;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Config\ConfigNameException;
use Drupal\Core\Entity\EntityStorageInterface;
/**
@ -19,31 +18,6 @@ use Drupal\Core\Entity\EntityStorageInterface;
*/
abstract class ConfigEntityBundleBase extends ConfigEntityBase {
/**
* Renames displays when a bundle is renamed.
*/
protected function renameDisplays() {
// Rename entity displays.
if ($this->getOriginalId() !== $this->id()) {
foreach ($this->loadDisplays('entity_view_display') as $display) {
$new_id = $this->getEntityType()->getBundleOf() . '.' . $this->id() . '.' . $display->getMode();
$display->set('id', $new_id);
$display->setTargetBundle($this->id());
$display->save();
}
}
// Rename entity form displays.
if ($this->getOriginalId() !== $this->id()) {
foreach ($this->loadDisplays('entity_form_display') as $form_display) {
$new_id = $this->getEntityType()->getBundleOf() . '.' . $this->id() . '.' . $form_display->getMode();
$form_display->set('id', $new_id);
$form_display->setTargetBundle($this->id());
$form_display->save();
}
}
}
/**
* Deletes display if a bundle is deleted.
*/
@ -80,12 +54,6 @@ abstract class ConfigEntityBundleBase extends ConfigEntityBase {
}
// Entity bundle field definitions may depend on bundle settings.
$entity_manager->clearCachedFieldDefinitions();
if ($this->getOriginalId() != $this->id()) {
// If the entity was renamed, update the displays.
$this->renameDisplays();
$entity_manager->onBundleRename($this->getOriginalId(), $this->id(), $bundle_of);
}
}
}
@ -101,6 +69,33 @@ abstract class ConfigEntityBundleBase extends ConfigEntityBase {
}
}
/**
* Acts on an entity before the presave hook is invoked.
*
* Used before the entity is saved and before invoking the presave hook.
*
* Ensure that config entities which are bundles of other entities cannot have
* their ID changed.
*
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* The entity storage object.
*
* @throws \Drupal\Core\Config\ConfigNameException
* Thrown when attempting to rename a bundle entity.
*/
public function preSave(EntityStorageInterface $storage) {
parent::preSave($storage);
// Only handle renames, not creations.
if (!$this->isNew() && $this->getOriginalId() !== $this->id()) {
$bundle_type = $this->getEntityType();
$bundle_of = $bundle_type->getBundleOf();
if (!empty($bundle_of)) {
throw new ConfigNameException("The machine name of the '{$bundle_type->getLabel()}' bundle cannot be changed.");
}
}
}
/**
* Returns view or form displays for this bundle.
*

View File

@ -0,0 +1,41 @@
<?php
/**
* @file
* Contains \Drupal\Core\Entity\BundleEntityFormBase.
*/
namespace Drupal\Core\Entity;
/**
* Class BundleEntityFormBase is a base form for bundle config entities.
*/
class BundleEntityFormBase extends EntityForm {
/**
* Protects the bundle entity's ID property's form element against changes.
*
* This method is assumed to be called on a completely built entity form,
* including a form element for the bundle config entity's ID property.
*
* @param array $form
* The completely built entity bundle form array.
*
* @return array
* The updated entity bundle form array.
*/
protected function protectBundleIdElement(array $form) {
$entity = $this->getEntity();
$id_key = $entity->getEntityType()->getKey('id');
assert('isset($form[$id_key])');
$element = &$form[$id_key];
// Make sure the element is not accidentally re-enabled if it has already
// been disabled.
if (empty($element['#disabled'])) {
$element['#disabled'] = !$entity->isNew();
}
return $form;
}
}

View File

@ -8,7 +8,7 @@
namespace Drupal\Core\Entity;
/**
* An interface for reacting to entity bundle creation, deletion, and renames.
* An interface for reacting to entity bundle creation and deletion.
*
* @todo Convert to Symfony events: https://www.drupal.org/node/2332935
*/
@ -24,20 +24,6 @@ interface EntityBundleListenerInterface {
*/
public function onBundleCreate($bundle, $entity_type_id);
/**
* Reacts to a bundle being renamed.
*
* This method runs before fields are updated with the new bundle name.
*
* @param string $bundle
* The name of the bundle being renamed.
* @param string $bundle_new
* The new name of the bundle.
* @param string $entity_type_id
* The entity type to which the bundle is bound; e.g. 'node' or 'user'.
*/
public function onBundleRename($bundle, $bundle_new, $entity_type_id);
/**
* Reacts to a bundle being deleted.
*

View File

@ -1367,31 +1367,6 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
$this->moduleHandler->invokeAll('entity_bundle_create', array($entity_type_id, $bundle));
}
/**
* {@inheritdoc}
*/
public function onBundleRename($bundle_old, $bundle_new, $entity_type_id) {
$this->clearCachedBundles();
// Notify the entity storage.
$storage = $this->getStorage($entity_type_id);
if ($storage instanceof EntityBundleListenerInterface) {
$storage->onBundleRename($bundle_old, $bundle_new, $entity_type_id);
}
// Rename existing base field bundle overrides.
$overrides = $this->getStorage('base_field_override')->loadByProperties(array('entity_type' => $entity_type_id, 'bundle' => $bundle_old));
foreach ($overrides as $override) {
$override->set('id', $entity_type_id . '.' . $bundle_new . '.' . $override->getName());
$override->set('bundle', $bundle_new);
$override->allowBundleRename();
$override->save();
}
// Invoke hook_entity_bundle_rename() hook.
$this->moduleHandler->invokeAll('entity_bundle_rename', array($entity_type_id, $bundle_old, $bundle_new));
$this->clearCachedFieldDefinitions();
}
/**
* {@inheritdoc}
*/

View File

@ -1493,40 +1493,6 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
*/
public function onBundleDelete($bundle, $entity_type_id) { }
/**
* {@inheritdoc}
*/
public function onBundleRename($bundle, $bundle_new, $entity_type_id) {
// The method runs before the field definitions are updated, so we use the
// old bundle name.
$field_definitions = $this->entityManager->getFieldDefinitions($this->entityTypeId, $bundle);
// We need to handle deleted fields too. For now, this only makes sense for
// configurable fields, so we use the specific API.
// @todo Use the unified store of deleted field definitions instead in
// https://www.drupal.org/node/2282119
$field_definitions += entity_load_multiple_by_properties('field_config', array('entity_type' => $this->entityTypeId, 'bundle' => $bundle, 'deleted' => TRUE, 'include_deleted' => TRUE));
$table_mapping = $this->getTableMapping();
foreach ($field_definitions as $field_definition) {
$storage_definition = $field_definition->getFieldStorageDefinition();
if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) {
$is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
$table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted);
$revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, $is_deleted);
$this->database->update($table_name)
->fields(array('bundle' => $bundle_new))
->condition('bundle', $bundle)
->execute();
if ($this->entityType->isRevisionable()) {
$this->database->update($revision_name)
->fields(array('bundle' => $bundle_new))
->condition('bundle', $bundle)
->execute();
}
}
}
}
/**
* {@inheritdoc}
*/

View File

@ -139,8 +139,8 @@ use Drupal\node\Entity\NodeType;
* - Field configuration preSave(): hook_field_storage_config_update_forbid()
* - Node postSave(): hook_node_access_records() and
* hook_node_access_records_alter()
* - Config entities that are acting as entity bundles, in postSave():
* hook_entity_bundle_create() or hook_entity_bundle_rename() as appropriate
* - Config entities that are acting as entity bundles in postSave():
* hook_entity_bundle_create()
* - Comment: hook_comment_publish() and hook_comment_unpublish() as
* appropriate.
*
@ -350,6 +350,7 @@ use Drupal\node\Entity\NodeType;
* 'bundle_entity_type' on the \Drupal\node\Entity\Node class. Also, the
* bundle config entity type annotation must have a 'bundle_of' entry,
* giving the machine name of the entity type it is acting as a bundle for.
* These machine names are considered permanent, they may not be renamed.
* - Additional annotations can be seen on entity class examples such as
* \Drupal\node\Entity\Node (content) and \Drupal\user\Entity\Role
* (configuration). These annotations are documented on
@ -740,31 +741,6 @@ function hook_entity_bundle_create($entity_type_id, $bundle) {
\Drupal::service('router.builder')->setRebuildNeeded();
}
/**
* Act on entity_bundle_rename().
*
* This hook is invoked after the operation has been performed.
*
* @param string $entity_type_id
* The entity type to which the bundle is bound.
* @param string $bundle_old
* The previous name of the bundle.
* @param string $bundle_new
* The new name of the bundle.
*
* @see entity_crud
*/
function hook_entity_bundle_rename($entity_type_id, $bundle_old, $bundle_new) {
// Update the settings associated with the bundle in my_module.settings.
$config = \Drupal::config('my_module.settings');
$bundle_settings = $config->get('bundle_settings');
if (isset($bundle_settings[$entity_type_id][$bundle_old])) {
$bundle_settings[$entity_type_id][$bundle_new] = $bundle_settings[$entity_type_id][$bundle_old];
unset($bundle_settings[$entity_type_id][$bundle_old]);
$config->set('bundle_settings', $bundle_settings);
}
}
/**
* Act on entity_bundle_delete().
*

View File

@ -163,8 +163,7 @@ class BaseFieldOverride extends FieldConfigBase {
* {@inheritdoc}
*
* @throws \Drupal\Core\Field\FieldException
* If the bundle is being changed and
* BaseFieldOverride::allowBundleRename() has not been called.
* If the bundle is being changed.
*/
public function preSave(EntityStorageInterface $storage) {
// Filter out unknown settings and make sure all settings are present, so
@ -189,7 +188,7 @@ class BaseFieldOverride extends FieldConfigBase {
if ($this->entity_type != $this->original->entity_type) {
throw new FieldException("Cannot change the entity_type of an existing base field bundle override (entity type:{$this->entity_type}, bundle:{$this->original->bundle}, field name: {$this->field_name})");
}
if ($this->bundle != $this->original->bundle && empty($this->bundleRenameAllowed)) {
if ($this->bundle != $this->original->bundle) {
throw new FieldException("Cannot change the bundle of an existing base field bundle override (entity type:{$this->entity_type}, bundle:{$this->original->bundle}, field name: {$this->field_name})");
}
$previous_definition = $this->original;

View File

@ -179,13 +179,6 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
*/
protected $itemDefinition;
/**
* Flag indicating whether the bundle name can be renamed or not.
*
* @var bool
*/
protected $bundleRenameAllowed = FALSE;
/**
* Array of constraint options keyed by constraint plugin ID.
*
@ -456,7 +449,7 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
// Only serialize necessary properties, excluding those that can be
// recalculated.
$properties = get_object_vars($this);
unset($properties['fieldStorage'], $properties['itemDefinition'], $properties['bundleRenameAllowed'], $properties['original']);
unset($properties['fieldStorage'], $properties['itemDefinition'], $properties['original']);
return array_keys($properties);
}
@ -528,13 +521,6 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
return $this->itemDefinition;
}
/**
* {@inheritdoc}
*/
public function allowBundleRename() {
$this->bundleRenameAllowed = TRUE;
}
/**
* {@inheritdoc}
*/

View File

@ -282,13 +282,4 @@ interface FieldConfigInterface extends FieldDefinitionInterface, ConfigEntityInt
*/
public function setConstraints(array $constraints);
/**
* Allows a bundle to be renamed.
*
* Renaming a bundle on the instance is allowed when an entity's bundle
* is renamed and when field_entity_bundle_rename() does internal
* housekeeping.
*/
public function allowBundleRename();
}

View File

@ -7,7 +7,7 @@
namespace Drupal\block_content;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\BundleEntityFormBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\language\Entity\ContentLanguageSettings;
@ -15,7 +15,7 @@ use Drupal\language\Entity\ContentLanguageSettings;
/**
* Base form for category edit forms.
*/
class BlockContentTypeForm extends EntityForm {
class BlockContentTypeForm extends BundleEntityFormBase {
/**
* {@inheritdoc}
@ -48,7 +48,6 @@ class BlockContentTypeForm extends EntityForm {
'exists' => '\Drupal\block_content\Entity\BlockContentType::load',
),
'#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
'#disabled' => !$block_type->isNew(),
);
$form['description'] = array(
@ -62,7 +61,7 @@ class BlockContentTypeForm extends EntityForm {
'#type' => 'checkbox',
'#title' => t('Create new revision'),
'#default_value' => $block_type->shouldCreateNewRevision(),
'#description' => t('Create a new revision by default for this block type.')
'#description' => t('Create a new revision by default for this block type.'),
);
if ($this->moduleHandler->moduleExists('language')) {
@ -91,7 +90,7 @@ class BlockContentTypeForm extends EntityForm {
'#value' => t('Save'),
);
return $form;
return $this->protectBundleIdElement($form);
}
/**

View File

@ -497,99 +497,6 @@ class BookTest extends WebTestBase {
$this->assertTrue(empty($node->book), 'Deleting childless top-level book node properly allowed.');
}
/*
* Tests node type changing machine name when type is a book allowed type.
*/
function testBookNodeTypeChange() {
$this->drupalLogin($this->adminUser);
// Change the name, machine name and description.
$edit = array(
'name' => 'Bar',
'type' => 'bar',
);
$this->drupalPostForm('admin/structure/types/manage/book', $edit, t('Save content type'));
// Ensure that the config book.settings:allowed_types has been updated with
// the new machine and the old one has been removed.
$this->assertTrue(book_type_is_allowed('bar'), 'Config book.settings:allowed_types contains the updated node type machine name "bar".');
$this->assertFalse(book_type_is_allowed('book'), 'Config book.settings:allowed_types does not contain the old node type machine name "book".');
$edit = array(
'name' => 'Basic page',
'title_label' => 'Title for basic page',
'type' => 'page',
);
$this->drupalPostForm('admin/structure/types/add', $edit, t('Save content type'));
// Add page to the allowed node types.
$edit = array(
'book_allowed_types[page]' => 'page',
'book_allowed_types[bar]' => 'bar',
);
$this->drupalPostForm('admin/structure/book/settings', $edit, t('Save configuration'));
$this->assertTrue(book_type_is_allowed('bar'), 'Config book.settings:allowed_types contains the bar node type.');
$this->assertTrue(book_type_is_allowed('page'), 'Config book.settings:allowed_types contains the page node type.');
// Test the order of the book.settings::allowed_types configuration is as
// expected. The point of this test is to prove that after changing a node
// type going to admin/structure/book/settings and pressing save without
// changing anything should not alter the book.settings configuration. The
// order will be:
// @code
// array(
// 'bar',
// 'page',
// );
// @endcode
$current_config = $this->config('book.settings')->get();
$this->drupalPostForm('admin/structure/book/settings', array(), t('Save configuration'));
$this->assertIdentical($current_config, $this->config('book.settings')->get());
// Change the name, machine name and description.
$edit = array(
'name' => 'Zebra book',
'type' => 'zebra',
);
$this->drupalPostForm('admin/structure/types/manage/bar', $edit, t('Save content type'));
$this->assertTrue(book_type_is_allowed('zebra'), 'Config book.settings:allowed_types contains the zebra node type.');
$this->assertTrue(book_type_is_allowed('page'), 'Config book.settings:allowed_types contains the page node type.');
// Test the order of the book.settings::allowed_types configuration is as
// expected. The order should be:
// @code
// array(
// 'page',
// 'zebra',
// );
// @endcode
$current_config = $this->config('book.settings')->get();
$this->drupalPostForm('admin/structure/book/settings', array(), t('Save configuration'));
$this->assertIdentical($current_config, $this->config('book.settings')->get());
$edit = array(
'name' => 'Animal book',
'type' => 'zebra',
);
$this->drupalPostForm('admin/structure/types/manage/zebra', $edit, t('Save content type'));
// Test the order of the book.settings::allowed_types configuration is as
// expected. The order should be:
// @code
// array(
// 'page',
// 'zebra',
// );
// @endcode
$current_config = $this->config('book.settings')->get();
$this->drupalPostForm('admin/structure/book/settings', array(), t('Save configuration'));
$this->assertIdentical($current_config, $this->config('book.settings')->get());
// Ensure that after all the node type changes book.settings:child_type has
// the expected value.
$this->assertEqual($this->config('book.settings')->get('child_type'), 'zebra');
}
/**
* Tests re-ordering of books.
*/

View File

@ -186,30 +186,6 @@ function field_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundl
}
}
/**
* Implements hook_entity_bundle_rename().
*/
function field_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) {
$fields = entity_load_multiple_by_properties('field_config', array('entity_type' => $entity_type, 'bundle' => $bundle_old, 'include_deleted' => TRUE));
foreach ($fields as $field) {
$id_new = $field->getTargetEntityTypeId() . '.' . $bundle_new . '.' . $field->getName();
$field->set('id', $id_new);
$field->set('bundle', $bundle_new);
// Save non-deleted fields.
if (!$field->isDeleted()) {
$field->allowBundleRename();
$field->save();
}
// Update deleted fields directly in the state storage.
else {
$state = \Drupal::state();
$deleted_fields = $state->get('field.field.deleted') ?: array();
$deleted_fields[$field->uuid] = $field->toArray();
$state->set('field.field.deleted', $deleted_fields);
}
}
}
/**
* Implements hook_entity_bundle_delete().
*

View File

@ -167,7 +167,7 @@ class FieldConfig extends FieldConfigBase implements FieldConfigInterface {
if ($this->entity_type != $this->original->entity_type) {
throw new FieldException("Cannot change an existing field's entity_type.");
}
if ($this->bundle != $this->original->bundle && empty($this->bundleRenameAllowed)) {
if ($this->bundle != $this->original->bundle) {
throw new FieldException("Cannot change an existing field's bundle.");
}
if ($storage_definition->uuid() != $this->original->getFieldStorageDefinition()->uuid()) {

View File

@ -275,9 +275,9 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
}
/**
* Test entity_bundle_create() and entity_bundle_rename().
* Test entity_bundle_create().
*/
function testEntityCreateRenameBundle() {
function testEntityCreateBundle() {
$entity_type = 'entity_test_rev';
$this->createFieldWithStorage('', $entity_type);
$cardinality = $this->fieldTestData->field_storage->getCardinality();
@ -298,20 +298,6 @@ class FieldAttachStorageTest extends FieldUnitTestBase {
// Verify the field data is present on load.
$entity = $this->entitySaveReload($entity);
$this->assertEqual(count($entity->{$this->fieldTestData->field_name}), $cardinality, "Data is retrieved for the new bundle");
// Rename the bundle.
$new_bundle = 'test_bundle_' . Unicode::strtolower($this->randomMachineName());
entity_test_rename_bundle($this->fieldTestData->field_definition['bundle'], $new_bundle, $entity_type);
// Check that the field definition has been updated.
$this->fieldTestData->field = FieldConfig::loadByName($entity_type, $new_bundle, $this->fieldTestData->field_name);
$this->assertIdentical($this->fieldTestData->field->getTargetBundle(), $new_bundle, "Bundle name has been updated in the field.");
// Verify the field data is present on load.
$controller = $this->container->get('entity.manager')->getStorage($entity->getEntityTypeId());
$controller->resetCache();
$entity = $controller->load($entity->id());
$this->assertEqual(count($entity->{$this->fieldTestData->field_name}), $cardinality, "Bundle name has been updated in the field storage");
}
/**

View File

@ -107,15 +107,6 @@ function field_ui_entity_bundle_create($entity_type, $bundle) {
\Drupal::service('router.builder')->setRebuildNeeded();
}
/**
* Implements hook_entity_bundle_rename().
*/
function field_ui_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) {
// When a bundle is renamed, the menu needs to be rebuilt to add our
// menu item tabs.
\Drupal::service('router.builder')->setRebuildNeeded();
}
/**
* Implements hook_form_FORM_ID_alter().
*

View File

@ -300,9 +300,9 @@ class EntityDisplayTest extends KernelTestBase {
}
/**
* Tests renaming and deleting a bundle.
* Tests deleting a bundle.
*/
public function testRenameDeleteBundle() {
public function testDeleteBundle() {
// Create a node bundle, display and form display object.
$type = NodeType::create(array('type' => 'article'));
$type->save();
@ -310,44 +310,11 @@ class EntityDisplayTest extends KernelTestBase {
entity_get_display('node', 'article', 'default')->save();
entity_get_form_display('node', 'article', 'default')->save();
// Rename the article bundle and assert the entity display is renamed.
$type->old_type = 'article';
$type->set('type', 'article_rename');
$type->save();
$old_display = entity_load('entity_view_display', 'node.article.default');
$this->assertFalse((bool) $old_display);
$old_form_display = entity_load('entity_form_display', 'node.article.default');
$this->assertFalse((bool) $old_form_display);
$new_display = entity_load('entity_view_display', 'node.article_rename.default');
$this->assertEqual('article_rename', $new_display->getTargetBundle());
$this->assertEqual('node.article_rename.default', $new_display->id());
$new_form_display = entity_load('entity_form_display', 'node.article_rename.default');
$this->assertEqual('article_rename', $new_form_display->getTargetBundle());
$this->assertEqual('node.article_rename.default', $new_form_display->id());
$expected_view_dependencies = array(
'config' => array('field.field.node.article_rename.body', 'node.type.article_rename'),
'module' => array('entity_test', 'text', 'user')
);
// Check that the display has dependencies on the bundle, fields and the
// modules that provide the formatters.
$dependencies = $new_display->calculateDependencies();
$this->assertEqual($expected_view_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();
$expected_form_dependencies = array(
'config' => array('field.field.node.article_rename.body', 'node.type.article_rename'),
'module' => array('text')
);
$this->assertEqual($expected_form_dependencies, $dependencies);
// Delete the bundle.
$type->delete();
$display = entity_load('entity_view_display', 'node.article_rename.default');
$display = entity_load('entity_view_display', 'node.article.default');
$this->assertFalse((bool) $display);
$form_display = entity_load('entity_form_display', 'node.article_rename.default');
$form_display = entity_load('entity_form_display', 'node.article.default');
$this->assertFalse((bool) $form_display);
}

View File

@ -590,19 +590,6 @@ class ManageFieldsTest extends WebTestBase {
}
}
/**
* Tests renaming a bundle.
*/
function testRenameBundle() {
$type2 = strtolower($this->randomMachineName(8)) . '_test';
$options = array(
'type' => $type2,
);
$this->drupalPostForm('admin/structure/types/manage/' . $this->contentType, $options, t('Save content type'));
$this->manageFieldsPage($type2);
}
/**
* Tests that a duplicate field name is caught by validation.
*/

View File

@ -217,15 +217,6 @@ function language_configuration_element_submit(&$form, FormStateInterface $form_
}
}
/**
* Implements hook_entity_bundle_rename().
*/
function language_entity_bundle_rename($entity_type_id, $bundle_old, $bundle_new) {
ContentLanguageSettings::loadByEntityTypeBundle($entity_type_id, $bundle_old)
->setTargetBundle($bundle_new)
->save();
}
/**
* Implements hook_entity_bundle_delete().
*/

View File

@ -152,7 +152,7 @@ class LanguageConfigurationElementTest extends WebTestBase {
}
/**
* Tests that the configuration is updated when the node type is changed.
* Tests that the configuration is retained when the node type is updated.
*/
public function testNodeTypeUpdate() {
// Create the article content type first if the profile used is not the
@ -172,16 +172,16 @@ class LanguageConfigurationElementTest extends WebTestBase {
$uuid = $configuration->uuid();
$this->assertEqual($configuration->getDefaultLangcode(), 'current_interface', 'The default language configuration has been saved on the Article content type.');
$this->assertTrue($configuration->isLanguageAlterable(), 'The alterable language configuration has been saved on the Article content type.');
// Rename the article content type.
// Update the article content type by changing the title label.
$edit = array(
'type' => 'article_2'
'title_label' => 'Name'
);
$this->drupalPostForm('admin/structure/types/manage/article', $edit, t('Save content type'));
// Check that we still have the settings for the new node type.
$configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', 'article_2');
$this->assertEqual($configuration->getDefaultLangcode(), 'current_interface', 'The default language configuration has been kept on the new Article content type.');
$this->assertTrue($configuration->isLanguageAlterable(), 'The alterable language configuration has been kept on the new Article content type.');
$this->assertEqual($configuration->uuid(), $uuid, 'The language configuration uuid has been kept on the new Article content type.');
// Check that we still have the settings for the updated node type.
$configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', 'article');
$this->assertEqual($configuration->getDefaultLangcode(), 'current_interface', 'The default language configuration has been kept on the updated Article content type.');
$this->assertTrue($configuration->isLanguageAlterable(), 'The alterable language configuration has been kept on the updated Article content type.');
$this->assertEqual($configuration->uuid(), $uuid, 'The language configuration uuid has been kept on the updated Article content type.');
}
/**
@ -220,7 +220,7 @@ class LanguageConfigurationElementTest extends WebTestBase {
}
/**
* Tests that the configuration is updated when a vocabulary is changed.
* Tests that the configuration is retained when a vocabulary is updated.
*/
public function testTaxonomyVocabularyUpdate() {
$vocabulary = entity_create('taxonomy_vocabulary', array(
@ -242,16 +242,16 @@ class LanguageConfigurationElementTest extends WebTestBase {
$uuid = $configuration->uuid();
$this->assertEqual($configuration->getDefaultLangcode(), 'current_interface', 'The default language configuration has been saved on the Country vocabulary.');
$this->assertTrue($configuration->isLanguageAlterable(), 'The alterable language configuration has been saved on the Country vocabulary.');
// Rename the vocabulary.
// Update the vocabulary.
$edit = array(
'vid' => 'nation'
'name' => 'Nation'
);
$this->drupalPostForm('admin/structure/taxonomy/manage/country', $edit, t('Save'));
// Check that we still have the settings for the new vocabulary.
$configuration = ContentLanguageSettings::loadByEntityTypeBundle('taxonomy_term', 'nation');
$this->assertEqual($configuration->getDefaultLangcode(), 'current_interface', 'The default language configuration has been kept on the new Country vocabulary.');
$this->assertTrue($configuration->isLanguageAlterable(), 'The alterable language configuration has been kept on the new Country vocabulary.');
$this->assertEqual($configuration->uuid(), $uuid, 'The language configuration uuid has been kept on the new Country vocabulary.');
// Check that we still have the settings for the updated vocabulary.
$configuration = ContentLanguageSettings::loadByEntityTypeBundle('taxonomy_term', 'country');
$this->assertEqual($configuration->getDefaultLangcode(), 'current_interface', 'The default language configuration has been kept on the updated Country vocabulary.');
$this->assertTrue($configuration->isLanguageAlterable(), 'The alterable language configuration has been kept on the updated Country vocabulary.');
$this->assertEqual($configuration->uuid(), $uuid, 'The language configuration uuid has been kept on the updated Country vocabulary.');
}
}

View File

@ -7,18 +7,17 @@
namespace Drupal\node;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\BundleEntityFormBase;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\language\Entity\ContentLanguageSettings;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Form controller for node type forms.
*/
class NodeTypeForm extends EntityForm {
class NodeTypeForm extends BundleEntityFormBase {
/**
* The entity manager.
@ -31,7 +30,7 @@ class NodeTypeForm extends EntityForm {
* Constructs the NodeTypeForm object.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager
* The entity manager.
*/
public function __construct(EntityManagerInterface $entity_manager) {
$this->entityManager = $entity_manager;
@ -148,7 +147,8 @@ class NodeTypeForm extends EntityForm {
// Prepare workflow options to be used for 'checkboxes' form element.
$keys = array_keys(array_filter($workflow_options));
$workflow_options = array_combine($keys, $keys);
$form['workflow']['options'] = array('#type' => 'checkboxes',
$form['workflow']['options'] = array(
'#type' => 'checkboxes',
'#title' => t('Default options'),
'#default_value' => $workflow_options,
'#options' => array(
@ -187,7 +187,8 @@ class NodeTypeForm extends EntityForm {
'#default_value' => $type->displaySubmitted(),
'#description' => t('Author username and publish date will be displayed.'),
);
return $form;
return $this->protectBundleIdElement($form);
}
/**
@ -247,7 +248,7 @@ class NodeTypeForm extends EntityForm {
// @todo Make it possible to get default values without an entity.
// https://www.drupal.org/node/2318187
$node = $this->entityManager->getStorage('node')->create(array('type' => $type->id()));
foreach (array('status', 'promote', 'sticky') as $field_name) {
foreach (array('status', 'promote', 'sticky') as $field_name) {
$value = (bool) $form_state->getValue(['options', $field_name]);
if ($node->$field_name->value != $value) {
$fields[$field_name]->getConfig($type->id())->setDefaultValue($value)->save();

View File

@ -1,145 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\node\Tests\NodeTypeRenameConfigImportTest.
*/
namespace Drupal\node\Tests;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\Entity\ConfigEntityStorage;
use Drupal\simpletest\WebTestBase;
use Drupal\node\Entity\NodeType;
/**
* Tests importing renamed node type via configuration synchronization.
*
* @group node
*/
class NodeTypeRenameConfigImportTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('node', 'text', 'config');
/**
* A normal logged in user.
*
* @var \Drupal\user\UserInterface
*/
protected $webUser;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->webUser = $this->drupalCreateUser(array('synchronize configuration'));
$this->drupalLogin($this->webUser);
}
/**
* Tests configuration renaming.
*/
public function testConfigurationRename() {
$content_type = $this->drupalCreateContentType(array(
'type' => Unicode::strtolower($this->randomMachineName(16)),
'name' => $this->randomMachineName(),
));
$staged_type = $content_type->id();
// Check the default status value for a node of this type.
$node = entity_create('node', array('type' => $staged_type));
$this->assertTrue($node->status->value, 'Node status defaults to TRUE.');
// Override a core base field.
$fields = \Drupal::entityManager()->getFieldDefinitions($content_type->getEntityType()->getBundleOf(), $content_type->id());
$fields['status']->getConfig($content_type->id())->setDefaultValue(FALSE)->save();
$active = $this->container->get('config.storage');
$staging = $this->container->get('config.storage.staging');
$config_name = $content_type->getEntityType()->getConfigPrefix() . '.' . $content_type->id();
// Emulate a staging operation.
$this->copyConfig($active, $staging);
// Change the machine name of the content type.
$content_type->set('type', Unicode::strtolower($this->randomMachineName(8)));
$content_type->save();
$active_type = $content_type->id();
// Ensure the base field override has been renamed and the value is correct.
$node = entity_create('node', array('type' => $active_type));
$this->assertFalse($node->status->value, 'Node status defaults to FALSE.');
$renamed_config_name = $content_type->getEntityType()->getConfigPrefix() . '.' . $content_type->id();
$this->assertTrue($active->exists($renamed_config_name), 'The content type has the new name in the active store.');
$this->assertFalse($active->exists($config_name), "The content type's old name does not exist active store.");
$this->configImporter()->reset();
$this->assertEqual(0, count($this->configImporter()->getUnprocessedConfiguration('create')), 'There are no configuration items to create.');
$this->assertEqual(0, count($this->configImporter()->getUnprocessedConfiguration('delete')), 'There are no configuration items to delete.');
$this->assertEqual(0, count($this->configImporter()->getUnprocessedConfiguration('update')), 'There are no configuration items to update.');
// We expect that changing the machine name of the content type will
// rename five configuration entities: the node type, the body field
// instance, two entity form displays, and the entity view display.
// @see \Drupal\node\Entity\NodeType::postSave()
$expected = array(
'node.type.' . $active_type . '::node.type.' . $staged_type,
'core.base_field_override.node.' . $active_type . '.status::core.base_field_override.node.' . $staged_type . '.status',
'core.entity_form_display.node.' . $active_type . '.default::core.entity_form_display.node.' . $staged_type . '.default',
'core.entity_view_display.node.' . $active_type . '.default::core.entity_view_display.node.' . $staged_type . '.default',
'core.entity_view_display.node.' . $active_type . '.teaser::core.entity_view_display.node.' . $staged_type . '.teaser',
'field.field.node.' . $active_type . '.body::field.field.node.' . $staged_type . '.body',
);
$renames = $this->configImporter()->getUnprocessedConfiguration('rename');
$this->assertIdentical($expected, $renames);
$this->drupalGet('admin/config/development/configuration');
foreach ($expected as $rename) {
$names = $this->configImporter()->getStorageComparer()->extractRenameNames($rename);
$this->assertText(SafeMarkup::format('@source_name to @target_name', array('@source_name' => $names['old_name'], '@target_name' => $names['new_name'])));
// Test that the diff link is present for each renamed item.
$href = \Drupal::urlGenerator()->getPathFromRoute('config.diff', array('source_name' => $names['old_name'], 'target_name' => $names['new_name']));
$this->assertLinkByHref($href);
$hrefs[$rename] = $href;
}
// Ensure that the diff works for each renamed item.
foreach ($hrefs as $rename => $href) {
$this->drupalGet($href);
$names = $this->configImporter()->getStorageComparer()->extractRenameNames($rename);
$config_entity_type = \Drupal::service('config.manager')->getEntityTypeIdByName($names['old_name']);
$entity_type = \Drupal::entityManager()->getDefinition($config_entity_type);
$old_id = ConfigEntityStorage::getIDFromConfigName($names['old_name'], $entity_type->getConfigPrefix());
$new_id = ConfigEntityStorage::getIDFromConfigName($names['new_name'], $entity_type->getConfigPrefix());
// Because table columns can be on multiple lines, need to assert a regex
// pattern rather than normal text.
$id_key = $entity_type->getKey('id');
$text = "$id_key: $old_id";
$this->assertTextPattern('/\-\s+' . preg_quote($text, '/') . '/', "'-$text' found.");
$text = "$id_key: $new_id";
$this->assertTextPattern('/\+\s+' . preg_quote($text, '/') . '/', "'+$text' found.");
}
// Run the import.
$this->drupalPostForm('admin/config/development/configuration', array(), t('Import all'));
$this->assertText(t('There are no configuration changes to import.'));
$this->assertFalse(NodeType::load($active_type), 'The content no longer exists with the old name.');
$content_type = NodeType::load($staged_type);
$this->assertIdentical($staged_type, $content_type->id());
// Ensure the base field override has been renamed and the value is correct.
$node = entity_create('node', array('type' => $staged_type));
$this->assertFALSE($node->status->value, 'Node status defaults to FALSE.');
}
}

View File

@ -99,10 +99,9 @@ class NodeTypeTest extends NodeTestBase {
$this->assertRaw('Foo', 'New title label was displayed.');
$this->assertNoRaw('Title', 'Old title label was not displayed.');
// Change the name, machine name and description.
// Change the name and the description.
$edit = array(
'name' => 'Bar',
'type' => 'bar',
'description' => 'Lorem ipsum.',
);
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
@ -111,16 +110,15 @@ class NodeTypeTest extends NodeTestBase {
$this->assertRaw('Bar', 'New name was displayed.');
$this->assertRaw('Lorem ipsum', 'New description was displayed.');
$this->clickLink('Bar');
$this->assertUrl(\Drupal::url('node.add', ['node_type' => 'bar'], ['absolute' => TRUE]), [], 'New machine name was used in URL.');
$this->assertRaw('Foo', 'Title field was found.');
$this->assertRaw('Body', 'Body field was found.');
// Remove the body field.
$this->drupalPostForm('admin/structure/types/manage/bar/fields/node.bar.body/delete', array(), t('Delete'));
$this->drupalPostForm('admin/structure/types/manage/page/fields/node.page.body/delete', array(), t('Delete'));
// Resave the settings for this type.
$this->drupalPostForm('admin/structure/types/manage/bar', array(), t('Save content type'));
$this->drupalPostForm('admin/structure/types/manage/page', array(), t('Save content type'));
// Check that the body field doesn't exist.
$this->drupalGet('node/add/bar');
$this->drupalGet('node/add/page');
$this->assertNoRaw('Body', 'Body field was not found.');
}

View File

@ -7,13 +7,13 @@
namespace Drupal\shortcut;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\BundleEntityFormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Form controller for the shortcut set entity edit forms.
*/
class ShortcutSetForm extends EntityForm {
class ShortcutSetForm extends BundleEntityFormBase {
/**
* {@inheritdoc}
@ -38,14 +38,13 @@ class ShortcutSetForm extends EntityForm {
'replace' => '-',
),
'#default_value' => $entity->id(),
'#disabled' => !$entity->isNew(),
// This id could be used for menu name.
'#maxlength' => 23,
);
$form['actions']['submit']['#value'] = t('Create new set');
return $form;
return $this->protectBundleIdElement($form);
}
/**

View File

@ -469,35 +469,6 @@ class FieldSqlStorageTest extends EntityUnitTestBase {
$this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update');
}
/**
* Tests reacting to a bundle being renamed.
*/
function testFieldSqlStorageBundleRename() {
$entity_type = $bundle = 'entity_test_rev';
$field_name = $this->fieldStorage->getName();
// Create an entity.
$value = mt_rand(1, 127);
$entity = entity_create($entity_type, array(
'type' => $bundle,
$field_name => $value,
));
$entity->save();
// Rename the bundle.
$bundle_new = $bundle . '_renamed';
entity_test_rename_bundle($bundle, $bundle_new, $entity_type);
// Check that the 'bundle' column has been updated in storage.
$row = db_select($this->table, 't')
->fields('t', array('bundle', $field_name . '_value'))
->condition('entity_id', $entity->id())
->execute()
->fetch();
$this->assertEqual($row->bundle, $bundle_new);
$this->assertEqual($row->{$field_name . '_value'}, $value);
}
/**
* Tests table name generation.
*/

View File

@ -170,26 +170,6 @@ function entity_test_create_bundle($bundle, $text = NULL, $entity_type = 'entity
\Drupal::entityManager()->onBundleCreate($bundle, $entity_type);
}
/**
* Renames a bundle for entity_test entities.
*
* @param string $bundle_old
* The machine-readable name of the bundle to rename.
* @param string $bundle_new
* The new machine-readable name of the bundle.
* @param string $entity_type
* (optional) The entity type for which the bundle is renamed. Defaults to
* 'entity_test'.
*/
function entity_test_rename_bundle($bundle_old, $bundle_new, $entity_type = 'entity_test') {
$bundles = \Drupal::state()->get($entity_type . '.bundles') ?: array($entity_type => array('label' => 'Entity Test Bundle'));
$bundles[$bundle_new] = $bundles[$bundle_old];
unset($bundles[$bundle_old]);
\Drupal::state()->set($entity_type . '.bundles', $bundles);
\Drupal::entityManager()->onBundleRename($bundle_old, $bundle_new, $entity_type);
}
/**
* Deletes a bundle for entity_test entities.
*

View File

@ -122,48 +122,6 @@ class Vocabulary extends ConfigEntityBundleBase implements VocabularyInterface {
return $this->description;
}
/**
* {@inheritdoc}
*/
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
parent::postSave($storage, $update);
if ($update && $this->getOriginalId() != $this->id() && !$this->isSyncing()) {
// Reflect machine name changes in the definitions of existing 'taxonomy'
// fields.
$field_ids = array();
$field_map = \Drupal::entityManager()->getFieldMapByFieldType('entity_reference');
foreach ($field_map as $entity_type => $field_storages) {
foreach ($field_storages as $field_storage => $info) {
$field_ids[] = $entity_type . '.' . $field_storage;
}
}
$field_storages = \Drupal::entityManager()->getStorage('field_storage_config')->loadMultiple($field_ids);
$taxonomy_fields = array_filter($field_storages, function ($field_storage) {
return $field_storage->getType() == 'entity_reference' && $field_storage->getSetting('target_type') == 'taxonomy_term';
});
foreach ($taxonomy_fields as $field_storage) {
$update_storage = FALSE;
$allowed_values = $field_storage->getSetting('allowed_values');
foreach ($allowed_values as &$value) {
if ($value['vocabulary'] == $this->getOriginalId()) {
$value['vocabulary'] = $this->id();
$update_storage = TRUE;
}
}
$field_storage->setSetting('allowed_values', $allowed_values);
if ($update_storage) {
$field_storage->save();
}
}
}
$storage->resetCache($update ? array($this->getOriginalId()) : array());
}
/**
* {@inheritdoc}
*/

View File

@ -142,37 +142,6 @@ class VocabularyCrudTest extends TaxonomyTestBase {
$this->assertEqual($vocabulary->id(), $vocabulary2->id(), 'Vocabulary loaded successfully by name and ID.');
}
/**
* Tests that machine name changes are properly reflected.
*/
function testTaxonomyVocabularyChangeMachineName() {
// Add a field to the vocabulary.
entity_create('field_storage_config', array(
'field_name' => 'field_test',
'entity_type' => 'taxonomy_term',
'type' => 'test_field',
))->save();
entity_create('field_config', array(
'field_name' => 'field_test',
'entity_type' => 'taxonomy_term',
'bundle' => $this->vocabulary->id(),
))->save();
// Change the machine name.
$old_name = $this->vocabulary->id();
$new_name = Unicode::strtolower($this->randomMachineName());
$this->vocabulary->set('vid', $new_name);
$this->vocabulary->save();
// Check that entity bundles are properly updated.
$info = entity_get_bundles('taxonomy_term');
$this->assertFalse(isset($info[$old_name]), 'The old bundle name does not appear in entity_get_bundles().');
$this->assertTrue(isset($info[$new_name]), 'The new bundle name appears in entity_get_bundles().');
// Check that the field is still attached to the vocabulary.
$this->assertTrue(FieldConfig::loadByName('taxonomy_term', $new_name, 'field_test'), 'The bundle name was updated correctly.');
}
/**
* Test uninstall and reinstall of the taxonomy module.
*/

View File

@ -7,18 +7,17 @@
namespace Drupal\taxonomy;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\BundleEntityFormBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\language\Entity\ContentLanguageSettings;
use Drupal\taxonomy\VocabularyStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Base form for vocabulary edit forms.
*/
class VocabularyForm extends EntityForm {
class VocabularyForm extends BundleEntityFormBase {
/**
* The vocabulary storage.
@ -112,7 +111,8 @@ class VocabularyForm extends EntityForm {
'#value' => '0',
);
return parent::form($form, $form_state, $vocabulary);
$form = parent::form($form, $form_state);
return $this->protectBundleIdElement($form);
}
/**
@ -147,14 +147,14 @@ class VocabularyForm extends EntityForm {
/**
* Determines if the vocabulary already exists.
*
* @param string $id
* The vocabulary ID
* @param string $vid
* The vocabulary ID.
*
* @return bool
* TRUE if the vocabulary exists, FALSE otherwise.
*/
public function exists($id) {
$action = $this->vocabularyStorage->load($id);
public function exists($vid) {
$action = $this->vocabularyStorage->load($vid);
return !empty($action);
}