From 4f9265c85d25697363d147df8f66389a268b3221 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Tue, 17 Nov 2015 10:44:05 +0000
Subject: [PATCH] Revert "Issue #2382675 by plach, yched:
 ContentEntityBase::addTranslation() should not fire hook_entity_create()"

This reverts commit 58332b2637d9b2f3f72d34d1e8701d712456eb42.
---
 .../Core/Config/Entity/ConfigEntityType.php   | 18 +++-
 .../Drupal/Core/Entity/ContentEntityBase.php  | 41 ++++++---
 .../Core/Entity/ContentEntityStorageBase.php  | 52 ++---------
 .../Entity/ContentEntityStorageInterface.php  | 31 -------
 .../Drupal/Core/Entity/ContentEntityType.php  | 16 ----
 core/lib/Drupal/Core/Entity/EntityType.php    | 14 ---
 .../KeyValueContentEntityStorage.php          | 75 ----------------
 core/lib/Drupal/Core/Entity/entity.api.php    | 88 +++----------------
 .../Core/TypedData/TranslatableInterface.php  |  8 --
 .../Tests/Entity/EntityTranslationTest.php    |  2 -
 .../keyvalue_test/keyvalue_test.module        |  2 +-
 11 files changed, 66 insertions(+), 281 deletions(-)
 delete mode 100644 core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
 delete mode 100644 core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php

diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityType.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityType.php
index 527a336f3e91..ab94c27f6e6c 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityType.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityType.php
@@ -69,6 +69,9 @@ public function __construct($definition) {
     // Always add a default 'uuid' key.
     $this->entity_keys['uuid'] = 'uuid';
     $this->entity_keys['langcode'] = 'langcode';
+    if (isset($this->handlers['storage'])) {
+      $this->checkStorageClass($this->handlers['storage']);
+    }
     $this->handlers += array(
       'storage' => 'Drupal\Core\Config\Entity\ConfigEntityStorage',
     );
@@ -132,12 +135,23 @@ public function getConfigDependencyKey() {
   /**
    * {@inheritdoc}
    *
-   * @see \Drupal\Core\Config\Entity\ConfigEntityStorage.
-   *
    * @throws \Drupal\Core\Config\Entity\Exception\ConfigEntityStorageClassException
    *   Exception thrown when the provided class is not an instance of
    *   \Drupal\Core\Config\Entity\ConfigEntityStorage.
    */
+  public function setStorageClass($class) {
+    $this->checkStorageClass($class);
+    parent::setStorageClass($class);
+  }
+
+  /**
+   * Checks that the provided class is an instance of ConfigEntityStorage.
+   *
+   * @param string $class
+   *   The class to check.
+   *
+   * @see \Drupal\Core\Config\Entity\ConfigEntityStorage.
+   */
   protected function checkStorageClass($class) {
     if (!is_a($class, 'Drupal\Core\Config\Entity\ConfigEntityStorage', TRUE)) {
       throw new ConfigEntityStorageClassException("$class is not \\Drupal\\Core\\Config\\Entity\\ConfigEntityStorage or it does not extend it");
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 1e198fe014d4..4f11f1e51227 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -808,13 +808,6 @@ public function hasTranslation($langcode) {
     return !empty($this->translations[$langcode]['status']);
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function isNewTranslation() {
-    return $this->translations[$this->activeLangcode]['status'] == static::TRANSLATION_CREATED;
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -829,11 +822,37 @@ public function addTranslation($langcode, array $values = array()) {
       throw new \InvalidArgumentException("The entity cannot be translated since it is language neutral ({$this->defaultLangcode}).");
     }
 
-    // Initialize the translation object.
-    /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
-    $storage = $this->entityManager()->getStorage($this->getEntityTypeId());
+    // Instantiate a new empty entity so default values will be populated in the
+    // specified language.
+    $entity_type = $this->getEntityType();
+
+    $default_values = array(
+      $entity_type->getKey('bundle') => $this->bundle(),
+      $this->langcodeKey => $langcode,
+    );
+    $entity = $this->entityManager()
+      ->getStorage($this->getEntityTypeId())
+      ->create($default_values);
+
+    foreach ($entity as $name => $field) {
+      if (!isset($values[$name]) && !$field->isEmpty()) {
+        $values[$name] = $field->getValue();
+      }
+    }
+    $values[$this->langcodeKey] = $langcode;
+    $values[$this->defaultLangcodeKey] = FALSE;
+
     $this->translations[$langcode]['status'] = static::TRANSLATION_CREATED;
-    return $storage->createTranslation($this, $langcode, $values);
+    $translation = $this->getTranslation($langcode);
+    $definitions = $translation->getFieldDefinitions();
+
+    foreach ($values as $name => $value) {
+      if (isset($definitions[$name]) && $definitions[$name]->isTranslatable()) {
+        $translation->values[$name][$langcode] = $value;
+      }
+    }
+
+    return $translation;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
index fb97c818b9d0..2a5475e2f2ad 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
@@ -13,10 +13,7 @@
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
-/**
- * Base class for content entity storage handlers.
- */
-abstract class ContentEntityStorageBase extends EntityStorageBase implements ContentEntityStorageInterface {
+abstract class ContentEntityStorageBase extends EntityStorageBase implements DynamicallyFieldableEntityStorageInterface {
 
   /**
    * The entity bundle key.
@@ -90,32 +87,13 @@ protected function doCreate(array $values) {
       $bundle = $values[$this->bundleKey];
     }
     $entity = new $this->entityClass(array(), $this->entityTypeId, $bundle);
-    $this->initFieldValues($entity, $values);
-    return $entity;
-  }
 
-  /**
-   * Initializes field values.
-   *
-   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
-   *   An entity object.
-   * @param array $values
-   *   (optional) An associative array of initial field values keyed by field
-   *   name. If none is provided default values will be applied.
-   * @param array $field_names
-   *   (optional) An associative array of field names to be initialized. If none
-   *   is provided all fields will be initialized.
-   */
-  protected function initFieldValues(ContentEntityInterface $entity, array $values = [], array $field_names = []) {
-    // Populate field values.
     foreach ($entity as $name => $field) {
-      if (!$field_names || isset($field_names[$name])) {
-        if (isset($values[$name])) {
-          $entity->$name = $values[$name];
-        }
-        elseif (!array_key_exists($name, $values)) {
-          $entity->get($name)->applyDefaultValue();
-        }
+      if (isset($values[$name])) {
+        $entity->$name = $values[$name];
+      }
+      elseif (!array_key_exists($name, $values)) {
+        $entity->get($name)->applyDefaultValue();
       }
       unset($values[$name]);
     }
@@ -124,23 +102,7 @@ protected function initFieldValues(ContentEntityInterface $entity, array $values
     foreach ($values as $name => $value) {
       $entity->$name = $value;
     }
-
-    // Make sure modules can alter field initial values.
-    $this->invokeHook('field_values_init', $entity);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []) {
-    $translation = $entity->getTranslation($langcode);
-    $definitions = array_filter($translation->getFieldDefinitions(), function(FieldDefinitionInterface $definition) { return $definition->isTranslatable(); });
-    $field_names = array_map(function(FieldDefinitionInterface $definition) { return $definition->getName(); }, $definitions);
-    $values[$this->langcodeKey] = $langcode;
-    $values[$this->getEntityType()->getKey('default_langcode')] = FALSE;
-    $this->initFieldValues($translation, $values, $field_names);
-    $this->invokeHook('translation_create', $entity);
-    return $translation;
+    return $entity;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
deleted file mode 100644
index f62e0ac64a24..000000000000
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\ContentEntityStorageInterface.
- */
-
-namespace Drupal\Core\Entity;
-
-/**
- * A storage that supports content entity types.
- */
-interface ContentEntityStorageInterface extends DynamicallyFieldableEntityStorageInterface {
-
-  /**
-   * Constructs a new entity translation object, without permanently saving it.
-   *
-   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
-   *   The entity object being translated.
-   * @param string $langcode
-   *   The translation language code.
-   * @param array $values
-   *   (optional) An associative array of initial field values keyed by field
-   *   name. If none is provided default values will be applied.
-   *
-   * @return \Drupal\Core\Entity\ContentEntityInterface
-   *   A new entity translation object.
-   */
-  public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []);
-
-}
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityType.php b/core/lib/Drupal/Core/Entity/ContentEntityType.php
index 8894f3348bac..0c375a980057 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityType.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityType.php
@@ -30,20 +30,4 @@ public function getConfigDependencyKey() {
     return 'content';
   }
 
-  /**
-   * {@inheritdoc}
-   *
-   * @see \Drupal\Core\Entity\ContentEntityStorageInterface.
-   *
-   * @throws \InvalidArgumentException
-   *   If the provided class does not implement
-   *   \Drupal\Core\Entity\ContentEntityStorageInterface.
-   */
-  protected function checkStorageClass($class) {
-    $required_interface = ContentEntityStorageInterface::class;
-    if (!is_subclass_of($class, $required_interface)) {
-      throw new \InvalidArgumentException("$class does not implement $required_interface");
-    }
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php
index d678623d99f0..7072ef5701d6 100644
--- a/core/lib/Drupal/Core/Entity/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/EntityType.php
@@ -276,9 +276,6 @@ public function __construct($definition) {
     $this->handlers += array(
       'access' => 'Drupal\Core\Entity\EntityAccessControlHandler',
     );
-    if (isset($this->handlers['storage'])) {
-      $this->checkStorageClass($this->handlers['storage']);
-    }
 
     // Automatically add the EntityChanged constraint if the entity type tracks
     // the changed time.
@@ -462,20 +459,9 @@ public function getStorageClass() {
    * {@inheritdoc}
    */
   public function setStorageClass($class) {
-    $this->checkStorageClass($class);
     $this->handlers['storage'] = $class;
   }
 
-  /**
-   * Checks that the provided class is an instance of ConfigEntityStorage.
-   *
-   * @param string $class
-   *   The class to check.
-   */
-  protected function checkStorageClass($class) {
-    // Nothing to check by default.
-  }
-
   /**
    * {@inheritdoc}
    */
diff --git a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php
deleted file mode 100644
index bbff156567ec..000000000000
--- a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\KeyValueStore\KeyValueContentEntityStorage.
- */
-
-namespace Drupal\Core\Entity\KeyValueStore;
-
-use Drupal\Core\Entity\ContentEntityInterface;
-use Drupal\Core\Entity\ContentEntityStorageInterface;
-use Drupal\Core\Field\FieldDefinitionInterface;
-use Drupal\Core\Field\FieldStorageDefinitionInterface;
-
-/**
- * Provides a key value backend for content entities.
- */
-class KeyValueContentEntityStorage extends KeyValueEntityStorage implements ContentEntityStorageInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function countFieldData($storage_definition, $as_bool = FALSE) {}
-
-  /**
-   * {@inheritdoc}
-   */
-  public function hasData() {}
-
-  /**
-   * {@inheritdoc}
-   */
-  public function purgeFieldData(FieldDefinitionInterface $field_definition, $batch_size) {}
-
-  /**
-   * {@inheritdoc}
-   */
-  public function finalizePurge(FieldStorageDefinitionInterface $storage_definition) {}
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onFieldDefinitionCreate(FieldDefinitionInterface $field_definition) {}
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onFieldDefinitionUpdate(FieldDefinitionInterface $field_definition, FieldDefinitionInterface $original) {}
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definition) {}
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) {}
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {}
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) {}
-
-  /**
-   * {@inheritdoc}
-   */
-  public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []) {}
-
-}
diff --git a/core/lib/Drupal/Core/Entity/entity.api.php b/core/lib/Drupal/Core/Entity/entity.api.php
index 85989bface2a..8883f78d038e 100644
--- a/core/lib/Drupal/Core/Entity/entity.api.php
+++ b/core/lib/Drupal/Core/Entity/entity.api.php
@@ -780,9 +780,10 @@ function hook_entity_bundle_delete($entity_type_id, $bundle) {
 }
 
 /**
- * Acts when creating a new entity.
+ * Act on a newly created entity.
  *
- * This hook runs after a new entity object has just been instantiated.
+ * This hook runs after a new entity object has just been instantiated. It can
+ * be used to set initial values, e.g. to provide defaults.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
  *   The entity object.
@@ -791,13 +792,16 @@ function hook_entity_bundle_delete($entity_type_id, $bundle) {
  * @see hook_ENTITY_TYPE_create()
  */
 function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
-  \Drupal::logger('example')->info('Entity created: @label', ['@label' => $entity->label()]);
+  if ($entity instanceof FieldableEntityInterface && !$entity->foo->value) {
+    $entity->foo->value = 'some_initial_value';
+  }
 }
 
 /**
- * Acts when creating a new entity of a specific type.
+ * Act on a newly created entity of a specific type.
  *
- * This hook runs after a new entity object has just been instantiated.
+ * This hook runs after a new entity object has just been instantiated. It can
+ * be used to set initial values, e.g. to provide defaults.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
  *   The entity object.
@@ -806,7 +810,9 @@ function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
  * @see hook_entity_create()
  */
 function hook_ENTITY_TYPE_create(\Drupal\Core\Entity\EntityInterface $entity) {
-  \Drupal::logger('example')->info('ENTITY_TYPE created: @label', ['@label' => $entity->label()]);
+  if (!$entity->foo->value) {
+    $entity->foo->value = 'some_initial_value';
+  }
 }
 
 /**
@@ -1005,38 +1011,6 @@ function hook_ENTITY_TYPE_update(Drupal\Core\Entity\EntityInterface $entity) {
     ->execute();
 }
 
-/**
- * Acts when creating a new entity translation.
- *
- * This hook runs after a new entity translation object has just been
- * instantiated.
- *
- * @param \Drupal\Core\Entity\EntityInterface $translation
- *   The entity object.
- *
- * @ingroup entity_crud
- * @see hook_ENTITY_TYPE_translation_create()
- */
-function hook_entity_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
-  \Drupal::logger('example')->info('Entity translation created: @label', ['@label' => $translation->label()]);
-}
-
-/**
- * Acts when creating a new entity translation of a specific type.
- *
- * This hook runs after a new entity translation object has just been
- * instantiated.
- *
- * @param \Drupal\Core\Entity\EntityInterface $translation
- *   The entity object.
- *
- * @ingroup entity_crud
- * @see hook_entity_translation_create()
- */
-function hook_ENTITY_TYPE_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
-  \Drupal::logger('example')->info('ENTITY_TYPE translation created: @label', ['@label' => $translation->label()]);
-}
-
 /**
  * Respond to creation of a new entity translation.
  *
@@ -1912,44 +1886,6 @@ function hook_entity_field_access_alter(array &$grants, array $context) {
   }
 }
 
-/**
- * Acts when initializing a fieldable entity object.
- *
- * This hook runs after a new entity object or a new entity translation object
- * has just been instantiated. It can be used to set initial values, e.g. to
- * provide defaults.
- *
- * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
- *   The entity object.
- *
- * @ingroup entity_crud
- * @see hook_ENTITY_TYPE_field_values_init()
- */
-function hook_entity_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
-  if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface && !$entity->foo->value) {
-    $entity->foo->value = 'some_initial_value';
-  }
-}
-
-/**
- * Acts when initializing a fieldable entity object.
- *
- * This hook runs after a new entity object or a new entity translation object
- * has just been instantiated. It can be used to set initial values, e.g. to
- * provide defaults.
- *
- * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
- *   The entity object.
- *
- * @ingroup entity_crud
- * @see hook_entity_field_values_init()
- */
-function hook_ENTITY_TYPE_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
-  if (!$entity->foo->value) {
-    $entity->foo->value = 'some_initial_value';
-  }
-}
-
 /**
  * Exposes "pseudo-field" components on content entities.
  *
diff --git a/core/lib/Drupal/Core/TypedData/TranslatableInterface.php b/core/lib/Drupal/Core/TypedData/TranslatableInterface.php
index f64242c0a96a..f87e7c891576 100644
--- a/core/lib/Drupal/Core/TypedData/TranslatableInterface.php
+++ b/core/lib/Drupal/Core/TypedData/TranslatableInterface.php
@@ -28,14 +28,6 @@ public function language();
    */
   public function isDefaultTranslation();
 
-  /**
-   * Checks whether the translation is new.
-   *
-   * @return bool
-   *   TRUE if the translation is new, FALSE otherwise.
-   */
-  public function isNewTranslation();
-
   /**
    * Returns the languages the data is translated to.
    *
diff --git a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php
index a2e9be4c15cc..07d0bd7b5f6c 100644
--- a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php
@@ -328,7 +328,6 @@ protected function doTestEntityTranslationAPI($entity_type) {
     // Verify that we obtain the entity object itself when we attempt to
     // retrieve a translation referring to it.
     $translation = $entity->getTranslation(LanguageInterface::LANGCODE_NOT_SPECIFIED);
-    $this->assertFalse($translation->isNewTranslation(), 'Existing translations are not marked as new.');
     $this->assertIdentical($entity, $translation, 'The translation object corresponding to a non-default language is the entity object itself when the entity is language-neutral.');
     $entity->{$langcode_key}->value = $default_langcode;
     $translation = $entity->getTranslation($default_langcode);
@@ -354,7 +353,6 @@ protected function doTestEntityTranslationAPI($entity_type) {
     $entity->name->value = $name;
     $name_translated = $langcode . '_' . $this->randomMachineName();
     $translation = $entity->addTranslation($langcode);
-    $this->assertTrue($translation->isNewTranslation(), 'Newly added translations are marked as new.');
     $this->assertNotIdentical($entity, $translation, 'The entity and the translation object differ from one another.');
     $this->assertTrue($entity->hasTranslation($langcode), 'The new translation exists.');
     $this->assertEqual($translation->language()->getId(), $langcode, 'The translation language matches the specified one.');
diff --git a/core/modules/system/tests/modules/keyvalue_test/keyvalue_test.module b/core/modules/system/tests/modules/keyvalue_test/keyvalue_test.module
index ed0599605856..d8c3666f1b72 100644
--- a/core/modules/system/tests/modules/keyvalue_test/keyvalue_test.module
+++ b/core/modules/system/tests/modules/keyvalue_test/keyvalue_test.module
@@ -11,7 +11,7 @@
 function keyvalue_test_entity_type_alter(array &$entity_types) {
   /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
   if (isset($entity_types['entity_test_label'])) {
-    $entity_types['entity_test_label']->setStorageClass('Drupal\Core\Entity\KeyValueStore\KeyValueContentEntityStorage');
+    $entity_types['entity_test_label']->setStorageClass('Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage');
     $entity_keys = $entity_types['entity_test_label']->getKeys();
     $entity_types['entity_test_label']->set('entity_keys', $entity_keys + array('uuid' => 'uuid'));
   }
-- 
GitLab