From 4831e2ee7c9185a6f00b418ea4d88f8cf32f80b0 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Mon, 16 Jun 2014 00:19:55 +0100
Subject: [PATCH] Issue #2226267 by fago, xjm, Berdir: Improve default value
 handling of fields to be consistent.

---
 .../lib/Drupal/Core/Field/FieldDefinition.php | 54 ++++++++++++-
 .../Core/Field/FieldDefinitionInterface.php   | 12 +--
 core/lib/Drupal/Core/Field/FieldItemList.php  | 20 +++--
 .../Core/Field/FieldItemListInterface.php     | 27 +++++++
 .../Plugin/Field/FieldType/StringItem.php     |  2 +-
 core/modules/aggregator/src/Entity/Feed.php   |  4 +-
 core/modules/comment/src/Entity/Comment.php   | 14 ++--
 .../Field/FieldType/DateTimeFieldItemList.php |  8 +-
 ...nfigurableEntityReferenceFieldItemList.php |  8 +-
 .../field/src/Entity/FieldInstanceConfig.php  | 27 +++----
 .../src/Tests/FieldAttachStorageTest.php      |  2 +-
 .../modules/field_test/field_test.field.inc   |  4 +-
 core/modules/node/src/Entity/Node.php         |  8 +-
 .../src/Tests/EntitySerializationTest.php     |  1 -
 core/modules/shortcut/src/Entity/Shortcut.php |  2 +-
 .../Entity/EntityFieldDefaultValueTest.php    | 11 +++
 .../modules/entity_test/entity_test.module    | 27 ++++---
 .../src/Entity/EntityTestDefaultValue.php     | 43 ++++++++++
 core/modules/taxonomy/src/Entity/Term.php     |  4 +-
 .../TaxonomyTermReferenceFieldItemList.php    | 81 ++++++++++---------
 core/modules/user/src/Entity/User.php         | 12 +--
 .../Tests/Core/Entity/FieldDefinitionTest.php |  8 +-
 22 files changed, 254 insertions(+), 125 deletions(-)
 create mode 100644 core/modules/system/tests/modules/entity_test/src/Entity/EntityTestDefaultValue.php

diff --git a/core/lib/Drupal/Core/Field/FieldDefinition.php b/core/lib/Drupal/Core/Field/FieldDefinition.php
index c6a661e4e452..006ceb008a28 100644
--- a/core/lib/Drupal/Core/Field/FieldDefinition.php
+++ b/core/lib/Drupal/Core/Field/FieldDefinition.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\Field;
 
-use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
 use Drupal\Core\TypedData\ListDataDefinition;
 use Drupal\field\FieldException;
@@ -378,8 +378,56 @@ public function isDisplayConfigurable($display_context) {
   /**
    * {@inheritdoc}
    */
-  public function getDefaultValue(EntityInterface $entity) {
-    return $this->getSetting('default_value');
+  public function getDefaultValue(ContentEntityInterface $entity) {
+    // Allow custom default values function.
+    if (isset($this->definition['default_value_callback'])) {
+      $value = call_user_func($this->definition['default_value_callback'], $entity, $this);
+    }
+    else {
+      $value = isset($this->definition['default_value']) ? $this->definition['default_value'] : NULL;
+    }
+    // Allow the field type to process default values.
+    $field_item_list_class = $this->getClass();
+    return $field_item_list_class::processDefaultValue($value, $entity, $this);
+  }
+
+  /**
+   * Sets a custom default value callback.
+   *
+   * If set, the callback overrides any set default value.
+   *
+   * @param string|array $callback
+   *   The callback to invoke for getting the default value. The callback will
+   *   be invoked with the following arguments:
+   *   - \Drupal\Core\Entity\ContentEntityInterface $entity
+   *     The entity being created.
+   *   - \Drupal\Core\Field\FieldDefinitionInterface $definition
+   *     The field definition.
+   *   It should return the default value as documented by
+   *   \Drupal\Core\Field\FieldDefinitionInterface::getDefaultValue().
+   *
+   * @return $this
+   */
+  public function setDefaultValueCallback($callback) {
+    $this->definition['default_value_callback'] = $callback;
+    return $this;
+  }
+
+  /**
+   * Sets a default value.
+   *
+   * Note that if a default value callback is set, it will take precedence over
+   * any value set here.
+   *
+   * @param mixed $value
+   *   The default value in the format as returned by
+   *   \Drupal\Core\Field\FieldDefinitionInterface::getDefaultValue().
+   *
+   * @return $this
+   */
+  public function setDefaultValue($value) {
+    $this->definition['default_value'] = $value;
+    return $this;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
index a6c91907ccac..20838192027b 100644
--- a/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\Field;
 
-use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\TypedData\ListDataDefinitionInterface;
 
 /**
@@ -114,19 +114,19 @@ public function isRequired();
   /**
    * Returns the default value for the field in a newly created entity.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity being created.
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
+   *   The entity for which the default value is generated.
    *
    * @return mixed
    *   The default value for the field, as accepted by
-   *   Drupal\field\Plugin\Core\Entity\FieldConfig::setValue(). This can be
-   *   either:
+   *   \Drupal\field\Plugin\Core\Entity\FieldItemListInterface::setValue(). This
+   *   can be either:
    *   - a literal, in which case it will be assigned to the first property of
    *     the first item.
    *   - a numerically indexed array of items, each item being a property/value
    *     array.
    *   - NULL or array() for no default value.
    */
-  public function getDefaultValue(EntityInterface $entity);
+  public function getDefaultValue(ContentEntityInterface $entity);
 
 }
diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php
index 2e0b442b9b18..c715902e1452 100644
--- a/core/lib/Drupal/Core/Field/FieldItemList.php
+++ b/core/lib/Drupal/Core/Field/FieldItemList.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Field;
 
+use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\TypedData\DataDefinitionInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
@@ -202,7 +203,7 @@ public function defaultAccess($operation = 'view', AccountInterface $account = N
    * {@inheritdoc}
    */
   public function applyDefaultValue($notify = TRUE) {
-    $value = $this->getDefaultValue();
+    $value = $this->getFieldDefinition()->getDefaultValue($this->getEntity());
 
     // NULL or array() mean "no default value", but  0, '0' and the empty string
     // are valid default values.
@@ -216,16 +217,6 @@ public function applyDefaultValue($notify = TRUE) {
     return $this;
   }
 
-  /**
-   * Returns the default value for the field.
-   *
-   * @return array
-   *   The default value for the field.
-   */
-  protected function getDefaultValue() {
-    return $this->getFieldDefinition()->getDefaultValue($this->getEntity());
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -347,6 +338,13 @@ public function defaultValuesFormSubmit(array $element, array &$form, array &$fo
     return $this->getValue();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function processDefaultValue($default_value, ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
+    return $default_value;
+  }
+
   /**
    * Returns the widget object used in default value form.
    *
diff --git a/core/lib/Drupal/Core/Field/FieldItemListInterface.php b/core/lib/Drupal/Core/Field/FieldItemListInterface.php
index 651f1e472419..0a1583b85aaf 100644
--- a/core/lib/Drupal/Core/Field/FieldItemListInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldItemListInterface.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Field;
 
+use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Access\AccessibleInterface;
 use Drupal\Core\TypedData\ListInterface;
@@ -228,4 +229,30 @@ public function defaultValuesFormValidate(array $element, array &$form, array &$
    */
   public function defaultValuesFormSubmit(array $element, array &$form, array &$form_state);
 
+  /**
+   * Processes the default value before being applied.
+   *
+   * Defined or configured default values of a field might need some processing
+   * in order to be a valid value for the field type; e.g., a date field could
+   * process the defined value of 'NOW' to a valid date.
+   *
+   * @param mixed
+   *   The default value as defined for the field.
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
+   *   The entity for which the default value is generated.
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $definition
+   *   The definition of the field.
+   *
+   * @return mixed
+   *   The default value for the field, as accepted by
+   *   \Drupal\field\Plugin\Core\Entity\FieldItemListInterface::setValue(). This
+   *   can be either:
+   *   - a literal, in which case it will be assigned to the first property of
+   *     the first item.
+   *   - a numerically indexed array of items, each item being a property/value
+   *     array.
+   *   - NULL or array() for no default value.
+   */
+  public static function processDefaultValue($default_value, ContentEntityInterface $entity, FieldDefinitionInterface $definition);
+
 }
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
index fe7c4f47d104..ba72dc38012e 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains \Drupal\Core\Entity\Plugin\Field\FieldType\StringItem.
+ * Contains \Drupal\Core\Field\Plugin\Field\FieldType\StringItem.
  */
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
diff --git a/core/modules/aggregator/src/Entity/Feed.php b/core/modules/aggregator/src/Entity/Feed.php
index 4222152ba2be..8c091c3caade 100644
--- a/core/modules/aggregator/src/Entity/Feed.php
+++ b/core/modules/aggregator/src/Entity/Feed.php
@@ -176,12 +176,12 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['checked'] = FieldDefinition::create('timestamp')
       ->setLabel(t('Checked'))
       ->setDescription(t('Last time feed was checked for new items, as Unix timestamp.'))
-      ->setSetting('default_value', 0);
+      ->setDefaultValue(0);
 
     $fields['queued'] = FieldDefinition::create('timestamp')
       ->setLabel(t('Queued'))
       ->setDescription(t('Time when this feed was queued for refresh, 0 if not queued.'))
-      ->setSetting('default_value', 0);
+      ->setDefaultValue(0);
 
     $fields['link'] = FieldDefinition::create('uri')
       ->setLabel(t('Link'))
diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php
index d7c7d11c4e1a..1164756f0c1f 100644
--- a/core/modules/comment/src/Entity/Comment.php
+++ b/core/modules/comment/src/Entity/Comment.php
@@ -233,18 +233,14 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['uid'] = FieldDefinition::create('entity_reference')
       ->setLabel(t('User ID'))
       ->setDescription(t('The user ID of the comment author.'))
-      ->setSettings(array(
-        'target_type' => 'user',
-        'default_value' => 0,
-      ));
+      ->setSetting('target_type', 'user')
+      ->setDefaultValue(0);
 
     $fields['name'] = FieldDefinition::create('string')
       ->setLabel(t('Name'))
       ->setDescription(t("The comment author's name."))
-      ->setSettings(array(
-        'default_value' => '',
-        'max_length' => 60,
-      ))
+      ->setSetting('max_length', 60)
+      ->setDefaultValue('')
       ->addConstraint('CommentName', array());
 
     $fields['mail'] = FieldDefinition::create('email')
@@ -274,7 +270,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['status'] = FieldDefinition::create('boolean')
       ->setLabel(t('Publishing status'))
       ->setDescription(t('A boolean indicating whether the comment is published.'))
-      ->setSetting('default_value', TRUE);
+      ->setDefaultValue(TRUE);
 
     $fields['thread'] = FieldDefinition::create('string')
       ->setLabel(t('Thread place'))
diff --git a/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeFieldItemList.php b/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeFieldItemList.php
index 81fa6df7a545..75a59ec4eb96 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeFieldItemList.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeFieldItemList.php
@@ -8,6 +8,8 @@
 namespace Drupal\datetime\Plugin\Field\FieldType;
 
 use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemList;
 
 /**
@@ -61,14 +63,14 @@ public function defaultValuesFormSubmit(array $element, array &$form, array &$fo
   /**
    * {@inheritdoc}
    */
-  public function getDefaultValue() {
-    $default_value = parent::getDefaultValue();
+  public static function processDefaultValue($default_value, ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
+    $default_value = parent::processDefaultValue($default_value, $entity, $definition);
 
     if (isset($default_value[0]['default_date']) && $default_value[0]['default_date'] == static::DEFAULT_VALUE_NOW) {
       // A default value should be in the format and timezone used for date
       // storage.
       $date = new DrupalDateTime('now', DATETIME_STORAGE_TIMEZONE);
-      $storage_format = $this->getFieldDefinition()->getSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE ? DATETIME_DATE_STORAGE_FORMAT: DATETIME_DATETIME_STORAGE_FORMAT;
+      $storage_format = $definition->getSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE ? DATETIME_DATE_STORAGE_FORMAT: DATETIME_DATETIME_STORAGE_FORMAT;
       $value = $date->format($storage_format);
       // We only provide a default value for the first item, as do all fields.
       // Otherwise, there is no way to clear out unwanted values on multiple value
diff --git a/core/modules/entity_reference/src/Plugin/Field/FieldType/ConfigurableEntityReferenceFieldItemList.php b/core/modules/entity_reference/src/Plugin/Field/FieldType/ConfigurableEntityReferenceFieldItemList.php
index bf9e38b424ec..3d4bec31aafb 100644
--- a/core/modules/entity_reference/src/Plugin/Field/FieldType/ConfigurableEntityReferenceFieldItemList.php
+++ b/core/modules/entity_reference/src/Plugin/Field/FieldType/ConfigurableEntityReferenceFieldItemList.php
@@ -8,6 +8,8 @@
 namespace Drupal\entity_reference\Plugin\Field\FieldType;
 
 use Drupal\Core\Field\EntityReferenceFieldItemList;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
 
 /**
  * Represents a configurable entity_reference entity field.
@@ -17,8 +19,8 @@ class ConfigurableEntityReferenceFieldItemList extends EntityReferenceFieldItemL
   /**
    * {@inheritdoc}
    */
-  protected function getDefaultValue() {
-    $default_value = parent::getDefaultValue();
+  public static function processDefaultValue($default_value, ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
+    $default_value = parent::processDefaultValue($default_value, $entity, $definition);
 
     if ($default_value) {
       // Convert UUIDs to numeric IDs.
@@ -33,7 +35,7 @@ protected function getDefaultValue() {
         }
       }
       if ($uuids) {
-        $target_type = $this->getSetting('target_type');
+        $target_type = $definition->getSetting('target_type');
         $entity_ids = \Drupal::entityQuery($target_type)
           ->condition('uuid', $uuids, 'IN')
           ->execute();
diff --git a/core/modules/field/src/Entity/FieldInstanceConfig.php b/core/modules/field/src/Entity/FieldInstanceConfig.php
index 11ab930f7956..77d1cf3d8b63 100644
--- a/core/modules/field/src/Entity/FieldInstanceConfig.php
+++ b/core/modules/field/src/Entity/FieldInstanceConfig.php
@@ -9,7 +9,7 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
-use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Field\FieldDefinition;
 use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
@@ -154,14 +154,10 @@ class FieldInstanceConfig extends ConfigEntityBase implements FieldInstanceConfi
    * The name of a callback function that returns default values.
    *
    * The function will be called with the following arguments:
-   * - \Drupal\Core\Entity\EntityInterface $entity
+   * - \Drupal\Core\Entity\ContentEntityInterface $entity
    *   The entity being created.
-   * - \Drupal\field\Entity\FieldConfig $field
-   *   The field object.
-   * - \Drupal\field\Entity\FieldInstanceConfig $instance
-   *   The field instance object.
-   * - string $langcode
-   *   The language of the entity being created.
+   * - \Drupal\Core\Field\FieldDefinitionInterface $definition
+   *   The field definition.
    * It should return an array of default values, in the same format as the
    * $default_value property.
    *
@@ -588,14 +584,17 @@ public function isMultiple() {
   /**
    * {@inheritdoc}
    */
-  public function getDefaultValue(EntityInterface $entity) {
-    if (!empty($this->default_value_function)) {
-      $function = $this->default_value_function;
-      return $function($entity, $this->getField(), $this, $entity->language()->id);
+  public function getDefaultValue(ContentEntityInterface $entity) {
+    // Allow custom default values function.
+    if ($function = $this->default_value_function) {
+      $value = call_user_func($function, $entity, $this);
     }
-    elseif (!empty($this->default_value)) {
-      return $this->default_value;
+    else {
+      $value = $this->default_value;
     }
+    // Allow the field type to process default values.
+    $field_item_list_class = $this->getClass();
+    return $field_item_list_class::processDefaultValue($value, $entity, $this);
   }
 
   /**
diff --git a/core/modules/field/src/Tests/FieldAttachStorageTest.php b/core/modules/field/src/Tests/FieldAttachStorageTest.php
index 492496c1eb78..9ec1d7cb42b8 100644
--- a/core/modules/field/src/Tests/FieldAttachStorageTest.php
+++ b/core/modules/field/src/Tests/FieldAttachStorageTest.php
@@ -220,7 +220,7 @@ function testFieldAttachSaveEmptyDataDefaultValue() {
 
     // Verify that fields are populated with default values.
     $entity_init = entity_create($entity_type, array('id' => 1, 'revision_id' => 1));
-    $default = field_test_default_value($entity_init, $this->field, $this->instance);
+    $default = field_test_default_value($entity_init, $this->instance);
     $this->assertEqual($entity_init->{$this->field_name}->getValue(), $default, 'Default field value correctly populated.');
 
     // Insert: Field is NULL.
diff --git a/core/modules/field/tests/modules/field_test/field_test.field.inc b/core/modules/field/tests/modules/field_test/field_test.field.inc
index 79b57a3c0215..e43a7e79b572 100644
--- a/core/modules/field/tests/modules/field_test/field_test.field.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.field.inc
@@ -5,7 +5,7 @@
  * Defines a field type and its formatters and widgets.
  */
 
-use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Session\AccountInterface;
@@ -31,7 +31,7 @@ function field_test_field_config_update_forbid(FieldConfigInterface $field, Fiel
 /**
  * Sample 'default value' callback.
  */
-function field_test_default_value(EntityInterface $entity, $field, $instance) {
+function field_test_default_value(ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
   return array(array('value' => 99));
 }
 
diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php
index 0cdac075c865..90ab790d645e 100644
--- a/core/modules/node/src/Entity/Node.php
+++ b/core/modules/node/src/Entity/Node.php
@@ -357,7 +357,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setRequired(TRUE)
       ->setTranslatable(TRUE)
       ->setRevisionable(TRUE)
-      ->setSetting('default_value', '')
+      ->setDefaultValue('')
       ->setSetting('max_length', 255)
       ->setDisplayOptions('view', array(
         'label' => 'hidden',
@@ -453,11 +453,11 @@ public static function bundleFieldDefinitions(EntityTypeInterface $entity_type,
 
     $options = $node_type->getModuleSettings('node')['options'];
     $fields['status'] = clone $base_field_definitions['status'];
-    $fields['status']->setSetting('default_value', !empty($options['status']) ? NODE_PUBLISHED : NODE_NOT_PUBLISHED);
+    $fields['status']->setDefaultValue(!empty($options['status']) ? NODE_PUBLISHED : NODE_NOT_PUBLISHED);
     $fields['promote'] = clone $base_field_definitions['promote'];
-    $fields['promote']->setSetting('default_value', !empty($options['promote']) ? NODE_PROMOTED : NODE_NOT_PROMOTED);
+    $fields['promote']->setDefaultValue(!empty($options['promote']) ? NODE_PROMOTED : NODE_NOT_PROMOTED);
     $fields['sticky'] = clone $base_field_definitions['sticky'];
-    $fields['sticky']->setSetting('default_value', !empty($options['sticky']) ? NODE_STICKY : NODE_NOT_STICKY);
+    $fields['sticky']->setDefaultValue(!empty($options['sticky']) ? NODE_STICKY : NODE_NOT_STICKY);
 
     return $fields;
   }
diff --git a/core/modules/serialization/src/Tests/EntitySerializationTest.php b/core/modules/serialization/src/Tests/EntitySerializationTest.php
index 6266454ba9dd..fc01ced7da91 100644
--- a/core/modules/serialization/src/Tests/EntitySerializationTest.php
+++ b/core/modules/serialization/src/Tests/EntitySerializationTest.php
@@ -8,7 +8,6 @@
 namespace Drupal\serialization\Tests;
 
 use Drupal\Core\Language\LanguageInterface;
-use Symfony\Component\Serializer\Serializer;
 use Drupal\Component\Utility\String;
 
 /**
diff --git a/core/modules/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php
index 80f0261d6958..ee76e9f0affa 100644
--- a/core/modules/shortcut/src/Entity/Shortcut.php
+++ b/core/modules/shortcut/src/Entity/Shortcut.php
@@ -174,7 +174,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setDescription(t('The name of the shortcut.'))
       ->setRequired(TRUE)
       ->setTranslatable(TRUE)
-      ->setSetting('default_value', '')
+      ->setDefaultValue('')
       ->setSetting('max_length', 255)
       ->setDisplayOptions('form', array(
         'type' => 'string',
diff --git a/core/modules/system/src/Tests/Entity/EntityFieldDefaultValueTest.php b/core/modules/system/src/Tests/Entity/EntityFieldDefaultValueTest.php
index aaa544cb08fd..61b9aae6e340 100644
--- a/core/modules/system/src/Tests/Entity/EntityFieldDefaultValueTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityFieldDefaultValueTest.php
@@ -59,4 +59,15 @@ protected function assertDefaultValues($entity_type) {
     $this->assertTrue(Uuid::isValid($entity->uuid->value), String::format('%entity_type: Default UUID', array('%entity_type' => $entity_type)));
     $this->assertEqual($entity->name->getValue(), array(0 => array('value' => NULL)), 'Field has one empty value by default.');
   }
+
+  /**
+   * Tests custom default value callbacks.
+   */
+  public function testDefaultValueCallback() {
+    $entity = $this->entityManager->getStorage('entity_test_default_value')->create();
+    // The description field has a default value callback for testing, see
+    // entity_test_field_default_value().
+    $this->assertEqual($entity->description->value, 'description_' . $entity->language()->id);
+  }
+
 }
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index 6c3a078dc695..864e302164db 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -6,13 +6,12 @@
  */
 
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\entity\Entity\EntityFormDisplay;
-use Drupal\field\FieldConfigInterface;
-use Drupal\field\FieldInstanceConfigInterface;
 
 /**
  * Filter that limits test entity list to revisable ones.
@@ -419,17 +418,19 @@ function entity_test_entity_test_mul_translation_delete(EntityInterface $transla
 /**
  * Field default value callback.
  *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity the field belongs to.
- * @param \Drupal\field\FieldConfigInterface $field
- *   The field for which default values should be provided.
- * @param \Drupal\field\FieldInstanceConfigInterface $instance
- *   The field instance for which default values should be provided.
- * @param string $langcode
- *   The field language code to fill-in with the default value.
- */
-function entity_test_field_default_value(EntityInterface $entity, FieldConfigInterface $field, FieldInstanceConfigInterface $instance, $langcode) {
-  return array(array('value' => $field->getName() . '_' . $langcode));
+ * @param \Drupal\Core\Entity\ContentEntityInterface $entity
+ *   The entity being created.
+ * @param \Drupal\Core\Field\FieldDefinitionInterface $definition
+ *   The field definition.
+ *
+ * @return array
+ *   An array of default values, in the same format as the $default_value
+ *   property.
+ *
+ * @see \Drupal\field\Entity\FieldInstanceConfig::$default_value
+ */
+function entity_test_field_default_value(ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
+  return array(array('value' => $definition->getName() . '_' . $entity->language()->id));
 }
 
 /**
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestDefaultValue.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestDefaultValue.php
new file mode 100644
index 000000000000..89fc15b879e9
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestDefaultValue.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity_test\Entity\EntityTestDefaultValue.
+ */
+
+namespace Drupal\entity_test\Entity;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\FieldDefinition;
+
+/**
+ * Defines a test entity class for testing default values.
+ *
+ * @ContentEntityType(
+ *   id = "entity_test_default_value",
+ *   label = @Translation("Test entity for default values"),
+ *   base_table = "entity_test_default_value",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "uuid" = "uuid",
+ *     "bundle" = "type"
+ *   }
+ * )
+ */
+class EntityTestDefaultValue extends EntityTest {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
+    $fields = parent::baseFieldDefinitions($entity_type);
+
+    $fields['description'] = FieldDefinition::create('string')
+      ->setLabel(t('Some custom description'))
+      ->setDefaultValueCallback('entity_test_field_default_value');
+
+    return $fields;
+  }
+
+}
diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php
index a31f0808edff..603286154066 100644
--- a/core/modules/taxonomy/src/Entity/Term.php
+++ b/core/modules/taxonomy/src/Entity/Term.php
@@ -156,7 +156,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['weight'] = FieldDefinition::create('integer')
       ->setLabel(t('Weight'))
       ->setDescription(t('The weight of this term in relation to other terms.'))
-      ->setSetting('default_value', 0);
+      ->setDefaultValue(0);
 
     // @todo Convert this to an entity_reference field, see
     // https://drupal.org/node/1915056
@@ -165,7 +165,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setDescription(t('The parents of this term.'))
       ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)
       // Save new terms with no parents by default.
-      ->setSetting('default_value', 0)
+      ->setDefaultValue(0)
       ->setSetting('unsigned', TRUE)
       ->addConstraint('TermParent', array());
 
diff --git a/core/modules/taxonomy/src/Plugin/Field/FieldType/TaxonomyTermReferenceFieldItemList.php b/core/modules/taxonomy/src/Plugin/Field/FieldType/TaxonomyTermReferenceFieldItemList.php
index 70ce2c28d3ec..bae89fd64194 100644
--- a/core/modules/taxonomy/src/Plugin/Field/FieldType/TaxonomyTermReferenceFieldItemList.php
+++ b/core/modules/taxonomy/src/Plugin/Field/FieldType/TaxonomyTermReferenceFieldItemList.php
@@ -8,52 +8,14 @@
 namespace Drupal\taxonomy\Plugin\Field\FieldType;
 
 use Drupal\Core\Field\EntityReferenceFieldItemList;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
 
 /**
  * Represents a configurable taxonomy_term_reference entity field item list.
  */
 class TaxonomyTermReferenceFieldItemList extends EntityReferenceFieldItemList {
 
-  /**
-   * {@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) {
-        $entity_ids = \Drupal::entityQuery('taxonomy_term')
-          ->condition('uuid', $uuids, 'IN')
-          ->execute();
-        $entities = \Drupal::entityManager()
-          ->getStorage('taxonomy_term')
-          ->loadMultiple($entity_ids);
-
-        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}
    */
@@ -76,4 +38,43 @@ public function defaultValuesFormSubmit(array $element, array &$form, array &$fo
     return $default_value;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function processDefaultValue($default_value, ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
+    $default_value = parent::processDefaultValue($default_value, $entity, $definition);
+
+    // Convert UUIDs to numeric IDs.
+    $uuids = array();
+    foreach ($default_value as $delta => $properties) {
+      $uuids[$delta] = $properties['target_uuid'];
+    }
+    if ($uuids) {
+      $entity_ids = \Drupal::entityQuery('taxonomy_term')
+        ->condition('uuid', $uuids, 'IN')
+        ->execute();
+      $entities = \Drupal::entityManager()
+        ->getStorage('taxonomy_term')
+        ->loadMultiple($entity_ids);
+
+      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;
+  }
+
 }
diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php
index 3cc77b730fdd..d6cfe967ccb0 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -470,7 +470,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['name'] = FieldDefinition::create('string')
       ->setLabel(t('Name'))
       ->setDescription(t('The name of this user.'))
-      ->setSetting('default_value', '')
+      ->setDefaultValue('')
       ->setPropertyConstraints('value', array(
         // No Length constraint here because the UserName constraint also covers
         // that.
@@ -485,7 +485,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['mail'] = FieldDefinition::create('email')
       ->setLabel(t('Email'))
       ->setDescription(t('The email of this user.'))
-      ->setSetting('default_value', '')
+      ->setDefaultValue('')
       ->setPropertyConstraints('value', array('UserMailUnique' => array()));
 
     // @todo Convert to a text field in https://drupal.org/node/1548204.
@@ -504,7 +504,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['status'] = FieldDefinition::create('boolean')
       ->setLabel(t('User status'))
       ->setDescription(t('Whether the user is active or blocked.'))
-      ->setSetting('default_value', FALSE);
+      ->setDefaultValue(FALSE);
 
     $fields['created'] = FieldDefinition::create('created')
       ->setLabel(t('Created'))
@@ -513,17 +513,17 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['access'] = FieldDefinition::create('timestamp')
       ->setLabel(t('Last access'))
       ->setDescription(t('The time that the user last accessed the site.'))
-      ->setSetting('default_value', 0);
+      ->setDefaultValue(0);
 
     $fields['login'] = FieldDefinition::create('timestamp')
       ->setLabel(t('Last login'))
       ->setDescription(t('The time that the user last logged in.'))
-      ->setSetting('default_value', 0);
+      ->setDefaultValue(0);
 
     $fields['init'] = FieldDefinition::create('email')
       ->setLabel(t('Initial email'))
       ->setDescription(t('The email address used for initial account creation.'))
-      ->setSetting('default_value', '');
+      ->setDefaultValue('');
 
     // @todo Convert this to entity_reference_field, see
     // https://drupal.org/node/2044859.
diff --git a/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php b/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php
index 9637ccbf372b..d620399953d9 100644
--- a/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php
@@ -149,12 +149,14 @@ public function testDefaultFieldSettings() {
    */
   public function testFieldDefaultValue() {
     $definition = FieldDefinition::create($this->fieldType);
-    $setting = 'default_value';
     $value = $this->randomName();
-    $definition->setSetting($setting, $value);
-    $entity = $this->getMockBuilder('Drupal\Core\Entity\Entity')
+    $definition->setDefaultValue($value);
+    $entity = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase')
       ->disableOriginalConstructor()
       ->getMock();
+    // Set the field item list class to be used to avoid requiring the typed
+    // data manager to retrieve it.
+    $definition->setClass('Drupal\Core\Field\FieldItemList');
     $this->assertEquals($value, $definition->getDefaultValue($entity));
   }
 
-- 
GitLab