Commit 72c63698 authored by catch's avatar catch

Issue #2090541 by plopesc, yched: Avoid storing numeric target_id()'s for...

Issue #2090541 by plopesc, yched: Avoid storing numeric target_id()'s for default values in field instance CMI files.
parent b469334d
...@@ -66,7 +66,7 @@ public function getDefinitions() { ...@@ -66,7 +66,7 @@ public function getDefinitions() {
$definition['id'] = $plugin_id; $definition['id'] = $plugin_id;
$definition['provider'] = $module; $definition['provider'] = $module;
$definition['configurable'] = TRUE; $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; $definitions[$plugin_id] = $definition;
} }
} }
......
...@@ -24,12 +24,14 @@ function entity_reference_field_info_alter(&$info) { ...@@ -24,12 +24,14 @@ function entity_reference_field_info_alter(&$info) {
$info['entity_reference']['instance_settings']['handler_settings'] = array(); $info['entity_reference']['instance_settings']['handler_settings'] = array();
$info['entity_reference']['default_widget'] = 'entity_reference_autocomplete'; $info['entity_reference']['default_widget'] = 'entity_reference_autocomplete';
$info['entity_reference']['default_formatter'] = 'entity_reference_label'; $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(). * 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() * @see \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::getPropertyDefinitions()
* *
......
<?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;
}
}
<?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());
}
}
<?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;
}
}
<?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');
}
}
...@@ -396,3 +396,35 @@ function taxonomy_update_8008() { ...@@ -396,3 +396,35 @@ function taxonomy_update_8008() {
); );
db_add_field('taxonomy_term_data', 'changed', $spec); 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();
}
}
}
}
...@@ -845,6 +845,7 @@ function taxonomy_field_info() { ...@@ -845,6 +845,7 @@ function taxonomy_field_info() {
), ),
), ),
), ),
'list_class' => '\Drupal\taxonomy\Plugin\Field\FieldType\TaxonomyTermReferenceFieldItemList',
), ),
); );
} }
......
<?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();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment