*/
abstract class ContentEntityBase extends Entity implements \IteratorAggregate, ContentEntityInterface, TranslationStatusInterface {
+ use EntityChangesDetectionTrait {
+ getFieldsToSkipFromTranslationChangesCheck as traitGetFieldsToSkipFromTranslationChangesCheck;
+ }
+
/**
* The plain data values of the contained fields.
*
*/
protected $loadedRevisionId;
+ /**
+ * The revision translation affected entity key.
+ *
+ * @var string
+ */
+ protected $revisionTranslationAffectedKey;
+
+ /**
+ * Whether the revision translation affected flag has been enforced.
+ *
+ * An array, keyed by the translation language code.
+ *
+ * @var bool[]
+ */
+ protected $enforceRevisionTranslationAffected = [];
+
/**
* {@inheritdoc}
*/
$this->entityKeys['bundle'] = $bundle ? $bundle : $this->entityTypeId;
$this->langcodeKey = $this->getEntityType()->getKey('langcode');
$this->defaultLangcodeKey = $this->getEntityType()->getKey('default_langcode');
+ $this->revisionTranslationAffectedKey = $this->getEntityType()->getKey('revision_translation_affected');
foreach ($values as $key => $value) {
// If the key matches an existing property set the value to the property
// When saving a new revision, set any existing revision ID to NULL so as
// to ensure that a new revision will actually be created.
$this->set($this->getEntityType()->getKey('revision'), NULL);
-
- // Make sure that the flag tracking which translations are affected by the
- // current revision is reset.
- foreach ($this->translations as $langcode => $data) {
- // But skip removed translations.
- if ($this->hasTranslation($langcode)) {
- $this->getTranslation($langcode)->setRevisionTranslationAffected(NULL);
- }
- }
+ }
+ elseif (!$value && $this->newRevision) {
+ // If ::setNewRevision(FALSE) is called after ::setNewRevision(TRUE) we
+ // have to restore the loaded revision ID.
+ $this->set($this->getEntityType()->getKey('revision'), $this->getLoadedRevisionId());
}
$this->newRevision = $value;
return $this->isNew() || $return;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function wasDefaultRevision() {
+ /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
+ $entity_type = $this->getEntityType();
+ if (!$entity_type->isRevisionable()) {
+ return TRUE;
+ }
+
+ $revision_default_key = $entity_type->getRevisionMetadataKey('revision_default');
+ $value = $this->isNew() || $this->get($revision_default_key)->value;
+ return $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isLatestRevision() {
+ /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
+ $storage = $this->entityTypeManager()->getStorage($this->getEntityTypeId());
+
+ return $this->getLoadedRevisionId() == $storage->getLatestRevisionId($this->id());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isLatestTranslationAffectedRevision() {
+ /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
+ $storage = $this->entityTypeManager()->getStorage($this->getEntityTypeId());
+
+ return $this->getLoadedRevisionId() == $storage->getLatestTranslationAffectedRevisionId($this->id(), $this->language()->getId());
+ }
+
/**
* {@inheritdoc}
*/
public function isRevisionTranslationAffected() {
- $field_name = $this->getEntityType()->getKey('revision_translation_affected');
- return $this->hasField($field_name) ? $this->get($field_name)->value : TRUE;
+ return $this->hasField($this->revisionTranslationAffectedKey) ? $this->get($this->revisionTranslationAffectedKey)->value : TRUE;
}
/**
* {@inheritdoc}
*/
public function setRevisionTranslationAffected($affected) {
- $field_name = $this->getEntityType()->getKey('revision_translation_affected');
- if ($this->hasField($field_name)) {
- $this->set($field_name, $affected);
+ if ($this->hasField($this->revisionTranslationAffectedKey)) {
+ $this->set($this->revisionTranslationAffectedKey, $affected);
}
return $this;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function isRevisionTranslationAffectedEnforced() {
+ return !empty($this->enforceRevisionTranslationAffected[$this->activeLangcode]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setRevisionTranslationAffectedEnforced($enforced) {
+ $this->enforceRevisionTranslationAffected[$this->activeLangcode] = $enforced;
+ return $this;
+ }
+
/**
* {@inheritdoc}
*/
* {@inheritdoc}
*/
public function isTranslatable() {
- // Check that the bundle is translatable, the entity has a language defined
- // and if we have more than one language on the site.
+ // Check the bundle is translatable, the entity has a language defined, and
+ // the site has more than one language.
$bundles = $this->entityManager()->getBundleInfo($this->entityTypeId);
return !empty($bundles[$this->bundle()]['translatable']) && !$this->getUntranslated()->language()->isLocked() && $this->languageManager()->isMultilingual();
}
}
}
$this->translations = array_diff_key($this->translations, $removed);
+
+ // Reset the new revision flag.
+ $this->newRevision = FALSE;
+
+ // Reset the enforcement of the revision translation affected flag.
+ $this->enforceRevisionTranslationAffected = [];
}
/**
// If the revision identifier field is being populated with the original
// value, we need to make sure the "new revision" flag is reset
// accordingly.
- if ($key === 'revision' && $this->getRevisionId() == $this->getLoadedRevisionId()) {
+ if ($key === 'revision' && $this->getRevisionId() == $this->getLoadedRevisionId() && !$this->isNew()) {
$this->newRevision = FALSE;
}
}
throw new \LogicException($message);
}
break;
+
+ case $this->revisionTranslationAffectedKey:
+ // If the revision translation affected flag is being set then enforce
+ // its value.
+ $this->setRevisionTranslationAffectedEnforced(TRUE);
+ break;
}
}
$translation->typedData = NULL;
$translation->loadedRevisionId = &$this->loadedRevisionId;
$translation->isDefaultRevision = &$this->isDefaultRevision;
+ $translation->enforceRevisionTranslationAffected = &$this->enforceRevisionTranslationAffected;
return $translation;
}
$duplicate = clone $this;
$entity_type = $this->getEntityType();
- $duplicate->{$entity_type->getKey('id')}->value = NULL;
+ if ($entity_type->hasKey('id')) {
+ $duplicate->{$entity_type->getKey('id')}->value = NULL;
+ }
$duplicate->enforceIsNew();
// Check if the entity type supports UUIDs and generate a new one if so.
// Ensure that the following properties are actually cloned by
// overwriting the original references with ones pointing to copies of
// them: enforceIsNew, newRevision, loadedRevisionId, fields, entityKeys,
- // translatableEntityKeys, values and isDefaultRevision.
+ // translatableEntityKeys, values, isDefaultRevision and
+ // enforceRevisionTranslationAffected.
$enforce_is_new = $this->enforceIsNew;
$this->enforceIsNew = &$enforce_is_new;
$default_revision = $this->isDefaultRevision;
$this->isDefaultRevision = &$default_revision;
+ $is_revision_translation_affected_enforced = $this->enforceRevisionTranslationAffected;
+ $this->enforceRevisionTranslationAffected = &$is_revision_translation_affected_enforced;
+
foreach ($this->fields as $name => $fields_by_langcode) {
$this->fields[$name] = [];
// Untranslatable fields may have multiple references for the same field
* An array of field names.
*/
protected function getFieldsToSkipFromTranslationChangesCheck() {
- /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
- $entity_type = $this->getEntityType();
- // A list of known revision metadata fields which should be skipped from
- // the comparision.
- $fields = [
- $entity_type->getKey('revision'),
- 'revision_translation_affected',
- ];
- $fields = array_merge($fields, array_values($entity_type->getRevisionMetadataKeys()));
-
- return $fields;
+ return $this->traitGetFieldsToSkipFromTranslationChangesCheck($this);
}
/**
// The list of fields to skip from the comparision.
$skip_fields = $this->getFieldsToSkipFromTranslationChangesCheck();
+ // We also check untranslatable fields, so that a change to those will mark
+ // all translations as affected, unless they are configured to only affect
+ // the default translation.
+ $skip_untranslatable_fields = !$this->isDefaultTranslation() && $this->isDefaultTranslationAffectedOnly();
+
foreach ($this->getFieldDefinitions() as $field_name => $definition) {
// @todo Avoid special-casing the following fields. See
// https://www.drupal.org/node/2329253.
- if (in_array($field_name, $skip_fields, TRUE)) {
+ if (in_array($field_name, $skip_fields, TRUE) || ($skip_untranslatable_fields && !$definition->isTranslatable())) {
continue;
}
$field = $this->get($field_name);
return FALSE;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function isDefaultTranslationAffectedOnly() {
+ $bundle_name = $this->bundle();
+ $bundle_info = \Drupal::service('entity_type.bundle.info')
+ ->getBundleInfo($this->getEntityTypeId());
+ return !empty($bundle_info[$bundle_name]['untranslatable_fields.default_translation_affected']);
+ }
+
}