When using the "Flag other translations as outdated" option the timestamp in the node_field_data table is set a few seconds after the node timestamp. This is probably because the one is set using REQUEST_TIME and the other from the time when the form was first built.

This results in the following error message:

The content on this page has either been modified by another user, or you have already submitted modifications using this form. As a result, your changes cannot be saved.

Steps to replicate:

  1. Enable at least one language in addition to English.
  2. Add a translatable field to a content type.
  3. Create a node in English.
  4. Add a translation in the other language, checking the option "Flag other translations as outdated".
  5. Try to edit the node in English again. The error message appears, and the node cannot be saved.

Comments

pfrenssen’s picture

Here is a test that shows the error message appearing during execution of ContentTranslationUITest::assertOutdatedStatus().

pfrenssen’s picture

Status: Active » Needs review

Status: Needs review » Needs work
gábor hojtsy’s picture

Issue tags: +language-content

WOAH! Sounds like both should be built using the same time (form time I guess).

pfrenssen’s picture

Status: Needs work » Needs review
StatusFileSize
new1.33 KB

Rerolled the test. Mind that this is not intended to be the actual test for this bug, it just indicates the error message appearing when translations are marked as outdated.

Status: Needs review » Needs work
pfrenssen’s picture

The way the 'changed' timestamps are updated in the node_field_data table is not consistent. If a node is saved in the original language, the timestamps of all languages are updated. When a translation is saved, only the timestamp of the translation is updated.

1. Create a new node in English. Only the english field data is saved.

mysql> select langcode,changed from node_field_data where nid=12;
+----------+------------+
| langcode | changed    |
+----------+------------+
| en       | 1379312173 |
+----------+------------+

2. Add a translation in Khmer. Only the timestamp of the Khmer translation is updated in node_field_data.

mysql> select langcode,changed from node_field_data where nid=12;
+----------+------------+
| langcode | changed    |
+----------+------------+
| en       | 1379312173 |
| km       | 1379312364 |
+----------+------------+

3. Resave the node in English. This time both the timestamps of the English and Khmer translations are updated again.

mysql> select langcode,changed from node_field_data where nid=12;
+----------+------------+
| langcode | changed    |
+----------+------------+
| en       | 1379312451 |
| km       | 1379312451 |
+----------+------------+

What is the correct one? I suppose only the updated translation should have its timestamp changed, otherwise you wouldn't know when exactly a translation was updated.

pfrenssen’s picture

From NodeFormController::validate():

    if ($node->id() && (node_last_changed($node->id(), $this->getFormLangcode($form_state)) > $node->getChangedTime())) {
      form_set_error('changed', t('The content on this page has either been modified by another user, or you have already submitted modifications using this form. As a result, your changes cannot be saved.'));
    }

node_last_changed() retrieves the changed timestamp of the current translation, while getChangedTime() gets the one from LANGUAGE_UNDEFINED. So as soon as any translation is more recent than the default language this fails. We should allow to pass a language parameter to getChangedTime().

I also wonder why the constructor of EntityNG only stores the timestamps of the default language version, wouldn't it be more interesting to store these for all translations?

pfrenssen’s picture

Status: Needs work » Needs review
StatusFileSize
new2.91 KB

This solves it, passing the langcode to getChangedTime(). If this is deemed a good approach we should extend this to the other properties.

Status: Needs review » Needs work

The last submitted patch, 2087299-9-content_translation-changed.patch, failed testing.

gábor hojtsy’s picture

I think the problem is when you have a node loaded, you have *all* the data on your loaded node. So when you go and re-save a node, even if you only changed a language that was not saved since, you may make another language outdated. So for that we are better off if we take max(all the changed dates we have), so we know the very latest modification time for all languages.

pfrenssen’s picture

Status: Needs work » Needs review
StatusFileSize
new2.53 KB

This patch returns the most recent changed timestamp across all translations. I also added getChangedTime() to the NodeInterface as it was missing.

The tests are failing because the $this->values['changed'] array sometimes contains strings, and sometimes arrays :-/

For example if I inspect it in Entity::__construct() when running NodeTranslationTest:

'changed' =>
  array (
    'und' => '1379485053',
    'it' => '1379485152',
  ),

and during CommentActionsTest:

'changed' =>
  array (
    'und' => 
      array (
        0 => 
          array (
            'value' => 1379485611,
          ),
      ),
  )
berdir’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php
    @@ -10,6 +10,7 @@
    +use Drupal\Core\StringTranslation\TranslationInterface;
    

    This seems unecessary?

  2. +++ b/core/modules/node/lib/Drupal/node/Entity/Node.php
    @@ -216,6 +216,10 @@ public function setCreatedTime($timestamp) {
    +    // Return the timestamp of the most recent translation if available.
    +    if (!empty($this->values['changed'])) {
    +      return max($this->values['changed']);
    +    }
    

    I don't think this is correct. The values shouldn't be directly accessed like this.

  3. +++ b/core/modules/node/lib/Drupal/node/NodeInterface.php
    @@ -64,6 +64,14 @@ public function getCreatedTime();
    +   * Returns the timestamp when the node was last changed.
    +   *
    +   * @return int
    +   *   Last changed timestamp of the node.
    +   */
    +  public function getChangedTime();
    

    This method is now defined in EntityChangedInterface, this needs to be removed.

pfrenssen’s picture

Assigned: Unassigned » pfrenssen
pfrenssen’s picture

Title: Impossible to save nodes after marking translation as outdated » Impossible to save nodes if translation is newer than default language
StatusFileSize
new2.19 KB
new1.95 KB

I'm working on this but I could use some input. In this patch I have addressed the remarks from #13, but I have trouble retrieving the 'changed' timestamp of a particular translation. I assume I should use EntityNG::getTranslatedField() for it but this is always returning the timestamp of the default language, because the 'changed' property is not marked as translatable:

Say I have a node with English as the default language, and a Spanish translation. I want to retrieve the 'changed' timestamp from the Spanish translation, so I call it with $this->getTranslatedField('changed', 'es'):

  protected function getTranslatedField($property_name, $langcode) {
    // ...
    if (!isset($this->fields[$property_name][$langcode])) {
      $definition = $this->getPropertyDefinition($property_name);
      // ...

      // $definition['translatable'] is not set for the 'changed' property, so this evaluates to TRUE:
      if ($langcode != Language::LANGCODE_DEFAULT && empty($definition['translatable'])) {
        // The English timestamp will be retrieved here instead of the Spanish one.
        $this->fields[$property_name][$langcode] = $this->getTranslatedField($property_name, Language::LANGCODE_DEFAULT);
      }
      // ...
    }
    return $this->fields[$property_name][$langcode];
  }

I understand that the 'changed' property is not something which we should 'translate' but it should still be accessible in some way. This also is the case for other properties which are not strictly translatable but are translation-specific.

berdir’s picture

Assigned: pfrenssen » plach

I don't know what the correct fix here is, the problem is that changed is defined as not translatable but holds translatable values..

@plach?

pfrenssen’s picture

Assigned: plach » Unassigned
Issue summary: View changes
Status: Needs work » Closed (duplicate)

This problem does not occur any more since #2129039: Integrity constraint violation when translating body field got in. Marking this as a duplicate.