This is a specific issue for multilingual websites. If you have just upgraded and do not have a multilingual site please see #1442080: "permission restrictions" in reports -> run re-analyze only task once after upgrade

In a multilingual site using i18n module the db_rewrite_sql() function causes language negotiation to be added to the query. This means a large percentage of broken links cannot be fixed (or are difficult to fix) because they are obscured by the "Permission restrictions deny you access to this broken link" text which makes it impossible to see where the link is, why permission is denied, or which language it is in. Since you do not need to speak the language to fix a URL I don't think this is desired functionality. linkchecker v2.4 showed all broken links in all languages so for me this is a feature regression.

I tracked this down to linkchecker.module -> _linkchecker_link_node_ids() -> two db_rewrite_sql() calls.

My temp fix is to trick i18n into thinking the sql already has language negotiation by adding "i18n" text to the sql that does not interfere with the query.

function _linkchecker_link_node_ids($link, $node_author_account = NULL) {
  static $fields_with_node_links = array();

  // If the user cannot access content, there is no need to check further.
  if (!user_access('access content')) {
    return array();
  }

  // Trick i18n module so that it does not add language negotiation sql
  $dummy = '';
  if (module_exists('i18n')) {
    $dummy = 'n.nid <> "i18n" AND ';
  }
  
  // Get a list of nodes containing the link, using db_rewrite_sql() to allow
  // node access modules to exclude nodes that the current user does not have
  // access to view.
  if (!empty($node_author_account)) {
    $nodes = db_query(db_rewrite_sql('SELECT n.nid
      FROM {node} n
      INNER JOIN {linkchecker_nodes} ln ON ln.nid = n.nid
      INNER JOIN {node_revisions} r ON r.vid = n.vid
      WHERE ' . $dummy . 'ln.lid = %d AND (n.uid = %d OR r.uid = %d)'), $link->lid, $node_author_account->uid, $node_author_account->uid);
  }
  else {
    $nodes = db_query(db_rewrite_sql('SELECT n.nid
      FROM {node} n
      INNER JOIN {linkchecker_nodes} ln ON ln.nid = n.nid
      WHERE ' . $dummy . 'ln.lid = %d'), $link->lid);
  }

// ...

Alternatively passing array('view' => NULL) as the $arg for db_rewrite_sql() will also stop i18n but I don't know the full ramification of using that.

$nodes = db_query(db_rewrite_sql('SELECT n.nid
      FROM {node} n
      INNER JOIN {linkchecker_nodes} ln ON ln.nid = n.nid
      WHERE ' . $dummy . 'ln.lid = %d', 'n', 'nid', array('view' => NULL)), $link->lid);

If one of these is acceptable I would gladly roll a patch. In my case this is a critical issue since the site has 5 languages. The rest of the "Permission restrictions" problems seem to be caused by unpublished content which probably should not be link checked?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

hass’s picture

Please make sure you run a Re-analyze after upgrading to 2.5. Does this solve the problem?

hass’s picture

Is there no better way from stopping i18n modue from extending the query?

tinker’s picture

Re-analyze is not the solution. Did that multiple times on two sites to no avail. I debugged the problem and its the sql. i18n injects language = english criteria where as the nodes that have the links are in other languages. Language is not a valid criteria here. There are a number of checks in i18n_db_rewrite_sql to stop language injection but I think I chose the most suitable. The checks are:

 // i18n.module
function i18n_db_rewrite_sql($query, $primary_table, $primary_key, $args = array()) {
  // If mode is 'off' = no rewrite, we cannot return any empty 'where' string so check here.
  $mode = i18n_selection_mode();
  if ($mode == 'off') return;

  // Disable language conditions for views.
  if (array_key_exists('view', $args)) return;

  switch ($primary_table) {
    case 'n':
    case 'node':
      // No rewrite for queries with subselect ? (views count queries).
      // @ TO DO Actually these queries look un-rewrittable, check with other developers.
      if (preg_match("/FROM \(SELECT/", $query)) return;
      // No rewrite for translation module queries.
      if (preg_match("/.*FROM {node} $primary_table WHERE.*$primary_table\.tnid/", $query)) return;
      // When loading specific nodes, language conditions shouldn't apply.
      if (preg_match("/WHERE.*\s$primary_table.nid\s*=\s*(\d|%d)/", $query)) return;
      // If language conditions already there, get out.
      if (preg_match("/i18n/", $query)) return;

My first method will not interfer with queries because NID will never equal "i18n" so I think it is the safest. The second method is cleaner PHP and SQL as there is no SQL addition but I could not find usage of $args array ('view' =>) in any modules outside of i18n so I don't know how it could interfere. Everything else involves larger changes to SQL.

I have only tested on MySQL.

hass’s picture

Priority: Major » Critical
Status: Needs review » Needs work

You should never compare a string value with an integer. This adds serious database load as it tries to convert the i18n to integer what could theroretically end with 18... And match on node 18, but i'm not sure about sql... This will at least happen in php. The arg views solution sounds ok, if this is clean - or we add "i18n == i18n" to be clean with datatypes. Like views do it for debugging...

CNW as there is no clean patch.

hass’s picture

hass’s picture

Title: "Permission restrictions deny" all broken links in other languages » I18n: "Permission restrictions deny" all broken links in other languages
hass’s picture

Title: I18n: "Permission restrictions deny" all broken links in other languages » i18n: "Permission restrictions deny" all broken links in other languages
susanmccormick’s picture

I am getting the same "permission restrictions deny" error and I don't have i18 installed. Using Linkchechecker 6.x-2.5.

hass’s picture

hass’s picture

Status: Needs work » Needs review
FileSize
1.14 KB

Here is a proper patch for D6. I'm myself not really sure if we need to set it to 'off' or one of the many other possible values. Please test this patch.

off = No language conditions inserted.
simple = Only current language and no language.
mixed = Only current and default languages.
strict = Only current language.
default = Only default language.
user = User defined, in the module's settings page.
params = Gets the stored params.
reset = Returns to previous.
custom = add custom where clause, like "%alias.language = 'en'".

What type of language detection are you using? Path based or domain based or any other? Aside, what about comments, blocks?

The last submitted patch, 1488572_linkchecker_i18n_D6.patch, failed testing.

hass’s picture

Status: Needs work » Needs review

#10: 1488572_linkchecker_i18n_D6.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, 1488572_linkchecker_i18n_D6.patch, failed testing.

hass’s picture

Status: Needs review » Needs work
FileSize
1.14 KB

Patch has no unix lf's. suxxx testbot.

hass’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 1488572_linkchecker_i18n_D6.patch, failed testing.

hass’s picture

Status: Needs review » Needs work
FileSize
1.08 KB

suxxx, same patch again

hass’s picture

Status: Needs work » Needs review
hass’s picture

Status: Needs work » Needs review
hass’s picture

@tinker: have you been able to test this patch?

hass’s picture

Status: Needs review » Fixed

Status: Fixed » Closed (fixed)

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

caktux’s picture

Version: 6.x-2.5 » 6.x-2.x-dev
Priority: Critical » Major
Status: Closed (fixed) » Needs review
FileSize
928 bytes

tinker's solution was right and the patch in #17 didn't work for me but might also be necessary, didn't check without it since it's already committed. I went with the array('view' => NULL) trick since it felt cleaner, although I'm also unsure of what it's actually doing. We're only changing the query in our module so it shouldn't affect anything else.

caktux’s picture

Just realized I applied his solution to _linkchecker_link_comment_ids instead of _linkchecker_link_node_ids, but that was actually the problem. Sweet.

caktux’s picture

Issue summary: View changes

Add link to other issue that will help most people.

hass’s picture

nerdcore’s picture

Version: 6.x-2.x-dev » 7.x-1.2

Having similar issues using i18n + linkchecker-7.x-1.2.

Note that D7 has neither "db_rewrite_sql" nor "i18n_selection_mode" functions in its API.

hass’s picture

Version: 7.x-1.2 » 6.x-2.x-dev
rsvelko’s picture

for the same issue but in D7 / 7.x - > #2195429: Permission restrictions deny for links to deleted nodes
EDIT: the one above is delete-node related - the issue here in 6 and D7 is i18n-related.
I found that the language_switcher module is not playing nice and alters the sql query in a bad manner for our link checker reports. See fix there - #2450175: Permission restrictions errors in linkchecker module - bec. of the language_switcher module

VladimirAus’s picture

Closing version 6 task. 🚪
Please reopen and update version if still applicable.

VladimirAus’s picture

Status: Needs review » Closed (outdated)