Drupal 7:从未发布的内容重定向用户

我最近被要求在 Drupal 站点上实现一项功能,如果该节点已取消发布,则特定类型的所有节点都将重定向到主列表页面。这样做的问题是,如果帖子未发布,则 Drupal 将在启动过程的早期发出拒绝访问的响应。当菜单项被加载时,它会通过一个访问回调,该回调看到帖子未发布并在其他任何事情发生之前发出拒绝访问。所以在这种情况下,你不能使用规则之类的东西来重定向用户,因为规则永远不会被触发。

解决方案是使用hook_menu_alter()更改节点页面的访问回调参数。我们本质上是node_access()用我们自己的版本替换正常的访问回调。

/**
 * Implements hook_menu_alter().
 */
function mymodule_menu_alter(&$items) {
 $items['node/%node']['access callback'] = 'mymodule_access_callback';
}

这是我们自己实现的新节点访问回调。我们感兴趣的是任何未发布且用户是否匿名的“文章”类型的节点。如果检测到这种情况,则重定向请求。如果没有检测到,那么我们只需返回 的正常结果node_access()。无需更改节点权限为此工作的方式,因此我们保持原样。

function mymodule_access_callback($op, $node, $account = NULL) {
  if ($node->type == 'article' && $node->status == 0 && ! user_is_logged_in()) {
    drupal_goto('/articles_list_page');
  }
  return node_access($op, $node, $account);
}

这只是故事的一半。我们还需要考虑如果页面是通过 Javscript 访问的,或者在 cron 运行期间遇到页面会发生什么。使用上面的代码,如果这些条件中的任何一个发生,那么 cron 将中断,并且您将在自动完成字段上收到奇怪的 JavaScript 错误。下面添加了一些更多的检查来防止这种情况。

function mymodule_access_callback($op, $node, $account = NULL) {
  // 如果这是未发表的 article_page。
  if ($node->type == 'article' && $node->status == 0 && ! user_is_logged_in()) {
    // 确保 cron 没有运行。
    if (variable_get('cron_semaphore', FALSE) === FALSE && $_SERVER['SCRIPT_NAME'] !== '/cron.php' && arg(3) !== 'run-cron') {
      // 不是 JavaScript 回调。
      if (arg(0) != 'js') {
        return drupal_goto('/articles_list_page');
      }
    }
  }
  return node_access($op, $node, $account);
}

上面的解决方案显示了了解 Drupal 中的内部引导过程以及在什么情况下使用什么钩子是多么重要。我们可以在这里使用类似的东西hook_init()来做同样的事情,但我们需要事先加载节点,因为它会在每个页面请求上触发,检查会更复杂,所以我们不会生成任何错误的重定向。通过以这种方式覆盖节点访问回调,我们确保我们只处理正确的节点。