Projet

Général

Profil

Paste
Télécharger (20,6 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / content_access / content_access.module @ 87dbc3bf

1 85ad3d82 Assos Assos
<?php
2
3
/**
4
 * @file Content access module file.
5
 */
6
7
/**
8
 * Implements hook_help().
9
 */
10
function content_access_help($path, $arg) {
11
  switch ($path) {
12
    case 'admin/help#content_access':
13
      $output = '<h3>' . t('About') . '</h3>';
14
      $output .= '<p>' . t('Content Access module provides flexible way to control how and who should read or control your site content. Content Access can define custom access control rules for content types and even for every piece of content.') . '</p>';
15
      $output .= '<h3>' . t('Uses') . '</h3>';
16
      $output .= '<dl>';
17
      $output .= '<dt>' . t('Default and custom settings') . '</dt>';
18
      $output .= '<dd>' . t("Each <a href='@content-type'>content type</a> can have its own default content access settings configured as: <em>View any content</em> to allow anyone to view content from this content type, <em>View own content</em> to allow only content creators to see their own content, <em>Edit any content</em> to allow anyone to edit content from this content type, <em>Edit own content</em> to allow only content creators to edit their own content, <em>Delete any content</em> to allow anyone to delete content from this content type, <em>Delete own content </em> to allow content creators to delete their own content. This default settings for each content type can be further customized per every piece of content per user if you have <a href='@acl'>ACL</a> module enabled.", array('@content-type' => url('admin/structure/types'), '@acl' => 'http://drupal.org/project/acl/')) . '</dd>';
19
      $output .= '</dl>';
20
      return $output;
21
  }
22
}
23
24
/**
25
 * Implements hook_admin_paths().
26
 */
27
function content_access_admin_paths() {
28
  $paths = array(
29
    'node/*/access' => TRUE,
30
  );
31
  return $paths;
32
}
33
34
/**
35
 * Implements hook_menu().
36
 */
37
function content_access_menu() {
38
  $items = array();
39
40
  $items['node/%node/access'] = array(
41
    'title' => 'Access control',
42
    'page callback' => 'drupal_get_form',
43
    'page arguments' => array('content_access_page', 1),
44
    'access callback' => 'content_access_node_page_access',
45
    'access arguments' => array(1),
46
    'file' => 'content_access.admin.inc',
47
    'theme callback' => '_node_custom_theme',
48
    'type' => MENU_LOCAL_TASK,
49
    'weight' => 3,
50
    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
51
  );
52
53
  $items['admin/structure/types/manage/%node_type/access'] = array(
54
    'title' => 'Access control',
55
    'description' => 'Configure content access control.',
56
    'page callback' => 'drupal_get_form',
57
    'page arguments' => array('content_access_admin_settings', 4),
58
    'access callback' => 'content_access_admin_settings_access',
59
    'access arguments' => array(),
60
    'type' => MENU_LOCAL_TASK,
61
    'file' => 'content_access.admin.inc',
62
    'theme callback' => '_node_custom_theme',
63
    'weight' => 1,
64
  );
65
66
  return $items;
67
}
68
69
/**
70
 * Implements hook_perm().
71
 */
72
function content_access_permission() {
73
  return array(
74
    'grant content access' => array(
75
      'title' => t('Grant content access'),
76
      'description' => t('View and modify content access for any nodes'),
77
    ),
78
    'grant own content access' => array(
79
      'title' => t('Grant own content access'),
80
      'description' => t('View and modify content access for own nodes'),
81
    ),
82
  );
83
}
84
85
/**
86
 * Get access tab page for the viewed node.
87
 */
88
function content_access_node_page_access($node) {
89
  global $user;
90
  return content_access_get_settings('per_node', $node->type) && user_access('grant content access') ||
91
    content_access_get_settings('per_node', $node->type) && (user_access('grant own content access') && ($user->uid == $node->uid));
92
}
93
94
/**
95
 * Content access settings for content type.
96
 */
97
function content_access_admin_settings_access() {
98
  return user_access('administer nodes') && user_access('administer content types');
99
}
100
101
/**
102
 * Implements hook_node_grants().
103
 */
104
function content_access_node_grants($account, $op) {
105
  return array(
106
    'content_access_author' => array($account->uid),
107
    'content_access_rid' => array_keys($account->roles),
108
  );
109
}
110
111
/**
112
 * Implements hook_node_access_records().
113
 */
114
function content_access_node_access_records($node) {
115
  if (content_access_disabling() || !$node->status) {
116
    return;
117
  }
118
119
  // Apply per node settings if necessary.
120
  if (content_access_get_settings('per_node', $node->type)) {
121
    $grants = array();
122
    foreach (array('view', 'update', 'delete') as $op) {
123
      foreach (content_access_get_rids_per_node_op($op, $node) as $rid) {
124
        $grants[$rid]['grant_' . $op] = 1;
125
      }
126
    }
127
    foreach ($grants as $rid => $grant) {
128
      $grants[$rid] = content_access_proccess_grant($grant, $rid, $node);
129
    }
130
131
    // Care for the author grant.
132
    $grant = array();
133
    foreach (array('view', 'update', 'delete') as $op) {
134
      // Get all roles that have access to use $op on this node.
135
      $any_roles = drupal_map_assoc(content_access_per_node_setting($op, $node));
136
      $any_roles = $any_roles ? $any_roles : array();
137
      $any_roles += ($op != 'view') ? content_access_get_settings($op, $node->type) : array();
138
      $grant['grant_' . $op] = content_access_own_op($node, $any_roles, content_access_get_rids_per_node_op($op . '_own', $node));
139
    }
140
141
    if (array_filter($grant)) {
142
      $grant['realm'] = 'content_access_author';
143
      $grants[] = content_access_proccess_grant($grant, $node->uid, $node);
144
    }
145
  }
146
  else {
147
    // Apply the content type defaults.
148
    $grants = content_access_get_type_grant($node);
149
  }
150
151
  if (empty($grants)) {
152
    // This means we grant no access.
153
    $grants[] = content_access_proccess_grant(array(), 0, $node);
154
  }
155
  else {
156
    content_access_optimize_grants($grants, $node);
157
  }
158
159
  return $grants;
160
}
161
162
/**
163
 * Implements hook_node_delete().
164
 */
165
function content_access_node_delete($node) {
166
  db_delete('content_access')
167
    ->condition('nid', $node->nid)
168
    ->execute();
169
}
170
171
/**
172
 * Used by the ACL module.
173
 */
174
function content_access_enabled() {
175
  return !content_access_disabling();
176
}
177
178
/**
179
 * Implements hook_disable().
180
 */
181
function content_access_disable() {
182
  content_access_disabling(TRUE);
183
}
184
185
/**
186
 * Remembers if we have disabled access.
187
 */
188
function content_access_disabling($set = NULL) {
189
  static $disabling = FALSE;
190
191
  if (isset($set)) {
192
    $disabling = $set;
193
  }
194
  return $disabling;
195
}
196
197
/**
198
 * Return content_access' settings.
199
 *
200
 * @param $setting
201
 *   One of the content_access_available_settings(), e.g. 'view' or 'per_node'.
202
 *   If 'all' is passed, all available settings are returned.
203
 * @param $type_name
204
 *   The name of the content type to return settings for.
205
 *
206
 * @return
207
 *   The value of the given setting or an array of all settings.
208
 */
209
function content_access_get_settings($setting, $type_name) {
210
  $settings = variable_get('content_access_' . $type_name, array());
211
  $settings += content_access_get_setting_defaults($type_name);
212
  if ($setting == 'all') {
213
    return $settings;
214
  }
215
  return isset($settings[$setting]) ? $settings[$setting] : NULL;
216
}
217
218
/**
219
 * Save content_access settings of a content type.
220
 */
221
function content_access_set_settings($settings, $type_name) {
222
  // Do not store default values so we do not have to care about synching our
223
  // settings with the permissions.
224
  foreach (content_access_get_setting_defaults($type_name) as $setting => $default_value) {
225
    if (isset($settings[$setting]) && $settings[$setting] == $default_value) {
226
      unset($settings[$setting]);
227
    }
228
  }
229
  variable_set('content_access_' . $type_name, $settings);
230
}
231
232
/**
233
 * Return an array containing all available content_access settings.
234
 */
235
function content_access_available_settings() {
236
  return array('view', 'update', 'delete', 'view_own', 'update_own', 'delete_own', 'per_node', 'priority');
237
}
238
239
/**
240
 * Defines default values for settings.
241
 */
242
function content_access_get_setting_defaults($type) {
243
  $defaults = array();
244
  $defaults['view'] = $defaults['view_own'] = array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID);
245
  foreach (array('update', 'delete') as $op) {
246
    $defaults[$op] = content_access_get_permission_access(content_access_get_permission_by_op($op, $type));
247
    $defaults[$op . '_own'] = content_access_get_permission_access(content_access_get_permission_by_op($op . '_own', $type));
248
  }
249
  $defaults['priority'] = 0;
250
  $defaults['per_node'] = FALSE;
251
  return $defaults;
252
}
253
254
/**
255
 * Returns an array of role ids that contain the given permission.
256
 */
257
function content_access_get_permission_access($perm, $reset = FALSE) {
258
  $roles = &drupal_static(__FUNCTION__, array());
259
260
  if ($reset) {
261
    $roles = array();
262
  }
263
  if (!isset($roles[$perm]) && $perm) {
264
    $roles[$perm] = array_keys(user_roles(0, $perm));
265
  }
266
  return isset($roles[$perm]) ? $roles[$perm] : array();
267
}
268
269
/**
270
 * Gets the name of a permission for the given operation, if there is a suiting one.
271
 */
272
function content_access_get_permission_by_op($op, $type) {
273
  switch ($op) {
274
    default:
275
      return FALSE;
276
    case 'update':
277
      return 'edit any ' . $type . ' content';
278
    case 'update_own':
279
      return 'edit own ' . $type . ' content';
280
    case 'delete':
281
      return 'delete any ' . $type . ' content';
282
    case 'delete_own':
283
      return 'delete own ' . $type . ' content';
284
  }
285
}
286
287
/**
288
 * Returns the default grants for a given node type.
289
 */
290
function content_access_get_type_grant($node) {
291
  // Cache per type default grants in a static array
292
  static $defaults = array();
293
294
  if (!isset($defaults[$node->type])) {
295
    $grants = array();
296
297
    // Only process the 'view' op as node_access() will take care of edit and delete
298
    foreach (content_access_get_settings('view', $node->type) as $rid) {
299
      $grants[$rid]['grant_view'] = 1;
300
      $grants[$rid] = content_access_proccess_grant($grants[$rid], $rid, $node);
301
    }
302
    $defaults[$node->type] = $grants;
303
  }
304
305
  // Care for the author grant.
306
  $grant = $grants = array();
307
  $grant['grant_view'] = content_access_own_op($node, content_access_get_settings('view', $node->type), content_access_get_settings('view_own', $node->type));
308
  if ($grant['grant_view']) {
309
    $grant['realm'] = 'content_access_author';
310
    $grants = array('author' => content_access_proccess_grant($grant, $node->uid, $node));
311
  }
312
313
  return $defaults[$node->type] + $grants;
314
}
315
316
/**
317
 * Process a grant, which means add priority, realm and other properties.
318
 */
319
function content_access_proccess_grant($grant, $gid, $node) {
320
  $grant += array('grant_view' => 0, 'grant_update' => 0, 'grant_delete' => 0, 'realm' => 'content_access_rid');
321
  $grant['gid'] = $gid;
322
  $grant['priority'] = content_access_get_settings('priority', $node->type);
323
  return $grant;
324
}
325
326
/**
327
 * Determines the grant for the node author and the given allowed roles of a operation.
328
 *
329
 * @param $any_roles
330
 *   The roles with which anybody has access (not optimized!)
331
 * @param $own_roles
332
 *   The roles with which only the author has acess (optimized!)
333
 */
334
function content_access_own_op($node, $any_roles, $own_roles) {
335
  static $roles = array();
336
337
  if (!isset($roles[$node->uid])) {
338
    $roles[$node->uid] = $node->uid ? array(DRUPAL_AUTHENTICATED_RID) : array(DRUPAL_ANONYMOUS_RID);
339
    $result = db_query('SELECT rid FROM {users_roles} WHERE uid = :uid', array(':uid' => $node->uid));
340
341
    foreach ($result as $role) {
342
      $roles[$node->uid][] = $role->rid;
343
    }
344
  }
345
  if (array_intersect($roles[$node->uid], $any_roles)) {
346
    // If there is access due to "any permissions" there is no need to at an author grant.
347
    return 0;
348
  }
349
  return array_intersect($roles[$node->uid], $own_roles) ? 1 : 0;
350
}
351
352
/**
353
 * Returns optimized role ids for the given operation and node to
354
 * grant access for.
355
 *
356
 * If to a role access is granted by permissions, it's not necessary
357
 * to write a grant for it. So it won't be returned.
358
 *
359
 * @param $op
360
 *   One of the supported operations.
361
 * @param $node
362
 *   The node object.
363
 */
364
function content_access_get_rids_per_node_op($op, $node) {
365
  $rids = content_access_per_node_setting($op, $node);
366
367
  if ($permission = content_access_get_permission_by_op($op, $node->type)) {
368
    $perm_roles = content_access_get_permission_access($permission);
369
    $rids = array_diff($rids, $perm_roles);
370
371
    if (in_array(DRUPAL_AUTHENTICATED_RID, $perm_roles)) {
372
      return in_array(DRUPAL_ANONYMOUS_RID, $rids) ? array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID) : array(DRUPAL_AUTHENTICATED_RID);
373
    }
374
  }
375
  return $rids;
376
}
377
378
379
/**
380
 * Returns the per node role settings. If no per node settings are available,
381
 * it will return the content type settings.
382
 *
383
 * @param $op
384
 *   One of the supported operations.
385
 * @param $node
386
 *   The node object.
387
 * @param $settings
388
 *    Optional array used to update the settings cache with the given settings.
389
 * @return
390
 *   An array of role ids which have access.
391
 */
392
function content_access_per_node_setting($op, $node, $settings = NULL) {
393
  static $grants = array();
394
395
  if (isset($settings)) {
396
    // Update settings cache
397
    $grants[$node->nid] = $settings;
398
    return;
399
  }
400
401
  if (!isset($grants[$node->nid]) || $grants[$node->nid] === FALSE) {
402
    $grants[$node->nid] = content_access_get_per_node_settings($node);
403
  }
404
405
  // Return the content type defaults if no per node settings are available
406
  return isset($grants[$node->nid][$op]) ? $grants[$node->nid][$op] : content_access_get_settings($op, $node->type);
407
}
408
409
/**
410
 * Gets the per node settings of a node.
411
 *
412
 * @note
413
 *   This function won't apply defaults, so if there are no other settings
414
 *   it will return an empty array.
415
 */
416
function content_access_get_per_node_settings($node) {
417
  foreach (db_query("SELECT settings FROM {content_access} WHERE nid = :nid", array(':nid' => $node->nid)) as $record) {
418
    $settings = $record->settings;
419
    if (!$settings) {
420
      return array();
421
    }
422
    return unserialize($settings);
423
  }
424
}
425
426
/**
427
 * Saves custom per node settings in the own content_access table.
428
 */
429
function content_access_save_per_node_settings($node, $settings) {
430
  $count = db_select('content_access')
431
            ->condition('nid', $node->nid)
432
            ->countQuery()->execute()->fetchField();
433
434
  if ($count > 0) {
435
    db_update('content_access')
436
      ->condition('nid', $node->nid)
437
      ->fields(array('settings' => serialize($settings)))
438
      ->execute();
439
  }
440
  else {
441
    db_insert('content_access')
442
      ->fields(array('nid' => $node->nid, 'settings' => serialize($settings)))
443
      ->execute();
444
  }
445
446
  // Make content_access_per_node_setting() use the new settings
447
  content_access_per_node_setting(NULL, $node, $settings);
448
}
449
450
/**
451
 * Deletes all custom per node settings, so that content type defaults are used again.
452
 */
453
function content_access_delete_per_node_settings($node) {
454
  db_delete('content_access')
455
    ->condition('nid', $node->nid)
456
    ->execute();
457
458
  // Clear the cache.
459
  content_access_per_node_setting(NULL, $node, FALSE);
460
461
  // Delete possible acl settings
462
  if (module_exists('acl')) {
463
    // @todo why content_access.admin.inc is not loaded before?
464
    module_load_include('inc', 'content_access', 'content_access.admin');
465
    foreach (array('view', 'update', 'delete') as $op) {
466
      $acl_id = content_access_get_acl_id($node, $op);
467
      acl_delete_acl($acl_id);
468
    }
469
  }
470
}
471
472
/**
473
 * Removes grants that doesn't change anything.
474
 *
475
 * @note
476
 *   The grants are compared with the normal access control settings.
477
 */
478
function content_access_optimize_grants(&$grants, $node) {
479
  $rids = array('view' => array(), 'update' => array(), 'delete' => array());
480
481
  foreach ($grants as $key => $grant) {
482
    foreach (array('view', 'update', 'delete') as $op) {
483
      if (is_numeric($key) && !empty($grant['grant_' . $op])) {
484
        $rids[$op][] = $key;
485
      }
486
    }
487
  }
488
489
  // Detect if all are allowed to view
490
  $all = array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID);
491
  if (count(array_diff($all, $rids['view'])) == 0) {
492
    //grant view access to all instead of single roles
493
    $rids['view'] = array('all');
494
    $grants['all'] = array('realm' => 'all', 'gid' => 0, 'grant_view' => 1, 'grant_update' => 0, 'grant_delete' => 0, 'priority' => content_access_get_settings('priority', $node->type));
495
  }
496
497
  // If authenticated users are involved, remove unnecessary other roles.
498
  foreach (array('view', 'update', 'delete') as $op) {
499
    if (in_array(DRUPAL_AUTHENTICATED_RID, $rids[$op])) {
500
      $rids[$op] = in_array(DRUPAL_ANONYMOUS_RID, $rids[$op]) ? array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID) : array(DRUPAL_AUTHENTICATED_RID);
501
    }
502
  }
503
504
  // Now let's remove unnecessary grants, if any.
505
  foreach ($grants as $key => $grant) {
506
    if (!is_numeric($key)) {
507
      continue;
508
    }
509
    foreach (array('view', 'update', 'delete') as $op) {
510
      if ($grant['grant_' . $op] && in_array($key, $rids[$op])) {
511
        //it's still here, so we can't remove this grant
512
        continue 2;
513
      }
514
    }
515
    //ok, remove it
516
    unset($grants[$key]);
517
  }
518
}
519
520
/**
521
 * Implements hook_node_type_delete().
522
 */
523
function content_access_node_type_delete($info) {
524
  variable_del('content_access_' . $info->type);
525
}
526
527
/**
528
 * Implements hook_node_type_update().
529
 *
530
 * Updates settings on node type name change.
531
 */
532
function content_access_node_type_update($info) {
533
  if (!empty($info->old_type) && $info->old_type != $info->type) {
534
    $settings = content_access_get_settings('all', $info->old_type);
535
    content_access_set_settings($settings, $info->type);
536
    variable_del('content_access_' . $info->old_type);
537
  }
538
}
539
540
/**
541
 * Implements hook_node_access_explain().
542
 */
543
function content_access_node_access_explain($row) {
544
  static $roles;
545
546
  if (!isset($roles)) {
547
    $roles = user_roles();
548
  }
549
  if (!$row->gid && $row->realm == 'content_access_rid') {
550
    return t('Content access: No access is granted.');
551
  }
552
  switch ($row->realm) {
553
    case 'content_access_author':
554
      return t('Content access: author of the content can access');
555
    case 'content_access_rid':
556
      return t('Content access: %role can access', array('%role' => $roles[$row->gid]));
557
  }
558
}
559
560
/**
561
 * Implements hook_form_alter().
562
 */
563
function content_access_form_alter(&$form, $form_state, $form_id) {
564
  if ($form_id == 'user_admin_perm') {
565
    module_load_include('inc', 'content_access', 'content_access.admin');
566
    $form['#submit'][] = 'content_access_user_admin_perm_submit';
567
  }
568
}
569
570
/**
571
 * Returns an array of possible operations on content and their labels.
572
 */
573
function _content_access_get_operations($type = NULL) {
574
  $operations = array(
575
    'view' => t('View any @type content', array('@type' => $type)),
576
    'view_own' => t('View own @type content', array('@type' => $type)),
577
    'update' => t('Edit any @type content', array('@type' => $type)),
578
    'update_own' => t('Edit own @type content', array('@type' => $type)),
579
    'delete' => t('Delete any @type content', array('@type' => $type)),
580
    'delete_own' => t('Delete own @type content', array('@type' => $type)),
581
  );
582
  return $operations;
583
}
584
585
/**
586
 * Formapi #process callback, that disables checkboxes for roles without access to content
587
 */
588
function content_access_disable_checkboxes($element) {
589
  $access_roles = content_access_get_permission_access('access content');
590
  $admin_roles = content_access_get_permission_access('administer nodes');
591
592
  foreach (element_children($element) as $key) {
593
    if (!in_array($key, $access_roles) &&
594
        $key == DRUPAL_ANONYMOUS_RID &&
595
        !in_array(DRUPAL_AUTHENTICATED_RID, $access_roles)) {
596
      $element[$key]['#disabled'] = TRUE;
597
      $element[$key]['#default_value'] = FALSE;
598
      $element[$key]['#prefix'] = '<span' . drupal_attributes(array('title' => t("This role is lacking the permission '@perm', so it has no access.", array('@perm' => t('access content'))))) . '>';
599
      $element[$key]['#suffix'] = "</span>";
600
    }
601
    elseif (in_array($key, $admin_roles) ||
602
            ($key != DRUPAL_ANONYMOUS_RID && in_array(DRUPAL_AUTHENTICATED_RID, $admin_roles))) {
603
      // Fix the checkbox to be enabled for users with administer node privileges
604
      $element[$key]['#disabled'] = TRUE;
605
      $element[$key]['#default_value'] = TRUE;
606
      $element[$key]['#prefix'] = '<span' . drupal_attributes(array('title' => t("This role has '@perm' permission, so access is granted.", array('@perm' => t('administer nodes'))))) . '>';
607
      $element[$key]['#suffix'] = "</span>";
608
    }
609
  }
610
611
  return $element;
612
}
613
614
/**
615
 * Gets node's access permissions.
616
 */
617
function _content_access_get_node_permissions($type) {
618
  return array_filter(array_map('content_access_get_permission_by_op', array_flip(_content_access_get_operations()), array_fill(0, 6, $type)));
619
}
620
621
/**
622
 * Gets the content access acl id of the node.
623
 */
624
function content_access_get_acl_id($node, $op) {
625
  $acl_id = acl_get_id_by_name('content_access', $op . '_' . $node->nid);
626
  if (!$acl_id) {
627
    $acl_id = acl_create_new_acl('content_access', $op . '_' . $node->nid);
628
  }
629
  return $acl_id;
630
}
631
632
/**
633
 * Detaches all our ACLs for the nodes of the given type.
634
 */
635
function _content_access_remove_acls($type) {
636
  $result = db_query("SELECT n.nid FROM {node} n WHERE type = :type", array('type' => $type));
637
  foreach ($result as $node) {
638
    acl_node_clear_acls($node->nid, 'content_access');
639
  }
640
}