From 1023ddaffaf383f15e1ba9a06caf42992a867ac3 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org>
Date: Thu, 29 Jun 2017 13:52:17 +0100
Subject: [PATCH] Issue #2854732 by amateescu, jibran, tstoeckler: Use initial
 values for content translation metadata fields and fix existing data

---
 .../Sql/SqlContentEntityStorageSchema.php     |   2 +-
 .../content_translation.install               |  56 ++++++++++
 .../src/ContentTranslationHandler.php         |   3 +
 .../Update/ContentTranslationUpdateTest.php   | 100 ++++++++++++++++++
 .../Functional/Views/TaxonomyTermViewTest.php |   7 ++
 5 files changed, 167 insertions(+), 1 deletion(-)
 create mode 100644 core/modules/content_translation/src/Tests/Update/ContentTranslationUpdateTest.php

diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
index 0a5e7febf61c..c5751e12db8b 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
@@ -1712,7 +1712,7 @@ protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $st
 
       // Use the initial value of the field storage, if available.
       if ($initial_value && isset($initial_value[$field_column_name])) {
-        $schema['fields'][$schema_field_name]['initial'] = $initial_value[$field_column_name];
+        $schema['fields'][$schema_field_name]['initial'] = drupal_schema_get_field_value($column_schema, $initial_value[$field_column_name]);
       }
       elseif (!empty($initial_value_from_field)) {
         $schema['fields'][$schema_field_name]['initial_from_field'] = $initial_value_from_field[$field_column_name];
diff --git a/core/modules/content_translation/content_translation.install b/core/modules/content_translation/content_translation.install
index dc12d9f3fcf9..67b161c5e259 100644
--- a/core/modules/content_translation/content_translation.install
+++ b/core/modules/content_translation/content_translation.install
@@ -5,6 +5,8 @@
  * Installation functions for Content Translation module.
  */
 
+use \Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
+use \Drupal\Core\Language\LanguageInterface;
 use \Drupal\Core\Url;
 
 /**
@@ -44,3 +46,57 @@ function content_translation_update_8001() {
 function content_translation_update_8002() {
   \Drupal::service('plugin.manager.field.field_type')->clearCachedDefinitions();
 }
+
+/**
+ * Fix the initial values for content translation metadata fields.
+ */
+function content_translation_update_8400() {
+  $database = \Drupal::database();
+  /** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
+  $content_translation_manager = \Drupal::service('content_translation.manager');
+  /** @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $last_installed_schema_repository */
+  $last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository');
+  $entity_type_manager = \Drupal::entityTypeManager();
+  $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+
+  foreach ($content_translation_manager->getSupportedEntityTypes() as $entity_type_id => $entity_type_definition) {
+    $storage = $entity_type_manager->getStorage($entity_type_id);
+    if ($storage instanceof SqlEntityStorageInterface) {
+      $entity_type = $entity_definition_update_manager->getEntityType($entity_type_id);
+      $storage_definitions = $last_installed_schema_repository->getLastInstalledFieldStorageDefinitions($entity_type_id);
+
+      // Since the entity type is managed by Content Translation, we can assume
+      // that it is translatable, so we use the data and revision data tables.
+      $tables_to_update = [$entity_type->getDataTable()];
+      if ($entity_type->isRevisionable()) {
+        $tables_to_update += [$entity_type->getRevisionDataTable()];
+      }
+
+      foreach ($tables_to_update as $table_name) {
+        // Fix the values of the 'content_translation_source' field.
+        if (isset($storage_definitions['content_translation_source'])) {
+          $database->update($table_name)
+            ->fields(['content_translation_source' => LanguageInterface::LANGCODE_NOT_SPECIFIED])
+            ->isNull('content_translation_source')
+            ->execute();
+        }
+
+        // Fix the values of the 'content_translation_outdated' field.
+        if (isset($storage_definitions['content_translation_outdated'])) {
+          $database->update($table_name)
+            ->fields(['content_translation_outdated' => 0])
+            ->isNull('content_translation_outdated')
+            ->execute();
+        }
+
+        // Fix the values of the 'content_translation_status' field.
+        if (isset($storage_definitions['content_translation_status'])) {
+          $database->update($table_name)
+            ->fields(['content_translation_status' => 1])
+            ->isNull('content_translation_status')
+            ->execute();
+        }
+      }
+    }
+  }
+}
diff --git a/core/modules/content_translation/src/ContentTranslationHandler.php b/core/modules/content_translation/src/ContentTranslationHandler.php
index c1688b9d6477..7e6a7ca3ea6c 100644
--- a/core/modules/content_translation/src/ContentTranslationHandler.php
+++ b/core/modules/content_translation/src/ContentTranslationHandler.php
@@ -116,6 +116,7 @@ public function getFieldDefinitions() {
       ->setLabel(t('Translation source'))
       ->setDescription(t('The source language from which this translation was created.'))
       ->setDefaultValue(LanguageInterface::LANGCODE_NOT_SPECIFIED)
+      ->setInitialValue(LanguageInterface::LANGCODE_NOT_SPECIFIED)
       ->setRevisionable(TRUE)
       ->setTranslatable(TRUE);
 
@@ -123,6 +124,7 @@ public function getFieldDefinitions() {
       ->setLabel(t('Translation outdated'))
       ->setDescription(t('A boolean indicating whether this translation needs to be updated.'))
       ->setDefaultValue(FALSE)
+      ->setInitialValue(FALSE)
       ->setRevisionable(TRUE)
       ->setTranslatable(TRUE);
 
@@ -142,6 +144,7 @@ public function getFieldDefinitions() {
         ->setLabel(t('Translation status'))
         ->setDescription(t('A boolean indicating whether the translation is visible to non-translators.'))
         ->setDefaultValue(TRUE)
+        ->setInitialValue(TRUE)
         ->setRevisionable(TRUE)
         ->setTranslatable(TRUE);
     }
diff --git a/core/modules/content_translation/src/Tests/Update/ContentTranslationUpdateTest.php b/core/modules/content_translation/src/Tests/Update/ContentTranslationUpdateTest.php
new file mode 100644
index 000000000000..85b952c35ba8
--- /dev/null
+++ b/core/modules/content_translation/src/Tests/Update/ContentTranslationUpdateTest.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Drupal\content_translation\Tests\Update;
+
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
+use Drupal\system\Tests\Update\UpdatePathTestBase;
+
+/**
+ * Tests the upgrade path for the Content Translation module.
+ *
+ * @group Update
+ */
+class ContentTranslationUpdateTest extends UpdatePathTestBase {
+
+  use EntityDefinitionTestTrait;
+
+  /**
+   * The database connection used.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * The entity definition update manager.
+   *
+   * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
+   */
+  protected $entityDefinitionUpdateManager;
+
+  /**
+   * The entity manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->database = \Drupal::database();
+    $this->entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager();
+    $this->entityManager = \Drupal::entityManager();
+    $this->state = \Drupal::state();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.0.0-rc1-filled.standard.entity_test_update_mul.php.gz',
+    ];
+  }
+
+  /**
+   * Tests that initial values for metadata fields are populated correctly.
+   */
+  public function testContentTranslationUpdate8400() {
+    $this->updateEntityTypeToTranslatable();
+
+    // The test database dump contains NULL values for
+    // 'content_translation_source', 'content_translation_outdated' and
+    // 'content_translation_status' for the first 50 test entities.
+    // @see _entity_test_update_create_test_entities()
+    $first_entity_record = $this->database->select('entity_test_update_data', 'etud')
+      ->fields('etud')
+      ->condition('etud.id', 1)
+      ->execute()
+      ->fetchAllAssoc('id');
+    $this->assertNull($first_entity_record[1]->content_translation_source);
+    $this->assertNull($first_entity_record[1]->content_translation_outdated);
+    $this->assertNull($first_entity_record[1]->content_translation_status);
+
+    $this->runUpdates();
+
+    // After running the updates, all those fields should be populated with
+    // their default values.
+    $first_entity_record = $this->database->select('entity_test_update_data', 'etud')
+      ->fields('etud')
+      ->condition('etud.id', 1)
+      ->execute()
+      ->fetchAllAssoc('id');
+    $this->assertEqual(LanguageInterface::LANGCODE_NOT_SPECIFIED, $first_entity_record[1]->content_translation_source);
+    $this->assertEqual(0, $first_entity_record[1]->content_translation_outdated);
+    $this->assertEqual(1, $first_entity_record[1]->content_translation_status);
+  }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyTermViewTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyTermViewTest.php
index 15f47231ca54..5f70d9042b29 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyTermViewTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyTermViewTest.php
@@ -5,6 +5,7 @@
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\node\Entity\Node;
 use Drupal\user\Entity\Role;
 use Drupal\user\RoleInterface;
 use Drupal\views\Views;
@@ -124,6 +125,12 @@ public function testTaxonomyTermView() {
     // query anymore.
     // @see \Drupal\views\Plugin\views\filter\LanguageFilter::query()
     $node->delete();
+
+    // We also have to remove the nodes created by the parent ::setUp() method
+    // if we want to be able to uninstall the Content Translation module.
+    foreach (Node::loadMultiple() as $node) {
+      $node->delete();
+    }
     \Drupal::service('module_installer')->uninstall(['content_translation', 'language']);
 
     $view = Views::getView('taxonomy_term');
-- 
GitLab