<?php
// $Id: acl.module,v 1.20.2.1 2010/02/18 14:40:38 salvis Exp $

/**
 * @file
 * An API module providing by-user access control lists.
 *
 * This module handles ACLs on behalf of other modules. The two main reasons
 * to do this are so that modules using ACLs can share them with each
 * other without having to actually know much about them, and so that
 * ACLs can easily co-exist with the existing node_access system.
 */

/**
 * Create a new ACL.
 */
function acl_create_new_acl($module, $name) {
  $acl = array('module' => $module, 'name' => $name);
  drupal_write_record('acl', $acl);
  return $acl['acl_id'];
}

/**
 * Delete an existing ACL.
 */
function acl_delete_acl($acl_id) {
  db_query("DELETE FROM {acl} WHERE acl_id = %d", $acl_id);
  db_query("DELETE FROM {acl_user} WHERE acl_id = %d", $acl_id);
  db_query("DELETE FROM {acl_node} WHERE acl_id = %d", $acl_id);
}

/**
 * Add the specified UID to an ACL.
 */
function acl_add_user($acl_id, $uid) {
  $test_uid = db_result(db_query("SELECT uid FROM {acl_user} WHERE acl_id = %d AND uid = %d ", $acl_id, $uid));
  if (!$test_uid) {
    db_query("INSERT INTO {acl_user} (acl_id, uid) VALUES (%d, %d)", $acl_id, $uid);
  }
}

/**
 * Remove the specified UID from an ACL.
 */
function acl_remove_user($acl_id, $uid) {
  db_query("DELETE FROM {acl_user} WHERE acl_id = %d AND uid = %d ", $acl_id, $uid);
}

/**
 * Provide a form to edit the ACL that can be embedded in other forms.
 * Pass $new_acl=TRUE if you have no ACL yet, but do supply a string
 * like 'my_module_new_acl' as $acl_id anyway.
 */
function acl_edit_form($acl_id, $label = NULL, $new_acl = FALSE) {
  module_load_include('admin.inc', 'acl');
  return _acl_edit_form($acl_id, $label, $new_acl);
}

/**
 * Provide access control to a node based upon an ACL id.
 */
function acl_node_add_acl($nid, $acl_id, $view, $update, $delete, $priority = 0) {
  db_query("DELETE FROM {acl_node} WHERE acl_id = %d AND nid = %d", $acl_id, $nid);
  db_query("INSERT INTO {acl_node} (acl_id, nid, grant_view, grant_update, grant_delete, priority) VALUES (%d, %d, %d, %d, %d, %d)", $acl_id, $nid, $view, $update, $delete, $priority);
}

/**
 * Remove an ACL completely from a node.
 */
function acl_node_remove_acl($nid, $acl_id) {
  db_query("DELETE FROM {acl_node} WHERE acl_id = %d AND nid = %d", $acl_id, $nid);
}

/**
 * Clear all of a module's ACL's from a node.
 */
function acl_node_clear_acls($nid, $module) {
  $result = db_query("SELECT acl_id FROM {acl} WHERE module = '%s'", $module);
  while ($o = db_fetch_object($result)) {
    $acls[] = $o->acl_id;
  }
  if ($acls) {
    db_query("DELETE FROM {acl_node} WHERE nid = %d AND acl_id IN (". db_placeholders($acls) .")", array_merge(array($nid), $acls));
  }
}

/**
 * Gets the id of an acl
 */
function acl_get_id_by_name($module, $name) {
  return db_result(db_query("SELECT acl_id FROM {acl} WHERE module = '%s' AND name = '%s'", $module, $name));
}

/**
 * Determines if an acl has some assigned users
 */
function acl_has_users($acl_id) {
  return db_result(db_query("SELECT COUNT(uid) FROM {acl_user} WHERE acl_id = %d", $acl_id));
}

/**
 * Determines if an acl has a specific assigned user
 */
function acl_has_user($acl_id, $uid) {
  return 0 != db_result(db_query("SELECT COUNT(uid) FROM {acl_user} WHERE acl_id = %d AND uid = %d", $acl_id, $uid));
}

/**
 * Gets the uids of an acl
 */
function acl_get_uids($acl_id) {
  $result = db_query("SELECT uid FROM {acl_user} WHERE acl_id = '%d'", $acl_id);
  $return = array();
  while ($row = db_fetch_object($result)) {
    $return[$row->uid] = $row->uid;
  }
  return (empty($return) ? NULL : $return);
}

/**
 * Implementation of hook_node_access_records().
 */
function acl_node_access_records($node) {
  if (!$node->nid) {
    return;
  }
  $result = db_query("SELECT n.*, 'acl' AS realm, n.acl_id AS gid, a.module FROM {acl_node} n INNER JOIN {acl} a ON n.acl_id = a.acl_id WHERE nid = %d", $node->nid);
  $grants = array();
  while ($grant = db_fetch_array($result)) {
    if (module_exists($grant['module']) && module_invoke($grant['module'], 'enabled')) {
      if (acl_has_users($grant['gid'])) {
        $grants[] = $grant;
      }
      else {
        //just deny access
        $grants[] = array(
          'realm' => 'acl',
          'gid' => 0,
          'grant_view' => 0,
          'grant_update' => 0,
          'grant_delete' => 0,
          'priority' => $grant['priority'],
        );
      }
    }
  }
  return $grants;
}

/**
 * Implementation of hook_node_grants().
 */
function acl_node_grants($account, $op) {
  $array = array('acl' => array());
  $result = db_query("SELECT acl_id FROM {acl_user} WHERE uid = %d", $account->uid);
  while ($row = db_fetch_object($result)) {
    $array['acl'][] = $row->acl_id;
  }
  return !empty($array['acl']) ? $array : NULL;
}

/**
 * Implementation of hook_nodeapi().
 */
function acl_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
  switch ($op) {
    case 'delete':
      db_query("DELETE FROM {acl_node} WHERE nid = %d", $node->nid);
      break;
  }
}

/**
 * Implementation of hook_user().
 */
function acl_user($op, &$edit, &$account, $category = NULL) {
  switch ($op) {
    case 'delete':
      db_query("DELETE FROM {acl_user} WHERE uid = %d", $account->uid);
      break;
  }
}

/**
 * Implementation of hook_node_access_explain().
 */
function acl_node_access_explain($row) {
  static $interpretations = array();
  if ($row->realm == 'acl') {
    if (!isset($interpretations[$row->gid])) {
      $acl = db_fetch_object(db_query("SELECT * FROM {acl} WHERE acl_id = %d", $row->gid));
      $result = db_query("SELECT u.name FROM {acl_user} au, {users} u WHERE au.acl_id = %d AND au.uid = u.uid", $row->gid);
      while ($user = db_fetch_object($result)) {
        $users[] = $user->name;
      }
      if (isset($users)) {
        $interpretations[$row->gid] = $acl->module .'/'. $acl->name .': '. implode(', ', $users);
      }
      elseif ($row->gid == 0) {
        $result = db_query("SELECT an.acl_id, a.module, a.name FROM {acl_node} an JOIN {acl} a ON an.acl_id = a.acl_id LEFT JOIN {acl_user} au ON a.acl_id = au.acl_id WHERE an.nid = %d AND au.uid IS NULL", $row->nid);
        while ($acl = db_fetch_object($result)) {
          $rows[] = $acl->acl_id .':&nbsp;'. $acl->module .'/'. $acl->name;
        }
        if (!empty($rows)) {
          return implode('<br />', $rows);
        }
        return 'No access via ACL.';
      }
      else {
        $interpretations[$row->gid] .= ': no users!';
      }
    }
    return $interpretations[$row->gid];
  }
}

