You are here

flexiaccess.users.inc in Flexi Access 7

Form handling for per user ACL.

File

flexiaccess.users.inc
View source
<?php

/**
 * @file
 * Form handling for per user ACL.
 */

/**
 * Build form to handle ACLs for user.
 *
 * See http://passingcuriosity.com/2011/drupal-7-forms-tables/.
 */
function flexiaccess_user($form, &$form_state, $user) {
  if (module_exists('acl')) {

    /**
     * First, a utility function to help code re-use.
     */
    function addrow($row, &$form, $user) {
      $cell_node = array(
        '#type' => 'markup',
        '#markup' => '<strong>' . t('#!nid: !title', array(
          '!nid' => $row->nid,
          '!title' => $row->title,
        )) . '</strong>',
      );
      $cell_view = array(
        '#type' => 'checkbox',
        '#title' => t('View'),
        '#default_value' => $row->grant_view,
      );
      $cell_update = array(
        '#type' => 'checkbox',
        '#title' => t('Update'),
        '#default_value' => $row->grant_update,
      );
      $cell_delete = array(
        '#type' => 'checkbox',
        '#title' => t('Delete'),
        '#default_value' => $row->grant_delete,
      );
      $form['acl'][] = array(
        'node' => &$cell_node,
        'nid' => array(
          '#type' => 'value',
          '#value' => $row->nid,
        ),
        'view' => &$cell_view,
        'update' => &$cell_update,
        'delete' => &$cell_delete,
      );
    }

    /* end function addrow */
    $query = db_select('acl_user', 'u');
    $query
      ->join('acl', 'a', 'a.acl_id = u.acl_id');
    $query
      ->join('acl_node', 'n', 'a.acl_id = n.acl_id');
    $query
      ->join('node', 'node', 'n.nid = node.nid');
    $query
      ->fields('a', array(
      'acl_id',
    ))
      ->fields('n', array(
      'nid',
    ))
      ->fields('node', array(
      'title',
    ))
      ->fields('node', array(
      'uid',
    ))
      ->fields('node', array(
      'type',
    ));
    $query
      ->addExpression('SUM(n.grant_view)', 'grant_view');
    $query
      ->addExpression('SUM(n.grant_update)', 'grant_update');
    $query
      ->addExpression('SUM(n.grant_delete)', 'grant_delete');
    $query
      ->condition('a.module', 'flexiaccess')
      ->condition('u.uid', $user->uid)
      ->groupBy('n.nid');
    $result = $query
      ->execute();
    $form_state['user'] = $user;

    // The permissions table:
    $form['acl'] = array(
      '#title' => t('Node based ACL'),
      '#prefix' => '<div id="flexiaccess-user-acl-table"><p><em>' . t('Manage the nodes which this user has access to.  Remember to press &#8220;Commit updates&#8221; when done (otherwise, your changes will not be saved).') . '</em></p>',
      '#suffix' => '</div>',
      '#tree' => TRUE,
      // @todo: Check this out.
      // See: http://drupal.stackexchange.com/questions/90282/d7-fapi-unexpected-bahviour-when-combining-ajax-checkbox-and-a-table-theme
      // '#theme' => 'table',
      '#header' => array(
        t("Node"),
        t("View"),
        t("Update"),
        t("Delete"),
      ),
      '#rows' => array(),
    );
    $form['add'] = array(
      '#type' => 'textfield',
      '#title' => t('Add node'),
      '#size' => 60,
      '#autocomplete_path' => 'flexiaccess/node_autocomplete',
    );
    $form['add_button'] = array(
      '#type' => 'button',
      '#name' => 'acl_user_' . $user->uid,
      '#value' => t('Add Node'),
      '#ajax' => array(
        'callback' => 'flexiaccess_user_ajax_callback',
        'wrapper' => 'flexiaccess-user',
        'method' => 'replace',
        'effect' => 'fade',
      ),
    );
    foreach ($result as $row) {
      addrow($row, $form, $user);
    }
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Commit updates'),
      '#weight' => 10,
      '#submit' => array(
        'flexiaccess_user_submit',
      ),
    );

    // Determine whether we are rebuilding the form.
    // This is 1 when a row is being added via ajax.
    if ($form_state['rebuild']) {

      // Check if there are previously added, but uncommitted rows.
      if (empty($form_state['uncommitted'])) {
        $form_state['uncommitted'] = array();
      }

      // Find the correct node titles.
      if (!empty($form_state['uncommitted'])) {
        $result = db_query('SELECT nid,title, 0 AS grant_view, 0 AS grant_update, 0 AS grant_delete FROM {node} WHERE nid IN (:nids)', array(
          ':nids' => $form_state['uncommitted'],
        ));

        // Add the rows.
        if ($result
          ->rowCount()) {
          foreach ($result as $row) {
            addrow($row, $form, $user);
          }
        }
      }
    }
    $form['#tree'] = TRUE;
    return $form;
  }
}

/**
 * AJAX callback to add a node to the table on the user page.
 */
function flexiaccess_user_ajax_callback($form, $form_state) {

  // Return the part of the (rebuilt) form to be replaced.
  return $form;
}

/**
 * Menu callback.
 *
 * Retrieve a JSON object containing autocomplete suggestions for
 * existing nodes.
 */
function flexiaccess_node_autocomplete($string = '') {

  // TODO: What if range(0,50) does not show the required nodes?
  // TODO: maybe break up search string in words and add separate OR'd
  // conditions with foreach?
  $matches = array();
  if ($string) {

    // Get active content types that have Flexi access managing their ACL.
    $types = array_filter(variable_get('flexiaccess_types', array()));
    if (!empty($types)) {
      $result = db_select('node')
        ->fields('node', array(
        'title',
        'nid',
      ))
        ->condition('type', $types, 'IN')
        ->condition('title', '%' . db_like($string) . '%', 'LIKE')
        ->range(0, 50)
        ->execute();
      foreach ($result as $node) {
        $matches[$node->nid] = check_plain("{$node->nid} : {$node->title}");
      }
    }
  }
  drupal_json_output($matches);
}

/**
 * Commit updates from user page.
 */
function flexiaccess_user_submit($form, &$form_state) {
  if (empty($form_state['uncommitted'])) {
    $form_state['uncommitted'] = array();
  }
  foreach ($form_state['values']['acl'] as $ac) {
    if (in_array($ac['nid'], $form_state['uncommitted'])) {

      // New relationship between user and node.
      // Create acls for node where necessary.
      _flexiaccess_create_acl_rows($ac['nid']);
    }

    // Add acls to user.

    /*
     * The following is easily accomplished with a single query.
     * The correct way to do it, however, is to use the API, which
     * unfortunately uses many queries.
     * Would a single query be better at avoiding race conditions?
     */
    foreach (array(
      'view',
      'update',
      'delete',
    ) as $op) {
      $acl_id = acl_get_id_by_name('flexiaccess', $op . '_' . $ac['nid']);

      // Doing both add and remove here ensures that the latest form
      // submission takes effect in the db:
      if (1 == $ac[$op]) {

        // Add permission.
        acl_add_user($acl_id, $form_state['user']->uid);
      }
      else {

        // This block will only be reached when multiple admins have
        // edited the same permissions. Remove permission.
        acl_remove_user($acl_id, $form_state['user']->uid);
      }
    }

    // Apply changes for the node.
    node_access_acquire_grants(node_load($ac['nid']));
  }
  cache_clear_all();
  drupal_set_message(t('Your changes to the ACL has been saved.'));
}
function flexiaccess_user_validate($form, &$form_state) {
  $types = array_filter(variable_get('flexiaccess_types', array()));
  if (!is_numeric($form_state['values']['add'])) {
    form_set_error('add', t('Enter a node ID, or type the title and select a node from the autocomplete list.'));
    return;
  }
  $node = node_load($form_state['values']['add']);
  if (!$node) {
    form_set_error('add', t('Could not find a node with ID @nid', array(
      '@nid' => $form_state['values']['add'],
    )));
    return;
  }
  if (!in_array($node->type, $types)) {
    form_set_error('add', t('Flexiaccess is not configured to manage nodes of type <code>!type</code>.', array(
      '!type' => $node->type,
    )));
    return;
  }

  // If all is OK, add the new nid to the list of uncommitted nodes.
  $form_state['uncommitted'][] = $node->nid;
}

Functions

Namesort descending Description
flexiaccess_node_autocomplete Menu callback.
flexiaccess_user Build form to handle ACLs for user.
flexiaccess_user_ajax_callback AJAX callback to add a node to the table on the user page.
flexiaccess_user_submit Commit updates from user page.
flexiaccess_user_validate