I'm trying to use Metatag with Commerce Product entities, translated with Entity Translation.

When loading an entity with existing translations, the metatag structure looks like:

array(
  'en' => array('keywords' => array('value' => 'product')),
  'es' => array('keywords' => array('value' => 'producto')),
);

But if I try to update the keywords for the English version via commerce_product_save(), next time I end up with:

array(
  'en' => array(
    'en' => array('keywords' => array('value' => 'product, updated')),
    'es' => array('keywords' => array('value' => 'producto')),
  ),
  'es' => array('keywords' => array('value' => 'producto')),
);

...The 'en' language record gets populated with the values of all other languages!

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

DamienMcKenna’s picture

When was the last time you updated Metatag? Can you please provide the exact version number you're using?

grahamC’s picture

Title: Metatags corrupted when saving translated entities » Metatags corrupted when saving translated entities programmatically

First discovered the problem in 1.0beta7, but it's still here in current Git HEAD.

I've just been trying to reproduce it with nodes, and I can see that it all works correctly when you update the keywords through the UI.

The problem comes if you try to node_load() then node_save() a node with multiple languages. Without even trying to update anything on the node object, all the translations are saved under the key of the current language, as described above.

grahamC’s picture

Related issues: +#1859136: Feeds Integration

This looks to have been broken by the fix at #1859136-33: Feeds Integration

DamienMcKenna’s picture

Or, more to the point, the Feeds integration wasn't updated to handle recent API changes.

DamienMcKenna’s picture

Could you please test this with the current -dev release? Thanks.

DamienMcKenna’s picture

Status: Active » Postponed (maintainer needs more info)

Need confirmation that the problem persists with the current -dev codebase. Thanks.

JeroenT’s picture

Status: Postponed (maintainer needs more info) » Active

I also have this problem using version 7.x-1.0-rc2 of 2014-aug-05.

To test this i added the following code in a hook_init(). :

  $node = node_load(54);
  node_save($node);

I think it has something todo with the following code in the function metatag_metatags_load_multiple :

  $metatags[$record->entity_id][$record->revision_id][$record->language] = $data;

When you save the entity an array with e.g. nl as language will be serialized. The next time you load the metatags it will be the data will already contain nl and the language is added again.

Edit:

When you save a node using the node form the entity that will be saved is :

[metatags] => Array
        (
            [title] => Array
                (
                    [value] => test | test
                )

            [description] => Array
                (
                    [value] => test
                )

        )

The language is not added here.

DamienMcKenna’s picture

I'll take a look at this today, thanks.

DamienMcKenna’s picture

I'm going to hammer on this a little bit, I don't want 1.0 going out with a data-corruption bug like this.

DamienMcKenna’s picture

Priority: Major » Critical

Data corruption = critical.

DamienMcKenna’s picture

Can you please install the Devel module and try this:

define('DRUPAL_ROOT', getcwd());
include_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

$nid = 1;

// Load the node the first time
$n = node_load($nid);
kpr($n->metatags);

// Save the node.
node_save($n);

// Reload the node again.
$x = node_load($nid, NULL, TRUE);
kpr($x);

Add that to a php file in your home directory. Create a node with multiple translations, and update the $nid in the code snippet to that node. Verify that you can go through the normal edit processes and everything works fine. Then run the new PHP script and confirm that the data structures are borked again.

Now try applying the enclosed patch and go through all of the steps again with a new node, it should all work correctly now.

DamienMcKenna’s picture

FileSize
6.08 KB

Doh! The file!

DamienMcKenna’s picture

Status: Active » Needs review
DamienMcKenna’s picture

PS this is the last remaining bug before we can release 1.0, any help testing it would be greatly appreciated!

grahamC’s picture

Status: Needs review » Needs work

Still a problem here I'm afraid...

Drupal allows custom language definitions, which may have codes longer than two characters. The site I'm testing on has 'en-gb' as the default language, for example. When I run node_save(), the structure still gets borked under 'en-gb'.

DamienMcKenna’s picture

@grahamC: Thanks for pointing that out. So instead of checking to see if the array keys have a length of 2 (e.g. "fr") it needs to specifically load the list of available languages and work from that.

JeroenT’s picture

Status: Needs work » Needs review
FileSize
6.13 KB
618 bytes

@DamienMcKenna,

I think it should be something like this. Patch attached.

DamienMcKenna’s picture

@JeroenT: Thanks, you beat me to it :)

grahamC’s picture

The language codes are the keys rather than the values in $languages...

This version works OK here now.

Bit weird that if you do:

$node->metatags = array();
node_save($node);

it only deletes the metatags for the current language - is that a bug?

EDIT: Whoops, Interdiff is backwards ... you get the idea though.

DamienMcKenna’s picture

@grahamC: Ugh. Yeah, that's a tricky one, because metatag_metatags_save() isn't going to know whether the $metatags array was empty because your code manually set $entity->metatags = array(), which should blank out values for all languages, or if there are no values passed from the entity edit form which would only delete the record for the requested language.

DamienMcKenna’s picture

How about "solving" this problem with documentation?

  • Delete meta tags for the current language:
    $node = node_load(123);
    $node->metatag = array();
    node_save($node);
    
  • Delete meta tags for all of the entity's languages:
    $node = node_load(123);
    foreach ($node->metatag as $langcode => $values) {
      $node->metatag[$langcode] = array();
    }
    node_save($node);
    
DamienMcKenna’s picture

FileSize
6.98 KB

I've updated the patch with a note in the developer section of the README.txt file to note the issue.

DamienMcKenna’s picture

Anyone have any feedback on the latest patch? And would a disclaimer in the README.txt be sufficient guard against confusion for people directly manipulating the entity meta tags?

grahamC’s picture

@DamienMcKenna: I could imagine someone testing on a single-language site where it does the right thing, but unknowingly leaving it broken for multilingual sites... I think the documentation route is reasonable though, given that we're only talking about developers specifically interacting with metatags now.

JeroenT’s picture

I tested the following situations as suggested in #11:

  • Without patch: Using node edit form: Works
  • Without patch: Using node edit form: Data structure is wrong
  • With patch: Using node edit form: Works
  • With patch: Using node edit form: Works

About directly manipulating the entity meta tags, I don't think if there is another solution to fix this.

Dave Reid’s picture

Without reading any of the history here and considering that this workflow is contrary to how field values are saved, I find this incredibly confusing.

DamienMcKenna’s picture

@Dave: Any recommendations would be greatly appreciated.

DamienMcKenna’s picture

@Dave: So instead of re-saving all languages, I should maybe just use the same language-check but then only save the current language, matching the effect of submitting an entity through the normal entity edit form?

DamienMcKenna’s picture

FileSize
2.58 KB

This version just saves the submitted language.

grahamC’s picture

@DamienMcKenna:
I'm pretty sure that's the exact opposite of what Dave meant...

node_save() should always persist the current state of the $node object, regardless of the current site language. For example, this is perfectly OK with fields:

$node->body['en-gb'][0]['value'] = "Body english";
$node->body['fr'][0]['value'] = "Body french";
node_save($node);

Or, $node->body = array(); for that matter, which deletes the body in both languages.

Trying to detect whether the data is in one format or another seems a bit daft - I would expect e.g. a form submit handler to marshall the values into the correct language key, so then you'll always be operating on the nested structure in the generic save function.

DamienMcKenna’s picture

I just checked - when you do a node_load() on a node which has content in multiple languages (via Entity_Translation) it does indeed contain multiple language values for each field all contained within the one $node object, just like grahamC said. I need to test how the data submission works for fields and then see what it'd take to make Metatag work the same way. Argh.

DamienMcKenna’s picture

FileSize
13.11 KB

Food for thought. This restructures the form fields to use a nested structure per language, similar to the Form API way of doing it. I've tested it with nodes, users and terms and it appeared to work correctly. When using node_save() it will update the values if they exist, e.g. $node->metatags['en'] will be updated, but just having $node->metatags equal to an empty array will not make any changes. Is this acceptable?

Status: Needs review » Needs work

The last submitted patch, 32: metatag-n2186155-32.patch, failed testing.

DamienMcKenna’s picture

FileSize
14.15 KB

Lets see if this makes the tests compete.

JeroenT’s picture

Status: Needs work » Needs review

Go testbot!

Status: Needs review » Needs work

The last submitted patch, 34: metatag-n2186155-34.patch, failed testing.

DamienMcKenna’s picture

DamienMcKenna’s picture

Argh, the submodules all need to be updated.

DamienMcKenna’s picture

FileSize
15.74 KB

Some updates to the Metatag:Context module so a) it still works, b) is backwards compatible with previous configurations, c) THE TESTS PASS (at least locally)!!1!

JeroenT’s picture

Status: Needs work » Needs review

Go testbot! :-p

DamienMcKenna’s picture

Status: Needs review » Needs work

Actually it needs further work to make integration with the Context UI work, right now just the By Path ui works.

DamienMcKenna’s picture

Status: Needs work » Needs review
FileSize
18.94 KB

This seems to resolve the context_ui problems too.

DamienMcKenna’s picture

There isn't as much funny stuff going on in metatag_panels or metatag_views as metatag_context, so I think they'll be ok.

DamienMcKenna’s picture

FileSize
19.48 KB

This version properly handles the scenario where $entity->metatags is an empty array, i.e. all meta tags for that id/revision combination are deleted.

DamienMcKenna’s picture

Would one (or several) of you here please test out my latest patch to let me know if it works for you? This is the last blocker for 1.0 and I'd like someone else looking over my shoulder so I don't accidentally mess it up (again). Thanks!

DamienMcKenna’s picture

FileSize
21 KB
2.6 KB

This version resolves a problem when creating an entity that the metatag values were not set to the correct language. I also changed the tests to the "Metatag" group, to make it easier to run all tests (drush test-run Metatag).

  • DamienMcKenna committed f9a7fa4 on
    Issue #2186155 by DamienMcKenna, grahamC, JeroenT: Resolved problems...
DamienMcKenna’s picture

Status: Needs review » Fixed

I've tested the patch in #46 rather extensively and have committed it as-is. I'm going to test it tomorrow a little more with a few additional test sites, if there are no problems then I'll release 1.0. Thanks all for the help!

DamienMcKenna’s picture

Status: Fixed » Needs review
FileSize
5.53 KB

I spoke too soon. There were several scenarios which were incorrectly handled by the code committed previously:

  • The logic in metatag_entity_update() for checking if an entity's language had changed was executed after the language value would have already changed; as a result it was necessary to directly check the $entity->original->language value.
  • When a node had its language setting altered, the node's old language record was not removed.
  • When a node had its language setting altered, the meta tag values were not changed over to using the new language.
  • There was old code in metatag_metatags_save() that would re-load the previous revisions metatag data and save it with the new revision ID. This was the wrong way of handling this issue as it clashed with the need to be able to correctly handle entity_save / node_save.
  • The workaround for the previous issue was to instead load all of the meta tag data for languages, other than the one being currently edited, in metatag_metatags_form_submit() and allow it to be saved via metatag_entity_update().
  • There were complications with logic for the above between the scenario of editing an untranslated node, versus editing the translation of a translated node; in the first scenario the $new_language would equal $entity->language, but not so in the second scenario, which could lead to false positives and both corruption & loss of data.

This patch resolves these issues.

  • DamienMcKenna committed 82dcf2b on 7.x-1.x
    Issue #2186155 by DamienMcKenna: Follow-up to fix a number of scenarios.
    
DamienMcKenna’s picture

Status: Needs review » Fixed

I've tested the latest patch a bunch more times on different entity types, and it all seemed to be working ok, so I'm committing it!

seaarg’s picture

Still corrupted (description and abstract are the keywords I tested) using
Feeds -dev
Metatags -dev

I did a quick & dirty fix by updating like this on metatag.feeds.inc and it worked.

  // Assign the value.
  //$entity->metatags[$name]['value'] = $value; <- commented out and replaced by the following line
  $entity->metatags[$entity->language][$name]['value'] = (is_array($value) ? $value[0] : $value);

The node form generated metatags array has 'und' as first level element of the array and the value that comes from feeds is an array on my case

Leeteq’s picture

Status: Fixed » Needs work
DamienMcKenna’s picture

Status: Needs work » Fixed

Lets handle the Feeds integration in a new issue: #2347193: Feeds integration borken in 1.0

Status: Fixed » Closed (fixed)

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