Commit dc3d8a01 authored by alexpott's avatar alexpott

Issue #2431329 by plach: Make (content) translation language available as a field definition

parent 1483ef10
...@@ -81,6 +81,13 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C ...@@ -81,6 +81,13 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
*/ */
protected $langcodeKey; protected $langcodeKey;
/**
* The default langcode entity key.
*
* @var string
*/
protected $defaultLangcodeKey;
/** /**
* Language code identifying the entity active language. * Language code identifying the entity active language.
* *
...@@ -144,6 +151,7 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans ...@@ -144,6 +151,7 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans
$this->entityTypeId = $entity_type; $this->entityTypeId = $entity_type;
$this->entityKeys['bundle'] = $bundle ? $bundle : $this->entityTypeId; $this->entityKeys['bundle'] = $bundle ? $bundle : $this->entityTypeId;
$this->langcodeKey = $this->getEntityType()->getKey('langcode'); $this->langcodeKey = $this->getEntityType()->getKey('langcode');
$this->defaultLangcodeKey = $this->getEntityType()->getKey('default_langcode');
foreach ($values as $key => $value) { foreach ($values as $key => $value) {
// If the key matches an existing property set the value to the property // If the key matches an existing property set the value to the property
...@@ -242,6 +250,13 @@ public function isDefaultRevision($new_value = NULL) { ...@@ -242,6 +250,13 @@ public function isDefaultRevision($new_value = NULL) {
return $return; return $return;
} }
/**
* {@inheritdoc}
*/
public function isDefaultTranslation() {
return $this->activeLangcode === LanguageInterface::LANGCODE_DEFAULT;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -543,14 +558,38 @@ public function onChange($name) { ...@@ -543,14 +558,38 @@ public function onChange($name) {
} }
} }
// Update the default internal language cache. switch ($name) {
if ($name == $this->langcodeKey) { case $this->langcodeKey:
$this->setDefaultLangcode(); if ($this->isDefaultTranslation()) {
if (isset($this->translations[$this->defaultLangcode])) { // Update the default internal language cache.
$message = String::format('A translation already exists for the specified language (@langcode).', array('@langcode' => $this->defaultLangcode)); $this->setDefaultLangcode();
throw new \InvalidArgumentException($message); if (isset($this->translations[$this->defaultLangcode])) {
} $message = String::format('A translation already exists for the specified language (@langcode).', array('@langcode' => $this->defaultLangcode));
$this->updateFieldLangcodes($this->defaultLangcode); throw new \InvalidArgumentException($message);
}
$this->updateFieldLangcodes($this->defaultLangcode);
}
else {
// @todo Allow the translation language to be changed. See
// https://www.drupal.org/node/2443989.
$items = $this->get($this->langcodeKey);
if ($items->value != $this->activeLangcode) {
$items->setValue($this->activeLangcode, FALSE);
$message = String::format('The translation language cannot be changed (@langcode).', array('@langcode' => $this->activeLangcode));
throw new \LogicException($message);
}
}
break;
case $this->defaultLangcodeKey:
// @todo Use a standard method to make the default_langcode field
// read-only. See https://www.drupal.org/node/2443991.
if (isset($this->values[$this->defaultLangcodeKey])) {
$this->get($this->defaultLangcodeKey)->setValue($this->isDefaultTranslation(), FALSE);
$message = String::format('The default translation flag cannot be changed (@langcode).', array('@langcode' => $this->activeLangcode));
throw new \LogicException($message);
}
break;
} }
} }
...@@ -684,6 +723,8 @@ public function addTranslation($langcode, array $values = array()) { ...@@ -684,6 +723,8 @@ public function addTranslation($langcode, array $values = array()) {
$values[$name] = $field->getValue(); $values[$name] = $field->getValue();
} }
} }
$values[$this->langcodeKey] = $langcode;
$values[$this->defaultLangcodeKey] = FALSE;
$this->translations[$langcode]['status'] = static::TRANSLATION_CREATED; $this->translations[$langcode]['status'] = static::TRANSLATION_CREATED;
$translation = $this->getTranslation($langcode); $translation = $this->getTranslation($langcode);
...@@ -691,7 +732,7 @@ public function addTranslation($langcode, array $values = array()) { ...@@ -691,7 +732,7 @@ public function addTranslation($langcode, array $values = array()) {
foreach ($values as $name => $value) { foreach ($values as $name => $value) {
if (isset($definitions[$name]) && $definitions[$name]->isTranslatable()) { if (isset($definitions[$name]) && $definitions[$name]->isTranslatable()) {
$translation->$name = $value; $translation->values[$name][$langcode] = $value;
} }
} }
......
...@@ -401,14 +401,35 @@ public function getBaseFieldDefinitions($entity_type_id) { ...@@ -401,14 +401,35 @@ public function getBaseFieldDefinitions($entity_type_id) {
protected function buildBaseFieldDefinitions($entity_type_id) { protected function buildBaseFieldDefinitions($entity_type_id) {
$entity_type = $this->getDefinition($entity_type_id); $entity_type = $this->getDefinition($entity_type_id);
$class = $entity_type->getClass(); $class = $entity_type->getClass();
$keys = array_filter($entity_type->getKeys());
// Fail with an exception for non-fieldable entity types. // Fail with an exception for non-fieldable entity types.
if (!$entity_type->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) { if (!$entity_type->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) {
throw new \LogicException(String::format('Getting the base fields is not supported for entity type @type.', array('@type' => $entity_type->getLabel()))); throw new \LogicException(String::format('Getting the base fields is not supported for entity type @type.', array('@type' => $entity_type->getLabel())));
} }
// Retrieve base field definitions and assign them the entity type provider. // Retrieve base field definitions.
/** @var FieldStorageDefinitionInterface[] $base_field_definitions */
$base_field_definitions = $class::baseFieldDefinitions($entity_type); $base_field_definitions = $class::baseFieldDefinitions($entity_type);
// Make sure translatable entity types are correctly defined.
if ($entity_type->isTranslatable()) {
// The langcode field should always be translatable if the entity type is.
if (isset($keys['langcode']) && isset($base_field_definitions[$keys['langcode']])) {
$base_field_definitions[$keys['langcode']]->setTranslatable(TRUE);
}
// A default_langcode field should always be defined.
if (!isset($base_field_definitions[$keys['default_langcode']])) {
$base_field_definitions[$keys['default_langcode']] = BaseFieldDefinition::create('boolean')
->setLabel($this->t('Default translation'))
->setDescription($this->t('A flag indicating whether this is the default translation.'))
->setTranslatable(TRUE)
->setRevisionable(TRUE)
->setDefaultValue(TRUE);
}
}
// Assign base field definitions the entity type provider.
$provider = $entity_type->getProvider(); $provider = $entity_type->getProvider();
foreach ($base_field_definitions as $definition) { foreach ($base_field_definitions as $definition) {
// @todo Remove this check once FieldDefinitionInterface exposes a proper // @todo Remove this check once FieldDefinitionInterface exposes a proper
...@@ -450,15 +471,32 @@ protected function buildBaseFieldDefinitions($entity_type_id) { ...@@ -450,15 +471,32 @@ protected function buildBaseFieldDefinitions($entity_type_id) {
// Ensure defined entity keys are there and have proper revisionable and // Ensure defined entity keys are there and have proper revisionable and
// translatable values. // translatable values.
$keys = array_filter($entity_type->getKeys()); foreach (array_intersect_key($keys, array_flip(['id', 'revision', 'uuid', 'bundle'])) as $key => $field_name) {
foreach ($keys as $key => $field_name) { if (!isset($base_field_definitions[$field_name])) {
if (isset($base_field_definitions[$field_name]) && in_array($key, array('id', 'revision', 'uuid', 'bundle')) && $base_field_definitions[$field_name]->isRevisionable()) { throw new \LogicException(String::format('The @field field definition does not exist and it is used as @key entity key.', array(
throw new \LogicException(String::format('The @field field cannot be revisionable as it is used as @key entity key.', array('@field' => $base_field_definitions[$field_name]->getLabel(), '@key' => $key))); '@field' => $base_field_definitions[$field_name]->getLabel(),
'@key' => $key,
)));
}
if ($base_field_definitions[$field_name]->isRevisionable()) {
throw new \LogicException(String::format('The @field field cannot be revisionable as it is used as @key entity key.', array(
'@field' => $base_field_definitions[$field_name]->getLabel(),
'@key' => $key,
)));
} }
if (isset($base_field_definitions[$field_name]) && in_array($key, array('id', 'revision', 'uuid', 'bundle', 'langcode')) && $base_field_definitions[$field_name]->isTranslatable()) { if ($base_field_definitions[$field_name]->isTranslatable()) {
throw new \LogicException(String::format('The @field field cannot be translatable as it is used as @key entity key.', array('@field' => $base_field_definitions[$field_name]->getLabel(), '@key' => $key))); throw new \LogicException(String::format('The @field field cannot be translatable as it is used as @key entity key.', array(
'@field' => $base_field_definitions[$field_name]->getLabel(),
'@key' => $key,
)));
} }
} }
// Make sure translatable entity types define the "langcode" field properly.
if ($entity_type->isTranslatable() && (!isset($keys['langcode']) || !isset($base_field_definitions[$keys['langcode']]) || !$base_field_definitions[$keys['langcode']]->isTranslatable())) {
throw new \LogicException(String::format('The @entity_type entity type cannot be translatable as it does not define a translatable "langcode" field.', array('@entity_type' => $entity_type->getLabel())));
}
return $base_field_definitions; return $base_field_definitions;
} }
......
...@@ -238,6 +238,7 @@ public function __construct($definition) { ...@@ -238,6 +238,7 @@ public function __construct($definition) {
'revision' => '', 'revision' => '',
'bundle' => '', 'bundle' => '',
'langcode' => '', 'langcode' => '',
'default_langcode' => 'default_langcode',
); );
$this->handlers += array( $this->handlers += array(
'access' => 'Drupal\Core\Entity\EntityAccessControlHandler', 'access' => 'Drupal\Core\Entity\EntityAccessControlHandler',
......
...@@ -192,6 +192,14 @@ public function getFields($include_computed = TRUE); ...@@ -192,6 +192,14 @@ public function getFields($include_computed = TRUE);
* *
* @param string $field_name * @param string $field_name
* The name of the field which is changed. * The name of the field which is changed.
*
* @throws \InvalidArgumentException
* When trying to assign a value to the language field that matches an
* existing translation.
* @throws \LogicException
* When trying to change:
* - The language of a translation.
* - The value of the flag identifying the default translation object.
*/ */
public function onChange($field_name); public function onChange($field_name);
......
...@@ -182,8 +182,8 @@ public function addField($field, $type, $langcode) { ...@@ -182,8 +182,8 @@ public function addField($field, $type, $langcode) {
$table = $this->ensureEntityTable($index_prefix, $sql_column, $type, $langcode, $base_table, $entity_id_field, $entity_tables); $table = $this->ensureEntityTable($index_prefix, $sql_column, $type, $langcode, $base_table, $entity_id_field, $entity_tables);
// If there is a field storage (some specifiers are not, like // If there is a field storage (some specifiers are not), check for case
// default_langcode), check for case sensitivity. // sensitivity.
if ($field_storage) { if ($field_storage) {
$column = $field_storage->getMainPropertyName(); $column = $field_storage->getMainPropertyName();
$base_field_property_definitions = $field_storage->getPropertyDefinitions(); $base_field_property_definitions = $field_storage->getPropertyDefinitions();
......
...@@ -66,6 +66,13 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt ...@@ -66,6 +66,13 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
*/ */
protected $langcodeKey = FALSE; protected $langcodeKey = FALSE;
/**
* The default language entity key.
*
* @var string
*/
protected $defaultLangcodeKey = FALSE;
/** /**
* The base table of the entity. * The base table of the entity.
* *
...@@ -200,7 +207,7 @@ protected function initTableLayout() { ...@@ -200,7 +207,7 @@ protected function initTableLayout() {
if ($translatable) { if ($translatable) {
$this->dataTable = $this->entityType->getDataTable() ?: $this->entityTypeId . '_field_data'; $this->dataTable = $this->entityType->getDataTable() ?: $this->entityTypeId . '_field_data';
$this->langcodeKey = $this->entityType->getKey('langcode'); $this->langcodeKey = $this->entityType->getKey('langcode');
$this->defaultLangcodeKey = $this->entityType->getKey('default_langcode') ?: 'default_langcode'; $this->defaultLangcodeKey = $this->entityType->getKey('default_langcode');
} }
if ($revisionable && $translatable) { if ($revisionable && $translatable) {
$this->revisionDataTable = $this->entityType->getRevisionDataTable() ?: $this->entityTypeId . '_field_revision'; $this->revisionDataTable = $this->entityType->getRevisionDataTable() ?: $this->entityTypeId . '_field_revision';
...@@ -342,11 +349,7 @@ public function getTableMapping(array $storage_definitions = NULL) { ...@@ -342,11 +349,7 @@ public function getTableMapping(array $storage_definitions = NULL) {
// the data table. // the data table.
$table_mapping $table_mapping
->setFieldNames($this->baseTable, $key_fields) ->setFieldNames($this->baseTable, $key_fields)
->setFieldNames($this->dataTable, array_values(array_diff($all_fields, array($this->uuidKey)))) ->setFieldNames($this->dataTable, array_values(array_diff($all_fields, array($this->uuidKey))));
// Add the denormalized 'default_langcode' field to the mapping. Its
// value is identical to the query expression
// "base_table.langcode = data_table.langcode"
->setExtraColumns($this->dataTable, array('default_langcode'));
} }
elseif ($revisionable && $translatable) { elseif ($revisionable && $translatable) {
// The revisionable multilingual layout stores key field values in the // The revisionable multilingual layout stores key field values in the
...@@ -356,31 +359,20 @@ public function getTableMapping(array $storage_definitions = NULL) { ...@@ -356,31 +359,20 @@ public function getTableMapping(array $storage_definitions = NULL) {
// holds the data field values for all non-revisionable fields. The data // holds the data field values for all non-revisionable fields. The data
// field values of revisionable fields are denormalized in the data // field values of revisionable fields are denormalized in the data
// table, as well. // table, as well.
$table_mapping->setFieldNames($this->baseTable, array_values(array_diff($key_fields, array($this->langcodeKey)))); $table_mapping->setFieldNames($this->baseTable, array_values($key_fields));
// Like in the multilingual, non-revisionable case the UUID is not // Like in the multilingual, non-revisionable case the UUID is not
// in the data table. Additionally, do not store revision metadata // in the data table. Additionally, do not store revision metadata
// fields in the data table. // fields in the data table.
$data_fields = array_values(array_diff($all_fields, array($this->uuidKey), $revision_metadata_fields)); $data_fields = array_values(array_diff($all_fields, array($this->uuidKey), $revision_metadata_fields));
$table_mapping $table_mapping->setFieldNames($this->dataTable, $data_fields);
->setFieldNames($this->dataTable, $data_fields)
// Add the denormalized 'default_langcode' field to the mapping. Its
// value is identical to the query expression
// "base_langcode = data_table.langcode" where "base_langcode" is
// the language code of the default revision.
->setExtraColumns($this->dataTable, array('default_langcode'));
$revision_base_fields = array_merge(array($this->idKey, $this->revisionKey, $this->langcodeKey), $revision_metadata_fields); $revision_base_fields = array_merge(array($this->idKey, $this->revisionKey, $this->langcodeKey), $revision_metadata_fields);
$table_mapping->setFieldNames($this->revisionTable, $revision_base_fields); $table_mapping->setFieldNames($this->revisionTable, $revision_base_fields);
$revision_data_key_fields = array($this->idKey, $this->revisionKey, $this->langcodeKey); $revision_data_key_fields = array($this->idKey, $this->revisionKey, $this->langcodeKey);
$revision_data_fields = array_diff($revisionable_fields, $revision_metadata_fields, array($this->langcodeKey)); $revision_data_fields = array_diff($revisionable_fields, $revision_metadata_fields, array($this->langcodeKey));
$table_mapping $table_mapping->setFieldNames($this->revisionDataTable, array_merge($revision_data_key_fields, $revision_data_fields));
->setFieldNames($this->revisionDataTable, array_merge($revision_data_key_fields, $revision_data_fields))
// Add the denormalized 'default_langcode' field to the mapping. Its
// value is identical to the query expression
// "revision_table.langcode = data_table.langcode".
->setExtraColumns($this->revisionDataTable, array('default_langcode'));
} }
// Add dedicated tables. // Add dedicated tables.
...@@ -673,12 +665,14 @@ protected function attachPropertyData(array &$entities) { ...@@ -673,12 +665,14 @@ protected function attachPropertyData(array &$entities) {
$table_mapping = $this->getTableMapping(); $table_mapping = $this->getTableMapping();
if ($this->revisionDataTable) { if ($this->revisionDataTable) {
// Find revisioned fields that are not entity keys. // Find revisioned fields that are not entity keys. Exclude the langcode
$fields = array_diff($table_mapping->getFieldNames($this->revisionDataTable), $table_mapping->getFieldNames($this->baseTable)); // key as the base table holds only the default language.
$base_fields = array_diff($table_mapping->getFieldNames($this->baseTable), array($this->langcodeKey));
$fields = array_diff($table_mapping->getFieldNames($this->revisionDataTable), $base_fields);
// Find fields that are not revisioned or entity keys. Data fields have // Find fields that are not revisioned or entity keys. Data fields have
// the same value regardless of entity revision. // the same value regardless of entity revision.
$data_fields = array_diff($table_mapping->getFieldNames($this->dataTable), $fields, $table_mapping->getFieldNames($this->baseTable)); $data_fields = array_diff($table_mapping->getFieldNames($this->dataTable), $fields, $base_fields);
if ($data_fields) { if ($data_fields) {
$fields = array_merge($fields, $data_fields); $fields = array_merge($fields, $data_fields);
$query->leftJoin($this->dataTable, 'data', "(revision.$this->idKey = data.$this->idKey)"); $query->leftJoin($this->dataTable, 'data', "(revision.$this->idKey = data.$this->idKey)");
...@@ -703,10 +697,9 @@ protected function attachPropertyData(array &$entities) { ...@@ -703,10 +697,9 @@ protected function attachPropertyData(array &$entities) {
// Field values in default language are stored with // Field values in default language are stored with
// LanguageInterface::LANGCODE_DEFAULT as key. // LanguageInterface::LANGCODE_DEFAULT as key.
$langcode = empty($values['default_langcode']) ? $values[$this->langcodeKey] : LanguageInterface::LANGCODE_DEFAULT; $langcode = empty($values[$this->defaultLangcodeKey]) ? $values[$this->langcodeKey] : LanguageInterface::LANGCODE_DEFAULT;
$translations[$id][$langcode] = TRUE; $translations[$id][$langcode] = TRUE;
foreach ($fields as $field_name) { foreach ($fields as $field_name) {
$columns = $table_mapping->getColumnNames($field_name); $columns = $table_mapping->getColumnNames($field_name);
// Do not key single-column fields by property name. // Do not key single-column fields by property name.
...@@ -776,13 +769,13 @@ protected function buildPropertyQuery(QueryInterface $entity_query, array $value ...@@ -776,13 +769,13 @@ protected function buildPropertyQuery(QueryInterface $entity_query, array $value
// apply to the default language. See http://drupal.org/node/1866330. // apply to the default language. See http://drupal.org/node/1866330.
// Default to the original entity language if not explicitly specified // Default to the original entity language if not explicitly specified
// otherwise. // otherwise.
if (!array_key_exists('default_langcode', $values)) { if (!array_key_exists($this->defaultLangcodeKey, $values)) {
$values['default_langcode'] = 1; $values[$this->defaultLangcodeKey] = 1;
} }
// If the 'default_langcode' flag is explicitly not set, we do not care // If the 'default_langcode' flag is explicitly not set, we do not care
// whether the queried values are in the original entity language or not. // whether the queried values are in the original entity language or not.
elseif ($values['default_langcode'] === NULL) { elseif ($values[$this->defaultLangcodeKey] === NULL) {
unset($values['default_langcode']); unset($values[$this->defaultLangcodeKey]);
} }
} }
...@@ -1156,8 +1149,6 @@ protected function mapToDataStorageRecord(EntityInterface $entity, $table_name = ...@@ -1156,8 +1149,6 @@ protected function mapToDataStorageRecord(EntityInterface $entity, $table_name =
$table_name = $this->dataTable; $table_name = $this->dataTable;
} }
$record = $this->mapToStorageRecord($entity, $table_name); $record = $this->mapToStorageRecord($entity, $table_name);
$record->{$this->langcodeKey} = $entity->language()->getId();
$record->default_langcode = intval($record->{$this->langcodeKey} == $entity->getUntranslated()->language()->getId());
return $record; return $record;
} }
...@@ -1171,7 +1162,7 @@ protected function mapToDataStorageRecord(EntityInterface $entity, $table_name = ...@@ -1171,7 +1162,7 @@ protected function mapToDataStorageRecord(EntityInterface $entity, $table_name =
* The revision id. * The revision id.
*/ */
protected function saveRevision(EntityInterface $entity) { protected function saveRevision(EntityInterface $entity) {
$record = $this->mapToStorageRecord($entity, $this->revisionTable); $record = $this->mapToStorageRecord($entity->getUntranslated(), $this->revisionTable);
$entity->preSaveRevision($this, $record); $entity->preSaveRevision($this, $record);
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
use Drupal\Component\Utility\String; use Drupal\Component\Utility\String;
use Drupal\Core\Database\Connection; use Drupal\Core\Database\Connection;
use Drupal\Core\Database\DatabaseException;
use Drupal\Core\Entity\ContentEntityTypeInterface; use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageException; use Drupal\Core\Entity\EntityStorageException;
...@@ -163,7 +164,6 @@ public function requiresFieldStorageSchemaChanges(FieldStorageDefinitionInterfac ...@@ -163,7 +164,6 @@ public function requiresFieldStorageSchemaChanges(FieldStorageDefinitionInterfac
$storage_definition->hasCustomStorage() != $original->hasCustomStorage() || $storage_definition->hasCustomStorage() != $original->hasCustomStorage() ||
$storage_definition->getSchema() != $original->getSchema() || $storage_definition->getSchema() != $original->getSchema() ||
$storage_definition->isRevisionable() != $original->isRevisionable() || $storage_definition->isRevisionable() != $original->isRevisionable() ||
$storage_definition->isTranslatable() != $original->isTranslatable() ||
$table_mapping->allowsSharedTableStorage($storage_definition) != $table_mapping->allowsSharedTableStorage($original) || $table_mapping->allowsSharedTableStorage($storage_definition) != $table_mapping->allowsSharedTableStorage($original) ||
$table_mapping->requiresDedicatedTableStorage($storage_definition) != $table_mapping->requiresDedicatedTableStorage($original) $table_mapping->requiresDedicatedTableStorage($storage_definition) != $table_mapping->requiresDedicatedTableStorage($original)
) { ) {
...@@ -386,8 +386,17 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $ ...@@ -386,8 +386,17 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
// Only configurable fields currently support purging, so prevent deletion // Only configurable fields currently support purging, so prevent deletion
// of ones we can't purge if they have existing data. // of ones we can't purge if they have existing data.
// @todo Add purging to all fields: https://www.drupal.org/node/2282119. // @todo Add purging to all fields: https://www.drupal.org/node/2282119.
if (!($storage_definition instanceof FieldStorageConfigInterface) && $this->storage->countFieldData($storage_definition, TRUE)) { try {
throw new FieldStorageDefinitionUpdateForbiddenException('Unable to delete a field with data that can\'t be purged.'); if (!($storage_definition instanceof FieldStorageConfigInterface) && $this->storage->countFieldData($storage_definition, TRUE)) {
throw new FieldStorageDefinitionUpdateForbiddenException('Unable to delete a field with data that cannot be purged.');
}
}
catch (DatabaseException $e) {
// This may happen when changing field storage schema, since we are not
// able to use a table mapping matching the passed storage definition.
// @todo Revisit this once we are able to instantiate the table mapping
// properly. See https://www.drupal.org/node/2274017.
return;
} }
// Retrieve a table mapping which contains the deleted field still. // Retrieve a table mapping which contains the deleted field still.
...@@ -506,13 +515,6 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res ...@@ -506,13 +515,6 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res
$schema[$table_name] = array_merge_recursive($schema[$table_name], $this->getSharedTableFieldSchema($storage_definition, $table_name, $column_names)); $schema[$table_name] = array_merge_recursive($schema[$table_name], $this->getSharedTableFieldSchema($storage_definition, $table_name, $column_names));
} }
} }
// Add the schema for extra fields.
foreach ($table_mapping->getExtraColumns($table_name) as $column_name) {
if ($column_name == 'default_langcode') {
$this->addDefaultLangcodeSchema($schema[$table_name]);
}
}
} }
// Process tables after having gathered field information. // Process tables after having gathered field information.
...@@ -726,25 +728,6 @@ protected function getFieldForeignKeys($field_name, array $field_schema, array $ ...@@ -726,25 +728,6 @@ protected function getFieldForeignKeys($field_name, array $field_schema, array $
return $foreign_keys; return $foreign_keys;
} }
/**
* Returns the schema for the 'default_langcode' metadata field.
*
* @param array $schema
* The table schema to add the field schema to, passed by reference.
*
* @return array
* A schema field array for the 'default_langcode' metadata field.
*/
protected function addDefaultLangcodeSchema(&$schema) {
$schema['fields']['default_langcode'] = array(
'description' => 'Boolean indicating whether field values are in the default entity language.',
'type' => 'int',
'size' => 'tiny',
'not null' => TRUE,
'default' => 1,
);
}
/** /**
* Loads stored schema data for the given entity type definition. * Loads stored schema data for the given entity type definition.
* *
......
...@@ -13,13 +13,21 @@ ...@@ -13,13 +13,21 @@
interface TranslatableInterface { interface TranslatableInterface {
/** /**
* Returns the default language. * Returns the translation language.
* *
* @return \Drupal\Core\Language\LanguageInterface * @return \Drupal\Core\Language\LanguageInterface
* The language object. * The language object.
*/ */
public function language(); public function language();
/**
* Checks whether the translation is the default one.
*
* @return bool
* TRUE if the translation is the default one, FALSE otherwise.
*/
public function isDefaultTranslation();
/** /**
* Returns the languages the data is translated to. * Returns the languages the data is translated to.
* *
......
...@@ -167,6 +167,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ...@@ -167,6 +167,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['langcode'] = BaseFieldDefinition::create('language') $fields['langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Language')) ->setLabel(t('Language'))
->setDescription(t('The custom block language code.')) ->setDescription(t('The custom block language code.'))
->setTranslatable(TRUE)
->setRevisionable(TRUE) ->setRevisionable(TRUE)
->setDisplayOptions('view', array( ->setDisplayOptions('view', array(
'type' => 'hidden', 'type' => 'hidden',
......
...@@ -224,6 +224,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ...@@ -224,6 +224,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['langcode'] = BaseFieldDefinition::create('language') $fields['langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Language')) ->setLabel(t('Language'))
->setDescription(t('The comment language code.')) ->setDescription(t('The comment language code.'))
->setTranslatable(TRUE)
->setDisplayOptions('view', array( ->setDisplayOptions('view', array(
'type' => 'hidden', 'type' => 'hidden',
)) ))
......
...@@ -102,7 +102,7 @@ function _content_translation_form_language_content_settings_form_alter(array &$ ...@@ -102,7 +102,7 @@ function _content_translation_form_language_content_settings_form_alter(array &$
foreach ($fields as $field_name => $definition) { foreach ($fields as $field_name => $definition) {
// Allow to configure only fields supporting multilingual storage. // Allow to configure only fields supporting multilingual storage.
// We skip our own fields as they are always translatable. // We skip our own fields as they are always translatable.
if (!empty($storage_definitions[$field_name]) && $storage_definitions[$field_name]->isTranslatable() && $storage_definitions[$field_name]->getProvider() != 'content_translation') { if (!empty($storage_definitions[$field_name]) && $storage_definitions[$field_name]->isTranslatable() && $storage_definitions[$field_name]->getProvider() != 'content_translation' && $field_name != $entity_type->getKey('langcode') && $field_name != $entity_type->getKey('default_langcode')) {
$form['settings'][$entity_type_id][$bundle]['fields'][$field_name] = array( $form['settings'][$entity_type_id][$bundle]['fields'][$field_name] = array(
'#label' => $definition->getLabel(), '#label' => $definition->getLabel(),
'#type' => 'checkbox', '#type' => 'checkbox',
......
...@@ -360,6 +360,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ...@@ -360,6 +360,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['langcode'] = BaseFieldDefinition::create('language') $fields['langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Language')) ->setLabel(t('Language'))
->setDescription(t('The menu link language code.')) ->setDescription(t('The menu link language code.'))
->setTranslatable(TRUE)
->setDisplayOptions('view', array( ->setDisplayOptions('view', array(
'type' => 'hidden', 'type' => 'hidden',
)) ))
......
...@@ -353,6 +353,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ...@@ -353,6 +353,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['langcode'] = BaseFieldDefinition::create('language') $fields['langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Language')) ->setLabel(t('Language'))
->setDescription(t('The node language code.')) ->setDescription(t('The node language code.'))
->setTranslatable(TRUE)
->setRevisionable(TRUE) ->setRevisionable(TRUE)
->setDisplayOptions('view', array( ->setDisplayOptions('view', array(
'type' => 'hidden', 'type' => 'hidden',
......
...@@ -95,21 +95,24 @@ public function testNormalize() { ...@@ -95,21 +95,24 @@ public function testNormalize() {
'type' => array( 'type' => array(
array('value' => 'entity_test_mulrev'), array('value' => 'entity_test_mulrev'),
), ),
'created' => array(
array('value' => $this->entity->created->value),
),
'user_id' => array( 'user_id' => array(
array('target_id' => $this->values['user_id']), array('target_id' => $this->values['user_id']),
), ),
'revision_id' => array( 'revision_id' => array(
array('value' => 1), array('value' => 1),
), ),
'default_langcode' => array(
array('value' => TRUE),
),
'field_test_text' => array(