Problem/Motivation

ContentEntityBase keeps track of the status of each translation - (TRANSLATION_REMOVED, TRANSLATION_EXISTING or TRANSLATION_CREATED). There are use cases in contrib, where this information needs to be accessed. Therefore the feature request to add the getTranslationStatus function to the entity API. This funciton was as well mentioned in #1810370: Entity Translation API improvements.

Proposed resolution

In order to make it work properly removeTranslation should completely remove any trace of a translation which has been added and then removed without being saved, in this case removeTranslaiton sets the status to TRANSLATION_REMOVED, which from the point of getTranslationStatus is wrong, as the translation did not existed yet, it was added and removed without saving it. The proper return status should be NULL.

The second change should be done in addTranslation. If an existing translation has been removed and afterwards added again (wihout saving) the translation status would be TRANSLATION_CREATED, which from the point of getTranslationStatus is wrong, as the translation existed already, it was removed but not saved and added again. The proper return status should be TRANSLATION_EXISTING.

Third change : After creating a new entity it had the TRANSLATION_EXISTING status, but it should be TRANSLATION_CREATED.

Fourth change : After saving the entity the translation states have to be updated.

Additionally there is the new interface TranslationStatusInterface, which ContentEntityBase implements and provides the new function TranslationStatusInterface::getTranslationStatus($langcode).

Remaining tasks

Review.

User interface changes

none

API changes

none.
Change record - https://www.drupal.org/node/2789903

Data model changes

none

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

hchonov created an issue. See original summary.

hchonov’s picture

Status: Active » Needs review
FileSize
4.3 KB
hchonov’s picture

Issue tags: +D8MI, +entity translation

Status: Needs review » Needs work

The last submitted patch, 2: 2666032-2.patch, failed testing.

hchonov’s picture

Status: Needs work » Needs review
FileSize
4.16 KB
639 bytes

I thought that the translatable entity keys could be removed as well, but it seems that the entity translation is used even after its deletion.

mkalkbrenner’s picture

Issue summary: View changes

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

hchonov’s picture

Issue tags: +DevDaysMilan
hchonov’s picture

Version: 8.1.x-dev » 8.2.x-dev
Gábor Hojtsy’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests, +language-content, +sprint

Reviewing the issue, I did not find anything that does not sound like oversights or where you are not fixing inaccuracies. Keep in mind I am not very well versed in the internals of this code, but the changes look very logical. Indeed tests are still needed.

+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -857,6 +853,16 @@ public function removeTranslation($langcode) {
+    if ($langcode ==  $this->defaultLangcode) {

Nitpick: spacing.

The biggest concern is changing an existing interface in core could very well be problematic for backwards compatibility. I think we ideally want to have a new interface that inherits from the old one and deprecate the old one. Otherwise this patch may start to WSOD contrib modules, which does not satisfy backwards compatibility.

hchonov’s picture

Status: Needs work » Needs review
FileSize
10.22 KB
6.33 KB

The biggest concern is changing an existing interface in core could very well be problematic for backwards compatibility. I think we ideally want to have a new interface that inherits from the old one and deprecate the old one. Otherwise this patch may start to WSOD contrib modules, which does not satisfy backwards compatibility.

There should be no problem that I am moving the constants from ContentEntityBase to the TranslatableInterface, because the ContentEntityInterface extends the TranslatableInterface

Beside that I've added tests and this way I've discovered, that there are needed some adjustments :
1. After creating a new entity it had the TRANSLATION_EXISTING status, but it should be TRANSLATION_CREATED.
2. After saving the entity the translation states have to be updated.

The tests cover all these cases now.

hchonov’s picture

Issue summary: View changes
mkalkbrenner’s picture

+1 for the new feature!

I think you should create a change record for the new API and point out that the move of the constants is backwards compatible.

Beside that => RTBC

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

hchonov’s picture

Issue summary: View changes
Issue tags: -Needs change record

Created a draft change record, which is to be published on commit.

Gábor Hojtsy’s picture

Repeating myself from two months ago:

The biggest concern is changing an existing interface in core could very well be problematic for backwards compatibility. I think we ideally want to have a new interface that inherits from the old one and deprecate the old one. Otherwise this patch may start to WSOD contrib modules, which does not satisfy backwards compatibility.

hchonov’s picture

@Gábor Hojtsy: I am not really sure, how my changes could cause any problems. Like I've already mentioned, currently the constants are defined in ContentEntityBase, which implements the ContentEntityInterface, which on its turn extends the TranslatableInterface. I could not imagine how moving the constants upward in the hierarchy (from ContentEntityInterface to the TranslatableInterface) could cause any problems, as they remain accessible downward in the hierarchy and contrib will not notice any change here.

Gábor Hojtsy’s picture

Status: Needs review » Needs work

@hchonov: you are adding a new method. Consider this:

1. core provides an interface
2. contrib implements the interface
3. core adds a new method on the interface
4. site updates to new core
5=> whitescreen because contrib does not yet implement the new method

I don't think that matches the definition of backwards compatibility we intend to support. See https://www.drupal.org/core/d8-allowed-changes#bc-break

A backward-compatibility break is a type of API change that requires changes in code implementing a public API.

Does this change require changes in code implementing a public API? Yes.

mkalkbrenner’s picture

Gabor's argumentation is correct.
If someone implements the TranslatableInterface in contrib, it will broken until public function getTranslationStatus() is implemented. Regarding the moved constants, everything is OK.

So a backward compatible implementation will be to create a TranslationStatusInterface for the constants and the accessor function.

Gábor Hojtsy’s picture

Yeah @mkalkbrenner's solution sounds almost elegant :) Also ContentEntityBase would then implement that interface thus implementing that method and receiving the constants.

hchonov’s picture

Status: Needs work » Needs review
FileSize
10.58 KB
10.58 KB
8.28 KB

I've created the TranslationStatusInterface and flagged it directly as deprecated and that it will be merged into the TranslatableInterface in Drupal 9.0.0.
The constants are currently present at two places - in ContentEntityBase and in TranslationStatusInterface, but in Drupal 9.0.0 should moved to the TranslatableInterface.

When this gets committed I'll create an another issue for both points.

hchonov’s picture

hchonov’s picture

Title: TranslatableInterface::getTranslationStatus to ask for the status of an entity translation » TranslationStatusInterface to ask for the status of an entity translation
Gábor Hojtsy’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
@@ -20,7 +21,7 @@
-interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, RevisionableInterface, TranslatableInterface {
+interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, RevisionableInterface, TranslatableInterface, TranslationStatusInterface {

I don't think this resolved any of the concerns with backwards compatibility. The same problem applies to ContentEntityInterface, making it composite does not make a difference.

Making ContentEntityBase implementing it would still let anyone else impplementing ContentEntityInterface to be backwards compatible with code extending from ContentEntityBase would get the new method/interface.

Gábor Hojtsy’s picture

Also I don't think its correct to mark something as deprecated if we don't have a replacement for it in core and we want people to use it. Things that are deprecated are supposed to be not used in favor of something better already available or in favor of being not used because they are bad.

mkalkbrenner’s picture

  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
    @@ -20,7 +21,7 @@
    +interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, RevisionableInterface, TranslatableInterface, TranslationStatusInterface {
    

    Like Gabor already pointed out, ContentEntityInterface must not extend TranslationStatusInterface to keep BC. Instead, ContentEntityBase should implement the interface.

  2. +++ b/core/lib/Drupal/Core/TypedData/TranslationStatusInterface.php
    @@ -0,0 +1,40 @@
    + * @deprecated in Drupal 8.3.0, will be merged into
    + * Drupal\Core\TypedData\TranslatableInterface before Drupal 9.0.0.
    

    The merge should be discussed after the new interface has been committed in a different issue.
    From my point of view it's just a nice to have.
    If someone currently implements the TranslatableInterface directly without extending ContentEntityBase, I bet he's not tracking the translation state in his implementation ;-)

The feature itself is very useful for us and we should get it to a committable state quickly :-)

hchonov’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
10.55 KB
3.19 KB

Now only ContentEntityBase implements the new interface.

Gábor Hojtsy’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +sprint

Yay, looks all good now.

catch’s picture

Title: TranslationStatusInterface to ask for the status of an entity translation » Add TranslationStatusInterface to ask for the status of an entity translation fix statuses in ContentEntityBase

The patch had more change than I anticipated, so just want to clarify exactly what's happening:

1. Right now there's no way to get this information (except in subclasses of ContentEntityBase maybe)

2. If we just add a way to get the information, it will be wrong in some cases (presumably these are cases ContentEntityBase itself does not care about).

3. So the patch both fixes the statuses, and adds the way to get them at the same time.

I don't see a way to disentangle these, tried an issue re-title to clarify what's happening a bit more.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 27: 2666032-27.patch, failed testing.

Gábor Hojtsy’s picture

Status: Needs work » Reviewed & tested by the community

Passes again.

catch’s picture

Title: Add TranslationStatusInterface to ask for the status of an entity translation fix statuses in ContentEntityBase » Add TranslationStatusInterface to ask for the status of an entity translation and fix statuses in ContentEntityBase
Gábor Hojtsy’s picture

@catch: any concerns that would stop committing this? :)

catch’s picture

I don't have anything concrete, but also haven't managed to commit it yet. I pinged xjm and alexpott for a second look just in case.

plach’s picture

Changes look reasonable to me too, thanks!

alexpott’s picture

Status: Reviewed & tested by the community » Needs review
  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
    @@ -368,6 +354,25 @@ public function preSaveRevision(EntityStorageInterface $storage, \stdClass $reco
    +  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
    +    parent::postSave($storage, $update);
    +
    +    // Update the status of all saved translations.
    +    $removed = [];
    +    foreach ($this->translations as $langcode => &$data) {
    +      if ($data['status'] == static::TRANSLATION_REMOVED) {
    +        $removed[$langcode] = TRUE;
    +      }
    +      else {
    +        $data['status'] = static::TRANSLATION_EXISTING;
    +      }
    +    }
    +    $this->translations = array_diff_key($this->translations, $removed);
    +  }
    

    I looked at this and worried about whether the statuses should be set before calling parent::postSave() but looking at the code this is fine because the only that occurs in the parent is:

      public function postSave(EntityStorageInterface $storage, $update = TRUE) {
        $this->invalidateTagsOnSave($update);
      }
    
  2. +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
    @@ -220,7 +206,7 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans
    -    $data = array('status' => static::TRANSLATION_EXISTING);
    

    This looks like this is fixing a bug / changing behaviour? Also I think we should call $this->isNew() instead of isset($values[$this->getEntityType()->getKey('id')]) since if we're enforcing newness then the status should be TRANSLATION_CREATED no?

hchonov’s picture

  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
    @@ -220,7 +206,7 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans
    -    $data = array('status' => static::TRANSLATION_EXISTING);
    

    This is what we remove and we introduce the following

  2. +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
    @@ -220,7 +206,7 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans
    +    $data = isset($values[$this->getEntityType()->getKey('id')]) ? ['status' => static::TRANSLATION_EXISTING] : ['status' => static::TRANSLATION_CREATED];
    

    where if the id entity key "id" is present in the "values" then the translation status is "existing" and otherwise "created".

I think I got problems when calling isNew.. but I'll give it another try.

Status: Needs review » Needs work

The last submitted patch, 37: interdiff-27-37.patch, failed testing.

The last submitted patch, 37: 2666032-37.patch, failed testing.

hchonov’s picture

Status: Needs work » Needs review

There we see we cannot use $this->isNew inside the constructor. So the patch from #27 still applies.

mkalkbrenner’s picture

So the patch from #27 still applies.

@hchonov: In this case, patch #27 should be uploaded again to be the latest one to not confusing people.

hchonov’s picture

@alexpott I've somehow got your question wrong.. Yes, we are fixing a bug, so if the entity is new, then the status should be TRANSLATION_CREATED instead of TRANSLATION_EXISTING.

And like we've seen in #37 the usage of $this->isNew is not possible in the constructor, so I am re-uploading the patch from #27 like mkalkbrenner suggested.

Gábor Hojtsy’s picture

Status: Needs review » Reviewed & tested by the community

As good as it was in #31 :)

catch’s picture

Status: Reviewed & tested by the community » Needs work

Sorry to knock this back again, but let's add a comment in the constructor explaining that we can't use isNew() there, then no-one will wonder next time they look at it.

hchonov’s picture

Status: Needs work » Needs review
FileSize
10.74 KB
799 bytes

@catch, sure it makes sense :).

Gábor Hojtsy’s picture

Status: Needs review » Reviewed & tested by the community

Thanks for the fix!

  • catch committed a469aa2 on 8.3.x
    Issue #2666032 by hchonov, Gábor Hojtsy, mkalkbrenner: Add...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed a469aa2 and pushed to 8.3.x. Thanks!

Gábor Hojtsy’s picture

Issue tags: -sprint

Yay thanks, made a minor modification to the change notice and published at https://www.drupal.org/node/2789903.

hchonov’s picture

Thanks everyone for the reviews and accepting the feature into the core.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.