If there's multiple entities involved in viewed page, metatags are not correctly rendered. This is because $i_will_say_this_only_once is set to TRUE too early in metatag_entity_view, indeed when TRUE this variable forbids the metatags to be set even if metatags have not been set yet.

For instance, if I put a dsm($entity) at the beginning of this function, on a user page I get :

  • Object Profile (Profile2 object)
  • Object StdClass (my user, where the metas are stored)
  • Object EntityForm Type (another object that may contain metatags)

So, if I set $i_will_say_this_only_once = TRUE; just after the first entity, there's no chance that I get my user metas being displayed.
The solution would be to put $i_will_say_this_only_once = TRUE; at the end of the function, so that we are at least sure that metas have been set (but we are not sure that this are the metas we want, if there are concurrent metas provided by multiple entities).

Comments

damienmckenna’s picture

Status: Active » Closed (works as designed)

That's part of the problem - if there are multiple entities loading on the one page, how do you tell which one is authoritative? The _metatag_entity_is_page() function should verify that the current page is only appropriate for a specific entity, so unless there's some strange way to have different entities both have the same internal URL, I don't see how this is possible.

If you can provide more details, and better identify a scenario this might actually happen, then I can reexamine it. Otherwise I think this works as it should.

Thanks for taking the time to report it.

hideaway’s picture

I have similar problem here. I have a content type which is a collection of nodes (article_collection) which contains articles (article). My urls are like /parent_nid/child_nid. Problem is, that I need to show metatags of active child node, and not parent node. How can I do this? I am fighting over a day in metatag_metatags_view_alter hook but even though I am able to load child node without any problem, I cannot find a way to override metatags (to use child node metatags instead of parent node) :(

hideaway’s picture

Ok I solved this with following code (not sure, if this is kosher). There is at least some duplicated code, since you cannot call something like metatag_override_tags($metatags).

// alter metatags for article collection to use the metatags of active child instead
function alters_metatag_metatags_view_alter(&$output, &$instance) {
  static $called = FALSE;

  if ($called) {
    return;
  }
  
  $called = TRUE;
  
  // for article collection use child metatags instead of parent ones
  if (($node = menu_get_object()) && $node->type == 'article_collection') {
    // node/%parent_node/%child_node
    $child = node_load(arg(2)); 
    
    // check if id for child article is legit
    if (!$child || $child->type != 'article_long') {
      // take first child, if id is not legit
      if (!empty($node->field_article_collection['und'][0])) {
        $child_id = reset($node->field_article_collection['und'][0]);
        $child = node_load($child_id);
      }
    }
    
    if (!$child) {
      return;
    }
    
    $child->metatags = reset(metatag_metatags_load('node', $child->nid));
    $entity = $child;
    
    $bundle = 'article_long';
    $entity_type = 'node';
    $entity_id = $child->nid;
    $view_mode = 'full';
    $langcode = $child->language;
    
    // All applicable pieces for this current page.
    $cid_parts = array(
      'entity_type' => $entity_type,
      'bundle' => 'article_long',
      'entity_id' => $entity_id,
      'revision_id' => $child->vid,
      'langcode' => $langcode,
      'view_mode' => $view_mode,
    );
    
    $cid = metatag_cache_default_cid_parts($cid_parts);

    if ($cache = metatag_cache_get($cid)) {
      $output = $cache->data;
    }
    else {
      // Separate the meta tags.
      $metatags = isset($entity->metatags) ? $entity->metatags : array();
      
      // Build options for meta tag rendering.
      $options = array(
        'entity' => $entity,
        'entity_type' => $entity_type,
        'view_mode' => $view_mode,
      );
      
      $options['token data']['node'] = $entity;

      // Ensure we actually pass a language object rather than language code.
      $languages = language_list();
      if (isset($languages[$langcode])) {
        $options['language'] = $languages[$langcode];
      }

      // Reload the entity object from cache as it may have been altered.
      $token_type = token_get_entity_mapping('entity', $entity_type);
      $entities = entity_load($entity_type, array($entity_id));
      $options['token data'][$token_type] = $entities[$entity_id];
      $options['entity'] = $entities[$entity_id];

      // Render the metatags and save to the cache.
      $output = metatag_metatags_view('node:' . $bundle, $metatags, $options);
      metatag_cache_set($cid, $output);
    }
  }
}
pedrosmoker’s picture

I have a similar problem, where should i put this code?

damienmckenna’s picture

@pedrosmoker: You need to create a custom module to put the code in.

roderik’s picture

Status: Closed (works as designed) » Needs review
StatusFileSize
new1.01 KB

I hope I can reopen this issue because it's not got any code attached to it yet - and I _think_ it directly addresses the original reported issue:

That's part of the problem - if there are multiple entities loading on the one page, how do you tell which one is authoritative?

Suggested extra rule: if an entity is not configured to have metatags in the first place, then never regard it as authoritative. This probably does not solve all possible exotic problems, but it does solve the profile vs. user problem. (In theory) it also offers more consistent behavior when a pair of entities with metatags enabled & disabled, are somehow entity_view()ed in a different order.
And I think/hope that this behavior change does not have an adverse effect on other situations.

At the moment, this happens on my site:

  • The Profile2 entity gets 'view'ed first, and since I have Metatag settings turned off profile2 entities, metatag_generate_entity_metatags() returns NULL. After $i_will_say_this_only_once = TRUE is set.
  • Then the User entity which does actually have metatags, gets 'view'ed... and its metatags get ignored because the metatag-less Profile2 entity got here first.

Attached patch fixes that. There are a dozen small things that could be done differently (like the location of metatag_entity_supports_metatags(), the fact that that is now called twice, code inside an if{} vs a return statement inside an if{}... but this is the cleanest patch I could produce without knowing all code details/preferences.

PS if you agree and want a test, feel free to tell me. My 'drupal volunteer time' is sparse atm but I'll get around to it sometime.

damienmckenna’s picture

Issue tags: +Needs tests

@roderik: Nice patch! I'll give it a more detailed test shortly, but this seems like a great improvement.

I think it'd be useful to add some tests to confirm the logic works correctly.

damienmckenna’s picture

Status: Needs review » Needs work
damienmckenna’s picture

Status: Needs work » Needs review
StatusFileSize
new861 bytes

I'm trying to work out what could cause this error to happen in the first place - would you happen to steps on how to configure Profile2 so it triggers the bug? Thanks. I'm trying to write some tests to cover the scenario.

damienmckenna’s picture

Hoping to finish this for the next release.

damienmckenna’s picture

Status: Needs review » Needs work

Changing this to Needs Work as we (I) need to write tests to make sure it works correctly.

roderik’s picture

Thanks for your ping via message; it's unfortunately necessary these days.

I'm taking the short route and not trying to write/test a test myself; just describing what I think should work, from what I was seeing on the live site I was tingering with.

Steps to reproduce:

* Enable metatags for user entities (if that isn't on by default - hey, I'm clueless.)
* Create a profile2 entity type/bundle. (if there isn't a default one after enabling the module. Again, clueless.) (Not sure if it should have a field or can work without. No metatag configuration here; metatags will/should be disallowed for this profile2 type.)
* Create/save a user, with an explicit (e.g.) description metatag value.
* Create/save a profile for this user.
* View the user page; assert that the <meta name="description" content=whatever> is present.

Assertion is expected to fail without the patch (because, as far as I can see, the code is set up such that the profile2 entity is always entity_view()ed before the user entity) -- and to succeed with the patch.

I just confirmed this on my site after upgrading it to 1.16 stable.

Thanks for the confirmation my fix is probably the right thing to do.

damienmckenna’s picture

@roderik: Thanks for the info, I thought there might have been more to it ;-) I'm working on some tests, will have something shortly.

damienmckenna’s picture

StatusFileSize
new359 bytes

Step 1: a patch to add the Profile2 dependency.

  • DamienMcKenna committed 663d597 on 7.x-1.x
    Issue #2162397 by DamienMckenna: Added test dependency for Profile2.
    
damienmckenna’s picture

Patch #14 has been committed so that we can then add tests.

damienmckenna’s picture

Status: Needs work » Needs review
StatusFileSize
new7.4 KB

Here's a test to confirm that the problem exists.

damienmckenna’s picture

StatusFileSize
new8.41 KB

And now with the original patch to confirm the bug is fixed.

The last submitted patch, 17: metatag-n2162397-17.patch, failed testing.

damienmckenna’s picture

This is where I run around screaming because it all works correctly. And then eat ice-cream.

damienmckenna’s picture

Status: Needs review » Fixed

Committed. Thanks roderick!

Status: Fixed » Closed (fixed)

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