workflow_access.module 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <?php
  2. /**
  3. * @file
  4. * Provides node access permissions based on workflow states.
  5. */
  6. /**
  7. * Implements hook_menu().
  8. *
  9. * Uses pattern of EntityWorkflowUIController::hook_menu().
  10. */
  11. function workflow_access_menu() {
  12. $items = array();
  13. $path = 'admin/config/workflow/workflow';
  14. $id_count = count(explode('/', $path));
  15. $items[$path . '/access'] = array(
  16. 'title' => 'Access settings',
  17. 'file' => 'workflow_access.pages.inc',
  18. 'access arguments' => array('administer workflow'),
  19. 'page callback' => 'drupal_get_form',
  20. 'page arguments' => array('workflow_access_priority_form'),
  21. 'type' => MENU_LOCAL_ACTION,
  22. );
  23. $items[$path . '/manage/%workflow/access'] = array(
  24. 'title' => 'Access',
  25. 'file' => 'workflow_access.pages.inc',
  26. 'access arguments' => array('administer workflow'),
  27. 'page callback' => 'drupal_get_form',
  28. 'page arguments' => array(
  29. 'workflow_access_form',
  30. $id_count + 1,
  31. $id_count + 2,
  32. ),
  33. // 'type' => MENU_CALLBACK,
  34. // 'type' => MENU_LOCAL_TASK,
  35. );
  36. return $items;
  37. }
  38. /**
  39. * Implements hook_help().
  40. */
  41. function workflow_access_help($path, $arg) {
  42. $help = '';
  43. switch ($path) {
  44. case 'admin/config/workflow/workflow/manage/%/access':
  45. $help = t("WARNING: Use of the 'Edit any', 'Edit own', and even 'View
  46. published content' permissions for the content type may override these
  47. access settings. You may need to <a href='!url'>alter the priority of
  48. the Workflow access module</a>.", array('!url' => url('admin/config/workflow/workflow/access'))
  49. );
  50. if (module_exists('og')) {
  51. $help .= '<br>';
  52. $help .= t('WARNING: Organic Groups (OG) is present and may interfere
  53. with these settings.');
  54. if (variable_get('og_node_access_strict', TRUE)) {
  55. $help .= ' ';
  56. $help .= t('In particular, <a href="!url">Strict node access
  57. permissions</a> is enabled and may override Workflow access
  58. settings.', array('!url' => url('admin/config/group/settings')));
  59. }
  60. }
  61. break;
  62. default:
  63. break;
  64. }
  65. return $help;
  66. }
  67. /**
  68. * Implements hook_features_api().
  69. *
  70. * Tell the Features module that we intend to provide one exportable component.
  71. */
  72. function workflow_access_features_api() {
  73. return array(
  74. 'workflow_access' => array(
  75. 'name' => t('Workflow access'),
  76. 'file' => drupal_get_path('module', 'workflow_access') . '/workflow_access.features.inc',
  77. 'default_hook' => 'workflow_access_features_default_settings',
  78. 'default_file' => FEATURES_DEFAULTS_INCLUDED,
  79. 'feature_source' => TRUE,
  80. ),
  81. );
  82. }
  83. /**
  84. * Implements hook_workflow_operations().
  85. *
  86. * Create action link for access form on EntityWorkflowUIController::overviewForm.
  87. */
  88. function workflow_access_workflow_operations($op, $workflow = NULL, $state = NULL) {
  89. switch ($op) {
  90. case 'workflow':
  91. $actions = array();
  92. $wid = $workflow->wid;
  93. $alt = t('Control content access for @wf', array('@wf' => $workflow->getName()));
  94. $actions += array(
  95. 'workflow_access_form' => array(
  96. 'title' => t('Access'),
  97. 'href' => "admin/config/workflow/workflow/manage/$wid/access",
  98. 'attributes' => array('alt' => $alt, 'title' => $alt),
  99. ),
  100. );
  101. return $actions;
  102. }
  103. }
  104. /**
  105. * Implements hook_node_grants().
  106. *
  107. * Supply the workflow access grants. We are simply using
  108. * roles as access lists, so rids translate directly to gids.
  109. */
  110. function workflow_access_node_grants($account, $op) {
  111. return array(
  112. 'workflow_access' => array_keys($account->roles),
  113. 'workflow_access_owner' => array($account->uid),
  114. );
  115. }
  116. /**
  117. * Implements hook_node_access_records().
  118. *
  119. * Returns a list of grant records for the passed in node object.
  120. * This hook is invoked by function node_access_acquire_grants().
  121. */
  122. function workflow_access_node_access_records($node) {
  123. $grants = array();
  124. $workflow_transitions_added = FALSE;
  125. // Only relevant for content with Workflow.
  126. if (!isset($node->workflow_transitions)) {
  127. $node->workflow_transitions = array();
  128. // Sometimes, a node is saved without going through workflow_transition_form.
  129. // E.g.,
  130. // - when saving a node with workflow_node programmatically with node_save();
  131. // - when changing a state on a node view page/history tab;
  132. // - when rebuilding permissions via batch for workflow_node and workflow_field.
  133. // In that case, we need to create the workflow_transitions ourselves to
  134. // calculate the grants.
  135. foreach (_workflow_info_fields($node, 'node') as $field_name => $field) {
  136. $old_sid = FALSE;
  137. // Create a dummy transition, just to set $node->workflow_transitions.
  138. if (isset($node->workflow)) {
  139. $old_sid = $new_sid = $node->workflow;
  140. }
  141. elseif (isset($node->{$field_name}[LANGUAGE_NONE])) {
  142. $old_sid = $new_sid = _workflow_get_sid_by_items($node->{$field_name}[LANGUAGE_NONE]);
  143. }
  144. if ($old_sid) {
  145. $transition = new WorkflowTransition();
  146. $transition->setValues('node', $node, $field_name, $old_sid, $new_sid, $node->uid, REQUEST_TIME, '');
  147. // Store the transition, so it can be easily fetched later on.
  148. // Store in an array, to prepare for multiple workflow_fields per entity.
  149. // This is a.o. used in hook_entity_update to trigger 'transition post'.
  150. $node->workflow_transitions[$field_name] = $transition;
  151. $workflow_transitions_added = TRUE;
  152. }
  153. }
  154. }
  155. // Get 'author' of this entity.
  156. // - Some entities (e.g., taxonomy_term) do not have a uid.
  157. // But then again: node_access is only for nodes...
  158. $uid = isset($node->uid) ? $node->uid : 0;
  159. $count = 0;
  160. foreach ($node->workflow_transitions as $transition) {
  161. // @todo: add support for +1 workflows per entity.
  162. if ($count++ == 1 ) {
  163. continue;
  164. }
  165. $field_name = $transition->field_name;
  166. $priority = variable_get('workflow_access_priority', 0);
  167. if ($current_sid = workflow_node_current_state($node, 'node', $field_name)) {
  168. foreach (workflow_access_get_workflow_access_by_sid($current_sid) as $grant) {
  169. $realm = ($uid > 0 && $grant->rid == WORKFLOW_ROLE_AUTHOR_RID) ? 'workflow_access_owner' : 'workflow_access';
  170. $gid = ($uid > 0 && $grant->rid == WORKFLOW_ROLE_AUTHOR_RID) ? $uid : $grant->rid;
  171. // Anonymous ($uid == 0) author is not allowed for role 'author' (== -1).
  172. // Both logically (Anonymous having more rights then authenticated)
  173. // and technically: $gid must be a positive int.
  174. if ($gid < 0) { // if ($uid == 0 && $grant->rid == WORKFLOW_ROLE_AUTHOR_RID) {
  175. continue;
  176. }
  177. $grants[] = array(
  178. 'realm' => $realm,
  179. 'gid' => $gid,
  180. 'grant_view' => $grant->grant_view,
  181. 'grant_update' => $grant->grant_update,
  182. 'grant_delete' => $grant->grant_delete,
  183. 'priority' => $priority,
  184. 'field_name' => $field_name, // Just for analysis and info.
  185. );
  186. }
  187. }
  188. }
  189. if ($workflow_transitions_added == TRUE) {
  190. unset($node->workflow_transitions);
  191. }
  192. return $grants;
  193. }
  194. /**
  195. * Implements hook_node_access_explain().
  196. *
  197. * This is a Devel Node Access hook.
  198. */
  199. function workflow_access_node_access_explain($row) {
  200. static $interpretations = array();
  201. switch ($row->realm) {
  202. case 'workflow_access_owner':
  203. $interpretations[$row->gid] = t('Workflow access: author of the content may access');
  204. break;
  205. case 'workflow_access':
  206. $roles = user_roles();
  207. $interpretations[$row->gid] = t('Workflow access: %role may access', array('%role' => $roles[$row->gid]));
  208. break;
  209. }
  210. return (!empty($interpretations[$row->gid]) ? $interpretations[$row->gid] : NULL);
  211. }
  212. /**
  213. * DB functions - all DB interactions are isolated here to make for easy updating should our schema change.
  214. */
  215. /**
  216. * Given a sid, retrieve the access information and return the row(s).
  217. */
  218. function workflow_access_get_workflow_access_by_sid($sid) {
  219. $results = db_query('SELECT * from {workflow_access} where sid = :sid', array(':sid' => $sid));
  220. return $results->fetchAll();
  221. }
  222. /**
  223. * Given a sid and rid (the unique key), delete all access data for this state.
  224. */
  225. function workflow_access_delete_workflow_access_by_sid_rid($sid, $rid) {
  226. db_delete('workflow_access')->condition('sid', $sid)->condition('rid', $rid)->execute();
  227. }
  228. /**
  229. * Given a sid, delete all access data for this state.
  230. */
  231. function workflow_access_delete_workflow_access_by_sid($sid) {
  232. db_delete('workflow_access')->condition('sid', $sid)->execute();
  233. }
  234. /**
  235. * Given data, insert into workflow access - we never update.
  236. */
  237. function workflow_access_insert_workflow_access_by_sid(&$data) {
  238. $data = (object) $data;
  239. workflow_access_delete_workflow_access_by_sid_rid($data->sid, $data->rid);
  240. drupal_write_record('workflow_access', $data);
  241. }