Commit 88d1f67e authored by catch's avatar catch

Issue #2896845 by amateescu, pk188, plach, timmillwood, Sam152: Provide the...

Issue #2896845 by amateescu, pk188, plach, timmillwood, Sam152: Provide the 'revision_translation_affected' base field by default for all revisionable and translatable entity types
parent 82f1a1ca
......@@ -322,7 +322,7 @@ public function isDefaultRevision($new_value = NULL) {
* {@inheritdoc}
*/
public function isRevisionTranslationAffected() {
$field_name = 'revision_translation_affected';
$field_name = $this->getEntityType()->getKey('revision_translation_affected');
return $this->hasField($field_name) ? $this->get($field_name)->value : TRUE;
}
......@@ -330,7 +330,7 @@ public function isRevisionTranslationAffected() {
* {@inheritdoc}
*/
public function setRevisionTranslationAffected($affected) {
$field_name = 'revision_translation_affected';
$field_name = $this->getEntityType()->getKey('revision_translation_affected');
if ($this->hasField($field_name)) {
$this->set($field_name, $affected);
}
......
......@@ -220,6 +220,20 @@ protected function buildBaseFieldDefinitions($entity_type_id) {
}
}
// Make sure that revisionable entity types are correctly defined.
if ($entity_type->isRevisionable() && $entity_type->isTranslatable()) {
// The 'revision_translation_affected' field should always be defined.
// This field has been added unconditionally in Drupal 8.4.0 and it is
// overriding any pre-existing definition on purpose so that any
// differences are immediately available in the status report.
$base_field_definitions[$keys['revision_translation_affected']] = BaseFieldDefinition::create('boolean')
->setLabel($this->t('Revision translation affected'))
->setDescription($this->t('Indicates if the last edit of a translation belongs to current revision.'))
->setReadOnly(TRUE)
->setRevisionable(TRUE)
->setTranslatable(TRUE);
}
// Assign base field definitions the entity type provider.
$provider = $entity_type->getProvider();
foreach ($base_field_definitions as $definition) {
......
......@@ -302,6 +302,7 @@ public function __construct($definition) {
'bundle' => '',
'langcode' => '',
'default_langcode' => 'default_langcode',
'revision_translation_affected' => 'revision_translation_affected',
];
$this->handlers += [
'access' => 'Drupal\Core\Entity\EntityAccessControlHandler',
......
......@@ -1689,8 +1689,9 @@ protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $st
// few "entity keys", in order to keep their indexes optimized.
// @todo Fix this in https://www.drupal.org/node/2841291.
$not_null_keys = $this->entityType->getKeys();
// Label fields are not necessarily required.
unset($not_null_keys['label']);
// Label and the 'revision_translation_affected' fields are not necessarily
// required.
unset($not_null_keys['label'], $not_null_keys['revision_translation_affected']);
// Because entity ID and revision ID are both serial fields in the base and
// revision table respectively, the revision ID is not known yet, when
// inserting data into the base table. Instead the revision ID in the base
......
......@@ -245,6 +245,7 @@ protected function copyData(array &$sandbox) {
$original_base_table = $original_entity_type->getBaseTable();
$revision_id_key = $temporary_entity_type->getKey('revision');
$revision_translation_affected_key = $temporary_entity_type->getKey('revision_translation_affected');
// If 'progress' is not set, then this will be the first run of the batch.
if (!isset($sandbox['progress'])) {
......@@ -287,6 +288,11 @@ protected function copyData(array &$sandbox) {
// Set the revision ID to be same as the entity ID.
$entity->set($revision_id_key, $entity_id);
// Set the 'revision_translation_affected' flag to TRUE to match the
// previous API return value: if the field was not defined the value
// returned was always TRUE.
$entity->set($revision_translation_affected_key, TRUE);
// Treat the entity as new in order to make the storage do an INSERT
// rather than an UPDATE.
$entity->enforceIsNew(TRUE);
......@@ -370,9 +376,26 @@ protected function updateFieldStorageDefinitionsToRevisionable(ContentEntityType
if ($update_cached_definitions) {
$this->entityDefinitionUpdateManager->installFieldStorageDefinition($revision_field->getName(), $entity_type->id(), $entity_type->getProvider(), $revision_field);
}
$updated_storage_definitions[$entity_type->getKey('revision')] = $revision_field;
// Add the 'revision_translation_affected' field if needed.
if ($entity_type->isTranslatable()) {
$revision_translation_affected_field = BaseFieldDefinition::create('boolean')
->setName($entity_type->getKey('revision_translation_affected'))
->setTargetEntityTypeId($entity_type->id())
->setTargetBundle(NULL)
->setLabel(new TranslatableMarkup('Revision translation affected'))
->setDescription(new TranslatableMarkup('Indicates if the last edit of a translation belongs to current revision.'))
->setReadOnly(TRUE)
->setRevisionable(TRUE)
->setTranslatable(TRUE);
if ($update_cached_definitions) {
$this->entityDefinitionUpdateManager->installFieldStorageDefinition($revision_translation_affected_field->getName(), $entity_type->id(), $entity_type->getProvider(), $revision_translation_affected_field);
}
$updated_storage_definitions[$entity_type->getKey('revision_translation_affected')] = $revision_translation_affected_field;
}
return $updated_storage_definitions;
}
......
......@@ -216,13 +216,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setSetting('target_type', 'user')
->setRevisionable(TRUE);
$fields['revision_translation_affected'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Revision translation affected'))
->setDescription(t('Indicates if the last edit of a translation belongs to current revision.'))
->setReadOnly(TRUE)
->setRevisionable(TRUE)
->setTranslatable(TRUE);
return $fields;
}
......
......@@ -400,13 +400,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
])
->setDisplayConfigurable('form', TRUE);
$fields['revision_translation_affected'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Revision translation affected'))
->setDescription(t('Indicates if the last edit of a translation belongs to current revision.'))
->setReadOnly(TRUE)
->setRevisionable(TRUE)
->setTranslatable(TRUE);
return $fields;
}
......
......@@ -126,6 +126,9 @@ public function testNormalize() {
'default_langcode' => [
['value' => TRUE],
],
'revision_translation_affected' => [
['value' => TRUE],
],
'non_rev_field' => [],
'field_test_text' => [
[
......@@ -197,6 +200,7 @@ public function testSerialize() {
'user_id' => '<user_id><target_id>' . $this->user->id() . '</target_id><target_type>' . $this->user->getEntityTypeId() . '</target_type><target_uuid>' . $this->user->uuid() . '</target_uuid><url>' . $this->user->url() . '</url></user_id>',
'revision_id' => '<revision_id><value>' . $this->entity->getRevisionId() . '</value></revision_id>',
'default_langcode' => '<default_langcode><value>1</value></default_langcode>',
'revision_translation_affected' => '<revision_translation_affected><value>1</value></revision_translation_affected>',
'non_rev_field' => '<non_rev_field/>',
'field_test_text' => '<field_test_text><value>' . $this->values['field_test_text']['value'] . '</value><format>' . $this->values['field_test_text']['format'] . '</format></field_test_text>',
];
......
......@@ -92,6 +92,9 @@ public function testMakeRevisionable() {
$entity_test_update = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
$this->assertTrue($entity_test_update->isRevisionable());
$field_storage_definitions = $this->lastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions('entity_test_update');
$this->assertTrue(isset($field_storage_definitions['revision_translation_affected']));
/** @var \Drupal\Core\Entity\Sql\SqlEntityStorageInterface $storage */
$storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
$this->assertEqual(count($storage->loadMultiple()), 102, 'All test entities were found.');
......@@ -104,6 +107,10 @@ public function testMakeRevisionable() {
$this->assertEqual($i, $revision->id());
$this->assertEqual($i, $revision->getRevisionId());
// Check that the correct initial value was provided for the
// 'revision_translation_affected' field.
$this->assertTrue($revision->revision_translation_affected->value);
$this->assertEqual($i . ' - test single property', $revision->test_single_property->value);
$this->assertEqual($i . ' - test multiple properties - value1', $revision->test_multiple_properties->value1);
......
<?php
namespace Drupal\system\Tests\Update;
use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
/**
* Tests the upgrade path for adding the 'revision_translation_affected' field.
*
* @see https://www.drupal.org/node/2896845
*
* @group Update
*/
class EntityUpdateAddRevisionTranslationAffectedTest extends UpdatePathTestBase {
use EntityDefinitionTestTrait;
use DbUpdatesTrait;
/**
* The entity manager service.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* The last installed schema repository service.
*
* @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
*/
protected $lastInstalledSchemaRepository;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->entityManager = \Drupal::entityManager();
$this->lastInstalledSchemaRepository = \Drupal::service('entity.last_installed_schema.repository');
$this->state = \Drupal::state();
}
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.0.0-rc1-filled.standard.entity_test_update_mul_rev.php.gz',
];
}
/**
* Tests the addition of the 'revision_translation_affected' base field.
*
* @covers system_update_8402
*/
public function testAddingTheRevisionTranslationAffectedField() {
// Make the entity type revisionable and translatable prior to running the
// updates.
$this->updateEntityTypeToRevisionableAndTranslatable();
// Check that the test entity type does not have the
// 'revision_translation_affected' field before running the updates.
$field_storage_definitions = $this->lastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions('entity_test_update');
$this->assertFalse(isset($field_storage_definitions['revision_translation_affected']));
$this->runUpdates();
// Check that the 'revision_translation_affected' field has been added by
// system_update_8402().
$field_storage_definitions = $this->lastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions('entity_test_update');
$this->assertTrue(isset($field_storage_definitions['revision_translation_affected']));
// Check that the correct initial value was set when the field was
// installed.
$entity = $this->entityManager->getStorage('entity_test_update')->load(1);
$this->assertTrue($entity->revision_translation_affected->value);
}
}
......@@ -77,6 +77,8 @@ protected function setDatabaseDumpFiles() {
/**
* Tests the conversion of an entity type to revisionable and publishable.
*
* @covers entity_test_update_update_8400
*/
public function testConvertToRevisionableAndPublishable() {
// Check that entity type is not revisionable nor publishable prior to
......
......@@ -17,6 +17,7 @@
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Site\Settings;
use Drupal\Core\StreamWrapper\PrivateStream;
use Drupal\Core\StreamWrapper\PublicStream;
......@@ -1984,3 +1985,43 @@ function system_update_8401() {
->clear('response')
->save();
}
/**
* Add the 'revision_translation_affected' field to all entity types.
*/
function system_update_8402() {
$definition_update_manager = \Drupal::entityDefinitionUpdateManager();
// Clear the cached entity type definitions so we get the new
// 'revision_translation_affected' entity key.
\Drupal::entityTypeManager()->clearCachedDefinitions();
// Get a list of revisionable and translatable entity types.
/** @var \Drupal\Core\Entity\ContentEntityTypeInterface[] $definitions */
$definitions = array_filter(\Drupal::entityTypeManager()->getDefinitions(), function (EntityTypeInterface $entity_type) use ($definition_update_manager) {
if ($entity_type = $definition_update_manager->getEntityType($entity_type->id())) {
return $entity_type->isRevisionable() && $entity_type->isTranslatable();
}
return FALSE;
});
foreach ($definitions as $entity_type_id => $entity_type) {
$field_name = $entity_type->getKey('revision_translation_affected');
// Install the 'revision_translation_affected' field if needed.
if (!$definition_update_manager->getFieldStorageDefinition($field_name, $entity_type_id)) {
$storage_definition = BaseFieldDefinition::create('boolean')
->setLabel(t('Revision translation affected'))
->setDescription(t('Indicates if the last edit of a translation belongs to current revision.'))
->setReadOnly(TRUE)
->setRevisionable(TRUE)
->setTranslatable(TRUE)
// Mark all pre-existing revisions as affected in order to be consistent
// with the previous API return value: if the field was not defined the
// value returned was always TRUE.
->setInitialValue(TRUE);
$definition_update_manager
->installFieldStorageDefinition($field_name, $entity_type_id, $entity_type_id, $storage_definition);
}
}
}
......@@ -79,6 +79,11 @@ function entity_test_update_entity_presave(EntityInterface $entity) {
* (e.g. content_translation_status);
* - 52 more test entities (with the IDs 51 - 102) crated, translated and saved.
*
* The 'drupal-8.0.0-rc1-filled.standard.entity_test_update_mul_rev.php.gz' db
* dump was created like the multilingual one described above, with one change:
* The annotation of the entity_test_update entity type was also manually edited
* in order to make it revisionable.
*
* @param int $start
* (optional) The entity ID to start from. Defaults to 1.
* @param int $end
......
......@@ -542,6 +542,7 @@ protected function setUpEntityWithFieldDefinition($custom_invoke_all = FALSE, $f
$this->entityType->getKeys()->willReturn($entity_keys + ['default_langcode' => 'default_langcode']);
$this->entityType->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
$this->entityType->isTranslatable()->willReturn(FALSE);
$this->entityType->isRevisionable()->willReturn(FALSE);
$this->entityType->getProvider()->willReturn('the_provider');
$this->entityType->id()->willReturn('the_entity_id');
......@@ -651,6 +652,7 @@ public function testGetFieldMap() {
$entity_type->getKeys()->willReturn(['default_langcode' => 'default_langcode']);
$entity_type->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
$entity_type->isTranslatable()->shouldBeCalled();
$entity_type->isRevisionable()->shouldBeCalled();
$entity_type->getProvider()->shouldBeCalled();
$non_content_entity_type->entityClassImplements(FieldableEntityInterface::class)->willReturn(FALSE);
......@@ -752,6 +754,7 @@ public function testGetFieldMapByFieldType() {
$entity_type->getKeys()->willReturn(['default_langcode' => 'default_langcode'])->shouldBeCalled();
$entity_type->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE)->shouldBeCalled();
$entity_type->isTranslatable()->shouldBeCalled();
$entity_type->isRevisionable()->shouldBeCalled();
$entity_type->getProvider()->shouldBeCalled();
$override_entity_type->entityClassImplements(FieldableEntityInterface::class)->willReturn(FALSE)->shouldBeCalled();
......
......@@ -58,7 +58,11 @@ public function testSet($key, $value) {
*/
public function testGetKeys($entity_keys, $expected) {
$entity_type = $this->setUpEntityType(['entity_keys' => $entity_keys]);
$this->assertSame($expected + ['default_langcode' => 'default_langcode'], $entity_type->getKeys());
$expected += [
'default_langcode' => 'default_langcode',
'revision_translation_affected' => 'revision_translation_affected',
];
$this->assertSame($expected, $entity_type->getKeys());
}
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment