? node_access_doc.patch
Index: developer/examples/node_access_example.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/docs/developer/examples/node_access_example.module,v
retrieving revision 1.4
diff -u -p -r1.4 node_access_example.module
--- developer/examples/node_access_example.module	29 Mar 2005 01:27:26 -0000	1.4
+++ developer/examples/node_access_example.module	22 Aug 2006 22:57:26 -0000
@@ -3,25 +3,26 @@
 
 /**
  * @file
- * This is an example illustrating how to restrict access to nodes based on some
- * criterion associated with the user.
+ * This is an example illustrating how to restrict access to nodes based on 
+ * some criterion associated with the user.
+ *
+ * This example module will simply set a single flag on a node: 'private'. If
+ * the flag is set, only users with the 'view private content' flag can see
+ * the node, and all users with 'edit private content' can edit (but not 
+ * delete) the node.
+ *
+ * Additionally we will ensure that the node author can always view, edit,
+ * and delete the node by providing an additional access realm that grants
+ * privileges to the node's author.
  *
  * Database definition:
  * @code
- *   DELETE FROM node_access;
- *   INSERT INTO node_access (nid, gid, realm, grant_view, grant_update,
- *      grant_delete) VALUES (0, 1, 'example', 1, 0, 0)
+ *   CREATE TABLE node_access_example (
+ *     nid int(10) unsigned NOT NULL default '0' PRIMARY KEY,
+ *     private int,
+ *     KEY `node_example_nid` (nid)
+ *   )
  * @endcode
- *
- * This SQL needs to be run before the module will work properly. The installer
- * system will probably perform this work in the future. The first line removes
- * any existing grants, including (most importantly) the universal grant
- * installed by default that gives read access to every node for everyone. The
- * second line grants read access to every node for users with the "access
- * private content" permission; in the scheme we're defining here, each node
- * will either be private (in which case it can always be read by anyone with
- * that permission) or public (in which case it can be read by everyone). We'll
- * take care of public nodes in node_access_example_nodeapi().
  */
 
 /**
@@ -35,88 +36,153 @@ function node_access_example_help($secti
 }
 
 /**
+ * Implementation of hook_enable().
+ *
+ * A node access module needs to force a rebuild of the node access table
+ * when it is enabled to ensure that things are set up.
+ */
+function node_access_example_enable() {
+  node_access_rebuild();
+}
+
+/**
+ * Implementation of hook_disable().
+ *
+ * A node access module needs to force a rebuild of the node access table
+ * when it is disabled to ensure that its entries are removed from the table.
+ */
+function node_access_example_disable() {
+  node_access_example_disabling(TRUE);
+  node_access_rebuild();
+}
+
+/**
+ * Simple function to make sure we don't respond with grants when disabling
+ * ourselves.
+ */
+function node_access_example_disabling($set = NULL) {
+  static $disabling = false;
+  if ($set !== NULL) {
+    $disabling = $set;
+  }
+  return $disabling;
+}
+
+/**
  * Implementation of hook_perm().
  *
  * In this example, we will use a simple permission to determine whether a user
  * has access to "private" content. This permission is defined here.
  */
 function node_access_example_perm() {
-  return array('access private content');
+  return array('access private content', 'edit private content');
 }
 
 /**
  * Implementation of hook_node_grants().
  *
- * Since we are restricting access based on a permission, we need to check that
- * permission and return the appropriate result.
+ * Tell the node access system what GIDs the user belongs to for each realm.
+ * In this example, we are providing two realms: the example realm, which
+ * has just one group id (1) and the user is either a member or not depending
+ * upon the operation and the access permission set.
+ *
+ * We are also setting up a realm for the node author, though, to give it
+ * special privileges. That has 1 GID for every UID, and each user is
+ * automatically a member of the group where GID == UID.
  *
  */
 function node_access_example_node_grants($account, $op) {
-  $grants = array();
-  if (user_access('access content', $account)) {
-    $grants[] = 0;
+  if ($op == 'view' && user_access('access private content', $account)) {
+    $grants['example'] = array(1);
+  }
+
+  if (($op == 'update' || $op == 'delete') && user_access('edit private content', $account)) {
+    $grants['example'] = array(1);
+  }
+
+  $grants['example_author'] = array($user->uid);
+  return $grants;
+}
+
+/**
+ * Implementation of hook_node_access_records().
+ *
+ * All node access modules must implement this hook. If the module is
+ * interested in the privacy of the node passed in, return a list
+ * of node access values for each grant ID we offer. Since this
+ * example module only offers 1 grant ID, we will only ever be
+ * returning one record.
+ */
+function node_access_example_node_access_records($node) {
+  if (node_access_example_disabling()) {
+    return;
+  }
+
+  // We only care about the node if it's been marked private. If not, it is
+  // treated just like any other node and we completely ignore it.
+  if ($node->private) {
+    $grants = array();
+    $grants[] = array(
+      'realm' => 'example', 
+      'gid' => TRUE, 
+      'grant_view' => TRUE, 
+      'grant_update' => FALSE, 
+      'grant_delete' => FALSE,
+      'priority' => 0,
+    );
+
+    // For the example_author array, the GID is equivalent to a UID, which
+    // means there are many many groups of just 1 user. 
+    $grants[] = array(
+      'realm' => 'example_author', 
+      'gid' => $node->uid, 
+      'grant_view' => TRUE, 
+      'grant_update' => TRUE, 
+      'grant_delete' => TRUE,
+      'priority' => 0,
+    );
+    return $grants;
   }
-  if (user_access('access private content', $account)) {
-    $grants[] = 1;
+}
+
+/**
+ * Implementation of hook_form_alter()
+ *
+ * This module adds a simple checkbox to the node form labeled private. If the
+ * checkbox is labelled, only the node author and users with 'access private content' 
+ * privileges may see it. 
+ */
+function node_access_example_form_alter($form_id, &$form) {
+  if ($form['#id'] == 'node-form') {
+    $form['private'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Private'),
+      '#description' => t('Check here if this content should be set private and only shown to privileged users.'),
+      '#default_value' => $node->private,
+    );
   }
-  return array('example' => $grants);
 }
 
 /**
  * Implementation of hook_nodeapi().
  *
- * Most of a node access module's work will be done via this hook. Several
- * values of $op will require responses:
- *
- * - "form admin", "form pre", and/or "form post":
- *   The module will need to provide some mechanism for the access rights of a
- *   node to be managed. Some sort of form element on the node editing form is
- *   a typical means to accomplish this.
  * - "delete", "insert", and "update":
- *   The module must take care of updating the node_access table appropriately
- *   when nodes are modified, probably using the form element mentioned above.
- *   Only the realm(s) handled by the module should be affected, so that multiple
- *   node access modules can peacefully coexist.
- * - "validate":
- *   Depending on the user interface provided in the node form, the selection
- *   may need to be verified and validated here.
- * - "settings":
- *   Modules may wish to provide default grants per node type using this hook.
+ * The module must track the access status of the node.
  */
 function node_access_example_nodeapi(&$node, $op, $arg = 0) {
   switch ($op) {
-    case 'form admin':
-      // We present the selection for who can view the node in the administrative
-      // block, so users with the "administer nodes" permission can view and edit
-      // the settings.
-      if (!isset($node->access_example)) {
-        // Load the grants from the database.
-        $result = db_query('SELECT na.gid FROM {node_access} na WHERE na.nid = %d AND na.realm = \'example\' AND na.grant_view = 1', $node->nid);
-        $grant = db_fetch_object($result);
-        if ($grant && $grant->gid == 0) {
-          // The "public" grant was set.
-          $node->access_example = 0;
-        }
-        else {
-          $node->access_example = 1;
-        }
-      }
-      $output = form_checkbox(t('private'), 'access_example', 1, $node->access_example);
-      return $output;
+    case 'load':
+      $result = db_fetch_object(db_query('SELECT * FROM {node_access_example} WHERE nid = %d', $node->nid));
+      $node->private = $result->private;
+      break;
     case 'delete':
-      // When a node is deleted, delete any relevant grants.
-      db_query('DELETE FROM {node_access} WHERE nid = %d AND realm = \'example\'', $node->nid);
+      db_query('DELETE FROM {node_access_example} WHERE nid = %d', $node->nid);
       break;
     case 'insert':
+      db_query('INSERT INTO {node_access_example} (nid, private) VALUES (%d, %d)', $node->nid, $node->private);
+      break;
     case 'update':
-      // Clear out any existing grants for the node, and set new ones.
-      db_query('DELETE FROM {node_access} WHERE nid = %d AND realm = \'example\'', $node->nid);
-      $node->access_example = isset($node->access_example) ? $node->access_example : 0;
-
-      if ($node->access_example == 0) {
-        // If the node is public, we need to grant access to everyone.
-        db_query('INSERT INTO {node_access} (nid, gid, realm, grant_view, grant_update, grant_delete) VALUES (%d, %d, \'example\', %d, %d, %d)', $node->nid, 0, 1, 0, 0);
-      }
+      db_query('UPDATE {node_access_example} SET private = %d WHERE nid = %d', $node->private, $node->nid);
       break;
   }
 }
Index: developer/hooks/core.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/docs/developer/hooks/core.php,v
retrieving revision 1.103
diff -u -p -r1.103 core.php
--- developer/hooks/core.php	22 Aug 2006 20:16:18 -0000	1.103
+++ developer/hooks/core.php	22 Aug 2006 22:57:28 -0000
@@ -736,13 +736,20 @@ function hook_menu($may_cache) {
 
 
 /**
- * Grant access to nodes.
+ * Inform the node access system what permissions the user has.
  *
  * This hook is for implementation by node access modules. In addition to
- * managing entries in the node_access table in the database, such modules must
- * implement this hook to inform Drupal which "grants" the current user has.
- * For example, in a role-based access scheme, this function would return a
- * list of the roles the user belongs to.
+ * managing access rights for nodes, the node access module must tell
+ * the node access system what 'grant IDs' the current user has. In many
+ * cases, the grant IDs will simply be role IDs, but grant IDs can be
+ * arbitrary based upon the module. 
+ *
+ * For example, modules can maintain their own lists of users, where each
+ * list has an ID. In that case, the module could return a list of all
+ * IDs of all lists that the current user is a member of.
+ *
+ * A node access module may implement as many realms as necessary to
+ * properly define the access privileges for the nodes.
  *
  * @param $user
  *   The user object whose grants are requested.
@@ -754,25 +761,71 @@ function hook_menu($may_cache) {
  *
  * For a detailed example, see node_access_example.module.
  */
-function hook_node_grants($user, $op) {
-  $grants = array();
-  if ($op == 'view') {
-    if (user_access('access content')) {
-      $grants[] = 0;
-    }
-    if (user_access('access private content')) {
-      $grants[] = 1;
-    }
+function hook_node_grants($account, $op) {
+  if (user_access('access private content', $account)) {
+    $grants['example'] = array(1);
   }
-  if ($op == 'update' || $op == 'delete') {
-    if (user_access('edit content')) {
-      $grants[] = 0;
-    }
-    if (user_access('edit private content')) {
-      $grants[] = 1;
-    }
+  $grants['example_owner'] = array($user->uid);
+  return $grants;
+}
+
+/**
+ * Set permissions for a node to be written to the database.
+ *
+ * When a node is saved, a module implementing node access will be asked
+ * if it is interested in the access permissions to a node. If it is
+ * interested, it must respond with an array of array of permissions for that 
+ * node.
+ *
+ * Each item in the array should contain:
+ *
+ * 'realm'
+ *    This should only be realms for which the module has returned
+ *    grant IDs in hook_node_grants.
+ * 'gid' 
+ *    This is a 'grant ID', which can have an arbitrary meaning per realm.
+ * 'grant_view'
+ *    If set to TRUE a user with the gid in the realm can view this node.
+ * 'grant_edit'
+ *    If set to TRUE a user with the gid in the realm can edit this node.
+ * 'grant_delete'
+ *    If set to TRUE a user with the gid in the realm can delete this node.
+ * 'priority'
+ *    If multiple modules seek to set permissions on a node, the realms
+ *    that have the highest priority will win out, and realms with a lower
+ *    priority will not be written. If there is any doubt, it is best to
+ *    leave this 0.
+ */
+function hook_node_access_records($node) {
+  if (node_access_example_disabling()) {
+    return;
+  }
+
+  // We only care about the node if it's been marked private. If not, it is
+  // treated just like any other node and we completely ignore it.
+  if ($node->private) {
+    $grants = array();
+    $grants[] = array(
+      'realm' => 'example', 
+      'gid' => TRUE, 
+      'grant_view' => TRUE, 
+      'grant_update' => FALSE, 
+      'grant_delete' => FALSE,
+      'priority' => 0,
+    );
+
+    // For the example_author array, the GID is equivalent to a UID, which
+    // means there are many many groups of just 1 user. 
+    $grants[] = array(
+      'realm' => 'example_author', 
+      'gid' => $node->uid, 
+      'grant_view' => TRUE, 
+      'grant_update' => TRUE, 
+      'grant_delete' => TRUE,
+      'priority' => 0,
+    );
+    return $grants;
   }
-  return array('example' => $grants);
 }
 
 /**
