Problem/Motivation

Domain node tokens are not available when a node is first created, causing problems with URL alias patterns that use [node:domain] or [node:domain:*] tokens.

Steps to reproduce

1. Enable Pathauto.
2. Set a content type's URL alias pattern to 'content/[node:domain]/[node:title]'.
3. Create a new node of the specific type in step 2, leaving the 'Automatic alias' checkbox checked. Also ensure the node is assigned to one domain.

Desired result

The node is assigned the URL alias 'content/mydomain-com/my-node-title'.

Actual result

The node is assigned the URL alias 'content/my-node-title'.

Explanation

This seems to be caused by the fact that domain does not save its access record data (e.g. $node->domains) when a node is saved - therefore in domain_tokens() the domain_get_node_match() function fails when run in hook_node_insert() from Pathauto.

Proposed resolution

Not sure - this seems to be a result of node access records not being able to be saved from inside node_save(). I'm not sure why we can't actually just save the records into {node_access} from a domain_node_insert() or domain_node_update().

Remaining tasks

  • Figure out appropriate solution
  • Write patch and tests

User interface changes

None as of yet

API changes

None as of yet

Comments

dave reid’s picture

This is the workaround I had to implement for now:

/**
 * Implements hook_tokens().
 */
function custom_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $sanitize = !empty($options['sanitize']);
  $replacements = array();

  // Workaround for http://drupal.org/node/1336698 to return valid tokens for
  // a new node being inserted.
  if ($type == 'node' && !empty($data['node'])) {
    $node = $data['node'];

    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'domain':
          if ($domain = custom_domain_get_node_match($node)) {
            $replacements[$original] = $sanitize ? check_plain($domain['subdomain']) : $domain['subdomain'];
          }
          break;
      }
    }

    if ($domain_tokens = token_find_with_prefix($tokens, 'domain')) {
      if ($domain = custom_domain_get_node_match($node)) {
        $replacements += token_generate('domain', $domain_tokens, array('domain' => $domain), $options);
      }
    }
  }

  return $replacements;
}

/**
 * Workaround for http://drupal.org/node/1336698 to return a new node's domain.
 */
function custom_domain_get_node_match($node) {
  $domain = domain_get_node_match($node->nid);
  if (is_array($domain)) {
    // If domain_get_node_match() return a valid domain, then we do not want
    // to continue.
    return FALSE;
  }
  elseif (isset($node->domains) && is_array($node->domains)) {
    $domains = array_filter($node->domains);
    if (!empty($domains) && $domain = domain_lookup(reset($domains))) {
      drupal_alter('domain_source', $domain, $node->nid);
    }
  }
  return is_array($domain) ? $domain : FALSE;
}
agentrickard’s picture

This probably has to do with the callstack order in saving Drupal nodes.

I think this is also related to #1219746: Cannot static cache _load() calls.

When does pathauto / token fire? What state is the $node object in at that point? (e.g. has it been saved or is it about to be saved?)

There may not be a better workaround than this, because the node data you are requesting doesn't get saved until hook_node_access_records() runs.

agentrickard’s picture

I assume this affects all the other tokens as well.

Best methods for testing?

agentrickard’s picture

Status: Active » Needs review
StatusFileSize
new1.91 KB

Here's a patch that seems to correct the issue.

agentrickard’s picture

dave reid’s picture

StatusFileSize
new2.03 KB

Here's the patch I had locally which also seemed to address it by writing the domain access records from within hook_node_insert() and hook_node_update(). Although this approach will be fragile if a module that also implements either hook runs *before* domain.module.

agentrickard’s picture

I think we might merge the update / insert part of that with the new if/else handling of my patch.

agentrickard’s picture

StatusFileSize
new3.25 KB

And here that is.

dave reid’s picture

Working on the tests...

dave reid’s picture

StatusFileSize
new6.33 KB

Here is a patch with tests based off patch #8. It also fixes an error that allowed the [node:domain:*] tokens to work because domain_get_node_match() returned NULL, which doesn't match the condition if ($domain == -1) in domain_tokens(). The sub-tokens would then erroneously use replace tokens with the default domain values because of the following code in domain_tokens():

    // Get the active domain context.
    if (!empty($data['domain'])) {
      $domain = $data['domain'];
    }
    else {
      $domain = domain_get_domain(); // NOTE: This domain data 'fallback' should be removed. People should be using the [default-domain:*] tokens instead since this can cause false positives in token replacement.
    }
dave reid’s picture

Also the [node:domain] token should be returning the sitename and not the subdomain as the 'name' of the domain should be the default token.

dave reid’s picture

Status: Needs review » Needs work

I'm going to add some more test coverage for the tokens as well since this really should be covered prior to 3.0.

dave reid’s picture

Status: Needs work » Needs review
StatusFileSize
new10.92 KB

Here's a final patch with full test coverage for the domain tokens.

Summary of changes

  • Includes changes in patch #8.
  • Does not fall back to the default domain in domain_tokens if $type is 'domain' and $data['domain'] is empty. This is what the [current-domain:*] tokens are for. Does not need to be backported as this is actually how the tokens in 7.x-2.x work.
  • Changes the [node:domain] token to output the site name rather than the sub-domain. The domain site name is a much better 'default' token to use - think of this like the [node:author] token. 'author' is a user object in this case, and it gets output as the user's formatted name. Does not need to be backported as there is no [node:domain] token in 7.x-2.x.
  • Renames the [domain:machine_name] token to [domain:machine-name] as dashes are what should be used if possible in token names. Does not need to be backported to 7.x-2.x as there is no machine name in that branch.
  • Test coverage for the domain, current-domain, and default-domain tokens, as well as a regression test specifically for this issue. Ideally some of these tests should be backported to 7.x-2.x. I can help with that post-commit.
dave reid’s picture

StatusFileSize
new10.98 KB

Revised patch that allows the [node:machine_name] token to still work with existing token replacement, but essentially deprecates it.

dave reid’s picture

StatusFileSize
new11 KB

Revised patch should fix failures in folder based install.

agentrickard’s picture

Status: Needs review » Fixed
StatusFileSize
new10.67 KB

And, we keep cross-posting.

Here's a patch with working tests. I'm punting the machine_name / machine-name token issue to a follow up.

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

Anonymous’s picture

Issue summary: View changes

Updating remaining tasks