You are here

forum_access.module in Forum Access 5

Same filename and directory in other branches
  1. 8 forum_access.module
  2. 6 forum_access.module
  3. 7 forum_access.module

forum_access.module

This module uses form_alter to add permissions and moderator settings to forums.

File

forum_access.module
View source
<?php

/**
 * @file forum_access.module
 *
 * This module uses form_alter to add permissions and moderator settings to
 * forums.
 *
 */

/**
 * This function supplies the forum access grants. forum_access simply uses
 * roles as ACLs, so rids translate directly to gids.
 */
function forum_access_node_grants($user, $op) {
  $grants['forum_access'] = array_keys($user->roles);
  return $grants;
}

/**
 * Implementation of hook_node_access_records().
 *
 * Returns a list of grant records for the passed in node object.
 * Checks to see if maybe we're being disabled.
 */
function forum_access_node_access_records($node) {
  if (!forum_access_enabled()) {
    return;
  }
  static $grants = array();
  if ($node->type == 'forum') {
    if (!isset($grants[$node->tid])) {
      $result = db_query('SELECT * FROM {forum_access} WHERE tid = %d', $node->tid);
      while ($grant = db_fetch_object($result)) {
        $grants[$node->tid][] = array(
          'realm' => 'forum_access',
          'gid' => $grant->rid,
          'grant_view' => $grant->grant_view,
          'grant_update' => $grant->grant_update,
          'grant_delete' => $grant->grant_delete,
          'priority' => 0,
        );
      }

      //drupal_set_message("forum_access_node_access_records($node->nid) (tid=$node->tid) returns ". var_export($grants[$node->tid], TRUE), 'status');
    }
    return $grants[$node->tid];
  }
}

/**
 * Implementation of hook_form_alter().
 *
 * Remove inaccessible forums from the node form.
 */
function forum_access_form_alter($form_id, &$form) {
  if (isset($form['type']) && $form['type']['#value'] . '_node_form' == $form_id) {
    forum_access_node_form($form_id, $form);
  }
  else {
    if ($form_id == 'forum_form_container') {
      forum_access_forum_form($form_id, $form, TRUE);
    }
    else {
      if ($form_id == 'forum_form_forum') {
        forum_access_forum_form($form_id, $form, FALSE);
      }
      else {
        if ($form_id == 'user_admin_role') {
          forum_access_user_admin_role_form($form_id, $form);
        }
        else {
          if ($form_id == 'content_access_admin_settings') {
            if (arg(3) == 'forum' && empty($_POST)) {
              drupal_set_message(t('Note: In Drupal, access can only be granted, not taken away. Whatever access you grant here will not be reflected on the !Forum_Access settings, but !Forum_Access can only allow <i>more</i> access, not less.', array(
                '!Forum_Access' => 'Forum Access',
              )), 'error');
            }
          }
        }
      }
    }
  }
}

/**
 * Rewrite the taxonomy item on the node form.
 */
function forum_access_node_form($form_id, &$form) {
  if (!is_array($form['taxonomy'][_forum_get_vid()]['#options'])) {
    return;
  }

  // forum administrators do NOT get their forms rewritten here.
  if (user_access('administer forums')) {
    return;
  }
  global $user;
  $roles = _forum_access_get_roles($user);
  $result = db_query("SELECT tid FROM {forum_access} WHERE rid IN (%s) AND grant_create = 1", $roles);
  while ($obj = db_fetch_object($result)) {
    $tids[$obj->tid] = $obj->tid;
  }

  // Also get all forums they happen to be able to moderate.
  $result = db_query("SELECT a.name AS tid FROM {acl} a INNER JOIN {acl_user} u ON a.acl_id = u.acl_id WHERE a.module = 'forum_access' AND u.uid = %d", $user->uid);
  while ($obj = db_fetch_object($result)) {
    $tids[$obj->tid] = $obj->tid;
  }

  // Ensure the forum they're trying to post to directly is allowed, otherwise
  // there will be much confusion.
  $forum_tid = arg(3);
  if (is_numeric($forum_tid) && $forum_tid && !$tids[$forum_tid]) {
    drupal_access_denied();
    module_invoke_all('exit');
    exit;
  }
  foreach ($form['taxonomy'][_forum_get_vid()]['#options'] as $tid => $name) {
    if (!is_numeric($tid)) {
      $options[$tid] = $name;
    }
    elseif (is_object($name)) {
      foreach ($name->option as $sub_tid => $sub_name) {
        if ($tids[$sub_tid]) {
          $options[$tid]->option[$sub_tid] = $sub_name;
        }
      }
    }
    elseif ($tids[$tid]) {
      $options[$tid] = $name;
    }
  }
  if ($options) {
    $form['taxonomy'][_forum_get_vid()]['#options'] = $options;
  }
  else {
    unset($form['taxonomy'][_forum_get_vid()]);
  }
}

/**
 * Rewrite the forum administration page with our new access rules.
 */
function forum_access_forum_form($form_id, &$form, $container) {
  $rids = array();
  $result = db_query("SELECT r.rid, r.name FROM {role} r ORDER BY r.name");
  while ($obj = db_fetch_object($result)) {
    $rids[$obj->rid] = $obj->name;
  }
  if (isset($form['tid']['#value'])) {

    // edit
    $result = db_query("SELECT * FROM {forum_access} where tid=%d", $form['tid']['#value']);
    while ($forum_access = db_fetch_object($result)) {
      $row_received = TRUE;
      if ($forum_access->grant_view) {
        $view[] = $forum_access->rid;
      }
      if ($forum_access->grant_update) {
        $update[] = $forum_access->rid;
      }
      if ($forum_access->grant_delete) {
        $delete[] = $forum_access->rid;
      }
      if ($forum_access->grant_create) {
        $create[] = $forum_access->rid;
      }
    }
    if (!isset($row_received) && empty($form['#post'])) {
      drupal_set_message(t('If you have only just installed !Forum_Access, then the posts in this forum may still be accessible, but once your permissions get rebuilt (intentionally or behind-the-scenes by some other module), they will vanish &mdash; so, be sure to set the desired Access Control below and save!', array(
        '!Forum_Access' => 'Forum Access',
      )), 'error');
    }
  }
  else {

    // create
    // Default to all users can read; all logged in users can post.
    $view = array(
      1,
      2,
    );
    $create = array(
      2,
    );
    $update = $delete = array();
  }
  $form['forum_access'] = array(
    '#type' => 'fieldset',
    '#title' => t('Access control'),
    '#collapsible' => TRUE,
    '#tree' => TRUE,
  );
  $form['forum_access']['view'] = array(
    '#type' => 'checkboxes',
    '#prefix' => '<div class="forum-access-div">',
    '#suffix' => '</div>',
    '#options' => $rids,
    '#title' => t('View this forum'),
    '#default_value' => $view,
  );
  $form['forum_access']['create'] = array(
    '#type' => 'checkboxes',
    '#prefix' => '<div class="forum-access-div">',
    '#suffix' => '</div>',
    '#options' => $rids,
    '#title' => t('Post in this forum'),
    '#default_value' => $create,
  );

  // Containers do not contain any nodes, so these fields become meaningless for them.
  if (!$container) {
    $form['forum_access']['update'] = array(
      '#type' => 'checkboxes',
      '#prefix' => '<div class="forum-access-div">',
      '#suffix' => '</div>',
      '#options' => $rids,
      '#title' => t('Edit posts'),
      '#default_value' => $update,
    );
    $form['forum_access']['delete'] = array(
      '#type' => 'checkboxes',
      '#prefix' => '<div class="forum-access-div">',
      '#suffix' => '</div>',
      '#options' => $rids,
      '#title' => t('Delete posts'),
      '#default_value' => $delete,
    );
  }

  // Find our moderator ACL:
  $form['forum_access']['clearer'] = array(
    '#value' => '<div class="forum-access-clearer"></div>',
  );
  drupal_add_css(drupal_get_path('module', 'forum_access') . '/forum_access.css');
  if (isset($form['tid']['#value'])) {

    // edit, not new
    $acl_id = db_result(db_query("SELECT acl_id from {acl} WHERE module = 'forum_access' AND name = '%d'", $form['tid']['#value']));
    if (!$acl_id) {

      // create one
      $acl_id = acl_create_new_acl('forum_access', $form['tid']['#value']);

      // update every existing node in this forum to use this acl.
      $result = db_query("SELECT nid FROM {term_node} WHERE tid = %d", $form['tid']['#value']);
      while ($node = db_fetch_object($result)) {

        // all privs to this ACL.
        acl_node_add_acl($node->nid, $acl_id, 1, 1, 1);
      }
    }
    $form['forum_access']['acl'] = acl_edit_form($acl_id, t('Moderators'));
  }
  $form['forum_access']['interference'] = array(
    '#type' => 'fieldset',
    '#title' => t('Module interference'),
    '#collapsible' => TRUE,
    '#attributes' => $is_conflict ? array(
      'class' => 'error',
    ) : array(),
  );
  $variables = array(
    '%content_type' => node_get_types('name', 'forum'),
    '!Forum_Access' => 'Forum Access',
    '!Content_Access' => 'Content Access',
    '!button' => t('Rebuild permissions'),
  );
  if (module_exists('content_access')) {
    $ca_settings = variable_get('content_access_settings', array());
    foreach (array(
      'view',
      'update',
      'delete',
      'per_node',
    ) as $type) {
      $value = content_access_get_settings($type, 'forum');
      if (!empty($value)) {
        $ca_interferes = TRUE;
      }
    }
    $ca_priority = content_access_get_settings('priority', 'forum');
    $fa_priority = 0;
    $is_conflict = $ca_priority >= $fa_priority && !empty($ca_interferes);
    $form['forum_access']['interference']['#attributes'] = $is_conflict ? array(
      'class' => 'error',
    ) : array();
    $variables['!link'] = l(t('!Content_Access configuration for the %content_type type', $variables), 'admin/content/types/forum/access', array(), NULL, NULL, FALSE, TRUE);
    $specifically = $ca_priority == $fa_priority ? t('Specifically, any grants given by !Content_Access cannot be taken back by !Forum_Access.', $variables) : '';
    if ($is_conflict) {
      $form['forum_access']['interference'][] = array(
        '#value' => '<p>' . t('You have set the !Content_Access module to control access to content of type %content_type&mdash;this can interfere with proper operation of !Forum_Access!', $variables) . " {$specifically}</p>",
      );
      if ($ca_priority == $fa_priority) {
        $form['forum_access']['interference'][] = array(
          '#value' => '<p>' . t("Unless you really know what you're doing, we recommend that you go to the !link page and clear all checkboxes.", $variables) . '</p>',
        );
      }
      else {
        $form['forum_access']['interference'][] = array(
          '#value' => '<div>' . t("The priority of !Content_Access ({$ca_priority}) is higher than the priority of !Forum_Access ({$fa_priority}), which means the latter is <b>completely disabled</b> for the %content_type type! Unless you really know what you're doing, we recommend that you go to the !link page and clear all checkboxes and/or change the priorities.", $variables) . '</div>',
        );
      }
    }
    else {
      $form['forum_access']['interference'][] = array(
        '#value' => '<p>' . t('Note: You have installed the !Content_Access module, which has the capability to grant access to content that would otherwise be protected by !Forum_Access. Be careful when configuring !Content_Access!', $variables) . '</p>',
      );
    }
  }
  $variables['!link'] = l('admin/content/node-settings', 'admin/content/node-settings');
  $form['forum_access']['interference'][] = array(
    // This doesn't apply to D6!
    '#value' => '<p>' . t("Note: If you have any other node access module installed besides !Forum_Access, and you've set that other module to use a grant priority (or 'weight') > 0 and to also control access to %content_type nodes, then you need to go to !link and click the [!button] button, <strong>after</strong> submitting this form. This is a limitation of Drupal 5 core. <br /> Be aware, however, that this may cause the !Forum_Access settings to be ignored in favor of that other modules' settings!", $variables) . '</p>',
  );

  // Move some stuff down so our block goes in a nice place.
  $form['submit']['#weight'] = 10;
  $form['delete']['#weight'] = 10;
  $form['#submit']['forum_access_form_submit'] = current($form['#submit']);
}
function forum_access_form_submit($form_id, $form_values) {
  db_query("DELETE FROM {forum_access} WHERE tid = %d", $form_values['tid']);
  $access = $form_values['forum_access'];

  // shortcut
  if (array_key_exists('acl', $access)) {
    acl_save_form($access['acl']);
  }
  foreach ($access['view'] as $rid => $checked) {
    $grants[] = array(
      'realm' => 'forum_access',
      'gid' => $rid,
      'grant_view' => (bool) $checked,
      'grant_update' => !empty($access['update'][$rid]),
      'grant_delete' => !empty($access['delete'][$rid]),
    );
    db_query("INSERT INTO {forum_access} (tid, rid, grant_view, grant_update, grant_delete, grant_create) VALUES (%d, %d, %d, %d, %d, %d)", $form_values['tid'], $rid, (bool) $checked, !empty($access['update'][$rid]), !empty($access['delete'][$rid]), !empty($access['create'][$rid]));
  }

  // mass update
  $result = db_query("SELECT n.nid FROM {node} n LEFT JOIN {term_node} tn ON tn.nid = n.nid WHERE tn.tid = %d", $form_values['tid']);
  while ($node = db_fetch_object($result)) {
    node_access_write_grants($node, $grants, 'forum_access');
  }
}

/**
 * We must know when a role is deleted.
 */
function forum_access_user_admin_role_form($form_id, &$form) {
  $form['#submit']['forum_access_user_admin_role_submit'] = array();
}

/**
 * If a role is deleted, we remove the grants it provided.
 */
function forum_access_user_admin_role_submit($form, &$form_state) {
  if ($form_state['op'] == $form_state['delete']) {
    db_query("DELETE FROM {forum_access} WHERE rid = %d", $form_state['rid']);
    db_query("DELETE FROM {node_access} WHERE gid = %d AND realm = 'forum_access'", $form_state['rid']);
  }
}

/**
 * Implementation of hook_db_rewrite_sql().
 *
 * Because in order to restrict the visible forums, we have to rewrite
 * the sql. This is because there isn't a node_access equivalent for
 * taxonomy. There should be.
 */
function forum_access_db_rewrite_sql($query, $primary_table, $primary_field, $args) {
  if ($primary_field == 'tid' && !user_access('administer forums')) {
    global $user;
    $roles = _forum_access_get_roles($user);
    $sql['join'] = "LEFT JOIN {forum_access} fa ON {$primary_table}.tid = fa.tid\n                    LEFT JOIN {acl} acl_fa ON acl_fa.name = " . ($GLOBALS['db_type'] == 'pgsql' ? 'CAST(' : '') . "{$primary_table}.tid" . ($GLOBALS['db_type'] == 'pgsql' ? ' AS VARCHAR)' : '') . " AND acl_fa.module = 'forum_access'\n                    LEFT JOIN {acl_user} aclu_fa ON aclu_fa.acl_id = acl_fa.acl_id AND aclu_fa.uid = {$user->uid}";
    $sql['where'] = "(fa.grant_view >= 1 AND fa.rid IN ({$roles})) OR fa.tid IS NULL OR aclu_fa.uid = {$user->uid}";
    $sql['distinct'] = 1;
    return $sql;
  }
}

/**
 * Implementation of hook_nodeapi().
 *
 * Add ACL data to fresh forum posts.
 */
function forum_access_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
  static $old_tid = NULL;
  if ($node && $node->type == 'forum') {
    switch ($op) {
      case 'prepare':
        $old_tid = $node->tid;
        break;
      case 'update':
        if ($node->tid == $old_tid) {
          break;
        }
        $acl_id = db_result(db_query("SELECT acl_id from {acl} WHERE module = 'forum_access' AND name = '%d'", $old_tid));
        acl_node_remove_acl($node->nid, $acl_id);

      // fall through to 'insert' to enter the new tid...
      case 'insert':
        $acl_id = db_result(db_query("SELECT acl_id from {acl} WHERE module = 'forum_access' AND name = '%d'", $node->tid));
        acl_node_add_acl($node->nid, $acl_id, 1, 1, 1);
        $old_tid = NULL;
        break;
    }
  }
}

/**
 * Get an array of moderator UIDs or NULL.
 */
function forum_access_get_moderator_uids($tid) {
  if ($acl_id = acl_get_id_by_name('forum_access', $tid)) {
    if ($uids = acl_get_uids($acl_id)) {
      return $uids;
    }
  }
}

/**
 * Return a themed list of moderators for a given forum.
 */
function forum_access_moderator_list($tid) {
  return theme('forum_access_moderator_list', forum_access_get_moderator_uids($tid));
}

/**
 * Theme function for list of moderators.
 */
function theme_forum_access_moderator_list($moderators) {
  static $users;
  if (!empty($moderators)) {
    $moderators_links = array();
    foreach ($moderators as $uid) {
      if (!isset($users[$uid])) {
        $users[$uid] = user_load(array(
          'uid' => $uid,
        ));
      }
      $moderators_links[] = theme('username', $users[$uid]);
    }
    $output = '<span class="moderator-list-title">' . format_plural(count($moderators_links), 'Moderator:', 'Moderators:') . ' </span>';
    $moderators = implode(', ', $moderators_links);
    $output .= '<span class="moderator-list-usernames">' . $moderators . '</span>';
    $output = '<div class="moderator-list">' . $output . '</div>';
    return $output;
  }
  else {
    return '';
  }
}

/**
 * This is also required by ACL module.
 */
function forum_access_enabled($set = NULL) {
  static $enabled = true;
  if ($set !== NULL) {
    $enabled = $set;
  }
  return $enabled;
}

/**
 * Implementation of hook_enable().
 */
function forum_access_enable() {
  node_access_rebuild();
}

/**
 * Implementation of hook_disable().
 */
function forum_access_disable() {
  forum_access_enabled(FALSE);
  node_access_rebuild();
}

/**
 * Implementation of hook_init().
 *
 * Deny access to forum if the user does not have access to it.
 */
function forum_access_init() {
  if (!function_exists('user_access')) {

    // page is cached; bail.
    return;
  }
  if (!user_access('administer forums') && arg(0) == 'forum' && is_numeric(arg(1))) {
    global $user;
    if (!forum_access_access(arg(1), 'view')) {
      drupal_access_denied();
      module_invoke_all('exit');
      exit;
    }
  }
}

/**
 * See if a given user has access to a forum.
 *
 * $tid -- the tid of the forum
 * $type -- view, update, delete or create
 * $account -- the account to test for. If NULL use current user.
 */
function forum_access_access($tid, $type, $account = NULL) {
  static $cache = array();
  if (!$account) {
    global $user;
    $account = $user;
  }
  if (user_access('administer forums', $account)) {
    return TRUE;
  }
  if (!isset($cache[$account->uid][$tid][$type])) {
    $roles = _forum_access_get_roles($account);
    $result = db_result(db_query("SELECT tid FROM {forum_access} WHERE rid IN (%s) AND grant_{$type} = 1 AND tid = %d", $roles, $tid));
    if ($result) {
      $cache[$account->uid][$tid][$type] = TRUE;
    }
    else {

      // check our moderators too
      $acl_id = db_result(db_query("SELECT acl_id from {acl} WHERE module = 'forum_access' AND name = '%d'", $tid));
      $result = db_result(db_query("SELECT uid FROM {acl_user} WHERE acl_id = %d AND uid = %d", $acl_id, $account->uid));
      if ($result) {
        $cache[$account->uid][$tid][$type] = TRUE;
      }
      else {
        $cache[$account->uid][$tid][$type] = FALSE;
      }
    }
  }
  return $cache[$account->uid][$tid][$type];
}

/**
 * Get the roles of a user.
 */
function _forum_access_get_roles($user) {
  return implode(', ', array_keys($user->roles));
}

/**
 * Implementation of hook_node_access_explain().
 */
function forum_access_node_access_explain($row) {
  static $roles = NULL;
  if ($row->realm == 'forum_access') {
    if (!isset($roles)) {
      $roles = user_roles();
    }
    if (isset($roles[$row->gid])) {
      return array(
        $roles[$row->gid],
      );
    }
    return array(
      '(unknown gid)',
    );
  }
}

Functions

Namesort descending Description
forum_access_access See if a given user has access to a forum.
forum_access_db_rewrite_sql Implementation of hook_db_rewrite_sql().
forum_access_disable Implementation of hook_disable().
forum_access_enable Implementation of hook_enable().
forum_access_enabled This is also required by ACL module.
forum_access_form_alter Implementation of hook_form_alter().
forum_access_form_submit
forum_access_forum_form Rewrite the forum administration page with our new access rules.
forum_access_get_moderator_uids Get an array of moderator UIDs or NULL.
forum_access_init Implementation of hook_init().
forum_access_moderator_list Return a themed list of moderators for a given forum.
forum_access_nodeapi Implementation of hook_nodeapi().
forum_access_node_access_explain Implementation of hook_node_access_explain().
forum_access_node_access_records Implementation of hook_node_access_records().
forum_access_node_form Rewrite the taxonomy item on the node form.
forum_access_node_grants This function supplies the forum access grants. forum_access simply uses roles as ACLs, so rids translate directly to gids.
forum_access_user_admin_role_form We must know when a role is deleted.
forum_access_user_admin_role_submit If a role is deleted, we remove the grants it provided.
theme_forum_access_moderator_list Theme function for list of moderators.
_forum_access_get_roles Get the roles of a user.