-
Notifications
You must be signed in to change notification settings - Fork 1
/
content_access.admin.inc
403 lines (347 loc) · 14.6 KB
/
content_access.admin.inc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
<?php
// $Id: content_access.admin.inc,v 1.1.2.24 2010/07/19 11:40:11 fago Exp $
/**
* @file Content access administration UI.
*/
/**
* Specifies the threshold until we try to mass update node grants immediately.
*/
define('CONTENT_ACCESS_MASS_UPDATE_THRESHOLD', 1000);
/**
* Per node settings page.
*/
function content_access_page($form, &$form_state, $node) {
drupal_set_title(t('Access control for @title', array('@title' => $node->title)));
foreach (_content_access_get_operations() as $op => $label) {
$defaults[$op] = content_access_per_node_setting($op, $node);
}
// Get roles form
content_access_role_based_form($form, $defaults);
// Add an after_build handler that disables checkboxes, which are enforced by permissions.
$form['per_role']['#after_build'] = array('content_access_force_permissions');
// ACL form
if (module_exists('acl')) {
// This is disabled when there is no node passed.
$form['acl'] = array(
'#type' => 'fieldset',
'#title' => t('User access control lists'),
'#description' => t('These settings allow you to grant access to specific users.'),
'#collapsible' => TRUE,
'#tree' => TRUE,
);
foreach (array('view', 'update', 'delete') as $op) {
$acl_id = content_access_get_acl_id($node, $op);
acl_node_add_acl($node->nid, $acl_id, (int) ($op == 'view'), (int) ($op == 'update'), (int) ($op == 'delete'), content_access_get_settings('priority', $node->type));
$form['acl'][$op] = acl_edit_form($form_state, $acl_id, t('Grant !op access', array('!op' => $op)));
$form['acl'][$op]['#collapsed'] = !isset($_POST['acl_' . $acl_id]) && !unserialize($form['acl'][$op]['user_list']['#default_value']);
}
}
$form_state['node'] = $node;
$form['reset'] = array(
'#type' => 'submit',
'#value' => t('Reset to defaults'),
'#weight' => 10,
'#submit' => array('content_access_page_reset'),
'#access' => count(content_access_get_per_node_settings($node)) > 0,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#weight' => 10,
);
// @todo not true anymore?
// http://drupal.org/update/modules/6/7#hook_node_access_records
if (!$node->status) {
drupal_set_message(t("Warning: Your content is not published, so this settings are not taken into account as long as the content remains unpublished."), 'error');
}
return $form;
}
/**
* Submit callback for content_access_page().
*/
function content_access_page_submit($form, &$form_state) {
$settings = array();
$node = $form_state['node'];
foreach (_content_access_get_operations() as $op => $label) {
// Set the settings so that further calls will return this settings.
$settings[$op] = array_keys(array_filter($form_state['values'][$op]));
}
// Save per-node settings.
content_access_save_per_node_settings($node, $settings);
if (module_exists('acl')) {
foreach (array('view', 'update', 'delete') as $op) {
acl_save_form($form_state['values']['acl'][$op]);
}
}
// Apply new settings.
node_access_acquire_grants($node);
// @todo is this required?
cache_clear_all();
drupal_set_message(t('Your changes have been saved.'));
}
/**
* Submit callback for reset on content_access_page().
*/
function content_access_page_reset($form, &$form_state) {
content_access_delete_per_node_settings($form_state['node']);
node_access_acquire_grants($form_state['node']);
cache_clear_all();
drupal_set_message(t('The permissions have been reseted to the content type defaults.'));
}
/**
* Per content type settings form.
*/
function content_access_admin_settings($form, $form_state, $content_type) {
$type = $content_type->type;
$form_state['type'] = $type;
// Add role based per content type settings
$defaults = array();
foreach (_content_access_get_operations() as $op => $label) {
$defaults[$op] = content_access_get_settings($op, $type);
}
content_access_role_based_form($form, $defaults);
// Per node:
$form['node'] = array(
'#type' => 'fieldset',
'#title' => t('Per content node access control settings'),
'#collapsible' => TRUE,
'#description' => t('Optionally you can enable per content node access control settings. If enabled, a new tab for the content access settings appears when viewing content. You have to configure permission to access these settings at the !permissions page.', array('!permissions' => l(t('permissions'), 'admin/people/permissions'))),
);
$form['node']['per_node'] = array(
'#type' => 'checkbox',
'#title' => t('Enable per content node access control settings'),
'#default_value' => content_access_get_settings('per_node', $type),
);
$form['advanced'] = array(
'#type' => 'fieldset',
'#title' => t('Advanced'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['advanced']['priority'] = array(
'#type' => 'weight',
'#title' => t('Give content node grants priority'),
'#default_value' => content_access_get_settings('priority', $type),
'#description' => t('If you are only using this access control module, you can safely ignore this. If you are using multiple access control modules you can adjust the priority of this module.'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#weight' => 10,
);
return $form;
}
/**
* Submit handler for per content type settings form.
*/
function content_access_admin_settings_submit($form, &$form_state) {
$roles_permissions = user_role_permissions(user_roles());
$permissions = user_permission_get_modules();
// Remove disabled modules permissions, so they can't raise exception
// in content_access_save_permissions()
foreach ($roles_permissions as $rid => $role_permissions) {
foreach ($role_permissions as $permission => $value) {
if (!array_key_exists($permission, $permissions)) {
unset($roles_permissions[$rid][$permission]);
}
}
}
foreach (array('update', 'update_own', 'delete', 'delete_own') as $op) {
foreach ($form_state['values'][$op] as $rid => $value) {
$permission = content_access_get_permission_by_op($op, $form_state['type']);
if ($value) {
$roles_permissions[$rid][$permission] = TRUE;
}
else {
$roles_permissions[$rid][$permission] = FALSE;
}
}
// Don't save the setting, so its default value (get permission) is applied always.
unset($form_state['values'][$op]);
}
content_access_save_permissions($roles_permissions);
// Update content access settings
$settings = content_access_get_settings();
foreach (content_access_available_settings() as $setting) {
unset($settings[$setting][$form_state['type']]);
if (isset($form_state['values'][$setting])) {
$settings[$setting][$form_state['type']] = is_array($form_state['values'][$setting]) ? array_keys(array_filter($form_state['values'][$setting])) : $form_state['values'][$setting];
}
}
content_access_set_settings($settings);
$type = $form_state['type'];
// Mass update the nodes, but only if necessary.
if (content_access_get_settings('per_node', $type) ||
content_access_get_settings('view', $type) != $form['per_role']['view']['#default_value'] ||
content_access_get_settings('view_own', $type) != $form['per_role']['view_own']['#default_value'] ||
content_access_get_settings('priority', $type) != $form['advanced']['priority']['#default_value'] ||
content_access_get_settings('per_node', $type) != $form['node']['per_node']['#default_value']
) {
// If per node has been disabled and we use the ACL integration, we have to remove possible ACLs now.
if (!content_access_get_settings('per_node', $type) && $form['node']['per_node']['#default_value'] && module_exists('acl')) {
_content_access_remove_acls($type);
}
if (content_access_mass_update(array($type))) {
drupal_set_message(t('Permissions have been successfully rebuilt for the content type @types.', array('@types' => node_type_get_name($type))));
}
}
drupal_set_message(t('Your changes have been saved.'));
}
/**
* Mass updates node access records for nodes of the given types.
* @param $types
* An array of content type names.
* @return
* Whether the operation has been processed successfully (TRUE) or postponed (FALSE).
*/
function content_access_mass_update($types) {
$q = db_select('node', 'n')
->fields('n', array('nid'))
->condition('n.type', $types, 'IN');
$count = $q->countQuery()->execute()->fetchField();
node_access_needs_rebuild(TRUE);
// If there not too much nodes affected, try to do it.
if ($count <= CONTENT_ACCESS_MASS_UPDATE_THRESHOLD) {
$records = $q->execute();
foreach ($records as $node) {
node_access_acquire_grants(node_load($node->nid));
}
cache_clear_all();
node_access_needs_rebuild(FALSE);
return TRUE;
}
return FALSE;
}
/**
* Saves the given permissions by role to the database.
*/
function content_access_save_permissions($roles_permissions) {
foreach ($roles_permissions as $rid => $permissions) {
user_role_change_permissions($rid, $permissions);
}
}
/**
* Builds the role based permission form for the given defaults.
*
* @param $defaults
* Array of defaults for all operations.
*/
function content_access_role_based_form(&$form, $defaults = array()) {
$form['per_role'] = array(
'#type' => 'fieldset',
'#title' => t('Role based access control settings'),
'#collapsible' => TRUE,
'#description' => t('Note that users need at least the %access_content permission to be able to deal in any way with content.', array('%access_content' => t('access content'))) .
' ' . t('Furthermore note that content which is not @published is treated in a different way by drupal: It can be viewed only by its author or users with the %administer_nodes permission.', array('@published' => t('published'), '%administer_nodes' => t('administer nodes'))),
);
$operations = _content_access_get_operations();
$roles = array_map('filter_xss_admin', user_roles());
foreach ($operations as $op => $label) {
// Make sure defaults are set properly
$defaults += array($op => array());
$form['per_role'][$op] = array('#type' => 'checkboxes',
'#prefix' => '<div class="content_access-div">',
'#suffix' => '</div>',
'#options' => $roles,
'#title' => $label,
'#default_value' => $defaults[$op],
'#process' => array('form_process_checkboxes', 'content_access_disable_checkboxes'),
);
}
$form['per_role']['clearer'] = array(
'#value' => '<br clear="all" />',
);
drupal_add_css(drupal_get_path('module', 'content_access') . '/content_access.css');
return $form;
}
/**
* Formapi #process callback, that disables checkboxes for roles without access to content
*/
function content_access_disable_checkboxes($element) {
$access_roles = content_access_get_permission_access('access content');
$admin_roles = content_access_get_permission_access('administer nodes');
foreach (element_children($element) as $key) {
if (!in_array($key, $access_roles) &&
!($key != DRUPAL_ANONYMOUS_RID &&
in_array(DRUPAL_AUTHENTICATED_RID, $access_roles))) {
$element[$key]['#disabled'] = TRUE;
$element[$key]['#default_value'] = FALSE;
$element[$key]['#prefix'] = '<span title="' . t("This role is lacking the permission '@perm', so it has no access.", array('@perm' => t('access content'))) . '">';
$element[$key]['#suffix'] = "</span>";
}
elseif (in_array($key, $admin_roles) ||
($key != DRUPAL_ANONYMOUS_RID && in_array(DRUPAL_AUTHENTICATED_RID, $admin_roles))) {
// Fix the checkbox to be enabled for users with administer node privileges
$element[$key]['#disabled'] = TRUE;
$element[$key]['#default_value'] = TRUE;
$element[$key]['#prefix'] = '<span title="' . t("This role has '@perm' permission, so access is granted.", array('@perm' => t('administer nodes'))) . '">';
$element[$key]['#suffix'] = "</span>";
}
}
return $element;
}
/**
* Formapi #after_build callback, that disables checkboxes for roles without access to content.
*/
function content_access_force_permissions($element, &$form_state) {
foreach (array('update', 'update_own', 'delete', 'delete_own') as $op) {
foreach (content_access_get_settings($op, $form_state['node']->type) as $rid) {
$element[$op][$rid]['#disabled'] = TRUE;
$element[$op][$rid]['#attributes']['disabled'] = 'disabled';
$element[$op][$rid]['#value'] = TRUE;
$element[$op][$rid]['#checked'] = TRUE;
$element[$op][$rid]['#prefix'] = '<span title="' . t("Permission is granted due to the content type's access control settings.") . '">';
$element[$op][$rid]['#suffix'] = "</span>";
}
}
return $element;
}
/**
* Submit callback for the user permissions form.
* Trigger changes to node permissions to rebuild our grants.
*/
function content_access_user_admin_perm_submit($form, $form_state) {
// Check for each content type, which has per node access activated
// whether permissions have been changed.
$types = array();
foreach (array_filter(content_access_get_settings('per_node')) as $type => $value) {
foreach (_content_access_get_node_permissions($type) as $perm) {
foreach (user_roles() as $rid => $role) {
if (isset($form_state['values'][$rid]) && in_array($perm, $form['checkboxes'][$rid]['#default_value']) != in_array($perm, $form_state['values'][$rid])) {
//permission changed!
$types[$type] = node_get_types('name', $type);
continue 2;
}
}
}
}
if ($types && content_access_mass_update(array_keys($types))) {
drupal_set_message(format_plural(count($types),
'Permissions have been successfully rebuilt for the content type @types.',
'Permissions have been successfully rebuilt for the content types @types.',
array('@types' => implode(', ', $types))
));
}
}
function _content_access_get_node_permissions($type) {
return array_filter(array_map('content_access_get_permission_by_op', array_flip(_content_access_get_operations()), array_fill(0, 6, $type)));
}
/**
* Gets the content access acl id of the node.
*/
function content_access_get_acl_id($node, $op) {
$acl_id = acl_get_id_by_name('content_access', $op . '_' . $node->nid);
if (!$acl_id) {
$acl_id = acl_create_new_acl('content_access', $op . '_' . $node->nid);
}
return $acl_id;
}
/**
* Detaches all our ACLs for the nodes of the given type.
*/
function _content_access_remove_acls($type) {
$result = db_query("SELECT n.nid FROM {node} n WHERE type = :type", array('type' => $type));
foreach ($result as $node) {
acl_node_clear_acls($node->nid, 'content_access');
}
}