content_access_rules.rules.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. <?php
  2. /**
  3. * @file
  4. * Rules specific functions that expose content_access' API.
  5. *
  6. * @todo
  7. * A way to enable per-node settings when a rule created, otherwise no effects
  8. * will be noticed.
  9. * Clean-up function names.
  10. */
  11. /**
  12. * Implements hook_rules_event_info().
  13. *
  14. * @ingroup rules
  15. */
  16. function content_access_rules_rules_event_info() {
  17. $events['content_access_content_type'] = array('label' => t('Content type access control was changed'));
  18. $events['content_access_per_node'] = array('label' => t('Per node access control was changed'));
  19. if (module_exists('acl')) {
  20. $events['content_access_user_acl'] = array('label' => t('User was added to ACL'));
  21. }
  22. $items = array();
  23. foreach ($events as $name => $event) {
  24. $items[$name] = array(
  25. 'label' => $event['label'],
  26. 'group' => t('Content Access'),
  27. );
  28. }
  29. return $items;
  30. }
  31. /**
  32. * Implementation of hook_rules_action_info().
  33. *
  34. * @ingroup rules
  35. */
  36. function content_access_rules_rules_action_info() {
  37. $role_actions = array(
  38. 'content_access_action_grant_node_permissions' => array(
  39. 'label' => t('Grant access by role'),
  40. 'description' => t('Grant access to the following content'),
  41. ),
  42. 'content_access_action_revoke_node_permissions' => array(
  43. 'label' => t('Revoke access by role'),
  44. 'description' => t('Revoke access to the following content'),
  45. ),
  46. );
  47. $reset_actions = array(
  48. 'content_access_action_reset_node_permissions' => array(
  49. 'label' => t('Reset access to content type defaults'),
  50. 'description' => t('Reset node permissions to default permissions'),
  51. ),
  52. );
  53. $user_actions = array(
  54. 'content_access_action_user_grant' => array(
  55. 'label' => t('Grant access by user'),
  56. 'operation' => t('Grant'),
  57. 'description' => t('Grant access to the following content'),
  58. ),
  59. 'content_access_action_user_revoke' => array(
  60. 'label' => t('Revoke access by user'),
  61. 'operation' => t('Revoke'),
  62. 'description' => t('Revoke access to the following content'),
  63. ),
  64. );
  65. $items = array();
  66. foreach ($role_actions as $name => $action) {
  67. $items[$name] = array(
  68. 'label' => $action['label'],
  69. 'parameter' => array(
  70. 'node' => array(
  71. 'type' => 'node',
  72. 'label' => t('Content'),
  73. 'description' => $action['description'],
  74. ),
  75. 'permissions' => array(
  76. 'type' => 'list<text>',
  77. 'label' => t('Role-based access control settings'),
  78. 'optional' => TRUE,
  79. 'options list' => 'content_access_action_roles_permissions_list',
  80. 'restriction' => 'input',
  81. ),
  82. ),
  83. 'group' => t('Content Access'),
  84. 'callbacks' => array(
  85. 'form_alter' => 'content_access_rules_action_form_alter',
  86. ),
  87. );
  88. }
  89. foreach ($reset_actions as $name => $action) {
  90. $items[$name] = array(
  91. 'label' => $action['label'],
  92. 'parameter' => array(
  93. 'node' => array(
  94. 'type' => 'node',
  95. 'label' => t('Content'),
  96. 'description' => $action['description'],
  97. ),
  98. ),
  99. 'group' => t('Content Access'),
  100. );
  101. }
  102. if (module_exists('acl')) {
  103. foreach ($user_actions as $name => $action) {
  104. $items[$name] = array(
  105. 'label' => $action['label'],
  106. 'named parameter' => TRUE,
  107. 'parameter' => array(
  108. 'node' => array(
  109. 'type' => 'node',
  110. 'label' => t('Content'),
  111. 'description' => $action['description'],
  112. ),
  113. 'content_access_user_view' => array(
  114. 'type' => 'user',
  115. 'label' => t('@action view access', array('@action' => $action['operation'])),
  116. 'optional' => TRUE,
  117. 'description' => t('@action view access to the following user.', array('@action' => $action['operation'])),
  118. ),
  119. 'content_access_user_update' => array(
  120. 'type' => 'user',
  121. 'label' => t('@action update access', array('@action' => $action['operation'])),
  122. 'optional' => TRUE,
  123. 'description' => t('@action edit access to the following user.', array('@action' => $action['operation'])),
  124. ),
  125. 'content_access_user_delete' => array(
  126. 'type' => 'user',
  127. 'label' => t('@action delete access', array('@action' => $action['operation'])),
  128. 'optional' => TRUE,
  129. 'description' => t('@action delete access to the following user.', array('@action' => $action['operation'])),
  130. ),
  131. ),
  132. 'group' => t('Content Access User'),
  133. );
  134. }
  135. }
  136. return $items;
  137. }
  138. /**
  139. * Returns an options list array for the content access permission parameter.
  140. */
  141. function content_access_action_roles_permissions_list() {
  142. $options = array();
  143. $roles = array_map('filter_xss_admin', user_roles());
  144. foreach (_content_access_get_operations() as $op => $label) {
  145. foreach ($roles as $rid => $role) {
  146. $options[$op][$op . ':' . $rid] = $op . ' ' . $role;
  147. }
  148. }
  149. return $options;
  150. }
  151. /**
  152. * Alter the settings form to render the text<list> as checkboxes.
  153. */
  154. function content_access_rules_action_form_alter(&$form, &$form_state) {
  155. // Per node access control should be enabled for this type of action to be
  156. // effective.
  157. drupal_set_message(t('Access control settings will not be executed for the affected node unless you enabled \'%per_node\' from the Access Control tab on content type page of the node', array('%per_node' => 'Per content node access control settings')), 'warning');
  158. // Alter the text<list> to make it into checkboxes groups
  159. $elements =& $form['parameter']['permissions']['settings']['permissions'];
  160. $elements = content_access_rules_checkboxes_form((array) $elements['#default_value']);
  161. // Add our own after build callback for fixing the form value afterwards.
  162. $elements['#after_build'][] = 'content_access_rules_action_form_after_build';
  163. // Make sure our include file is loaded when FAPI processes the form.
  164. form_load_include($form_state, 'inc', 'content_access_rules', 'content_access_rules.rules');
  165. }
  166. /**
  167. * Form after build callback.
  168. *
  169. * Get the form settings as checkboxes, convert them back to list<text>.
  170. */
  171. function content_access_rules_action_form_after_build($element, &$form_state) {
  172. if ($form_state['process_input']) {
  173. $form_value = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
  174. $value = content_access_rules_transform_to_rule_value($form_value);
  175. form_set_value($element, $value, $form_state);
  176. }
  177. return $element;
  178. }
  179. /**
  180. * Returns the form elements for configuring content access per-role permissions.
  181. */
  182. function content_access_rules_checkboxes_form($value) {
  183. $form = array();
  184. $roles = array_map('filter_xss_admin', user_roles());
  185. $defaults = content_access_rules_transform_rules_value($value);
  186. foreach (_content_access_get_operations() as $op => $label) {
  187. $form[$op] = array(
  188. '#type' => 'checkboxes',
  189. '#prefix' => '<div class="content_access-div">',
  190. '#suffix' => '</div>',
  191. '#options' => $roles,
  192. '#title' => $label,
  193. '#default_value' => isset($defaults[$op]) ? $defaults[$op] : array(),
  194. '#process' => array('form_process_checkboxes', 'content_access_disable_checkboxes'),
  195. );
  196. }
  197. $form['clearer'] = array(
  198. '#value' => '<br clear="all" />',
  199. );
  200. drupal_add_css(drupal_get_path('module', 'content_access') . '/content_access.css');
  201. return $form;
  202. }
  203. /**
  204. * Transforms the array of text values used by Rules to an array keyed by $op and $rid.
  205. *
  206. * @see content_access_rules_transform_to_rule_value()
  207. */
  208. function content_access_rules_transform_rules_value($value) {
  209. $array = array();
  210. foreach ($value as $op_role) {
  211. $parts = explode(':', $op_role);
  212. // The first item is $op and the second $rid.
  213. $array[$parts[0]][] = $parts[1];
  214. }
  215. return $array;
  216. }
  217. /**
  218. * Transform the form values array keyed by $op and $rid to an array of text values as used by Rules.
  219. *
  220. * @see content_access_rules_transform_rules_value()
  221. */
  222. function content_access_rules_transform_to_rule_value($form_values) {
  223. $value = array();
  224. foreach ($form_values as $op => $array) {
  225. foreach (array_filter($array) as $rid => $data) {
  226. $value[] = $op . ':' . $rid;
  227. }
  228. }
  229. return $value;
  230. }
  231. /**
  232. * Action implementation: Grant permissions for a node.
  233. */
  234. function content_access_action_grant_node_permissions($node, $permissions) {
  235. if (!empty($node->nid) && _content_access_rules_check_setting($node)) {
  236. // Transform the value to the content-access format.
  237. $settings = content_access_rules_transform_rules_value($permissions);
  238. $ca_settings = array();
  239. foreach (_content_access_get_operations() as $op => $label) {
  240. // Merge in the array of role-ids for each operation.
  241. $settings += array($op => array());
  242. $ca_settings[$op] = array_keys(array_flip(content_access_per_node_setting($op, $node)) + array_flip($settings[$op]));
  243. }
  244. content_access_save_per_node_settings($node, $ca_settings);
  245. content_access_action_aquire_grants($node);
  246. }
  247. }
  248. /**
  249. * Action implementation: Revoke permissions for a node.
  250. */
  251. function content_access_action_revoke_node_permissions($node, $permissions) {
  252. if (!empty($node->nid) && _content_access_rules_check_setting($node)) {
  253. // Transform the value to the content-access format.
  254. $settings = content_access_rules_transform_rules_value($permissions);
  255. $ca_settings = array();
  256. foreach (_content_access_get_operations() as $op => $label) {
  257. $settings += array($op => array());
  258. $ca_settings[$op] = array_diff(content_access_per_node_setting($op, $node), $settings[$op]);
  259. }
  260. content_access_save_per_node_settings($node, $ca_settings);
  261. content_access_action_aquire_grants($node);
  262. }
  263. }
  264. /**
  265. * Action implementation: Reset permissions for a node.
  266. */
  267. function content_access_action_reset_node_permissions($node) {
  268. content_access_delete_per_node_settings($node);
  269. content_access_action_aquire_grants($node);
  270. }
  271. /**
  272. * Verifies that per content settings are activated for the given node.
  273. */
  274. function _content_access_rules_check_setting($node) {
  275. $type = $node->type;
  276. $settings = variable_get('content_access_' . $type, array());
  277. if (isset($settings['per_node']) && $settings['per_node']) {
  278. return TRUE;
  279. }
  280. // If we didn't find any settings in content access for this type return
  281. // false as we don't want to process it.
  282. rules_log("Can't set per content permissions for content type @type. Make sure to have per content settings activated for content types you want to alter access control for.", array('@type' => $node->type), RulesLog::WARN);
  283. return FALSE;
  284. }
  285. /**
  286. * Split the settings string into array.
  287. */
  288. function content_access_action_settings($action_settings = array()) {
  289. $roles_ids = array_flip(user_roles());
  290. foreach (_content_access_get_operations() as $op => $label) {
  291. $settings[$op] = array();
  292. }
  293. foreach ($action_settings as $op_role => $role) {
  294. $op = substr($op_role, 0, strpos($op_role, ':'));
  295. $rid = $roles_ids[$role];
  296. $settings[$op][] = $rid;
  297. }
  298. return $settings;
  299. }
  300. /**
  301. * Action implementation: Grant user access.
  302. */
  303. function content_access_action_user_grant($params) {
  304. content_access_action_user($params, 'grant');
  305. }
  306. /**
  307. * Action implementation: Revoke user access.
  308. */
  309. function content_access_action_user_revoke($params) {
  310. content_access_action_user($params, 'revoke');
  311. }
  312. /**
  313. * Process Rule's param, and grant by the passed operation.
  314. */
  315. function content_access_action_user($params, $type) {
  316. $ops = array('view', 'update', 'delete');
  317. $settings = array();
  318. $node = $params['node'];
  319. foreach ($ops as $op) {
  320. if ($params['content_access_user_' . $op]) {
  321. $settings[$op] = $params['content_access_user_' . $op]->uid;
  322. }
  323. }
  324. foreach ($settings as $op => $uid) {
  325. $acl_id = content_access_get_acl_id($node, $op);
  326. acl_node_add_acl($node->nid, $acl_id, (int) ($op == 'view'), (int) ($op == 'update'), (int) ($op == 'delete'), content_access_get_settings('priority', $node->type));
  327. db_delete('acl_user')
  328. ->condition('acl_id', $acl_id)
  329. ->condition('uid', $uid)
  330. ->execute();
  331. if ($type == 'grant') {
  332. db_insert('acl_user')
  333. ->fields(array(
  334. 'acl_id' => $acl_id,
  335. 'uid' => $uid,
  336. ))
  337. ->execute();
  338. }
  339. }
  340. content_access_action_aquire_grants($node);
  341. }
  342. /**
  343. * Apply the new grants to the affected node.
  344. */
  345. function content_access_action_aquire_grants($node) {
  346. // node_save() does implement node_access_acquire_grants() so we don't want
  347. // to execute it again or we'll get a duplicated key exception
  348. if (!isset($node->op) ||
  349. (isset($node->op) && $node->op != 'Save')) {
  350. node_access_acquire_grants($node);
  351. }
  352. }