diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install index d9307a6..cae81b3 100644 --- a/core/modules/taxonomy/taxonomy.install +++ b/core/modules/taxonomy/taxonomy.install @@ -6,23 +6,6 @@ */ use Drupal\Core\Field\BaseFieldDefinition; -use Drupal\Core\Entity\Sql\SqlContentEntityStorage; - -/** - * Implements hook_update_dependencies(). - */ -function taxonomy_update_dependencies() { - // The update function that adds the status field must run after - // content_translation_update_8400() which fixes NULL values for the - // 'content_translation_status' field. - if (\Drupal::moduleHandler()->moduleExists('content_translation')) { - $dependencies['taxonomy'][8600] = [ - 'content_translation' => 8400, - ]; - - return $dependencies; - } -} /** * Convert the custom taxonomy term hierarchy storage to a default storage. @@ -151,11 +134,10 @@ function taxonomy_update_8503() { */ function taxonomy_update_8600() { $definition_update_manager = \Drupal::entityDefinitionUpdateManager(); + $entity_type = $definition_update_manager->getEntityType('taxonomy_term'); // Add the published entity key and revisionable metadata fields to the // taxonomy_term entity type. - $entity_type = $definition_update_manager->getEntityType('taxonomy_term'); - $entity_keys = $entity_type->getKeys(); $entity_keys['published'] = 'status'; $entity_type->set('entity_keys', $entity_keys); @@ -204,36 +186,14 @@ function taxonomy_update_8600() { ->setLabel(t('Revision log message')) ->setDescription(t('Briefly describe the changes you have made.')) ->setRevisionable(TRUE) - ->setDefaultValue('') - ->setDisplayOptions('form', [ - 'type' => 'string_textarea', - 'weight' => 25, - 'region' => 'hidden', - 'settings' => [ - 'rows' => 4, - ], - ]); + ->setDefaultValue(''); $definition_update_manager->installFieldStorageDefinition('revision_log_message', 'taxonomy_term', 'taxonomy_term', $revision_log_message); // Uninstall the 'content_translation_status' field if needed. - $storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term'); - if ($has_content_translation_status_field &&$storage instanceof SqlContentEntityStorage) { - $database = \Drupal::database(); - // First we have to remove the field data. - $database->update($entity_type->getDataTable()) - ->fields(['content_translation_status' => NULL]) - ->execute(); - - // A site may have disabled revisionability for this entity type. - if ($entity_type->isRevisionable()) { - $database->update($entity_type->getRevisionDataTable()) - ->fields(['content_translation_status' => NULL]) - ->execute(); - } - + if ($has_content_translation_status_field) { $content_translation_status = $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'taxonomy_term'); $definition_update_manager->uninstallFieldStorageDefinition($content_translation_status); } - return t('Taxonomy terms have been converted to revisionable and publishable.'); + return t('The publishing status and revision metadata fields have been added to taxonomy terms.'); } diff --git a/core/modules/taxonomy/taxonomy.post_update.php b/core/modules/taxonomy/taxonomy.post_update.php index 8ecda9a..a900929 100644 --- a/core/modules/taxonomy/taxonomy.post_update.php +++ b/core/modules/taxonomy/taxonomy.post_update.php @@ -7,12 +7,14 @@ use Drupal\Core\Config\Entity\ConfigEntityUpdater; use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchemaConverter; +use Drupal\Core\Entity\Sql\SqlEntityStorageInterface; use Drupal\views\ViewExecutable; /** - * Add a 'published' = TRUE filter for all Taxonomy term views. + * Add a 'published' = TRUE filter for all Taxonomy term views and converts + * existing ones that were using the 'content_translation_status' field. */ -function taxonomy_post_update_add_status_filter_to_taxonomy_views(&$sandbox = NULL) { +function taxonomy_post_update_handle_publishing_status_addition_in_views(&$sandbox = NULL) { $definition_update_manager = \Drupal::entityDefinitionUpdateManager(); $entity_type = $definition_update_manager->getEntityType('taxonomy_term'); $published_key = $entity_type->getKey('published'); @@ -71,13 +73,43 @@ function taxonomy_post_update_add_status_filter_to_taxonomy_views(&$sandbox = NU $displays = $view->get('display'); foreach ($displays as $display_name => &$display) { - // Add a filter on the published field for the default and all the - // overridden displays. + // Update any existing 'content_translation_status fields. + $fields = isset($display['display_options']['fields']) ? $display['display_options']['fields'] : []; + foreach ($fields as $id => $field) { + if (isset($field['field']) && $field['field'] == 'content_translation_status') { + $fields[$id]['field'] = $published_key; + } + } + $display['display_options']['fields'] = $fields; + + // Update any existing 'content_translation_status sorts. + $sorts = isset($display['display_options']['sorts']) ? $display['display_options']['sorts'] : []; + foreach ($sorts as $id => $sort) { + if (isset($sort['field']) && $sort['field'] == 'content_translation_status') { + $sorts[$id]['field'] = $published_key; + } + } + $display['display_options']['sorts'] = $sorts; + + // Update any existing 'content_translation_status' filters or add a new + // one if necessary. $filters = isset($display['display_options']['filters']) ? $display['display_options']['filters'] : []; - $status_filter['id'] = ViewExecutable::generateHandlerId($published_key, $filters); + $has_status_filter = FALSE; + foreach ($filters as $id => $filter) { + if (isset($filter['field']) && $filter['field'] == 'content_translation_status') { + $filters[$id]['field'] = $published_key; + $has_status_filter = TRUE; + } + } - $display['display_options']['filters'][$status_filter['id']] = $status_filter; + if (!$has_status_filter) { + $status_filter['id'] = ViewExecutable::generateHandlerId($published_key, $filters); + $filters[$status_filter['id']] = $status_filter; + } + $display['display_options']['filters'] = $filters; } + $view->set('display', $displays); + return TRUE; }); } @@ -86,6 +118,14 @@ function taxonomy_post_update_add_status_filter_to_taxonomy_views(&$sandbox = NU * Update taxonomy terms to be revisionable. */ function taxonomy_post_update_make_taxonomy_term_revisionable(&$sandbox) { + $definition_update_manager = \Drupal::entityDefinitionUpdateManager(); + $entity_type = $definition_update_manager->getEntityType('taxonomy_term'); + + // Bail out early if the entity type is not using a SQL storage class. + if (!is_a($entity_type->getStorageClass(), SqlEntityStorageInterface::class, TRUE)) { + return t('Taxonomy term data has not been converted to be revisionable.'); + } + $schema_converter = new SqlContentEntityStorageSchemaConverter( 'taxonomy_term', \Drupal::entityTypeManager(), diff --git a/core/modules/taxonomy/tests/fixtures/update/drupal-8.views-taxonomy-term-publishing-status-2880149.php b/core/modules/taxonomy/tests/fixtures/update/drupal-8.views-taxonomy-term-publishing-status-2880149.php new file mode 100644 index 0000000..b29b288 --- /dev/null +++ b/core/modules/taxonomy/tests/fixtures/update/drupal-8.views-taxonomy-term-publishing-status-2880149.php @@ -0,0 +1,32 @@ +insert('config') + ->fields(['collection', 'name', 'data']) + ->values([ + 'collection' => '', + 'name' => 'views.view.test_taxonomy_term_view_with_content_translation_status', + 'data' => serialize($view_with_cts_config), + ]) + ->values([ + 'collection' => '', + 'name' => 'views.view.test_taxonomy_term_view_without_content_translation_status', + 'data' => serialize($view_without_cts_config), + ]) + ->execute(); diff --git a/core/modules/taxonomy/tests/fixtures/update/views.view.test_taxonomy_term_view_with_content_translation_status.yml b/core/modules/taxonomy/tests/fixtures/update/views.view.test_taxonomy_term_view_with_content_translation_status.yml new file mode 100644 index 0000000..f4cc45b --- /dev/null +++ b/core/modules/taxonomy/tests/fixtures/update/views.view.test_taxonomy_term_view_with_content_translation_status.yml @@ -0,0 +1,250 @@ +langcode: en +status: true +dependencies: + module: + - taxonomy + - user +id: test_taxonomy_term_view_with_content_translation_status +label: 'Test taxonomy term view with content translation status' +module: views +description: '' +tag: '' +base_table: taxonomy_term_field_data +base_field: tid +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + access: + type: perm + options: + perm: 'access content' + cache: + type: tag + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + replica: false + query_comment: '' + query_tags: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: none + options: + offset: 0 + style: + type: default + options: + grouping: { } + row_class: '' + default_row_class: true + uses_fields: false + row: + type: fields + options: + inline: { } + separator: '' + hide_empty: false + default_field_elements: true + fields: + name: + id: name + table: taxonomy_term_field_data + field: name + entity_type: taxonomy_term + entity_field: name + label: '' + alter: + alter_text: false + make_link: false + absolute: false + trim: false + word_boundary: false + ellipsis: false + strip_tags: false + html: false + hide_empty: false + empty_zero: false + type: string + settings: + link_to_entity: true + plugin_id: term_name + relationship: none + group_type: group + admin_label: '' + exclude: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_alter_empty: true + click_sort_column: value + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + convert_spaces: false + content_translation_status: + id: content_translation_status + table: taxonomy_term_field_data + field: content_translation_status + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: boolean + settings: + format: true-false + format_custom_true: '' + format_custom_false: '' + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: taxonomy_term + entity_field: content_translation_status + plugin_id: field + filters: + content_translation_status: + id: content_translation_status + table: taxonomy_term_field_data + field: content_translation_status + relationship: none + group_type: group + admin_label: '' + operator: '=' + value: All + group: 1 + exposed: true + expose: + operator_id: '' + label: 'Translation status' + description: '' + use_operator: false + operator: content_translation_status_op + identifier: content_translation_status + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + entity_type: taxonomy_term + entity_field: content_translation_status + plugin_id: boolean + sorts: + content_translation_status: + id: content_translation_status + table: taxonomy_term_field_data + field: content_translation_status + relationship: none + group_type: group + admin_label: '' + order: ASC + exposed: false + expose: + label: '' + entity_type: taxonomy_term + entity_field: content_translation_status + plugin_id: standard + header: { } + footer: { } + empty: { } + relationships: { } + arguments: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - user.permissions + tags: { } diff --git a/core/modules/taxonomy/tests/fixtures/update/views.view.test_taxonomy_term_view_without_content_translation_status.yml b/core/modules/taxonomy/tests/fixtures/update/views.view.test_taxonomy_term_view_without_content_translation_status.yml new file mode 100644 index 0000000..53eb09c --- /dev/null +++ b/core/modules/taxonomy/tests/fixtures/update/views.view.test_taxonomy_term_view_without_content_translation_status.yml @@ -0,0 +1,128 @@ +langcode: en +status: true +dependencies: + module: + - taxonomy + - user +id: test_taxonomy_term_view_without_content_translation_status +label: 'Test taxonomy term view without content translation status' +module: views +description: '' +tag: '' +base_table: taxonomy_term_field_data +base_field: tid +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + access: + type: perm + options: + perm: 'access content' + cache: + type: tag + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + replica: false + query_comment: '' + query_tags: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: none + options: + offset: 0 + style: + type: default + options: + grouping: { } + row_class: '' + default_row_class: true + uses_fields: false + row: + type: fields + options: + inline: { } + separator: '' + hide_empty: false + default_field_elements: true + fields: + name: + id: name + table: taxonomy_term_field_data + field: name + entity_type: taxonomy_term + entity_field: name + label: '' + alter: + alter_text: false + make_link: false + absolute: false + trim: false + word_boundary: false + ellipsis: false + strip_tags: false + html: false + hide_empty: false + empty_zero: false + type: string + settings: + link_to_entity: true + plugin_id: term_name + relationship: none + group_type: group + admin_label: '' + exclude: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_alter_empty: true + click_sort_column: value + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + convert_spaces: false + filters: { } + sorts: { } + header: { } + footer: { } + empty: { } + relationships: { } + arguments: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - user.permissions + tags: { } diff --git a/core/modules/taxonomy/tests/src/Functional/TaxonomyTermContentModerationTest.php b/core/modules/taxonomy/tests/src/Functional/TaxonomyTermContentModerationTest.php index ce5e885..67495ee 100644 --- a/core/modules/taxonomy/tests/src/Functional/TaxonomyTermContentModerationTest.php +++ b/core/modules/taxonomy/tests/src/Functional/TaxonomyTermContentModerationTest.php @@ -32,7 +32,13 @@ class TaxonomyTermContentModerationTest extends TaxonomyTestBase { protected function setUp() { parent::setUp(); - $this->drupalLogin($this->drupalCreateUser(['administer taxonomy', 'use editorial transition create_new_draft', 'use editorial transition publish'])); + $this->drupalLogin($this->drupalCreateUser([ + 'administer taxonomy', + 'use editorial transition create_new_draft', + 'use editorial transition publish', + 'view any unpublished content', + 'view latest version', + ])); // Create a vocabulary. $this->vocabulary = $this->createVocabulary(); @@ -149,4 +155,40 @@ public function testTaxonomyTermParents() { $this->assertSession()->pageTextNotContains($validation_message); } + /** + * Tests changing field values in pending revisions of taxonomy terms. + */ + public function testTaxonomyTermPendingRevisions() { + $default_term_name = 'term - default revision'; + $default_term_description = 'The default revision of a term.'; + $term = $this->createTerm($this->vocabulary, [ + 'name' => $default_term_name, + 'description' => $default_term_description, + 'langcode' => 'en', + 'moderation_state' => 'published', + ]); + + // Add a pending revision without changing the term parent. + $this->drupalGet('taxonomy/term/' . $term->id() . '/edit'); + $this->assertSession()->pageTextContains($default_term_name); + $this->assertSession()->pageTextContains($default_term_description); + + // Check the revision log message field does not appear on the term edit + // page. + $this->drupalGet('taxonomy/term/' . $term->id() . '/edit'); + $this->assertSession()->fieldNotExists('revision_log_message[0][value]'); + + $pending_term_name = 'term - pending revision'; + $pending_term_description = 'The pending revision of a term.'; + $this->drupalPostForm(NULL, [ + 'name[0][value]' => $pending_term_name, + 'description[0][value]' => $pending_term_description, + 'moderation_state[0][state]' => 'draft', + ], 'Save'); + + $this->assertSession()->pageTextContains($pending_term_name); + $this->assertSession()->pageTextContains($pending_term_description); + $this->assertSession()->pageTextNotContains($default_term_description); + } + } diff --git a/core/modules/taxonomy/tests/src/Functional/Update/TermRevisionablePublishableUpdateTest.php b/core/modules/taxonomy/tests/src/Functional/Update/TermRevisionablePublishableUpdateTest.php index 1b1cbe5..6c9618c 100644 --- a/core/modules/taxonomy/tests/src/Functional/Update/TermRevisionablePublishableUpdateTest.php +++ b/core/modules/taxonomy/tests/src/Functional/Update/TermRevisionablePublishableUpdateTest.php @@ -4,6 +4,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase; use Drupal\user\Entity\User; +use Drupal\views\Entity\View; /** * Tests the upgrade path for taxonomy terms. @@ -19,6 +20,7 @@ class TermRevisionablePublishableUpdateTest extends UpdatePathTestBase { protected function setDatabaseDumpFiles() { $this->databaseDumpFiles = [ __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.filled.standard.php.gz', + __DIR__ . '/../../../fixtures/update/drupal-8.views-taxonomy-term-publishing-status-2880149.php', ]; } @@ -51,12 +53,20 @@ public function testConversionToRevisionableAndPublishable() { $this->drupalGet('taxonomy/term/2/translations'); $assert_session->linkExists('Test root term - Spanish'); - // Check that taxonomy terms can be created, saved and then loaded. $storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term'); + + // Check that the 'content_translation_status' field has been updated + // correctly. /** @var \Drupal\taxonomy\TermInterface $term */ + $term = $storage->load(2); + $translation = $term->getTranslation('es'); + $this->assertTrue($translation->isPublished()); + + // Check that taxonomy terms can be created, saved and then loaded. $term = $storage->create([ 'name' => 'Test term', 'vid' => 'article', + 'revision_log_message' => 'Initial revision.', ]); $term->save(); @@ -65,12 +75,48 @@ public function testConversionToRevisionableAndPublishable() { $this->assertEquals('Test term', $term->label()); $this->assertEquals('article', $term->bundle()); + $this->assertEquals('Initial revision.', $term->getRevisionLogMessage()); $this->assertTrue($term->isPublished()); + } + + /** + * Tests handling of the publishing status in taxonomy term views updates. + * + * @see taxonomy_post_update_handle_publishing_status_addition_in_views() + */ + public function testPublishingStatusUpdateForTaxonomyTermViews() { + // Check that the test view was previously using the + // 'content_translation_status' field. + $config = \Drupal::config('views.view.test_taxonomy_term_view_with_content_translation_status'); + $display_options = $config->get('display.default.display_options'); + $this->assertEquals('content_translation_status', $display_options['fields']['content_translation_status']['field']); + $this->assertEquals('content_translation_status', $display_options['filters']['content_translation_status']['field']); + $this->assertEquals('content_translation_status', $display_options['sorts']['content_translation_status']['field']); + + // Check a test view without any filter. + $config = \Drupal::config('views.view.test_taxonomy_term_view_without_content_translation_status'); + $display_options = $config->get('display.default.display_options'); + $this->assertEmpty($display_options['filters']); + + $this->runUpdates(); - // Tests the revision log message field has been set to hidden. - $definitions = \Drupal::service('entity.last_installed_schema.repository')->getLastInstalledFieldStorageDefinitions('taxonomy_term'); - $form_display_options = $definitions['revision_log_message']->getDisplayOptions('form'); - $this->assertEquals('hidden', $form_display_options['region']); + // Check that a view which had a field, filter and a sort on the + // 'content_translation_status' field has been updated to use the new + // 'status' field. + $view = View::load('test_taxonomy_term_view_with_content_translation_status'); + foreach ($view->get('display') as $display) { + $this->assertEquals('status', $display['display_options']['fields']['content_translation_status']['field']); + $this->assertEquals('status', $display['display_options']['sorts']['content_translation_status']['field']); + $this->assertEquals('status', $display['display_options']['filters']['content_translation_status']['field']); + } + + // Check that a view without any filters has been updated to include a + // filter for the 'status' field. + $view = View::load('test_taxonomy_term_view_without_content_translation_status'); + foreach ($view->get('display') as $display) { + $this->assertNotEmpty($display['display_options']['filters']); + $this->assertEquals('status', $display['display_options']['filters']['status']['field']); + } } /** diff --git a/core/modules/taxonomy/tests/src/Functional/Update/TermRevisionablePublishableViewsUpdateTest.php b/core/modules/taxonomy/tests/src/Functional/Update/TermRevisionablePublishableViewsUpdateTest.php deleted file mode 100644 index ef0acfa..0000000 --- a/core/modules/taxonomy/tests/src/Functional/Update/TermRevisionablePublishableViewsUpdateTest.php +++ /dev/null @@ -1,56 +0,0 @@ -databaseDumpFiles = [ - __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz', - ]; - } - - /** - * Tests the conversion of taxonomy terms to be revisionable and publishable. - * - * @see taxonomy_post_update_add_status_filter_to_taxonomy_views() - */ - public function testAddPublishedFilterToTaxonomyTermFieldDataViews() { - $this->runUpdates(); - - $views = Views::getAllViews(); - foreach ($views as $view) { - // Only check taxonomy term views. - if ($view->get('base_table') !== 'taxonomy_term_field_data') { - continue; - } - - foreach ($view->display as $display) { - // Assert that a published filter was added. - if (!empty($display['display_options']['filters'])) { - $this->assertArrayHasKey('status', $display['display_options']['filters']); - } - } - } - } - -}