如何使用hook_menu_alter()来操作路径访问控制

enj*_*ife 3 drupal drupal-6 drupal-modules

/**
 * Implementation of hook_menu_alter().
 */
function joke_menu_alter(&$callbacks) {
  // If the user does not have 'administer nodes' permission,
  // disable the joke menu item by setting its access callback to FALSE.
  if (!user_access('administer nodes')) {
    $callbacks['node/add/joke']['access callback'] = FALSE;
    // Must unset access arguments or Drupal will use user_access()
    // as a default access callback.
    unset($callbacks['node/add/joke']['access arguments']);
  }
}
Run Code Online (Sandbox Code Playgroud)

以上功能来自pro开发drupal.我不能理解它.为什么我必须取消设置访问参数(unset($callbacks['node/add/joke']['access arguments']);)

谢谢.

Gra*_*ide 19

整个例子看起来很糟糕.总之,一个笑话.首先,让我回答你的问题,然后我将继续解释为什么你不应该在实践中遵循这个例子.

来自includes/menu.inc:

if (!isset($item['access callback']) && isset($item['access arguments'])) {
  // Default callback.
  $item['access callback'] = 'user_access';
}
Run Code Online (Sandbox Code Playgroud)

当您不再需要它们时取消设置访问回调(毕竟现在依赖于布尔值)可以防止Drupal的路由系统中的过于聪明的逻辑被打入,user_access()因此它有一些事情要做.

现在,为什么这是错误的代码.

hook_menu()并且hook_menu_alter()都在缓存清除上运行(更具体地说,在重建菜单路由系统时).这意味着任何用户点击网站重建菜单的权限都将被硬编码到菜单路由行为中.这是一个非常糟糕和不一致的安排.

如果要根据权限阻止对路径的访问,则需要将回调更改为将测试该权限的内容.然后,当重建菜单时,它将检查每页加载的新回调函数,以查看是否应授予当前用户权限.

一个简单的例子可能如下所示:

/**
 * Implementation of hook_menu_alter().
 */
function joke_menu_alter(&$items) {
  $items['node/add/joke']['access callback'] = 'user_access';
  $items['node/add/joke']['access arguments'] = array('administer nodes');
}
Run Code Online (Sandbox Code Playgroud)

现在我们有一个函数,它接受node/add/joke路径并声明唯一重要的是用户是否有administer nodes权限.当然,这比示例的明显意图更有限,这是为了保留现有的访问控制,但也要求用户获得administer nodes许可.

这也是可以修复的,但更复杂.借用Spaces项目中的一些概念:

/**
 * Implementation of hook_menu_alter().
 */
function joke_menu_alter(&$items) {
  $path = 'node/add/joke';
  $items[$path]['access arguments'][] = $items[$path]['access callback'];
  $items[$path]['access callback'] = 'joke_menu_access';
}

function joke_menu_access() {
  $args = func_get_args();
  $access_callback = array_pop($args);
  $original_access = call_user_func_array($access_callback, $args);
  return $original_access && user_access('administer nodes');
}
Run Code Online (Sandbox Code Playgroud)

我们已经成功地将原始访问回调包装在一个新的访问回调中,我们可以添加我们需要的任何其他逻辑.

请注意,在最后两个函数示例中,我使用该$path变量来保持代码简单.我也分开$original_access了它自己的行并首先检查过,实际上我会user_access()首先检查,因为它几乎肯定会比原始访问回调中发生的更高效.