123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- <?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.
- */
- function workflow_access_node_access_records($node) {
- $grants = array();
- if ($state = workflow_get_workflow_node_by_nid($node->nid)) {
- $state = workflow_get_workflow_states_by_sid($state->sid);
- foreach (workflow_access_get_workflow_access_by_sid($state->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->name));
- $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->name)));
- }
- 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.
- $options = array('status' => 1);
- foreach (workflow_get_workflow_states_by_wid($workflow->wid, $options) as $state) {
- if ($state->state == t('(creation)')) {
- // 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' => t('@state', array('@state' => $state->state)),
- '#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.
- $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'];
- }
- /**
- * Implements hook_workflow().
- *
- * Update grants when a node changes workflow state.
- * This is already called when node_save is called.
- */
- function workflow_access_workflow($op, $old_sid, $sid, $node) {
- // ALERT:
- // This is a tricky spot when called on node_insert as part of the transition from create to state1.
- // node_save invokes this function as a hook before calling node_access_acquire_grants.
- // But when it calls node_access_acquire_grants later, it does so without deleting the access
- // set when calling workflow_node_insert because it is an insert and no prior grants are expected.
- // This leads to a SQL error of duplicate grants which causes a rollback of all changes.
- // Unfortunately, setting access rights isn't the only thing we're doing on node_insert so we
- // can't skip the whole thing. So we need to fix it further downstream in order to get this to work.
- // Here we don't want to run this in the case of (and ONLY in the case of) a brand new node.
- // Node access grants will be run as part of node_save's own granting after this.
- //
- // NOTE: Any module that sets node access rights on insert will hit this situation.
- //
- if ($old_state = workflow_get_workflow_states_by_sid($old_sid)) {
- if ($op == 'transition post' && $old_sid != $sid && (empty($node->is_new) || !$node->is_new)) {
- node_access_acquire_grants($node);
- }
- }
- }
- /**
- * 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,
- ),
- );
- }
|