diff --git a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php index ce21acc..2428b6a 100644 --- a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php @@ -281,11 +281,21 @@ protected function mapFromStorageRecords(array $records, $load_revision = FALSE) $entities = array(); foreach ($records as $id => $record) { $entities[$id] = array(); + // Skip the item delta and item value levels (if possible) but let the + // field assign the value as suiting. This avoids unnecessary array + // hierarchies and saves memory here. foreach ($record as $name => $value) { - // Skip the item delta and item value levels but let the field assign - // the value as suiting. This avoids unnecessary array hierarchies and - // saves memory here. - $entities[$id][$name][Language::LANGCODE_DEFAULT] = $value; + // Handle columns named [field_name]__[column_name] (e.g for field types + // that store several properties). + if ($field = strstr($name, '__', TRUE)) { + $property_name = substr($name, strpos($name, '__') + 2); + $entities[$id][$field][Language::LANGCODE_DEFAULT][$property_name] = $value; + } + else { + // Handle columns named directly after the field (e.g if the field + // type only stores one property). + $entities[$id][$name][Language::LANGCODE_DEFAULT] = $value; + } } // If we have no multilingual values we can instantiate entity objecs // right now, otherwise we need to collect all the field values first. @@ -332,13 +342,13 @@ protected function attachPropertyData(array &$entities, $revision_id = FALSE) { } $data = $query->execute(); - $field_definition = \Drupal::entityManager()->getFieldDefinitions($this->entityType); + $field_definitions = \Drupal::entityManager()->getFieldDefinitions($this->entityType); $translations = array(); if ($this->revisionDataTable) { - $data_fields = array_flip(array_diff(drupal_schema_fields_sql($this->entityInfo['revision_data_table']), drupal_schema_fields_sql($this->entityInfo['base_table']))); + $data_column_names = array_flip(array_diff(drupal_schema_fields_sql($this->entityInfo['revision_data_table']), drupal_schema_fields_sql($this->entityInfo['base_table']))); } else { - $data_fields = array_flip(drupal_schema_fields_sql($this->entityInfo['data_table'])); + $data_column_names = array_flip(drupal_schema_fields_sql($this->entityInfo['data_table'])); } foreach ($data as $values) { @@ -349,9 +359,24 @@ protected function attachPropertyData(array &$entities, $revision_id = FALSE) { $langcode = empty($values['default_langcode']) ? $values['langcode'] : Language::LANGCODE_DEFAULT; $translations[$id][$langcode] = TRUE; - foreach ($field_definition as $name => $definition) { - if (isset($data_fields[$name])) { - $entities[$id][$name][$langcode] = $values[$name]; + foreach (array_keys($field_definitions) as $field_name) { + // Handle columns named directly after the field. + if (isset($data_column_names[$field_name])) { + $entities[$id][$field_name][$langcode] = $values[$field_name]; + } + else { + // @todo Change this logic to be based on a mapping of field + // definition properties (translatability, revisionability) in + // https://drupal.org/node/2144631. + foreach ($data_column_names as $data_column_name) { + // Handle columns named [field_name]__[column_name], for which we + // need to look through all column names from the table that start + // with the name of the field. + if (($field = strstr($data_column_name, '__', TRUE)) && $field === $field_name) { + $property_name = substr($data_column_name, strpos($data_column_name, '__') + 2); + $entities[$id][$field_name][$langcode][$property_name] = $values[$data_column_name]; + } + } } } } @@ -744,17 +769,41 @@ protected function savePropertyData(EntityInterface $entity, $table_key = 'data_ */ protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'base_table') { $record = new \stdClass(); + $values = array(); $definitions = $entity->getPropertyDefinitions(); $schema = drupal_get_schema($this->entityInfo[$table_key]); $is_new = $entity->isNew(); + $multi_column_fields = array(); foreach (drupal_schema_fields_sql($this->entityInfo[$table_key]) as $name) { - $info = $schema['fields'][$name]; - $value = isset($definitions[$name]) && isset($entity->$name->value) ? $entity->$name->value : NULL; + // Check for fields which store data in multiple columns and process them + // separately. + if ($field = strstr($name, '__', TRUE)) { + $multi_column_fields[$field] = TRUE; + continue; + } + $values[$name] = isset($definitions[$name]) && isset($entity->$name->value) ? $entity->$name->value : NULL; + } + + // Handle fields that store multiple properties and match each property name + // to its schema column name. + foreach (array_keys($multi_column_fields) as $field_name) { + $field_items = $entity->get($field_name); + $field_value = $field_items->getValue(); + // @todo Reconsider the usage of getPropertyDefinitions() after + // https://drupal.org/node/2144327. + foreach (array_keys($field_items[0]->getPropertyDefinitions()) as $property_name) { + if (isset($schema['fields'][$field_name . '__' . $property_name])) { + $values[$field_name . '__' . $property_name] = isset($field_value[0][$property_name]) ? $field_value[0][$property_name] : NULL; + } + } + } + + foreach ($values as $field_name => $value) { // If we are creating a new entity, we must not populate the record with // NULL values otherwise defaults would not be applied. if (isset($value) || !$is_new) { - $record->$name = drupal_schema_get_field_value($info, $value); + $record->$field_name = drupal_schema_get_field_value($schema['fields'][$field_name], $value); } } diff --git a/core/lib/Drupal/Core/Field/FieldDefinition.php b/core/lib/Drupal/Core/Field/FieldDefinition.php index 8e57f7c..eecb75b 100644 --- a/core/lib/Drupal/Core/Field/FieldDefinition.php +++ b/core/lib/Drupal/Core/Field/FieldDefinition.php @@ -107,7 +107,7 @@ public function getFieldSetting($setting_name) { * {@inheritdoc} */ public function getFieldPropertyNames() { - return array_keys(\Drupal::typedData()->create($this->definition['type'])->getPropertyDefinitions()); + return array_keys(\Drupal::typedData()->create($this->definition)->getPropertyDefinitions()); } /** diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php index a638245..879c02b 100644 --- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php @@ -22,7 +22,7 @@ class EntityReferenceItemTest extends FieldUnitTestBase { * * @var array */ - public static $modules = array('entity_reference', 'taxonomy', 'options'); + public static $modules = array('entity_reference', 'taxonomy', 'options', 'text', 'filter'); /** * The taxonomy vocabulary to test with. diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php index 8e7bbb4..caa03e1 100644 --- a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php +++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php @@ -387,7 +387,7 @@ function createForum($type, $parent = 0) { ); // Verify forum. - $term = db_query("SELECT * FROM {taxonomy_term_data} t WHERE t.vid = :vid AND t.name = :name AND t.description = :desc", array(':vid' => \Drupal::config('forum.settings')->get('vocabulary'), ':name' => $name, ':desc' => $description))->fetchAssoc(); + $term = db_query("SELECT * FROM {taxonomy_term_data} t WHERE t.vid = :vid AND t.name = :name AND t.description__value = :desc", array(':vid' => \Drupal::config('forum.settings')->get('vocabulary'), ':name' => $name, ':desc' => $description))->fetchAssoc(); $this->assertTrue(!empty($term), 'The ' . $type . ' exists in the database'); // Verify forum hierarchy. diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/Field/TaxonomyTermReferenceRdfaTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/Field/TaxonomyTermReferenceRdfaTest.php index 274b5aa..2a3eb4a 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/Field/TaxonomyTermReferenceRdfaTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/Field/TaxonomyTermReferenceRdfaTest.php @@ -37,7 +37,7 @@ class TaxonomyTermReferenceRdfaTest extends FieldRdfaTestBase { /** * {@inheritdoc} */ - public static $modules = array('taxonomy'); + public static $modules = array('taxonomy', 'options', 'text', 'filter'); public static function getInfo() { return array( diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php index a091d1a..5bf7203 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php @@ -230,13 +230,8 @@ public static function baseFieldDefinitions($entity_type) { $properties['description'] = array( 'label' => t('Description'), 'description' => t('A description of the term'), - 'type' => 'string_field', - ); - // @todo Combine with description. - $properties['format'] = array( - 'label' => t('Description format'), - 'description' => t('The filter format ID of the description.'), - 'type' => 'string_field', + 'type' => 'field_item:text_long', + 'settings' => array('text_processing' => 1), ); $properties['weight'] = array( 'label' => t('Weight'), diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php index e7fb41e..bbed7fd 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php @@ -74,7 +74,7 @@ public function form(array $form, array &$form_state) { '#type' => 'text_format', '#title' => $this->t('Description'), '#default_value' => $term->description->value, - '#format' => $term->format->value, + '#format' => $term->description->format, '#weight' => 0, ); $language_configuration = $this->moduleHandler->moduleExists('language') ? language_get_default_configuration('taxonomy_term', $vocabulary->id()) : FALSE; @@ -178,7 +178,7 @@ public function buildEntity(array $form, array &$form_state) { // \Drupal\Core\Entity\Entity::save() method. $description = $form_state['values']['description']; $term->description->value = $description['value']; - $term->format->value = $description['format']; + $term->description->format = $description['format']; // Assign parents with proper delta values starting from 0. $term->parent = array_keys($form_state['values']['parent']); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermViewBuilder.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermViewBuilder.php index b33dbfd..96f925f 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermViewBuilder.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermViewBuilder.php @@ -24,10 +24,11 @@ public function buildContent(array $entities, array $displays, $view_mode, $lang foreach ($entities as $entity) { // Add the description if enabled. + // @todo Remove this when base fields will be able to use formatters. $display = $displays[$entity->bundle()]; if (!empty($entity->description->value) && $display->getComponent('description')) { $entity->content['description'] = array( - '#markup' => check_markup($entity->description->value, $entity->format->value, '', TRUE), + '#markup' => $entity->description->processed, '#prefix' => '
', '#suffix' => '
', ); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php index ee6bf42..a9a5e12 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php @@ -23,7 +23,7 @@ class TaxonomyTermReferenceItemTest extends FieldUnitTestBase { * * @var array */ - public static $modules = array('taxonomy', 'options'); + public static $modules = array('taxonomy', 'options', 'text', 'filter'); public static function getInfo() { return array( diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php index 4ae4823..9fd5a34 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php @@ -55,9 +55,11 @@ function createTerm($vocabulary) { $format = array_pop($filter_formats); $term = entity_create('taxonomy_term', array( 'name' => $this->randomName(), - 'description' => $this->randomName(), - // Use the first available text format. - 'format' => $format->format, + 'description' => array( + 'value' => $this->randomName(), + // Use the first available text format. + 'format' => $format->format, + ), 'vid' => $vocabulary->id(), 'langcode' => Language::LANGCODE_NOT_SPECIFIED, )); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php index dcae350..c6b21e4 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php @@ -346,11 +346,16 @@ function testTermInterface() { // Did this page request display a 'term-listing-heading'? $this->assertPattern('|class="taxonomy-term-description"|', 'Term page displayed the term description element.'); // Check that it does NOT show a description when description is blank. - $term->description = ''; + $term->description->value = NULL; $term->save(); $this->drupalGet('taxonomy/term/' . $term->id()); $this->assertNoPattern('|class="taxonomy-term-description"|', 'Term page did not display the term description when description was blank.'); + // Check that the description value is processed. + $term->description->value = $value = $this->randomName(); + $term->save(); + $this->assertEqual($term->description->processed, "

$value

\n"); + // Check that the term feed page is working. $this->drupalGet('taxonomy/term/' . $term->id() . '/feed'); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php index be4ed53..a91675c 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php @@ -88,7 +88,7 @@ function testTaxonomyTokenReplacement() { $tests = array(); $tests['[term:tid]'] = $term1->id(); $tests['[term:name]'] = check_plain($term1->name->value); - $tests['[term:description]'] = check_markup($term1->description->value, $term1->format->value); + $tests['[term:description]'] = $term1->description->processed; $tests['[term:url]'] = url('taxonomy/term/' . $term1->id(), array('absolute' => TRUE)); $tests['[term:node-count]'] = 0; $tests['[term:parent:name]'] = '[term:parent:name]'; @@ -103,7 +103,7 @@ function testTaxonomyTokenReplacement() { $tests = array(); $tests['[term:tid]'] = $term2->id(); $tests['[term:name]'] = check_plain($term2->name->value); - $tests['[term:description]'] = check_markup($term2->description->value, $term2->format->value); + $tests['[term:description]'] = $term2->description->processed; $tests['[term:url]'] = url('taxonomy/term/' . $term2->id(), array('absolute' => TRUE)); $tests['[term:node-count]'] = 1; $tests['[term:parent:name]'] = check_plain($term1->name->value); diff --git a/core/modules/taxonomy/taxonomy.info.yml b/core/modules/taxonomy/taxonomy.info.yml index 88de323..2fe12f0 100644 --- a/core/modules/taxonomy/taxonomy.info.yml +++ b/core/modules/taxonomy/taxonomy.info.yml @@ -6,4 +6,5 @@ version: VERSION core: 8.x dependencies: - options + - text configure: taxonomy.vocabulary_list diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install index 086c030..f7abc20 100644 --- a/core/modules/taxonomy/taxonomy.install +++ b/core/modules/taxonomy/taxonomy.install @@ -48,13 +48,13 @@ function taxonomy_schema() { 'default' => '', 'description' => 'The term name.', ), - 'description' => array( + 'description__value' => array( 'type' => 'text', 'not null' => FALSE, 'size' => 'big', 'description' => 'A description of the term.', ), - 'format' => array( + 'description__format' => array( 'type' => 'varchar', 'length' => 255, 'not null' => FALSE, @@ -428,3 +428,24 @@ function taxonomy_update_8009() { } } } + +/** + * Rename the 'description' and 'format' columns for taxonomy terms. + */ +function taxonomy_update_8010() { + $description = array( + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + 'description' => 'A description of the term.', + ); + db_change_field('taxonomy_term_data', 'description', 'description__value', $description); + + $format = array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'The filter format ID of the description.', + ); + db_change_field('taxonomy_term_data', 'format', 'description__format', $format); +} diff --git a/core/modules/taxonomy/taxonomy.pages.inc b/core/modules/taxonomy/taxonomy.pages.inc index 96f84aa..84f9c32 100644 --- a/core/modules/taxonomy/taxonomy.pages.inc +++ b/core/modules/taxonomy/taxonomy.pages.inc @@ -76,7 +76,7 @@ function taxonomy_term_feed(Term $term) { $channel['title'] = \Drupal::config('system.site')->get('name') . ' - ' . $term->label(); // Only display the description if we have a single term, to avoid clutter and confusion. // HTML will be removed from feed description. - $channel['description'] = check_markup($term->description->value, $term->format->value, '', TRUE); + $channel['description'] = $term->description->processed; $nids = taxonomy_select_nodes($term->id(), FALSE, \Drupal::config('system.rss')->get('items.limit')); return node_feed($nids, $channel); diff --git a/core/modules/taxonomy/taxonomy.tokens.inc b/core/modules/taxonomy/taxonomy.tokens.inc index ad0a1d4..ce77a7c 100644 --- a/core/modules/taxonomy/taxonomy.tokens.inc +++ b/core/modules/taxonomy/taxonomy.tokens.inc @@ -108,7 +108,7 @@ function taxonomy_tokens($type, $tokens, array $data = array(), array $options = break; case 'description': - $replacements[$original] = $sanitize ? check_markup($term->description->value, $term->format->value, '', TRUE) : $term->description->value; + $replacements[$original] = $sanitize ? $term->description->processed : $term->description->value; break; case 'url': diff --git a/core/modules/taxonomy/taxonomy.views.inc b/core/modules/taxonomy/taxonomy.views.inc index 3404b3a..6e06490 100644 --- a/core/modules/taxonomy/taxonomy.views.inc +++ b/core/modules/taxonomy/taxonomy.views.inc @@ -128,12 +128,12 @@ function taxonomy_views_data() { ); // Term description - $data['taxonomy_term_data']['description'] = array( + $data['taxonomy_term_data']['description__value'] = array( 'title' => t('Term description'), 'help' => t('The description associated with a taxonomy term.'), 'field' => array( 'id' => 'markup', - 'format' => array('field' => 'format'), + 'format' => array('field' => 'description__format'), 'click sortable' => FALSE, ), 'filter' => array( diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/RowEntityTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/RowEntityTest.php index 4486157..d71c283 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/RowEntityTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/RowEntityTest.php @@ -21,7 +21,7 @@ class RowEntityTest extends ViewUnitTestBase { * * @var array */ - public static $modules = array('taxonomy', 'field', 'entity', 'system'); + public static $modules = array('taxonomy', 'text', 'filter', 'field', 'entity', 'system'); /** * Views used by this test.