You are here

workflow_access.module in Workflow 7

Provides node access permissions based on workflow states.

File

workflow_access/workflow_access.module
View source
<?php

/**
 * @file
 *   Provides node access permissions based on workflow states.
 */

/**
 * Implements hook_menu().
 */
function workflow_access_menu() {
  $items = array();
  $items["admin/config/workflow/access/%workflow"] = array(
    'title' => 'Access',
    'access arguments' => array(
      'administer workflow',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'workflow_access_form',
      4,
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_node_grants().
 *
 * Supply the workflow access grants. We are simply using
 * roles as access lists, so rids translate directly to gids.
 */
function workflow_access_node_grants($account, $op) {
  return array(
    'workflow_access' => array_keys($account->roles),
    'workflow_access_owner' => array(
      $account->uid,
    ),
  );
}

/**
 * Implements hook_node_access_records().
 *
 * Returns a list of grant records for the passed in node object.
 * @todo: support Workflow Field.
 */
function workflow_access_node_access_records($node) {
  $grants = array();

  //@todo: read history or field instead of workflow_node, to read current status.
  if ($workflow_node = workflow_get_workflow_node_by_nid($node->nid)) {
    $sid = $workflow_node->sid;

    // @todo: since we only use sid, the next line is superfluous.
    $state = WorkflowState::load($workflow_node->sid);
    foreach (workflow_access_get_workflow_access_by_sid($sid) as $grant) {
      $grants[] = array(
        'realm' => $grant->rid == -1 ? 'workflow_access_owner' : 'workflow_access',
        'gid' => $grant->rid == -1 ? $node->uid : $grant->rid,
        'grant_view' => $grant->grant_view,
        'grant_update' => $grant->grant_update,
        'grant_delete' => $grant->grant_delete,
        'priority' => variable_get('workflow_access_priority', 0),
      );
    }
  }
  return $grants;
}

/**
 * Implements hook_node_access_explain().
 * This is a Devel Node Access hook.
 */
function workflow_access_node_access_explain($row) {
  static $interpretations = array();
  switch ($row->realm) {
    case 'workflow_access_owner':
      $interpretations[$row->gid] = t('Workflow access: author of the content may access');
      break;
    case 'workflow_access':
      $roles = user_roles();
      $interpretations[$row->gid] = t('Workflow access: %role may access', array(
        '%role' => $roles[$row->gid],
      ));
      break;
  }
  return !empty($interpretations[$row->gid]) ? $interpretations[$row->gid] : NULL;
}

/**
 * Implements hook_workflow_operations().
 * Create action link for access form.
 */
function workflow_access_workflow_operations($op, $workflow = NULL, $state = NULL) {
  switch ($op) {
    case 'workflow':
      $alt = t('Control content access for @wf', array(
        '@wf' => $workflow
          ->getName(),
      ));
      $actions = array(
        'workflow_access_form' => array(
          'title' => t('Access'),
          'href' => "admin/config/workflow/access/{$workflow->wid}",
          'attributes' => array(
            'alt' => $alt,
            'title' => $alt,
          ),
        ),
      );
      return $actions;
  }
}

/**
 * Implements hook_form().
 *
 * Add a "three dimensional" (state, role, permission type) configuration
 * interface to the workflow edit form.
 */
function workflow_access_form($form, $form_state, $workflow) {
  if ($workflow) {
    drupal_set_title(t('@name Access', array(
      '@name' => $workflow
        ->getName(),
    )));
  }
  else {
    drupal_set_message(t('Unknown workflow'));
    drupal_goto('admin/config/workflow/workflow');
  }
  $bc = array(
    l(t('Home'), '<front>'),
  );
  $bc[] = l(t('Configuration'), 'admin/config');
  $bc[] = l(t('Workflow'), 'admin/config/workflow');
  $bc[] = l(t('Workflow'), 'admin/config/workflow/workflow');
  $bc[] = l(t($workflow->name), "admin/config/workflow/workflow/{$workflow->wid}");

  //  $bc[] = l(t('Access'), "admin/config/workflow/access/$workflow->wid");
  drupal_set_breadcrumb($bc);
  $form = array(
    '#tree' => TRUE,
  );
  $form['#wid'] = $workflow->wid;

  // A list of roles available on the site and our
  // special -1 role used to represent the node author.
  $rids = user_roles(FALSE, 'participate in workflow');
  $rids['-1'] = t('author');

  // Add a table for every workflow state.
  foreach ($workflow
    ->getStates() as $state) {
    if ($state
      ->isCreationState()) {

      // No need to set perms on creation.
      continue;
    }
    $view = $update = $delete = array();
    $count = 0;
    foreach (workflow_access_get_workflow_access_by_sid($state->sid) as $access) {
      $count++;
      if ($access->grant_view) {
        $view[] = $access->rid;
      }
      if ($access->grant_update) {
        $update[] = $access->rid;
      }
      if ($access->grant_delete) {
        $delete[] = $access->rid;
      }
    }

    // Allow view grants by default for anonymous and authenticated users,
    // if no grants were set up earlier.
    if (!$count) {
      $view = array(
        DRUPAL_ANONYMOUS_RID,
        DRUPAL_AUTHENTICATED_RID,
      );
    }

    // TODO better tables using a #theme function instead of direct #prefixing
    $form[$state->sid] = array(
      '#type' => 'fieldset',
      '#title' => $state
        ->label(),
      '#collapsible' => TRUE,
      '#collapsed' => FALSE,
      '#tree' => TRUE,
    );
    $form[$state->sid]['view'] = array(
      '#type' => 'checkboxes',
      '#options' => $rids,
      '#default_value' => $view,
      '#title' => t('Roles who can view posts in this state'),
      '#prefix' => '<table width="100%" style="border: 0;"><tbody style="border: 0;"><tr><td>',
    );
    $form[$state->sid]['update'] = array(
      '#type' => 'checkboxes',
      '#options' => $rids,
      '#default_value' => $update,
      '#title' => t('Roles who can edit posts in this state'),
      '#prefix' => "</td><td>",
    );
    $form[$state->sid]['delete'] = array(
      '#type' => 'checkboxes',
      '#options' => $rids,
      '#default_value' => $delete,
      '#title' => t('Roles who can delete posts in this state'),
      '#prefix' => "</td><td>",
      '#suffix' => "</td></tr></tbody></table>",
    );
  }
  $form['warning'] = array(
    '#type' => 'markup',
    '#markup' => '<p><strong>' . t('WARNING:') . '</strong> ' . t('Use of the "Edit any," "Edit own," and even "View published content" permissions
        for the content type may override these access settings.') . '</p>',
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save configuration'),
  );
  return $form;

  // Place our block comfortably down the page.
  $form['submit']['#weight'] = 10;
  $form['#submit'][] = 'workflow_access_form_submit';
}

/**
 * Store permission settings for workflow states.
 */
function workflow_access_form_submit($form, &$form_state) {
  foreach ($form_state['values'] as $sid => $access) {

    // Ignore irrelevant keys.
    if (!is_numeric($sid)) {
      continue;
    }
    $grants = array();
    foreach ($access['view'] as $rid => $checked) {
      $data = array(
        'sid' => $sid,
        'rid' => $rid,
        'grant_view' => !empty($checked) ? (bool) $checked : 0,
        'grant_update' => !empty($access['update'][$rid]) ? (bool) $access['update'][$rid] : 0,
        'grant_delete' => !empty($access['delete'][$rid]) ? (bool) $access['delete'][$rid] : 0,
      );
      $id = workflow_access_insert_workflow_access_by_sid($data);
    }

    // Update all nodes having same workflow state to reflect new settings.
    foreach (workflow_get_workflow_node_by_sid($sid) as $data) {

      // Instead of trying to construct what the grants should be per node as we save.
      // Let's fall back on existing node grant systems that will find it for us.
      $entity_type = 'node';

      // @todo: add support for other entity types in workflow_access_form_submit()
      $node = node_load($data->nid);
      node_access_acquire_grants($node);
    }
  }
  drupal_set_message(t('Workflow access permissions updated.'));
  $form_state['redirect'] = 'admin/config/workflow/workflow/' . $form['#wid'];
}

/**
 * DB functions - all DB interactions are isolated here to make for easy updating should our schema change.
 */

/**
 * Given a sid, retrieve the access information and return the row(s).
 */
function workflow_access_get_workflow_access_by_sid($sid) {
  $results = db_query('SELECT * from {workflow_access} where sid = :sid', array(
    ':sid' => $sid,
  ));
  return $results
    ->fetchAll();
}

/**
 * Given a sid and a rid (the  unique key), delete all access data for this state.
 */
function workflow_access_delete_workflow_access_by_sid_rid($sid, $rid) {
  db_delete('workflow_access')
    ->condition('sid', $sid)
    ->condition('rid', $rid)
    ->execute();
}

/**
 * Given a sid, delete all access data for this state.
 */
function workflow_access_delete_workflow_access_by_sid($sid) {
  db_delete('workflow_access')
    ->condition('sid', $sid)
    ->execute();
}

/**
 * Given data, insert into workflow access - we never update.
 */
function workflow_access_insert_workflow_access_by_sid(&$data) {
  $data = (object) $data;
  workflow_access_delete_workflow_access_by_sid_rid($data->sid, $data->rid);
  drupal_write_record('workflow_access', $data);
}

/**
 * Implements hook_form_alter().
 *
 * Tell the Features module that we intend to provide one exportable component.
 */
function workflow_access_form_alter(&$form, &$form_state, $form_id) {
  switch ($form_id) {
    case 'workflow_admin_ui_types_form':
      $form['workflow_access'] = array(
        '#type' => 'fieldset',
        '#title' => t('Workflow Access'),
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
      );
      $form['workflow_access']['workflow_access_priority'] = array(
        '#type' => 'weight',
        '#delta' => 10,
        '#title' => t('Workflow Access Priority'),
        '#default_value' => variable_get('workflow_access_priority', 0),
        '#description' => t('This sets the node access priority. This can be dangerous. If there is any doubt,
          leave it at 0.') . ' ' . l(t('Read the manual.'), 'https://api.drupal.org/api/drupal/modules!node!node.api.php/function/hook_node_access_records/7'),
      );
      $form['#submit'][] = 'workflow_access_priority_submit';
      return;
  }
}

/**
 * Submit handler.
 */
function workflow_access_priority_submit($form, &$form_state) {
  variable_set('workflow_access_priority', $form_state['values']['workflow_access']['workflow_access_priority']);
}

/**
 * Implements hook_features_api().
 *
 * Tell the Features module that we intend to provide one exportable component.
 */
function workflow_access_features_api() {
  return array(
    'workflow_access' => array(
      'name' => t('Workflow access'),
      'file' => drupal_get_path('module', 'workflow_access') . '/workflow_access.features.inc',
      'default_hook' => 'workflow_access_features_default_settings',
      'default_file' => FEATURES_DEFAULTS_INCLUDED,
      'feature_source' => TRUE,
    ),
  );
}

Functions

Namesort descending Description
workflow_access_delete_workflow_access_by_sid Given a sid, delete all access data for this state.
workflow_access_delete_workflow_access_by_sid_rid Given a sid and a rid (the unique key), delete all access data for this state.
workflow_access_features_api Implements hook_features_api().
workflow_access_form Implements hook_form().
workflow_access_form_alter Implements hook_form_alter().
workflow_access_form_submit Store permission settings for workflow states.
workflow_access_get_workflow_access_by_sid Given a sid, retrieve the access information and return the row(s).
workflow_access_insert_workflow_access_by_sid Given data, insert into workflow access - we never update.
workflow_access_menu Implements hook_menu().
workflow_access_node_access_explain Implements hook_node_access_explain(). This is a Devel Node Access hook.
workflow_access_node_access_records Implements hook_node_access_records().
workflow_access_node_grants Implements hook_node_grants().
workflow_access_priority_submit Submit handler.
workflow_access_workflow_operations Implements hook_workflow_operations(). Create action link for access form.