¿Tiene un ejemplo de devolución de llamada de acceso hook_menu ()?

18

He descargado el proyecto de ejemplos , pero en el módulo menu_example todos access callbackestán configurados en true... difícil de entender cómo funciona.

En mi ejemplo, mi entrada meno debería estar visible en los nodos, pero solo para los roles que tienen los permisos para editar sus propios nodos.

No puedo encontrar un ejemplo un poco más detallado de una devolución de llamada de acceso.

¿Alguien tiene uno?

Strae
fuente

Respuestas:

12

Editar: me perdí la parte sobre el permiso "editar propio nodo", porque entonces no solo debe verificar el permiso sino también si ese nodo pertenece al usuario actual. He actualizado mi ejemplo a continuación, pero dejo la explicación anterior tal como estaba.

¿Está su entrada de menú debajo de nodo / nid (por ejemplo, nodo / 1234 / algo)? Entonces, probablemente ni siquiera necesite una devolución de llamada de acceso personalizada.

Si define su ruta de menú como en el siguiente ejemplo, solo llamará a la devolución de llamada de acceso (y, por lo tanto, a la devolución de llamada de su página), si está viendo un nodo válido.

'node/%node/something'

Esto significa que llamará a node_load (1234) para el ejemplo anterior y solo continuará si se devuelve un objeto de nodo válido. Entonces puede definir su permiso con argumentos de acceso como de costumbre.

Dicho esto, escribir una devolución de llamada de acceso es realmente simple. Es solo una función que recibirá los argumentos que haya definido en los argumentos de acceso. Por ejemplo, la devolución de llamada de acceso predeterminada es user_access () y cuando define sus argumentos de acceso como 'access arguments' => array('a permission string'), dará como resultado la siguiente llamada:user_access('a permission string') .

Si tiene múltiples argumentos, estos se pasarán como segundo, tercero y así sucesivamente a su función. Para acceder al nodo actualmente activo, puede usar menu_get_object () .

Por lo tanto, podría escribir su devolución de llamada de acceso de esta manera, pero nuevamente, es posible que ni siquiera necesite crear una.

function yourmodule_access_check() {
  global $user;
  $node = menu_get_object();

  return $node && $node->uid == $user->uid && user_access('edit own ' . $node->type . ' content');
}

En lugar de codificar la cadena de permisos, puede pasarla como un argumento a la función o lo que quiera hacer.

Berdir
fuente
nunca podría lograr el último ejemplo: con $items['node/%node/edit']['access callback'] = 'admin_access_only'; y $node = menu_get_object();en la devolución de llamada fn, $nodenunca devolvió nada. En su lugar, utilicé lo $node = node_load(arg(1)); que funcionó ... Más explicaciones serían realmente bienvenidas
Kojo
19

Drupal es en sí mismo un ejemplo de cómo escribir código.

El ejemplo más fácil es aggregator_menu () , que contiene el siguiente código.

  $items['admin/config/services/aggregator'] = array(
    'title' => 'Feed aggregator', 
    'description' => "Configure which content your site aggregates from other sites, how often it polls them, and how they're categorized.", 
    'page callback' => 'aggregator_admin_overview', 
    'access arguments' => array('administer news feeds'), 
    'weight' => 10, 
    'file' => 'aggregator.admin.inc',
  );
  $items['admin/config/services/aggregator/add/feed'] = array(
    'title' => 'Add feed', 
    'page callback' => 'drupal_get_form', 
    'page arguments' => array('aggregator_form_feed'), 
    'access arguments' => array('administer news feeds'), 
    'type' => MENU_LOCAL_ACTION, 
    'file' => 'aggregator.admin.inc',
  );

En este caso, la devolución de llamada de acceso es la predeterminada ( user_access () ), y los argumentos de acceso son una matriz que contiene la cadena para el permiso. El código no puede verificar más que un permiso; si los permisos para verificar son dos, o las condiciones para verificar no son solo permisos, entonces la devolución de llamada de acceso debe ser diferente, incluida una personalizada.

node_menu () define algunos menús que usan una devolución de llamada de acceso diferente a la predeterminada. La función contiene el siguiente código.

  foreach (node_type_get_types() as $type) {
    $type_url_str = str_replace('_', '-', $type->type);
    $items['node/add/' . $type_url_str] = array(
      'title' => $type->name, 
      'title callback' => 'check_plain', 
      'page callback' => 'node_add', 
      'page arguments' => array($type->type), 
      'access callback' => 'node_access', 
      'access arguments' => array('create', $type->type), 
      'description' => $type->description, 
      'file' => 'node.pages.inc',
    );
  }

La función que se define como devolución de llamada de acceso ( node_access () ) es la siguiente:

function node_access($op, $node, $account = NULL) {
  $rights = &drupal_static(__FUNCTION__, array());

  if (!$node || !in_array($op, array('view', 'update', 'delete', 'create'), TRUE)) {
    // If there was no node to check against, or the $op was not one of the
    // supported ones, we return access denied.
    return FALSE;
  }
  // If no user object is supplied, the access check is for the current user.
  if (empty($account)) {
    $account = $GLOBALS['user'];
  }

  // $node may be either an object or a node type. Since node types cannot be
  // an integer, use either nid or type as the static cache id.

  $cid = is_object($node) ? $node->nid : $node;

  // If we've already checked access for this node, user and op, return from
  // cache.
  if (isset($rights[$account->uid][$cid][$op])) {
    return $rights[$account->uid][$cid][$op];
  }

  if (user_access('bypass node access', $account)) {
    $rights[$account->uid][$cid][$op] = TRUE;
    return TRUE;
  }
  if (!user_access('access content', $account)) {
    $rights[$account->uid][$cid][$op] = FALSE;
    return FALSE;
  }

  // We grant access to the node if both of the following conditions are met:
  // - No modules say to deny access.
  // - At least one module says to grant access.
  // If no module specified either allow or deny, we fall back to the
  // node_access table.
  $access = module_invoke_all('node_access', $node, $op, $account);
  if (in_array(NODE_ACCESS_DENY, $access, TRUE)) {
    $rights[$account->uid][$cid][$op] = FALSE;
    return FALSE;
  }
  elseif (in_array(NODE_ACCESS_ALLOW, $access, TRUE)) {
    $rights[$account->uid][$cid][$op] = TRUE;
    return TRUE;
  }

  // Check if authors can view their own unpublished nodes.
  if ($op == 'view' && !$node->status && user_access('view own unpublished content', $account) && $account->uid == $node->uid && $account->uid != 0) {
    $rights[$account->uid][$cid][$op] = TRUE;
    return TRUE;
  }

  // If the module did not override the access rights, use those set in the
  // node_access table.
  if ($op != 'create' && $node->nid) {
    if (module_implements('node_grants')) {
      $query = db_select('node_access');
      $query->addExpression('1');
      $query->condition('grant_' . $op, 1, '>=');
      $nids = db_or()->condition('nid', $node->nid);
      if ($node->status) {
        $nids->condition('nid', 0);
      }
      $query->condition($nids);
      $query->range(0, 1);

      $grants = db_or();
      foreach (node_access_grants($op, $account) as $realm => $gids) {
        foreach ($gids as $gid) {
          $grants->condition(db_and()
            ->condition('gid', $gid)
            ->condition('realm', $realm)
          );
        }
      }
      if (count($grants) > 0) {
        $query->condition($grants);
      }
      $result =  (bool) $query
        ->execute()
        ->fetchField();
      $rights[$account->uid][$cid][$op] = $result;
      return $result;
    }
    elseif (is_object($node) && $op == 'view' && $node->status) {
      // If no modules implement hook_node_grants(), the default behavior is to
      // allow all users to view published nodes, so reflect that here.
      $rights[$account->uid][$cid][$op] = TRUE;
      return TRUE;
    }
  }

  return FALSE;
}

Hay tres puntos a notar:

  • Los argumentos declarados con "argumentos de acceso" se pasarán a la función en el mismo orden; la función usa un tercer parámetro porque no se usa solo para devolver la llamada.
  • La función vuelve TRUEsi el usuario tiene acceso al menú y FALSEsi el usuario no tiene acceso al menú.
  • Una devolución de llamada de acceso también se puede utilizar cuando un menú debe mostrarse solo en circunstancias específicas.
kiamlaluno
fuente
Al declarar una access callbackfunción personalizada , parece que debe vivir en su .modulearchivo, porque Drupal parece no poder encontrarla en la filedeclaración (al menos para mí).
tyler.frankenstein