comparison core/lib/Drupal/Core/Entity/ContentEntityBase.php @ 14:1fec387a4317

Update Drupal core to 8.5.2 via Composer
author Chris Cannam
date Mon, 23 Apr 2018 09:46:53 +0100
parents 7a779792577d
children c2387f117808
comparison
equal deleted inserted replaced
13:5fb285c0d0e3 14:1fec387a4317
18 * 18 *
19 * @ingroup entity_api 19 * @ingroup entity_api
20 */ 20 */
21 abstract class ContentEntityBase extends Entity implements \IteratorAggregate, ContentEntityInterface, TranslationStatusInterface { 21 abstract class ContentEntityBase extends Entity implements \IteratorAggregate, ContentEntityInterface, TranslationStatusInterface {
22 22
23 use EntityChangesDetectionTrait {
24 getFieldsToSkipFromTranslationChangesCheck as traitGetFieldsToSkipFromTranslationChangesCheck;
25 }
26
23 /** 27 /**
24 * The plain data values of the contained fields. 28 * The plain data values of the contained fields.
25 * 29 *
26 * This always holds the original, unchanged values of the entity. The values 30 * This always holds the original, unchanged values of the entity. The values
27 * are keyed by language code, whereas LanguageInterface::LANGCODE_DEFAULT 31 * are keyed by language code, whereas LanguageInterface::LANGCODE_DEFAULT
153 * The loaded revision ID before the new revision was set. 157 * The loaded revision ID before the new revision was set.
154 * 158 *
155 * @var int 159 * @var int
156 */ 160 */
157 protected $loadedRevisionId; 161 protected $loadedRevisionId;
162
163 /**
164 * The revision translation affected entity key.
165 *
166 * @var string
167 */
168 protected $revisionTranslationAffectedKey;
169
170 /**
171 * Whether the revision translation affected flag has been enforced.
172 *
173 * An array, keyed by the translation language code.
174 *
175 * @var bool[]
176 */
177 protected $enforceRevisionTranslationAffected = [];
158 178
159 /** 179 /**
160 * {@inheritdoc} 180 * {@inheritdoc}
161 */ 181 */
162 public function __construct(array $values, $entity_type, $bundle = FALSE, $translations = []) { 182 public function __construct(array $values, $entity_type, $bundle = FALSE, $translations = []) {
163 $this->entityTypeId = $entity_type; 183 $this->entityTypeId = $entity_type;
164 $this->entityKeys['bundle'] = $bundle ? $bundle : $this->entityTypeId; 184 $this->entityKeys['bundle'] = $bundle ? $bundle : $this->entityTypeId;
165 $this->langcodeKey = $this->getEntityType()->getKey('langcode'); 185 $this->langcodeKey = $this->getEntityType()->getKey('langcode');
166 $this->defaultLangcodeKey = $this->getEntityType()->getKey('default_langcode'); 186 $this->defaultLangcodeKey = $this->getEntityType()->getKey('default_langcode');
187 $this->revisionTranslationAffectedKey = $this->getEntityType()->getKey('revision_translation_affected');
167 188
168 foreach ($values as $key => $value) { 189 foreach ($values as $key => $value) {
169 // If the key matches an existing property set the value to the property 190 // If the key matches an existing property set the value to the property
170 // to set properties like isDefaultRevision. 191 // to set properties like isDefaultRevision.
171 // @todo: Should this be converted somehow? 192 // @todo: Should this be converted somehow?
267 288
268 if ($value && !$this->newRevision) { 289 if ($value && !$this->newRevision) {
269 // When saving a new revision, set any existing revision ID to NULL so as 290 // When saving a new revision, set any existing revision ID to NULL so as
270 // to ensure that a new revision will actually be created. 291 // to ensure that a new revision will actually be created.
271 $this->set($this->getEntityType()->getKey('revision'), NULL); 292 $this->set($this->getEntityType()->getKey('revision'), NULL);
272 293 }
273 // Make sure that the flag tracking which translations are affected by the 294 elseif (!$value && $this->newRevision) {
274 // current revision is reset. 295 // If ::setNewRevision(FALSE) is called after ::setNewRevision(TRUE) we
275 foreach ($this->translations as $langcode => $data) { 296 // have to restore the loaded revision ID.
276 // But skip removed translations. 297 $this->set($this->getEntityType()->getKey('revision'), $this->getLoadedRevisionId());
277 if ($this->hasTranslation($langcode)) {
278 $this->getTranslation($langcode)->setRevisionTranslationAffected(NULL);
279 }
280 }
281 } 298 }
282 299
283 $this->newRevision = $value; 300 $this->newRevision = $value;
284 } 301 }
285 302
319 } 336 }
320 337
321 /** 338 /**
322 * {@inheritdoc} 339 * {@inheritdoc}
323 */ 340 */
341 public function wasDefaultRevision() {
342 /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
343 $entity_type = $this->getEntityType();
344 if (!$entity_type->isRevisionable()) {
345 return TRUE;
346 }
347
348 $revision_default_key = $entity_type->getRevisionMetadataKey('revision_default');
349 $value = $this->isNew() || $this->get($revision_default_key)->value;
350 return $value;
351 }
352
353 /**
354 * {@inheritdoc}
355 */
356 public function isLatestRevision() {
357 /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
358 $storage = $this->entityTypeManager()->getStorage($this->getEntityTypeId());
359
360 return $this->getLoadedRevisionId() == $storage->getLatestRevisionId($this->id());
361 }
362
363 /**
364 * {@inheritdoc}
365 */
366 public function isLatestTranslationAffectedRevision() {
367 /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
368 $storage = $this->entityTypeManager()->getStorage($this->getEntityTypeId());
369
370 return $this->getLoadedRevisionId() == $storage->getLatestTranslationAffectedRevisionId($this->id(), $this->language()->getId());
371 }
372
373 /**
374 * {@inheritdoc}
375 */
324 public function isRevisionTranslationAffected() { 376 public function isRevisionTranslationAffected() {
325 $field_name = $this->getEntityType()->getKey('revision_translation_affected'); 377 return $this->hasField($this->revisionTranslationAffectedKey) ? $this->get($this->revisionTranslationAffectedKey)->value : TRUE;
326 return $this->hasField($field_name) ? $this->get($field_name)->value : TRUE;
327 } 378 }
328 379
329 /** 380 /**
330 * {@inheritdoc} 381 * {@inheritdoc}
331 */ 382 */
332 public function setRevisionTranslationAffected($affected) { 383 public function setRevisionTranslationAffected($affected) {
333 $field_name = $this->getEntityType()->getKey('revision_translation_affected'); 384 if ($this->hasField($this->revisionTranslationAffectedKey)) {
334 if ($this->hasField($field_name)) { 385 $this->set($this->revisionTranslationAffectedKey, $affected);
335 $this->set($field_name, $affected); 386 }
336 } 387 return $this;
388 }
389
390 /**
391 * {@inheritdoc}
392 */
393 public function isRevisionTranslationAffectedEnforced() {
394 return !empty($this->enforceRevisionTranslationAffected[$this->activeLangcode]);
395 }
396
397 /**
398 * {@inheritdoc}
399 */
400 public function setRevisionTranslationAffectedEnforced($enforced) {
401 $this->enforceRevisionTranslationAffected[$this->activeLangcode] = $enforced;
337 return $this; 402 return $this;
338 } 403 }
339 404
340 /** 405 /**
341 * {@inheritdoc} 406 * {@inheritdoc}
353 418
354 /** 419 /**
355 * {@inheritdoc} 420 * {@inheritdoc}
356 */ 421 */
357 public function isTranslatable() { 422 public function isTranslatable() {
358 // Check that the bundle is translatable, the entity has a language defined 423 // Check the bundle is translatable, the entity has a language defined, and
359 // and if we have more than one language on the site. 424 // the site has more than one language.
360 $bundles = $this->entityManager()->getBundleInfo($this->entityTypeId); 425 $bundles = $this->entityManager()->getBundleInfo($this->entityTypeId);
361 return !empty($bundles[$this->bundle()]['translatable']) && !$this->getUntranslated()->language()->isLocked() && $this->languageManager()->isMultilingual(); 426 return !empty($bundles[$this->bundle()]['translatable']) && !$this->getUntranslated()->language()->isLocked() && $this->languageManager()->isMultilingual();
362 } 427 }
363 428
364 /** 429 /**
399 else { 464 else {
400 $data['status'] = static::TRANSLATION_EXISTING; 465 $data['status'] = static::TRANSLATION_EXISTING;
401 } 466 }
402 } 467 }
403 $this->translations = array_diff_key($this->translations, $removed); 468 $this->translations = array_diff_key($this->translations, $removed);
469
470 // Reset the new revision flag.
471 $this->newRevision = FALSE;
472
473 // Reset the enforcement of the revision translation affected flag.
474 $this->enforceRevisionTranslationAffected = [];
404 } 475 }
405 476
406 /** 477 /**
407 * {@inheritdoc} 478 * {@inheritdoc}
408 */ 479 */
714 unset($this->translatableEntityKeys[$key][$this->activeLangcode]); 785 unset($this->translatableEntityKeys[$key][$this->activeLangcode]);
715 } 786 }
716 // If the revision identifier field is being populated with the original 787 // If the revision identifier field is being populated with the original
717 // value, we need to make sure the "new revision" flag is reset 788 // value, we need to make sure the "new revision" flag is reset
718 // accordingly. 789 // accordingly.
719 if ($key === 'revision' && $this->getRevisionId() == $this->getLoadedRevisionId()) { 790 if ($key === 'revision' && $this->getRevisionId() == $this->getLoadedRevisionId() && !$this->isNew()) {
720 $this->newRevision = FALSE; 791 $this->newRevision = FALSE;
721 } 792 }
722 } 793 }
723 } 794 }
724 795
751 if (isset($this->values[$this->defaultLangcodeKey]) && $this->get($this->defaultLangcodeKey)->value != $this->isDefaultTranslation()) { 822 if (isset($this->values[$this->defaultLangcodeKey]) && $this->get($this->defaultLangcodeKey)->value != $this->isDefaultTranslation()) {
752 $this->get($this->defaultLangcodeKey)->setValue($this->isDefaultTranslation(), FALSE); 823 $this->get($this->defaultLangcodeKey)->setValue($this->isDefaultTranslation(), FALSE);
753 $message = SafeMarkup::format('The default translation flag cannot be changed (@langcode).', ['@langcode' => $this->activeLangcode]); 824 $message = SafeMarkup::format('The default translation flag cannot be changed (@langcode).', ['@langcode' => $this->activeLangcode]);
754 throw new \LogicException($message); 825 throw new \LogicException($message);
755 } 826 }
827 break;
828
829 case $this->revisionTranslationAffectedKey:
830 // If the revision translation affected flag is being set then enforce
831 // its value.
832 $this->setRevisionTranslationAffectedEnforced(TRUE);
756 break; 833 break;
757 } 834 }
758 } 835 }
759 836
760 /** 837 /**
835 $translation->translatableEntityKeys = &$this->translatableEntityKeys; 912 $translation->translatableEntityKeys = &$this->translatableEntityKeys;
836 $translation->translationInitialize = FALSE; 913 $translation->translationInitialize = FALSE;
837 $translation->typedData = NULL; 914 $translation->typedData = NULL;
838 $translation->loadedRevisionId = &$this->loadedRevisionId; 915 $translation->loadedRevisionId = &$this->loadedRevisionId;
839 $translation->isDefaultRevision = &$this->isDefaultRevision; 916 $translation->isDefaultRevision = &$this->isDefaultRevision;
917 $translation->enforceRevisionTranslationAffected = &$this->enforceRevisionTranslationAffected;
840 918
841 return $translation; 919 return $translation;
842 } 920 }
843 921
844 /** 922 /**
1047 throw new \InvalidArgumentException("The entity object refers to a removed translation ({$this->activeLangcode}) and cannot be manipulated."); 1125 throw new \InvalidArgumentException("The entity object refers to a removed translation ({$this->activeLangcode}) and cannot be manipulated.");
1048 } 1126 }
1049 1127
1050 $duplicate = clone $this; 1128 $duplicate = clone $this;
1051 $entity_type = $this->getEntityType(); 1129 $entity_type = $this->getEntityType();
1052 $duplicate->{$entity_type->getKey('id')}->value = NULL; 1130 if ($entity_type->hasKey('id')) {
1131 $duplicate->{$entity_type->getKey('id')}->value = NULL;
1132 }
1053 $duplicate->enforceIsNew(); 1133 $duplicate->enforceIsNew();
1054 1134
1055 // Check if the entity type supports UUIDs and generate a new one if so. 1135 // Check if the entity type supports UUIDs and generate a new one if so.
1056 if ($entity_type->hasKey('uuid')) { 1136 if ($entity_type->hasKey('uuid')) {
1057 $duplicate->{$entity_type->getKey('uuid')}->value = $this->uuidGenerator()->generate(); 1137 $duplicate->{$entity_type->getKey('uuid')}->value = $this->uuidGenerator()->generate();
1102 $this->translations = &$translations; 1182 $this->translations = &$translations;
1103 1183
1104 // Ensure that the following properties are actually cloned by 1184 // Ensure that the following properties are actually cloned by
1105 // overwriting the original references with ones pointing to copies of 1185 // overwriting the original references with ones pointing to copies of
1106 // them: enforceIsNew, newRevision, loadedRevisionId, fields, entityKeys, 1186 // them: enforceIsNew, newRevision, loadedRevisionId, fields, entityKeys,
1107 // translatableEntityKeys, values and isDefaultRevision. 1187 // translatableEntityKeys, values, isDefaultRevision and
1188 // enforceRevisionTranslationAffected.
1108 $enforce_is_new = $this->enforceIsNew; 1189 $enforce_is_new = $this->enforceIsNew;
1109 $this->enforceIsNew = &$enforce_is_new; 1190 $this->enforceIsNew = &$enforce_is_new;
1110 1191
1111 $new_revision = $this->newRevision; 1192 $new_revision = $this->newRevision;
1112 $this->newRevision = &$new_revision; 1193 $this->newRevision = &$new_revision;
1126 $values = $this->values; 1207 $values = $this->values;
1127 $this->values = &$values; 1208 $this->values = &$values;
1128 1209
1129 $default_revision = $this->isDefaultRevision; 1210 $default_revision = $this->isDefaultRevision;
1130 $this->isDefaultRevision = &$default_revision; 1211 $this->isDefaultRevision = &$default_revision;
1212
1213 $is_revision_translation_affected_enforced = $this->enforceRevisionTranslationAffected;
1214 $this->enforceRevisionTranslationAffected = &$is_revision_translation_affected_enforced;
1131 1215
1132 foreach ($this->fields as $name => $fields_by_langcode) { 1216 foreach ($this->fields as $name => $fields_by_langcode) {
1133 $this->fields[$name] = []; 1217 $this->fields[$name] = [];
1134 // Untranslatable fields may have multiple references for the same field 1218 // Untranslatable fields may have multiple references for the same field
1135 // object keyed by language. To avoid creating different field objects 1219 // object keyed by language. To avoid creating different field objects
1291 * 1375 *
1292 * @return array 1376 * @return array
1293 * An array of field names. 1377 * An array of field names.
1294 */ 1378 */
1295 protected function getFieldsToSkipFromTranslationChangesCheck() { 1379 protected function getFieldsToSkipFromTranslationChangesCheck() {
1296 /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */ 1380 return $this->traitGetFieldsToSkipFromTranslationChangesCheck($this);
1297 $entity_type = $this->getEntityType();
1298 // A list of known revision metadata fields which should be skipped from
1299 // the comparision.
1300 $fields = [
1301 $entity_type->getKey('revision'),
1302 'revision_translation_affected',
1303 ];
1304 $fields = array_merge($fields, array_values($entity_type->getRevisionMetadataKeys()));
1305
1306 return $fields;
1307 } 1381 }
1308 1382
1309 /** 1383 /**
1310 * {@inheritdoc} 1384 * {@inheritdoc}
1311 */ 1385 */
1341 $translation = $original->getTranslation($this->activeLangcode); 1415 $translation = $original->getTranslation($this->activeLangcode);
1342 1416
1343 // The list of fields to skip from the comparision. 1417 // The list of fields to skip from the comparision.
1344 $skip_fields = $this->getFieldsToSkipFromTranslationChangesCheck(); 1418 $skip_fields = $this->getFieldsToSkipFromTranslationChangesCheck();
1345 1419
1420 // We also check untranslatable fields, so that a change to those will mark
1421 // all translations as affected, unless they are configured to only affect
1422 // the default translation.
1423 $skip_untranslatable_fields = !$this->isDefaultTranslation() && $this->isDefaultTranslationAffectedOnly();
1424
1346 foreach ($this->getFieldDefinitions() as $field_name => $definition) { 1425 foreach ($this->getFieldDefinitions() as $field_name => $definition) {
1347 // @todo Avoid special-casing the following fields. See 1426 // @todo Avoid special-casing the following fields. See
1348 // https://www.drupal.org/node/2329253. 1427 // https://www.drupal.org/node/2329253.
1349 if (in_array($field_name, $skip_fields, TRUE)) { 1428 if (in_array($field_name, $skip_fields, TRUE) || ($skip_untranslatable_fields && !$definition->isTranslatable())) {
1350 continue; 1429 continue;
1351 } 1430 }
1352 $field = $this->get($field_name); 1431 $field = $this->get($field_name);
1353 // When saving entities in the user interface, the changed timestamp is 1432 // When saving entities in the user interface, the changed timestamp is
1354 // automatically incremented by ContentEntityForm::submitForm() even if 1433 // automatically incremented by ContentEntityForm::submitForm() even if
1365 } 1444 }
1366 1445
1367 return FALSE; 1446 return FALSE;
1368 } 1447 }
1369 1448
1449 /**
1450 * {@inheritdoc}
1451 */
1452 public function isDefaultTranslationAffectedOnly() {
1453 $bundle_name = $this->bundle();
1454 $bundle_info = \Drupal::service('entity_type.bundle.info')
1455 ->getBundleInfo($this->getEntityTypeId());
1456 return !empty($bundle_info[$bundle_name]['untranslatable_fields.default_translation_affected']);
1457 }
1458
1370 } 1459 }