diff --git a/core/lib/Drupal/Core/Field/LegacyFieldTypeDiscoveryDecorator.php b/core/lib/Drupal/Core/Field/LegacyFieldTypeDiscoveryDecorator.php index 381f69e099f08c2e06ab47b6f777fa50841be6e5..6e43b3f848a9d3a54afba20aa2b34c51156856a0 100644 --- a/core/lib/Drupal/Core/Field/LegacyFieldTypeDiscoveryDecorator.php +++ b/core/lib/Drupal/Core/Field/LegacyFieldTypeDiscoveryDecorator.php @@ -66,7 +66,7 @@ public function getDefinitions() { $definition['id'] = $plugin_id; $definition['provider'] = $module; $definition['configurable'] = TRUE; - $definition['list_class'] = '\Drupal\Core\Field\Plugin\Field\FieldType\LegacyConfigFieldItemList'; + $definition += array('list_class' => '\Drupal\Core\Field\Plugin\Field\FieldType\LegacyConfigFieldItemList'); $definitions[$plugin_id] = $definition; } } diff --git a/core/modules/entity_reference/entity_reference.module b/core/modules/entity_reference/entity_reference.module index 508af8a8ef068d1f08e678e0c2147d6b1bf415cf..120b754a9b62f62ea9993849ec2b50f34282e27b 100644 --- a/core/modules/entity_reference/entity_reference.module +++ b/core/modules/entity_reference/entity_reference.module @@ -24,12 +24,14 @@ function entity_reference_field_info_alter(&$info) { $info['entity_reference']['instance_settings']['handler_settings'] = array(); $info['entity_reference']['default_widget'] = 'entity_reference_autocomplete'; $info['entity_reference']['default_formatter'] = 'entity_reference_label'; + $info['entity_reference']['list_class'] = '\Drupal\entity_reference\Plugin\Field\FieldType\ConfigurableEntityReferenceFieldItemList'; } /** * Implements hook_entity_field_info_alter(). * - * Set the "target_type" property definition for entity reference fields. + * Set the "target_type" and "list_class" property definition for entity + * reference fields. * * @see \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::getPropertyDefinitions() * diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Field/FieldType/ConfigurableEntityReferenceFieldItemList.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Field/FieldType/ConfigurableEntityReferenceFieldItemList.php new file mode 100644 index 0000000000000000000000000000000000000000..5aa477aa1d8899f97730a9e17fb34ebc3b53de8e --- /dev/null +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Field/FieldType/ConfigurableEntityReferenceFieldItemList.php @@ -0,0 +1,96 @@ +<?php + +/** + * @file + * Contains \Drupal\entity_reference\Plugin\Field\FieldType\ConfigurableEntityReferenceFieldItemList. + */ + +namespace Drupal\entity_reference\Plugin\Field\FieldType; + +use Drupal\Core\Field\ConfigFieldItemList; + +/** + * Represents a configurable entity_reference entity field. + */ +class ConfigurableEntityReferenceFieldItemList extends ConfigFieldItemList { + + /** + * {@inheritdoc} + */ + protected function getDefaultValue() { + $default_value = parent::getDefaultValue(); + + if ($default_value) { + // Convert UUIDs to numeric IDs. + $uuids = array(); + $fixed = array(); + foreach ($default_value as $delta => $properties) { + if ($properties['target_uuid'] == 'anonymous' || $properties['target_uuid'] == 'administrator') { + $fixed[$delta] = ($properties['target_uuid'] == 'anonymous') ? '0' : '1'; + } + else { + $uuids[$delta] = $properties['target_uuid']; + } + } + if ($uuids) { + $entities = \Drupal::entityManager() + ->getStorageController($this->getFieldDefinition()->getFieldSetting('target_type')) + ->loadByProperties(array('uuid' => $uuids)); + + foreach ($entities as $id => $entity) { + $entity_ids[$entity->uuid()] = $id; + } + foreach ($uuids as $delta => $uuid) { + if ($entity_ids[$uuid]) { + $default_value[$delta]['target_id'] = $entity_ids[$uuid]; + unset($default_value[$delta]['target_uuid']); + } + else { + unset($default_value[$delta]); + } + } + } + + if ($fixed) { + foreach ($fixed as $delta => $id) { + $default_value[$delta]['target_id'] = $id; + unset($default_value[$delta]['target_uuid']); + } + } + + // Ensure we return consecutive deltas, in case we removed unknown UUIDs. + $default_value = array_values($default_value); + } + return $default_value; + } + + /** + * {@inheritdoc} + */ + public function defaultValuesFormSubmit(array $element, array &$form, array &$form_state) { + $default_value = parent::defaultValuesFormSubmit($element, $form, $form_state); + + // Convert numeric IDs to UUIDs to ensure config deployability. + $ids = array(); + foreach ($default_value as $delta => $properties) { + $ids[] = $properties['target_id']; + } + $entities = \Drupal::entityManager() + ->getStorageController($this->getFieldDefinition()->getFieldSetting('target_type')) + ->loadMultiple($ids); + + foreach ($default_value as $delta => $properties) { + $uuid = $entities[$properties['target_id']]->uuid(); + // @todo Some entities do not have uuid. IE: Anonymous and admin user. + // Remove this special case once http://drupal.org/node/2050843 + // has been fixed. + if (!$uuid) { + $uuid = ($properties['target_id'] == '0') ? 'anonymous' : 'administrator'; + } + unset($default_value[$delta]['target_id']); + $default_value[$delta]['target_uuid'] = $uuid; + } + return $default_value; + } + +} diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldDefaultValueTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldDefaultValueTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7d61f3d62e2f8919b699f46b8f7a95eae4b0cf7c --- /dev/null +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldDefaultValueTest.php @@ -0,0 +1,95 @@ +<?php + +/** + * @file + * Contains \Drupal\entity_reference\Tests\EntityReferenceFieldDefaultValueTest. + */ + +namespace Drupal\entity_reference\Tests; + +use Drupal\simpletest\WebTestBase; + +/** + * Tests entity reference field default values storage in CMI. + */ +class EntityReferenceFieldDefaultValueTest extends WebTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('entity_reference', 'field_ui', 'node', 'options'); + + public static function getInfo() { + return array( + 'name' => 'Entity Reference field default value', + 'description' => 'Tests the entity reference field default values storage in CMI.', + 'group' => 'Entity Reference', + ); + } + + function setUp() { + parent::setUp(); + + // Create default content type. + $this->drupalCreateContentType(array('type' => 'reference_content')); + $this->drupalCreateContentType(array('type' => 'referenced_content')); + + // Create admin user. + $this->admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer node fields', 'administer node form display', 'bypass node access')); + $this->drupalLogin($this->admin_user); + } + + /** + * Tests that default values are correctly translated to UUIDs in config. + */ + function testEntityReferenceDefaultValue() { + // Create a node to be referenced. + $referenced_node = $this->drupalCreateNode(array('type' => 'referenced_content')); + + $this->field = entity_create('field_entity', array( + 'name' => drupal_strtolower($this->randomName()), + 'entity_type' => 'node', + 'type' => 'entity_reference', + 'settings' => array('target_type' => 'node'), + )); + $this->field->save(); + $this->instance = entity_create('field_instance', array( + 'field_name' => $this->field->name, + 'entity_type' => 'node', + 'bundle' => 'reference_content', + 'settings' => array( + 'handler' => 'default', + 'handler_settings' => array( + 'target_bundles' => array('referenced_content'), + 'sort' => array('field' => '_none'), + ), + ), + )); + $this->instance->save(); + + // Set created node as default_value. + $instance_edit = array( + 'default_value_input[' . $this->field->name . '][0][target_id]' => $referenced_node->getTitle() . ' (' .$referenced_node->id() . ')', + ); + $this->drupalPostForm('admin/structure/types/manage/reference_content/fields/node.reference_content.' . $this->field->name, $instance_edit, t('Save settings')); + + // Check that default value is selected in default value form. + $this->drupalGet('admin/structure/types/manage/reference_content/fields/node.reference_content.' . $this->field->name); + $this->assertRaw('name="default_value_input[' . $this->field->name . '][0][target_id]" value="' . $referenced_node->getTitle() .' (' .$referenced_node->id() . ')', 'The default value is selected in instance settings page'); + + // Check if the ID has been converted to UUID in config entity. + $config_entity = $this->container->get('config.factory')->get('field.instance.node.reference_content.' . $this->field->name)->get(); + $this->assertTrue(isset($config_entity['default_value'][0]['target_uuid']), 'Default value contains target_uuid property'); + $this->assertEqual($config_entity['default_value'][0]['target_uuid'], $referenced_node->uuid(), 'Content uuid and config entity uuid are the same'); + + // Clean field_info cache in order to avoid stale cache values. + field_info_cache_clear(); + + // Create a new node to check that UUID has been converted to numeric ID. + $new_node = entity_create('node', array('type' => 'reference_content')); + $this->assertEqual($new_node->get($this->field->name)->offsetGet(0)->target_id, $referenced_node->id()); + } + +} diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceFieldItemList.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceFieldItemList.php new file mode 100644 index 0000000000000000000000000000000000000000..096a45205abee406a1535b05431c9c9b93b89352 --- /dev/null +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceFieldItemList.php @@ -0,0 +1,76 @@ +<?php + +/** + * @file + * Contains \Drupal\taxonomy\Plugin\Field\FieldType\TaxonomyTermReferenceFieldItemList. + */ + +namespace Drupal\taxonomy\Plugin\Field\FieldType; + +use Drupal\Core\Field\Plugin\Field\FieldType\LegacyConfigFieldItemList; + +/** + * Represents a configurable taxonomy_term_reference entity field. + */ +class TaxonomyTermReferenceFieldItemList extends LegacyConfigFieldItemList { + + /** + * {@inheritdoc} + */ + protected function getDefaultValue() { + $default_value = parent::getDefaultValue(); + + if ($default_value) { + // Convert UUIDs to numeric IDs. + $uuids = array(); + foreach ($default_value as $delta => $properties) { + $uuids[$delta] = $properties['target_uuid']; + } + if ($uuids) { + $entities = \Drupal::entityManager() + ->getStorageController('taxonomy_term') + ->loadByProperties(array('uuid' => $uuids)); + + foreach ($entities as $id => $entity) { + $entity_ids[$entity->uuid()] = $id; + } + foreach ($uuids as $delta => $uuid) { + if (isset($entity_ids[$uuid])) { + $default_value[$delta]['target_id'] = $entity_ids[$uuid]; + unset($default_value[$delta]['target_uuid']); + } + else { + unset($default_value[$delta]); + } + } + } + + // Ensure we return consecutive deltas, in case we removed unknown UUIDs. + $default_value = array_values($default_value); + } + return $default_value; + } + + /** + * {@inheritdoc} + */ + public function defaultValuesFormSubmit(array $element, array &$form, array &$form_state) { + $default_value = parent::defaultValuesFormSubmit($element, $form, $form_state); + + // Convert numeric IDs to UUIDs to ensure config deployability. + $ids = array(); + foreach ($default_value as $delta => $properties) { + $ids[] = $properties['target_id']; + } + $entities = \Drupal::entityManager() + ->getStorageController('taxonomy_term') + ->loadMultiple($ids); + + foreach ($default_value as $delta => $properties) { + unset($default_value[$delta]['target_id']); + $default_value[$delta]['target_uuid'] = $entities[$properties['target_id']]->uuid(); + } + return $default_value; + } + +} diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyUpgradePathTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyUpgradePathTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b28863651cd1672f29a33a71f08167af6befb9e0 --- /dev/null +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyUpgradePathTest.php @@ -0,0 +1,52 @@ +<?php + +/** + * @file + * Contains \Drupal\taxonomy\Tests\TaxonomyUpgradePathTest. + */ + +namespace Drupal\taxonomy\Tests; + +use Drupal\system\Tests\Upgrade\UpgradePathTestBase; + +/** + * Tests upgrade of taxonomy variables. + */ +class TaxonomyUpgradePathTest extends UpgradePathTestBase { + + public static function getInfo() { + return array( + 'name' => 'Taxonomy upgrade test', + 'description' => 'Tests upgrade of Taxonomy module.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + $this->databaseDumpFiles = array( + drupal_get_path('module', 'system') . '/tests/upgrade/drupal-7.bare.standard_all.database.php.gz', + drupal_get_path('module', 'taxonomy') . '/tests/upgrade/drupal-7.taxonomy.database.php', + ); + parent::setUp(); + } + + /** + * Tests upgrade of taxonomy_term_reference field default values. + */ + public function testEntityDisplayUpgrade() { + $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.'); + + // Check that the configuration entries were created. + $config_entity = \Drupal::config('field.instance.node.article.field_tags')->get(); + $this->assertTrue(!empty($config_entity), 'Config entity has been created'); + $this->assertTrue(!empty($config_entity['default_value'][0]['target_uuid']), 'Default value contains target_uuid property'); + + // Load taxonomy term to check UUID conversion. + $taxonomy_term = entity_load('taxonomy_term', 2); + + // Check that default_value has been converted to Drupal 8 structure. + $this->assertEqual($taxonomy_term->uuid(), $config_entity['default_value'][0]['target_uuid'], 'Default value contains the right target_uuid'); + $this->assertEqual('', $config_entity['default_value'][0]['revision_id'], 'Default value contains the right revision_id'); + } + +} diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install index 845a44e0c2143f18daae801e358ab6859a207b52..086c030e40a8384be9d5efb4f7db6874ee3bc2f9 100644 --- a/core/modules/taxonomy/taxonomy.install +++ b/core/modules/taxonomy/taxonomy.install @@ -396,3 +396,35 @@ function taxonomy_update_8008() { ); db_add_field('taxonomy_term_data', 'changed', $spec); } + +/** + * Convert numeric IDs to UUIDs to ensure config deployability. + */ +function taxonomy_update_8009() { + foreach (config_get_storage_names_with_prefix('field.instance.') as $instance_config_name) { + $instance_config = \Drupal::config($instance_config_name); + if ($instance_config->get('field_type') == 'taxonomy_term_reference') { + $default_value = $instance_config->get('default_value'); + $ids = array(); + // Load all referenced taxonomy_terms in default_value. + foreach ($default_value as $delta => $value) { + $ids[] = $value['tid']; + } + if ($ids) { + $entities = db_select('taxonomy_term_data', 't') + ->fields('t', array('tid', 'uuid')) + ->condition('t.tid', $ids) + ->execute()->fetchAllAssoc('tid'); + + // Convert IDs to UUIDs and save the new default_value. + foreach ($default_value as $delta => &$value) { + $value = array( + 'target_uuid' => $entities[$value['tid']]->uuid, + 'revision_id' => '', + ); + } + $instance_config->set('default_value', $default_value)->save(); + } + } + } +} diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index ad014a0f9384db93bc032c3852cc3d55fff1a1f8..72222aa7928318d64585dc2e5493c043f08c6b65 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -845,6 +845,7 @@ function taxonomy_field_info() { ), ), ), + 'list_class' => '\Drupal\taxonomy\Plugin\Field\FieldType\TaxonomyTermReferenceFieldItemList', ), ); } diff --git a/core/modules/taxonomy/tests/upgrade/drupal-7.taxonomy.database.php b/core/modules/taxonomy/tests/upgrade/drupal-7.taxonomy.database.php new file mode 100644 index 0000000000000000000000000000000000000000..6c729f0904d5ad7eaf8a23c7c94b4a99994be16b --- /dev/null +++ b/core/modules/taxonomy/tests/upgrade/drupal-7.taxonomy.database.php @@ -0,0 +1,49 @@ +<?php + +/** + * @file + * Database additions for taxonomy variables. Used in TaxonomyUpgradePathTest. + * + * The drupal-7.bare.standard_all.php file is imported before this dump, so the + * two form the database structure expected in tests altogether. + */ + +// Add a new taxonomy_term in taxonomy_term_data table. +db_insert('taxonomy_term_data') + ->fields(array( + 'tid', + 'vid', + 'name', + 'description', + 'format', + 'weight', + )) + ->values(array( + 'tid' => '2', + 'vid' => '1', + 'name' => 'Default term', + 'description' => '', + 'format' => NULL, + 'weight' => '0', + )) + ->execute(); + +// Add a new taxonomy_term in taxonomy_term_hierarchy table. +db_insert('taxonomy_term_hierarchy') + ->fields(array( + 'tid', + 'parent', + )) + ->values(array( + 'tid' => '2', + 'parent' => '0', + )) + ->execute(); + +// Set the taxonomy_term added as default value for field_tags instance. +db_update('field_config_instance') + ->fields(array( + 'data' => 'a:7:{s:5:"label";s:4:"Tags";s:11:"description";s:63:"Enter a comma-separated list of words to describe your content.";s:6:"widget";a:5:{s:6:"weight";i:-4;s:4:"type";s:21:"taxonomy_autocomplete";s:6:"module";s:8:"taxonomy";s:6:"active";i:0;s:8:"settings";a:2:{s:4:"size";i:60;s:17:"autocomplete_path";s:21:"taxonomy/autocomplete";}}s:7:"display";a:2:{s:7:"default";a:5:{s:4:"type";s:28:"taxonomy_term_reference_link";s:6:"weight";i:10;s:5:"label";s:5:"above";s:8:"settings";a:0:{}s:6:"module";s:8:"taxonomy";}s:6:"teaser";a:5:{s:4:"type";s:28:"taxonomy_term_reference_link";s:6:"weight";i:10;s:5:"label";s:5:"above";s:8:"settings";a:0:{}s:6:"module";s:8:"taxonomy";}}s:8:"settings";a:1:{s:18:"user_register_form";b:0;}s:8:"required";i:0;s:13:"default_value";a:1:{i:0;a:8:{s:3:"tid";s:1:"2";s:3:"vid";s:1:"1";s:4:"name";s:12:"Default term";s:11:"description";s:0:"";s:6:"format";s:13:"filtered_html";s:6:"weight";s:1:"0";s:23:"vocabulary_machine_name";s:4:"tags";s:11:"rdf_mapping";a:5:{s:7:"rdftype";a:1:{i:0;s:12:"skos:Concept";}s:4:"name";a:1:{s:10:"predicates";a:2:{i:0;s:10:"rdfs:label";i:1;s:14:"skos:prefLabel";}}s:11:"description";a:1:{s:10:"predicates";a:1:{i:0;s:15:"skos:definition";}}s:3:"vid";a:2:{s:10:"predicates";a:1:{i:0;s:13:"skos:inScheme";}s:4:"type";s:3:"rel";}s:6:"parent";a:2:{s:10:"predicates";a:1:{i:0;s:12:"skos:broader";}s:4:"type";s:3:"rel";}}}}}') + ) + ->condition('field_name', 'field_tags') + ->execute();