Commit 5ce9bcd9 authored by catch's avatar catch

Issue #2880149 by amateescu, timmillwood, Manuel Garcia, Jo Fitzgerald,...

Issue #2880149 by amateescu, timmillwood, Manuel Garcia, Jo Fitzgerald, fidodido06, plach, blazey, hchonov, benjifisher, lbodiguel, vpeltot, larowlan, dixon_, jojototh, Wim Leers, catch, jibran, Berdir, mikelutz, pameeela, webchick, Bojhan: Convert taxonomy terms to be revisionable
parent 2af907ca
......@@ -2,9 +2,7 @@
namespace Drupal\taxonomy\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityPublishedTrait;
use Drupal\Core\Entity\EditorialContentEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
......@@ -40,16 +38,24 @@
* },
* base_table = "taxonomy_term_data",
* data_table = "taxonomy_term_field_data",
* revision_table = "taxonomy_term_revision",
* revision_data_table = "taxonomy_term_field_revision",
* uri_callback = "taxonomy_term_uri",
* translatable = TRUE,
* entity_keys = {
* "id" = "tid",
* "revision" = "revision_id",
* "bundle" = "vid",
* "label" = "name",
* "langcode" = "langcode",
* "uuid" = "uuid",
* "published" = "status",
* },
* revision_metadata_keys = {
* "revision_user" = "revision_user",
* "revision_created" = "revision_created",
* "revision_log_message" = "revision_log_message",
* },
* bundle_entity_type = "taxonomy_vocabulary",
* field_ui_base_route = "entity.taxonomy_vocabulary.overview_form",
* common_reference_target = TRUE,
......@@ -59,13 +65,13 @@
* "edit-form" = "/taxonomy/term/{taxonomy_term}/edit",
* "create" = "/taxonomy/term",
* },
* permission_granularity = "bundle"
* permission_granularity = "bundle",
* constraints = {
* "TaxonomyHierarchy" = {}
* }
* )
*/
class Term extends ContentEntityBase implements TermInterface {
use EntityChangedTrait;
use EntityPublishedTrait;
class Term extends EditorialContentEntityBase implements TermInterface {
/**
* {@inheritdoc}
......@@ -120,8 +126,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
$fields = parent::baseFieldDefinitions($entity_type);
// Add the published field.
$fields += static::publishedBaseFieldDefinitions($entity_type);
// @todo Remove the usage of StatusItem in
// https://www.drupal.org/project/drupal/issues/2936864.
$fields['status']->getItemDefinition()->setClass(StatusItem::class);
......@@ -139,6 +143,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['name'] = BaseFieldDefinition::create('string')
->setLabel(t('Name'))
->setTranslatable(TRUE)
->setRevisionable(TRUE)
->setRequired(TRUE)
->setSetting('max_length', 255)
->setDisplayOptions('view', [
......@@ -155,6 +160,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['description'] = BaseFieldDefinition::create('text_long')
->setLabel(t('Description'))
->setTranslatable(TRUE)
->setRevisionable(TRUE)
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'text_default',
......@@ -181,7 +187,14 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['changed'] = BaseFieldDefinition::create('changed')
->setLabel(t('Changed'))
->setDescription(t('The time that the term was last edited.'))
->setTranslatable(TRUE);
->setTranslatable(TRUE)
->setRevisionable(TRUE);
// @todo Keep this field hidden until we have a revision UI for terms.
// @see https://www.drupal.org/project/drupal/issues/2936995
$fields['revision_log_message']->setDisplayOptions('form', [
'region' => 'hidden',
]);
return $fields;
}
......
......@@ -2,6 +2,7 @@
namespace Drupal\taxonomy\Form;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
use Drupal\Core\Entity\EntityRepositoryInterface;
......@@ -251,6 +252,64 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
}
}
$args = [
'%capital_name' => Unicode::ucfirst($taxonomy_vocabulary->label()),
'%name' => $taxonomy_vocabulary->label(),
];
if ($this->currentUser()->hasPermission('administer taxonomy') || $this->currentUser()->hasPermission('edit terms in ' . $taxonomy_vocabulary->id())) {
switch ($vocabulary_hierarchy) {
case VocabularyInterface::HIERARCHY_DISABLED:
$help_message = $this->t('You can reorganize the terms in %capital_name using their drag-and-drop handles, and group terms under a parent term by sliding them under and to the right of the parent.', $args);
break;
case VocabularyInterface::HIERARCHY_SINGLE:
$help_message = $this->t('%capital_name contains terms grouped under parent terms. You can reorganize the terms in %capital_name using their drag-and-drop handles.', $args);
break;
case VocabularyInterface::HIERARCHY_MULTIPLE:
$help_message = $this->t('%capital_name contains terms with multiple parents. Drag and drop of terms with multiple parents is not supported, but you can re-enable drag-and-drop support by editing each term to include only a single parent.', $args);
break;
}
}
else {
switch ($vocabulary_hierarchy) {
case VocabularyInterface::HIERARCHY_DISABLED:
$help_message = $this->t('%capital_name contains the following terms.', $args);
break;
case VocabularyInterface::HIERARCHY_SINGLE:
$help_message = $this->t('%capital_name contains terms grouped under parent terms', $args);
break;
case VocabularyInterface::HIERARCHY_MULTIPLE:
$help_message = $this->t('%capital_name contains terms with multiple parents.', $args);
break;
}
}
// Get the IDs of the terms edited on the current page which have pending
// revisions.
$edited_term_ids = array_map(function ($item) {
return $item->id();
}, $current_page);
$pending_term_ids = array_intersect($this->storageController->getTermIdsWithPendingRevisions(), $edited_term_ids);
if ($pending_term_ids) {
$help_message = $this->formatPlural(
count($pending_term_ids),
'%capital_name contains 1 term with pending revisions. Drag and drop of terms with pending revisions is not supported, but you can re-enable drag-and-drop support by getting each term to a published state.',
'%capital_name contains @count terms with pending revisions. Drag and drop of terms with pending revisions is not supported, but you can re-enable drag-and-drop support by getting each term to a published state.',
$args
);
}
// Only allow access to change parents and reorder the tree if there are no
// pending revisions and there are no terms with multiple parents.
$update_tree_access = AccessResult::allowedIf(empty($pending_term_ids) && $vocabulary_hierarchy !== VocabularyInterface::HIERARCHY_MULTIPLE);
$form['help'] = [
'#type' => 'container',
'message' => ['#markup' => $help_message],
];
if (!$update_tree_access->isAllowed()) {
$form['help']['#attributes']['class'] = ['messages', 'messages--warning'];
}
$errors = $form_state->getErrors();
$row_position = 0;
// Build the actual form.
......@@ -268,7 +327,7 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
'#header' => [
'term' => $this->t('Name'),
'operations' => $this->t('Operations'),
'weight' => $this->t('Weight'),
'weight' => $update_tree_access->isAllowed() ? $this->t('Weight') : NULL,
],
'#attributes' => [
'id' => 'taxonomy',
......@@ -276,14 +335,11 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
];
$this->renderer->addCacheableDependency($form['terms'], $create_access);
// Only allow access to changing weights if the user has update access for
// all terms.
$change_weight_access = AccessResult::allowed();
foreach ($current_page as $key => $term) {
$form['terms'][$key] = [
'term' => [],
'operations' => [],
'weight' => [],
'weight' => $update_tree_access->isAllowed() ? [] : NULL,
];
/** @var $term \Drupal\Core\Entity\EntityInterface */
$term = $this->entityRepository->getTranslationFromContext($term);
......@@ -301,7 +357,16 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
'#title' => $term->getName(),
'#url' => $term->toUrl(),
];
if ($vocabulary_hierarchy != VocabularyInterface::HIERARCHY_MULTIPLE && count($tree) > 1) {
// Add a special class for terms with pending revision so we can highlight
// them in the form.
$form['terms'][$key]['#attributes']['class'] = [];
if (in_array($term->id(), $pending_term_ids)) {
$form['terms'][$key]['#attributes']['class'][] = 'color-warning';
$form['terms'][$key]['#attributes']['class'][] = 'taxonomy-term--pending-revision';
}
if ($update_tree_access->isAllowed() && count($tree) > 1) {
$parent_fields = TRUE;
$form['terms'][$key]['term']['tid'] = [
'#type' => 'hidden',
......@@ -330,9 +395,9 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
];
}
$update_access = $term->access('update', NULL, TRUE);
$change_weight_access = $change_weight_access->andIf($update_access);
$update_tree_access = $update_tree_access->andIf($update_access);
if ($update_access->isAllowed()) {
if ($update_tree_access->isAllowed()) {
$form['terms'][$key]['weight'] = [
'#type' => 'weight',
'#delta' => $delta,
......@@ -350,7 +415,6 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
];
}
$form['terms'][$key]['#attributes']['class'] = [];
if ($parent_fields) {
$form['terms'][$key]['#attributes']['class'][] = 'draggable';
}
......@@ -378,8 +442,8 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
$row_position++;
}
$this->renderer->addCacheableDependency($form['terms'], $change_weight_access);
if ($change_weight_access->isAllowed()) {
$this->renderer->addCacheableDependency($form['terms'], $update_tree_access);
if ($update_tree_access->isAllowed()) {
if ($parent_fields) {
$form['terms']['#tabledrag'][] = [
'action' => 'match',
......@@ -408,7 +472,7 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
];
}
if (($vocabulary_hierarchy !== VocabularyInterface::HIERARCHY_MULTIPLE && count($tree) > 1) && $change_weight_access->isAllowed()) {
if ($update_tree_access->isAllowed() && count($tree) > 1) {
$form['actions'] = ['#type' => 'actions', '#tree' => FALSE];
$form['actions']['submit'] = [
'#type' => 'submit',
......@@ -505,12 +569,25 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
}
}
// Save all updated terms.
foreach ($changed_terms as $term) {
$term->save();
}
if (!empty($changed_terms)) {
$pending_term_ids = $this->storageController->getTermIdsWithPendingRevisions();
$this->messenger()->addStatus($this->t('The configuration options have been saved.'));
// Force a form rebuild if any of the changed terms has a pending
// revision.
if (array_intersect_key(array_flip($pending_term_ids), $changed_terms)) {
$this->messenger()->addError($this->t('The terms with updated parents have been modified by another user, the changes could not be saved.'));
$form_state->setRebuild();
return;
}
// Save all updated terms.
foreach ($changed_terms as $term) {
$term->save();
}
$this->messenger()->addStatus($this->t('The configuration options have been saved.'));
}
}
/**
......
<?php
namespace Drupal\taxonomy\Plugin\Validation\Constraint;
use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase;
/**
* Validation constraint for changing the term hierarchy in pending revisions.
*
* @Constraint(
* id = "TaxonomyHierarchy",
* label = @Translation("Taxonomy term hierarchy.", context = "Validation"),
* )
*/
class TaxonomyTermHierarchyConstraint extends CompositeConstraintBase {
/**
* The default violation message.
*
* @var string
*/
public $message = 'You can only change the hierarchy for the <em>published</em> version of this term.';
/**
* {@inheritdoc}
*/
public function coversFields() {
return ['parent', 'weight'];
}
}
<?php
namespace Drupal\taxonomy\Plugin\Validation\Constraint;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\taxonomy\TermStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Constraint validator for changing term parents in pending revisions.
*/
class TaxonomyTermHierarchyConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
private $entityTypeManager;
/**
* Creates a new TaxonomyTermHierarchyConstraintValidator instance.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager')
);
}
/**
* {@inheritdoc}
*/
public function validate($entity, Constraint $constraint) {
$term_storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId());
assert($term_storage instanceof TermStorageInterface);
// Newly created entities should be able to specify a parent.
if ($entity && $entity->isNew()) {
return;
}
$is_pending_revision = !$entity->isDefaultRevision();
$pending_term_ids = $term_storage->getTermIdsWithPendingRevisions();
$ancestors = $term_storage->loadAllParents($entity->id());
$ancestor_is_pending_revision = (bool) array_intersect_key($ancestors, array_flip($pending_term_ids));
$new_parents = array_column($entity->parent->getValue(), 'target_id');
$original_parents = array_keys($term_storage->loadParents($entity->id())) ?: [0];
if (($is_pending_revision || $ancestor_is_pending_revision) && $new_parents != $original_parents) {
$a = 1;
$this->context->buildViolation($constraint->message)
->atPath('parent')
->addViolation();
}
$original = $term_storage->loadUnchanged($entity->id());
if (($is_pending_revision || $ancestor_is_pending_revision) && !$entity->weight->equals($original->weight)) {
$this->context->buildViolation($constraint->message)
->atPath('weight')
->addViolation();
}
}
}
......@@ -3,6 +3,7 @@
namespace Drupal\taxonomy;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Entity\EntityConstraintViolationListInterface;
use Drupal\Core\Form\FormStateInterface;
/**
......@@ -121,6 +122,31 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
return $term;
}
/**
* {@inheritdoc}
*/
protected function getEditedFieldNames(FormStateInterface $form_state) {
return array_merge(['parent', 'weight'], parent::getEditedFieldNames($form_state));
}
/**
* {@inheritdoc}
*/
protected function flagViolations(EntityConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state) {
// Manually flag violations of fields not handled by the form display. This
// is necessary as entity form displays only flag violations for fields
// contained in the display.
// @see ::form()
foreach ($violations->getByField('parent') as $violation) {
$form_state->setErrorByName('parent', $violation->getMessage());
}
foreach ($violations->getByField('weight') as $violation) {
$form_state->setErrorByName('weight', $violation->getMessage());
}
parent::flagViolations($violations, $form, $form_state);
}
/**
* {@inheritdoc}
*/
......
......@@ -5,11 +5,12 @@
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\RevisionLogInterface;
/**
* Provides an interface defining a taxonomy term entity.
*/
interface TermInterface extends ContentEntityInterface, EntityChangedInterface, EntityPublishedInterface {
interface TermInterface extends ContentEntityInterface, EntityChangedInterface, EntityPublishedInterface, RevisionLogInterface {
/**
* Gets the term description.
......
......@@ -372,6 +372,38 @@ public function getNodeTerms(array $nids, array $vocabs = [], $langcode = NULL)
return $terms;
}
/**
* {@inheritdoc}
*/
public function getTermIdsWithPendingRevisions() {
$table_mapping = $this->getTableMapping();
$id_field = $table_mapping->getColumnNames($this->entityType->getKey('id'))['value'];
$revision_field = $table_mapping->getColumnNames($this->entityType->getKey('revision'))['value'];
$rta_field = $table_mapping->getColumnNames($this->entityType->getKey('revision_translation_affected'))['value'];
$langcode_field = $table_mapping->getColumnNames($this->entityType->getKey('langcode'))['value'];
$revision_default_field = $table_mapping->getColumnNames($this->entityType->getRevisionMetadataKey('revision_default'))['value'];
$query = $this->database->select($this->getRevisionDataTable(), 'tfr');
$query->fields('tfr', [$id_field]);
$query->addExpression("MAX(tfr.$revision_field)", $revision_field);
$query->join($this->getRevisionTable(), 'tr', "tfr.$revision_field = tr.$revision_field AND tr.$revision_default_field = 0");
$inner_select = $this->database->select($this->getRevisionDataTable(), 't');
$inner_select->condition("t.$rta_field", '1');
$inner_select->fields('t', [$id_field, $langcode_field]);
$inner_select->addExpression("MAX(t.$revision_field)", $revision_field);
$inner_select
->groupBy("t.$id_field")
->groupBy("t.$langcode_field");
$query->join($inner_select, 'mr', "tfr.$revision_field = mr.$revision_field AND tfr.$langcode_field = mr.$langcode_field");
$query->groupBy("tfr.$id_field");
return $query->execute()->fetchAllKeyed(1, 0);
}
/**
* {@inheritdoc}
*/
......
......@@ -141,4 +141,15 @@ public function getNodeTerms(array $nids, array $vocabs = [], $langcode = NULL);
*/
public function getVocabularyHierarchyType($vid);
/**
* Gets a list of term IDs with pending revisions.
*
* @return int[]
* An array of term IDs which have pending revisions, keyed by their
* revision IDs.
*
* @internal
*/
public function getTermIdsWithPendingRevisions();
}
......@@ -6,7 +6,6 @@
*/
use Drupal\Component\Utility\Tags;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\Core\Render\Element;
......@@ -78,33 +77,19 @@ function taxonomy_help($route_name, RouteMatchInterface $route_match) {
case 'entity.taxonomy_vocabulary.collection':
$output = '<p>' . t('Taxonomy is for categorizing content. Terms are grouped into vocabularies. For example, a vocabulary called "Fruit" would contain the terms "Apple" and "Banana".') . '</p>';
return $output;
case 'entity.taxonomy_vocabulary.overview_form':
$vocabulary = $route_match->getParameter('taxonomy_vocabulary');
$vocabulary_hierarchy = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->getVocabularyHierarchyType($vocabulary->id());
if (\Drupal::currentUser()->hasPermission('administer taxonomy') || \Drupal::currentUser()->hasPermission('edit terms in ' . $vocabulary->id())) {
switch ($vocabulary_hierarchy) {
case VocabularyInterface::HIERARCHY_DISABLED:
return '<p>' . t('You can reorganize the terms in %capital_name using their drag-and-drop handles, and group terms under a parent term by sliding them under and to the right of the parent.', ['%capital_name' => Unicode::ucfirst($vocabulary->label()), '%name' => $vocabulary->label()]) . '</p>';
case VocabularyInterface::HIERARCHY_SINGLE:
return '<p>' . t('%capital_name contains terms grouped under parent terms. You can reorganize the terms in %capital_name using their drag-and-drop handles.', ['%capital_name' => Unicode::ucfirst($vocabulary->label()), '%name' => $vocabulary->label()]) . '</p>';
case VocabularyInterface::HIERARCHY_MULTIPLE:
return '<p>' . t('%capital_name contains terms with multiple parents. Drag and drop of terms with multiple parents is not supported, but you can re-enable drag-and-drop support by editing each term to include only a single parent.', ['%capital_name' => Unicode::ucfirst($vocabulary->label())]) . '</p>';
}
}
else {
switch ($vocabulary_hierarchy) {
case VocabularyInterface::HIERARCHY_DISABLED:
return '<p>' . t('%capital_name contains the following terms.', ['%capital_name' => Unicode::ucfirst($vocabulary->label())]) . '</p>';
case VocabularyInterface::HIERARCHY_SINGLE:
return '<p>' . t('%capital_name contains terms grouped under parent terms', ['%capital_name' => Unicode::ucfirst($vocabulary->label())]) . '</p>';
case VocabularyInterface::HIERARCHY_MULTIPLE:
return '<p>' . t('%capital_name contains terms with multiple parents.', ['%capital_name' => Unicode::ucfirst($vocabulary->label())]) . '</p>';
}
}
}
}
/**
* Implements hook_entity_type_alter().
*/
function taxonomy_entity_type_alter(array &$entity_types) {
// @todo Moderation is disabled for taxonomy terms until when we have an UI
// for them.
// @see https://www.drupal.org/project/drupal/issues/2899923
$entity_types['taxonomy_term']->setHandlerClass('moderation', '');
}
/**
* Entity URI callback.
*/
......
......@@ -6,6 +6,8 @@
*/
use Drupal\Core\Config\Entity\ConfigEntityUpdater;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\views\ViewExecutable;
/**
......@@ -134,3 +136,94 @@ function taxonomy_post_update_remove_hierarchy_from_vocabularies(&$sandbox = NUL
return TRUE;
});
}
/**
* Update taxonomy terms to be revisionable.
*/
function taxonomy_post_update_make_taxonomy_term_revisionable(&$sandbox) {
$definition_update_manager = \Drupal::entityDefinitionUpdateManager();
/** @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $last_installed_schema_repository */
$last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository');
$entity_type = $definition_update_manager->getEntityType('taxonomy_term');
$field_storage_definitions = $last_installed_schema_repository->getLastInstalledFieldStorageDefinitions('taxonomy_term');
// Update the entity type definition.
$entity_keys = $entity_type->getKeys();
$entity_keys['revision'] = 'revision_id';
$entity_keys['revision_translation_affected'] = 'revision_translation_affected';
$entity_type->set('entity_keys', $entity_keys);
$entity_type->set('revision_table', 'taxonomy_term_revision');
$entity_type->set('revision_data_table', 'taxonomy_term_field_revision');
$revision_metadata_keys = [
'revision_default' => 'revision_default',
'revision_user' => 'revision_user',
'revision_created' => 'revision_created',
'revision_log_message' => 'revision_log_message',
];
$entity_type->set('revision_metadata_keys', $revision_metadata_keys);
// Update the field storage definitions and add the new ones required by a
// revisionable entity type.
$field_storage_definitions['langcode']->setRevisionable(TRUE);
$field_storage_definitions['name']->setRevisionable(TRUE);
$field_storage_definitions['description']->setRevisionable(TRUE);
$field_storage_definitions['changed']->setRevisionable(TRUE);
$field_storage_definitions['revision_id'] = BaseFieldDefinition::create('integer')
->setName('revision_id')
->setTargetEntityTypeId('taxonomy_term')
->setTargetBundle(NULL)
->setLabel(new TranslatableMarkup('Revision ID'))
->setReadOnly(TRUE)
->setSetting('unsigned', TRUE);
$field_storage_definitions['revision_default'] = BaseFieldDefinition::create('boolean')
->setName('revision_default')
->setTargetEntityTypeId('taxonomy_term')
->setTargetBundle(NULL)
->setLabel(new TranslatableMarkup('Default revision'))
->setDescription(new TranslatableMarkup('A flag indicating whether this was a default revision when it was saved.'))
->setStorageRequired(TRUE)
->setInternal(TRUE)
->setTranslatable(FALSE)
->setRevisionable(TRUE);
$field_storage_definitions['revision_translation_affected'] = BaseFieldDefinition::create('boolean')
->setName('revision_translation_affected')
->setTargetEntityTypeId('taxonomy_term')
->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);
$field_storage_definitions['revision_created'] = BaseFieldDefinition::create('created')
->setName('revision_created')
->setTargetEntityTypeId('taxonomy_term')
->setTargetBundle(NULL)
->setLabel(new TranslatableMarkup('Revision create time'))
->setDescription(new TranslatableMarkup('The time that the current revision was created.'))
->setRevisionable(TRUE);
$field_storage_definitions['revision_user'] = BaseFieldDefinition::create('entity_reference')
->setName('revision_user')
->setTargetEntityTypeId('taxonomy_term')
->setTargetBundle(NULL)
->setLabel(new TranslatableMarkup('Revision user'))
->setDescription(new TranslatableMarkup('The user ID of the author of the current revision.'))
->setSetting('target_type', 'user')
->setRevisionable(TRUE);
$field_storage_definitions['revision_log_message'] = BaseFieldDefinition::create('string_long')
->setName('revision_log_message')
->setTargetEntityTypeId('taxonomy_term')
->setTargetBundle(NULL)
->setLabel(new TranslatableMarkup('Revision log message'))
->setDescription(new TranslatableMarkup('Briefly describe the changes you have made.'))
->setRevisionable(TRUE)
->setDefaultValue('');
$definition_update_manager->updateFieldableEntityType($entity_type, $field_storage_definitions, $sandbox);
return t('Taxonomy terms have been converted to be revisionable.');
}
......@@ -156,6 +156,9 @@ protected function getExpectedNormalizedEntity() {
'tid' => [
['value' => 1],
],
'revision_id' => [
['value' => 1],
],
'uuid' => [
['value' => $this->entity->uuid()],
],
......@@ -205,6 +208,16 @@ protected function getExpectedNormalizedEntity() {
'value' => TRUE,
],
],
'revision_created' => [
$this->formatExpectedTimestampItemValues((int) $this->entity->getRevisionCreationTime()),
],
'revision_user' => [],
'revision_log_message' => [],
'revision_translation_affected' => [
[
'value' => TRUE,
],
],
];
}
......
......@@ -129,6 +129,67 @@ public function testPublishingStatusUpdateForTaxonomyTermViews() {
}
}
/**
* Tests the conversion of taxonomy terms to be revisionable.
*
* @see taxonomy_post_update_make_taxonomy_term_revisionable()
*/
public function testConversionToRevisionable() {
$this->runUpdates();
// Check the database tables and the field storage definitions.
$schema = \Drupal::database()->schema();
$this->assertTrue($schema->tableExists('taxonomy_term_data'));
$this->assertTrue($schema->tableExists('taxonomy_term_field_data'));
$this->assertTrue($schema->tableExists('taxonomy_term_revision'));
$this->assertTrue($schema->tableExists('taxonomy_term_field_revision'));