Projet

Général

Profil

Paste
Télécharger (39,9 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / forum_access / forum_access.admin.inc @ e9734207

1
<?php
2

    
3
/**
4
 * @file
5
 * forum_access.admin.inc
6
 *
7
 * Include file for forum_access.module, containing (sub-)page handling
8
 * (form_alter) and batch code.
9
 *
10
 */
11

    
12
/**
13
 * Rewrite the forum administration page with our new access rules.
14
 */
15
function _forum_access_forum_form(&$form, &$form_state, $is_container) {
16
  $tid = (isset($form['tid']['#value']) ? $form['tid']['#value'] : NULL);
17
  if (isset($tid) && !forum_access_access('view', $tid, NULL, FALSE)) {
18
    drupal_access_denied(); // Deny access if the user doesn't have View access.
19
    drupal_exit();
20
  }
21

    
22
  if (isset($tid)) { // edit
23
    $template_tid = variable_get('forum_access_default_template_tid', 0);
24
    $settings = _forum_access_get_settings($tid);
25
  }
26
  else { // create
27
    $template_tid = variable_get('forum_access_new_template_tid', NULL);
28
    $settings = _forum_access_get_settings($template_tid);
29
  }
30
  $fa_priority = $settings['priority'];
31

    
32
  foreach (module_implements('node_access_records') as $module) {
33
    $na_modules[$module] = $module;
34
  }
35
  unset($na_modules['forum_access']);
36
  unset($na_modules['acl']);
37

    
38
  $form['forum_access'] = array(
39
    '#type' => 'fieldset',
40
    '#title' => t('Access control'),
41
    '#collapsible' => TRUE,
42
    '#tree' => TRUE,
43
  );
44

    
45
  $form['forum_access']['permissions'] = _forum_access_forum_permissions_form($is_container);
46
  $form['forum_access']['template'] = _forum_access_forum_template_form($template_tid);
47
  $form['forum_access']['#after_build'][] = '_forum_access_forum_form_after_build_template';
48
  $form['forum_access']['grants'] = _forum_access_forum_grants_form($form_state, $is_container, $settings);
49
  $form['forum_access']['acl'] = _forum_access_forum_acl_form($form_state, $tid);
50
  $form['forum_access']['interference'] = _forum_access_forum_interference_form($is_container, $na_modules, $fa_priority);
51
  $form['forum_access']['troubleshooting'] = _forum_access_forum_troubleshooting_form($is_container, count($na_modules));
52

    
53
  if (!$is_container && isset($tid) && !node_access_needs_rebuild()) {
54
    $count = db_query("SELECT COUNT(DISTINCT ti.nid) FROM {taxonomy_index} ti WHERE ti.tid = :tid", array(
55
      ':tid' => $tid,
56
    ))->fetchField();
57
    $limit = variable_get('forum_access_update_limit', 20); // for _node_access_rebuild_batch_operation()
58
    $threshold = variable_get('forum_access_batch_threshold', $limit); // change the variable if you want
59
    $form['forum_access']['update_limit'] = array(
60
      '#type' => 'value',
61
      '#value' => $limit,
62
    );
63
    $form['forum_access']['update_choice'] = array(
64
      '#type' => 'radios',
65
      '#title' => t('Update the permissions'),
66
      '#description' => t('<em>If</em> you make any node access changes, then each node in this forum needs to be updated. Hover over the radiobuttons for details.'),
67
      '#options' => array(0, 1, 2),
68
      0 => array(
69
        '#type' => 'radio',
70
        '#title' => t('for all %count nodes immediately', array('%count' => $count)),
71
        '#attributes' => array('title' => t('This option is the fastest, but with many nodes it can still take considerable time and memory. If it does not complete, it will leave your !node_access table in an inconsistent state.', array('!node_access' => '{node_access}'))),
72
        '#return_value' => 0,
73
        '#default_value' => ($count <= $threshold ? 0 : 1),
74
        '#parents' => array('forum_access', 'update_choice'),
75
      ),
76
      1 => array(
77
        '#type' => 'radio',
78
        '#title' => check_plain(t('in batches of !limit now', array('!limit' => $limit))),
79
        '#attributes' => array('title' => t('The batch option will always work reliably, but it takes longer to complete.')),
80
        '#return_value' => 1,
81
        '#default_value' => ($count <= $threshold ? 0 : 1),
82
        '#parents' => array('forum_access', 'update_choice'),
83
      ),
84
      2 => array(
85
        '#type' => 'radio',
86
        '#title' => t('rebuild <strong>all</strong> permissions later'),
87
        '#attributes' => array('title' => t("This option will only set a flag to remind you to rebuild all permissions later; this is useful if you want to make multiple changes to your node access settings quickly and delay the updating until you're done.")),
88
        '#return_value' => 2,
89
        '#default_value' => ($count <= $threshold ? 0 : 1),
90
        '#parents' => array('forum_access', 'update_choice'),
91
      ),
92
      '#attributes' => array('class' => array('forum-access-flowed')),
93
    );
94
  }
95
  if (isset($tid)) {
96
    $form['forum_access']['force_update'] = array(
97
      '#type' => 'checkbox',
98
      '#title' => t('Update even if unchanged'),
99
    );
100
  }
101

    
102
  // Move some stuff down so our block goes in a nice place.
103
  $form['submit']['#weight'] = 10;
104
  $form['delete']['#weight'] = 10;
105

    
106
  $form['#validate'][] = '_forum_access_form_validate';
107
  $form['#submit'][] = '_forum_access_form_submit';
108
}
109

    
110
function _forum_access_forum_permissions_form($is_container) {
111
  $tr = 't';
112
  $variables = array();
113
  foreach (array(
114
    'access content' => 'node',
115
    'access comments' => 'comment',
116
    'create forum content' => 'node',
117
    'post comments' => 'comment',
118
    'skip comment approval' => 'comment',
119
    'edit own forum content' => 'node',
120
    'edit any forum content' => 'node',
121
    'delete own forum content' => 'node',
122
    'delete any forum content' => 'node',
123
    'administer comments' => 'comment',
124
    'administer forums' => 'forum',
125
    'administer nodes' => 'node',
126
    'access content overview' => 'node',
127
    'view own unpublished content' => 'node',
128
    'edit own comments' => 'comment',
129
  ) as $perm => $module) {
130
    $variables += _forum_access_permission_link($module, $perm, '<em title="' . $perm . '">', '</em>');
131
  }
132
  if (!$is_container) {
133
    $form = array(
134
      '#type' => 'fieldset',
135
      '#title' => check_plain($tr('Permissions information')),
136
      '#collapsible' => TRUE,
137
      '#collapsed' => TRUE,
138
    );
139
    $form[] = array(
140
      '#type'    => 'markup',
141
      '#markup'  => '<div>' . t('Note that users need') . '<ul style="margin-top: 0"><li>' .
142
                              t('the !access_content and !access_comments permissions <strong>AND <em>View</em></strong> to be able to see this forum and its content at all,', $variables) . '</li><li>' .
143
                              t('the !create_forum_content (and similar) permissions <strong>AND <em>Post</em></strong> to be able to create forum content, and', $variables) . '</li><li>' .
144
                              t('the !post_comments (and optionally !skip_comment_approval) permission AND <em>Post</em> to be able to post comments/replies;', $variables) . '</li><li>' .
145
                              t('the !edit_own_forum_content or !edit_any_forum_content (and similar) permissions (<strong>OR <em>Edit</em></strong>) can be added if desired, <strong>plus</strong>', $variables) . '</li><li>' .
146
                              t('the !delete_own_forum_content or !delete_any_forum_content (and similar) permissions (<strong>OR <em>Delete</em></strong>) if desired;', $variables) . '</li><li>' .
147
                              t('the !administer_comments (global!) permission <strong>OR <em>Edit</em>/<em>Delete</em></strong> to be able to edit/delete comments;', $variables) . '</li><li>' .
148
                              t('the !administer_forums permission <strong>AND <em>View</em></strong> to be able to administer forums (and change access!).', $variables) . '</li></ul>' .
149
                              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 (with the !view_own_unpublished_content permission) or by users with the !administer_nodes permission. Unpublished comments and replies are accessible to users with <strong><em>Edit</em> OR <em>Delete</em></strong>, <strong>OR</strong> with the !administer_comments permission, but they are never counted on the forum page.', $variables) . '<br />' .
150
                              t('The global !edit_own_comments permission is ignored, but the edit/delete forum content permissions are extended to comments; the per-forum <strong><em>Edit</em></strong> and <strong><em>Delete</em></strong> apply to both nodes and comments, too.', $variables) . '</div>',
151
    );
152
    return $form;
153
  }
154
}
155

    
156
function _forum_access_forum_template_form($template_tid) {
157
  $tr = 't';
158
  // Load a template:
159
  $vid = _forum_access_get_vid();
160
  $form = array(
161
      '#type' => 'fieldset',
162
      '#title' => check_plain($tr('Template')),
163
      '#collapsible' => TRUE,
164
      '#collapsed' => empty($template_tid),
165
  );
166
  $form['taxonomy'][$vid] = _forum_parent_select(NULL, t('Template forum'), 'forum');
167
  $form['taxonomy'][$vid]['#required'] = FALSE;
168
  $form['taxonomy'][$vid]['#default_value'] = $template_tid;
169
  $form['taxonomy'][$vid]['#description'] = t("Select a forum and click !Load to retrieve that forum's settings as a starting point for this forum or container.", array('!Load' => '[' . t('Load') . ']'));
170
  $form['load_button'] = array(
171
    '#type'   => 'button',
172
    '#name'   => 'load_template',
173
    '#value'  => t('Load'),
174
    '#submit' => FALSE,
175
  );
176
  $form['template_tid'] = array(
177
    '#type'   => 'value',
178
    '#value'  => NULL,
179
  );
180
  $form['select_by_default'] = array(
181
    '#type'   => 'checkbox',
182
    '#title'  => t('Remember this selection.'),
183
    '#default_value' => FALSE,
184
  );
185
  $form['load_for_new'] = array(
186
    '#type'   => 'checkbox',
187
    '#title'  => t("Use the selected forum's settings as defaults for new forums and containers."),
188
    '#default_value' => FALSE,
189
  );
190
  return $form;
191
}
192

    
193
function _forum_access_forum_form_after_build_template($form, &$form_state) {
194
  if (isset($form_state['clicked_button']['#name']) && $form_state['clicked_button']['#name'] == $form['template']['load_button']['#name']) {
195
    // Load a setting from a template:
196
    $template_tid = reset($form_state['input']['forum_access']['template']['taxonomy']);
197
    $form_state['values']['forum_access']['template']['template_tid'] = $template_tid;
198
    $form['template']['#collapsed'] = FALSE;
199
    $form['template']['#attributes']['class'] = array('collapsible');
200

    
201
    $settings = _forum_access_get_settings($template_tid);
202
    foreach (array('view', 'create', 'update', 'delete') as $grant_type) {
203
      if (empty($form['grants']['checkboxes'][$grant_type])) {
204
        continue;
205
      }
206
      foreach (element_children($form['grants']['checkboxes'][$grant_type]) as $tid) {
207
        $checked = array_search($tid, $settings[$grant_type]) !== FALSE;
208
        $form['grants']['checkboxes'][$grant_type][$tid]['#value'] = ($checked ? $tid : 0);
209
        $form['grants']['checkboxes'][$grant_type][$tid]['#checked'] = ($checked ? TRUE : FALSE);
210
      }
211
    }
212
    $form['interference']['advanced']['priority']['#value'] = $settings['priority'];
213
    if ($settings['priority'] != 0) {
214
      $form['interference']['advanced']['#collapsed'] = FALSE;
215
    }
216
  }
217
  elseif (is_array(reset($form_state['values']['forum_access']['template']['taxonomy']))) {
218
    $template_tid = current(current($form_state['values']['forum_access']['template']['taxonomy']));
219
  }
220
  if (isset($template_tid)) {
221
    $form['template']['select_by_default']['#value'] = ($template_tid && $template_tid == variable_get('forum_access_default_template_tid', 0));
222
    $form['template']['load_for_new']['#value'] = ($template_tid && $template_tid == variable_get('forum_access_new_template_tid', 0));
223
  }
224
  return $form;
225
}
226

    
227
function _forum_access_forum_grants_form(&$form_state, $is_container, $settings) {
228
  $tr = 't';
229
  $roles = user_roles();
230
  $administer_forums_roles = user_roles(FALSE, 'administer forums');
231
  $bypass_node_access_roles = user_roles(FALSE, 'bypass node access');
232

    
233
  $form = array(
234
    '#type'       => 'item',
235
    '#theme'      => 'forum_access_table',
236
    //'#process'    => array('_forum_access_process_grant_form'), // too early
237
    //'#after_build'=> array('_forum_access_after_build_grant_form'), // too late
238
    //'#pre_render' => array('drupal_pre_render_markup', '_forum_access_pre_render_grant_form'), // too late
239
  );
240
  $forum_vocabulary = taxonomy_vocabulary_load(_forum_access_get_vid());
241
  if ($is_container) {
242
    $cols = array(
243
      'view'   => t('View this container'),
244
      'create' => t('See this container in the %Forums selection list', array('%Forums' => $forum_vocabulary->name)),
245
    );
246
  }
247
  else {
248
    $cols = array(
249
      'view'   => t('View this forum'),
250
      'create' => t('Post in this forum'),
251
      'update' => t('Edit posts'),
252
      'delete' => t('Delete posts'),
253
    );
254
  }
255
  $form['col_ids'] = array(
256
    '#type'  => 'value',
257
    '#value' => array_keys($cols),
258
  );
259
  foreach ($roles as $rid => $role) {
260
    $form['rows'][$rid] = array(
261
      '#type'        => 'item',
262
      '#markup'      => check_plain($role),
263
    );
264
    $special = NULL;
265
    if (isset($administer_forums_roles[$rid])) {
266
      $special = t("This role has the '@administer_forums' permission, and granting 'View' enables the role holders to change the settings on this page, including Access Control!", array('@administer_forums' => $tr('administer forums')));
267
      if (isset($bypass_node_access_roles[$rid])) {
268
        $special .= ' ' . t("Because the role also has the '@bypass_node_access' permission, it has full access to all nodes either way.", array('@bypass_node_access' => $tr('bypass node access')));
269
      }
270
    }
271
    elseif (isset($bypass_node_access_roles[$rid])) {
272
      $special = t("This role has the '@bypass_node_access' permission and thus full access to all nodes.", array('@bypass_node_access' => $tr('bypass node access')));
273
    }
274
    if (isset($special)) {
275
      $form['rows'][$rid] += array(
276
        '#prefix' => '<em><span title="' . $special . '" class="forum-access-special-role">',
277
        '#suffix' => '</span></em>',
278
        '#disabled' => TRUE,
279
      );
280
    }
281
    $roles[$rid] = '';
282
  }
283
  foreach ($cols as $cid => $ctitle) {
284
    $form['checkboxes'][$cid] = array(
285
      '#type'          => 'checkboxes',
286
      '#options'       => $roles,
287
      '#default_value' => $settings[$cid],
288
      '#process'       => array('form_process_checkboxes', '_forum_access_process_grant_form_checkboxes'),
289
    );
290
    $form['col_ids'][$cid] = array(
291
      '#markup' => $ctitle,
292
      '#tree'   => TRUE,
293
    );
294
  }
295
  $form_state['build_info']['files'][] = _forum_access_module_load_include('admin.inc');
296
  $form['info1'] = array(
297
    '#type'            => 'item',
298
    '#description'     => t('For explanations of <em class="placeholder">special cases</em>, hover your mouse over role names.'),
299
  );
300
  if ($is_container) {
301
    $form['info2'] = array(
302
      '#type'          => 'item',
303
      '#description'   => t('Users who can see any forum or container within this one should get the <strong><em>View</em></strong> grant. <br /> Users who can post to a forum within this container should get the <strong><em>See</em></strong> grant, so that the forum appears in the proper context in the %Forums selection list.', array('%Forums' => $forum_vocabulary->name)),
304
    );
305
  }
306

    
307
  drupal_add_css(drupal_get_path('module', 'forum_access') . '/forum_access.css', array('preprocess' => FALSE));
308
  return $form;
309
}
310

    
311
function _forum_access_process_grant_form_checkboxes($element, &$form_state, $form_state_complete) {
312
  $administer_forums_roles = user_roles(FALSE, 'administer forums');
313
  $bypass_node_access_roles = user_roles(FALSE, 'bypass node access');
314
  $access = end($element['#parents']);
315
  if ($access != 'create') {
316
    foreach (element_children($element) as $rid) {
317
      if (isset($bypass_node_access_roles[$rid]) && !($access == 'view' && isset($administer_forums_roles[$rid]))) {
318
        $element[$rid]['#disabled'] = TRUE;
319
        $element[$rid]['#default_value'] = TRUE;
320
      }
321
    }
322
  }
323
  return $element;
324
}
325

    
326
function _forum_access_forum_acl_form(&$form_state, $tid) {
327
  $tr = 't';
328
  // Find our moderator ACL:
329
  if (isset($tid)) { // edit, not new
330
    $acl_id = _forum_access_get_acl($tid);
331
    $form = acl_edit_form($form_state, $acl_id, t('Moderators'));
332
    $form[] = array(
333
      '#type'   => 'markup',
334
      '#markup' => '<div>' . t('Moderators receive all grants above.') . '</div>',
335
      '#weight' => -1,
336
    );
337
    $form['note'] = array(
338
      '#type'   => 'markup',
339
      '#markup' => '<div><span>' . t('Note: Changes to moderators are not saved until you click [!Save] below.', array('!Save' => $tr('Save'))) . '</span></div>',
340
    );
341
    $form['#after_build'][] = '_forum_access_forum_form_after_build_acl0';
342
    $form['#after_build'] = array_reverse($form['#after_build']);
343
    $form['#after_build'][] = '_forum_access_forum_form_after_build_acl2';
344
    return $form;
345
  }
346
}
347

    
348
function _forum_access_forum_form_after_build_acl0($element, &$form_state) {
349
  if (isset($form_state['input']['forum_access']['template']['taxonomy'])) {
350
    // Get ACL's user_list for the template and replace it before ACL's after_build function gets its shot at it.
351
    $template_tid = reset($form_state['input']['forum_access']['template']['taxonomy']);
352
    if ($acl_id = acl_get_id_by_number('forum_access', $template_tid)) {
353
      $f = _acl_edit_form($acl_id, 'DUMMY');
354
      $element['user_list']['#value'] = $f['user_list']['#default_value'];
355
    }
356
  }
357
  return $element;
358
}
359

    
360
function _forum_access_forum_form_after_build_acl2($element, &$form_state) {
361
  if (!count(unserialize($element['user_list']['#default_value'])) && !count(unserialize($element['user_list']['#value']))) {
362
    $element['#collapsed'] = TRUE;
363
    $element['#attributes']['class'][] = 'collapsed'; // We're already 'after build' and have to do this ourselves!
364
  }
365
  if ($element['user_list']['#default_value'] != $element['user_list']['#value']) {
366
    $element['note']['#markup'] = preg_replace('/<span>/', '<span class="warning">', $element['note']['#markup']);
367
  }
368
  return $element;
369
}
370

    
371
function _forum_access_forum_interference_form($is_container, $na_modules, $fa_priority) {
372
  $tr = 't';
373
  $l = 'l';
374
  $form = array();
375
  if (count($na_modules) && !$is_container) {
376
    $form = array(
377
      '#type'        => 'fieldset',
378
      '#title'       => t('Module interference'),
379
      '#collapsible' => TRUE,
380
    );
381
    $variables = array(
382
      '%content_type'   => node_type_get_name('forum'),
383
      '!Forum_Access'   => 'Forum Access',
384
      '!Content_Access' => $l('Content Access', 'http://drupal.org/project/content_access'),
385
      '@Content_Access' => 'Content Access',
386
      '!ACL'            => 'ACL',
387
      '!module_list'    => '<ul><li>' . implode($na_modules, '</li><li>') . '</li></ul>',
388
    );
389
    $description = t('Forums can contain other content types besides %content_type; !Forum_Access will contribute the grants defined above to every node in this forum, but other node access control modules may also contribute their grants, especially to nodes of types other than %content_type.', $variables);
390
    $form[] = array(
391
      '#type'        => 'item',
392
      '#markup'      => '<p>' . t("Besides !Forum_Access (and !ACL) you have installed the following node access module(s): !module_list   The grants of every module are combined for each node. Access can only be granted, not removed &mdash; if a certain module grants a permission, the other(s) cannot deny it.", $variables) . '</p>',
393
      '#description' => filter_xss($description),
394
    );
395

    
396
    if (module_exists('content_access')) {
397
      foreach (array('view', 'update', 'delete', 'per_node') as $type) {
398
        $value = content_access_get_settings($type, 'forum');
399
        if (!empty($value)) {
400
          $ca_interferes = TRUE;
401
        }
402
      }
403
      $ca_priority = content_access_get_settings('priority', 'forum');
404
      $is_conflict = (($ca_priority >= $fa_priority && !empty($ca_interferes)) || $ca_priority > $fa_priority);
405
      $variables += array(
406
        '!link'     => l(t('@Content_Access configuration for the %content_type type', $variables), 'admin/structure/types/manage/forum/access', array('html' => TRUE)),
407
        '%Advanced' => $tr('Advanced'),
408
      );
409
      $specifically = ($ca_priority == $fa_priority ? t('Specifically, any grants given by !Content_Access cannot be taken back by !Forum_Access.', $variables) : '');
410
      if ($is_conflict) {
411
        $form['by_content_access'] = array(
412
          '#type'        => 'fieldset',
413
          '#title'       => 'Content Access',
414
          '#collapsible' => FALSE,
415
          '#attributes'  => array('class' => array('error')),
416
        );
417
        $form['by_content_access'][] = array(
418
          '#markup' => '<div>' . t('You have set the !Content_Access module to control access to content of type %content_type&mdash;this can interfere with proper operation of !Forum_Access!', $variables) . " $specifically</div>",
419
        );
420
        if ($ca_priority == $fa_priority) {
421
          $form['by_content_access'][] = array(
422
            '#markup' => '<div>' . t("Unless you really know what you're doing, we recommend that you go to the !link page and clear all checkboxes. This will instruct @Content_Access to leave the %content_type nodes alone. However, if you put nodes of other content types into forums as well, then these content types will continue to have this problem.", $variables) . '</div>',
423
          );
424
        }
425
        else {
426
          $variables += array(
427
            '%ca_priority' => $ca_priority,
428
            '%fa_priority' => $fa_priority,
429
          );
430
          $form['by_content_access'][] = array(
431
            '#markup' => '<div>' . t("The priority of @Content_Access (%ca_priority) is higher than the priority of !Forum_Access (%fa_priority), which means the latter is <strong>completely disabled</strong> for the %content_type type! Unless you really know what you're doing, we recommend that you go to the !link page, change the priority (under %Advanced) to 0, and clear all checkboxes.", $variables) . '</div>',
432
          );
433
        }
434
        $form['by_content_access'][] = array(
435
          '#markup' => '<div>' . t("Alternatively, you can give !Forum_Access priority over @Content_Access by either raising the priority of !Forum_Access in every forum above the priority of @Content_Access, or by lowering the priority of @Content_Access for the content types in question below the priority of !Forum_Access.", $variables) . '</div>',
436
        );
437
      }
438
      else {
439
        $form[] = array(
440
          '#markup' => '<p>' . t('Note: You have installed the !Content_Access module, which has the capability to grant access to content that would otherwise be protected by !Forum_Access. Be careful when configuring @Content_Access!', $variables) . '</p>',
441
        );
442
      }
443
    }
444

    
445
    $form['advanced'] = array(
446
      '#type' => 'fieldset',
447
      '#title' => t('Advanced'),
448
      '#collapsible' => TRUE,
449
      '#collapsed' => !($fa_priority != 0),
450
    );
451
    $description = t("If you have no other node access control modules installed, you should leave this at the default 0. <br /> Otherwise you can raise or lower the priority of !Forum_Access' grants. Out of all the grants contributed to a node, only those with the highest priority are used, and all others are discarded.", $variables);
452
    $form['advanced']['priority'] = array(
453
      '#type' => 'weight',
454
      '#title' => check_plain(t('Priority of !Forum_Access node grants in this forum', $variables)),
455
      '#default_value' => $fa_priority,
456
      '#description' => filter_xss($description, array('br')),
457
    );
458
  }
459
  return $form;
460
}
461

    
462
function _forum_access_forum_troubleshooting_form($is_container, $has_interference) {
463
  if (!$is_container) {
464
    $tr = 't';
465
    $l = 'l';
466
    $variables = array(
467
      '!Forum_Access'        => $l('Forum Access', 'http://drupal.org/project/forum_access'),
468
      '!ACL'                 => $l('ACL', 'http://drupal.org/project/acl'),
469
      '%Module_interference' => t('Module interference'),
470
      '!Forum_Access-dev'    => $l('Forum&nbsp;Access&nbsp;7.x-1.x-dev', 'http://drupal.org/node/964638', array('html' => TRUE)),
471
      '!ACL-dev'             => $l('ACL&nbsp;7.x-1.x-dev', 'http://drupal.org/node/694210', array('html' => TRUE)),
472
      '%devel_node_access'   => 'devel_node_access',
473
      '!Devel'               => $l('Devel', 'http://drupal.org/project/devel'),
474
      '!DNA'                 => 'DNA',
475
      '!debug_mode'          => $l('debug mode', 'admin/config/development/devel', array('fragment' => 'edit-devel-node-access-debug-mode')),
476
      '!block'               => $l('block', 'admin/structure/block/list'),
477
      '!dna_summary'         => $l('devel/node_access/summary', 'devel/node_access/summary'),
478
      '!Rebuild_permissions' => $l($tr('Rebuild permissions'), 'admin/reports/status/rebuild'),
479
      '!Forum_Access_'       => $l('Forum Access', 'http://drupal.org/project/issues/forum_access'),
480
      '!ACL_'                => $l('ACL', 'http://drupal.org/project/issues/acl'),
481
    );
482
    $form = array(
483
      '#type' => 'fieldset',
484
      '#title' => t('Trouble-shooting node access'),
485
      '#collapsible' => TRUE,
486
      '#collapsed' => TRUE,
487
    );
488
    $form[] = array(
489
      '#type'    => 'item',
490
      '#markup'  => '<div>' . t("In case of problems, follow these steps until you've got it worked out:") . '<ol style="margin-top: 0"><li>' .
491
                              t("Update to the 'recommended' !Forum_Access and !ACL releases for your version of Drupal.", $variables) . '</li><li>' .
492
        ($has_interference ?  t("Read %Module_interference above and update your other node access modules.", $variables) . '</li><li>' : '') .
493
                              t("Check the release notes of the development snapshots for issues that might have been fixed in !Forum_Access-dev or !ACL-dev since the latest release.", $variables) . '</li><li>' .
494
                              t("Install the %devel_node_access module (!DNA, part of the !Devel module) and enable its !debug_mode: !DNA will show you all the grants that actually control your nodes in a footer block on each node's page.", $variables) . '</li><li>' .
495
                              t("Additional insight can be gained from !dna_summary and by enabling the second !DNA !block.", $variables) . '</li><li>' .
496
                              t("!Rebuild_permissions and check DNA for changes.", $variables) . '</li><li>' .
497
                              t("Check the issues queues of !Forum_Access_ and !ACL_ for existing reports and possible solutions.", $variables) . '</li><li>' .
498
                              t("If all of this hasn't helped, then pick ONE node that is misbehaving, look at it using an account that can see the node (and that should NOT have access if that's your problem!), create a new issue in the issues queue, describe the problem... <ul><li> what did you do? </li><li> what did you expect? </li><li> what happened instead? </li></ul> ... and <strong>attach a screenshot of all the DNA records</strong> for that one node. <br /> Be sure to indicate paths (URLs) for every page and module that you mention.") . '</li></ol><br />' .
499
                              t("Note: You should not keep the !Devel module enabled on a production site.", $variables) . '</div>',
500
    );
501
    return $form;
502
  }
503
}
504

    
505
function _forum_access_permission_link($module, $permission, $prefix = '', $postfix = '') {
506
  $permissions = &drupal_static(__FUNCTION__, array());
507
  if (empty($permissions[$module])) {
508
    $permissions[$module] = module_invoke($module, 'permission');
509
  }
510
  $key = '!' . str_replace(' ', '_', $permission);
511
  $value = $prefix . l($permissions[$module][$permission]['title'], 'admin/people/permissions', array('fragment' => "module-$module", 'html' => TRUE)) . $postfix;
512
  return array($key => $value);
513
}
514

    
515
function theme_forum_access_table($variables) {
516
  $tr = 't';
517
  $form = $variables['form'];
518
  foreach (element_children($form['rows']) as $key) {
519
    $row = array();
520
    $row[] = array(
521
      'data' => drupal_render($form['rows'][$key]),
522
      'class' => array('grant'),
523
    );
524
    foreach (element_children($form['checkboxes']) as $cid) {
525
      $row[] = array('data' => drupal_render($form['checkboxes'][$cid][$key]), 'class' => array('checkbox'), 'title' => $form['rows'][$key]['#markup']);
526
    }
527
    $rows[] = $row;
528
  }
529
  $header[] = ($tr('Roles'));
530
  foreach (element_children($form['col_ids']) as $cid) {
531
    $header[] = array('data' => drupal_render($form['col_ids'][$cid]), 'class' => array('checkbox'));
532
  }
533
  $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'grants')));
534
  $output .= drupal_render_children($form);
535
  return $output;
536
}
537

    
538
function _forum_access_form_validate($form, &$form_state) {
539
  global $user;
540

    
541
  if (user_access('bypass node access', $user)) {
542
    return;
543
  }
544
  $access = $form_state['values']['forum_access']['grants']['checkboxes']; // shortcut
545
  foreach ($access['view'] as $rid => $checked) {
546
    if ($checked && isset($user->roles[$rid])) {
547
      return;
548
    }
549
  }
550
  form_set_error('forum_access][view', t('You must assign %View access to a role that you hold.', array('%View' => 'View')));
551
}
552

    
553
function _forum_access_form_submit($form, &$form_state) {
554
  $fa_values = $form_state['values']['forum_access']; // shortcut
555
  $access = $fa_values['grants']['checkboxes']; // shortcut
556

    
557
  // Save template choice.
558
  $template_tid = reset($fa_values['template']['taxonomy']);
559
  if ($fa_values['template']['select_by_default']) {
560
    variable_set('forum_access_default_template_tid', $template_tid);
561
  }
562
  elseif (variable_get('forum_access_default_template_tid', 0) == $template_tid) {
563
    variable_del('forum_access_default_template_tid');
564
  }
565
  if ($fa_values['template']['load_for_new']) {
566
    variable_set('forum_access_new_template_tid', $template_tid);
567
  }
568
  elseif (variable_get('forum_access_new_template_tid', 0) == $template_tid) {
569
    variable_del('forum_access_new_template_tid');
570
  }
571

    
572
  // Check for changes.
573
  $is_changed = $is_new = strpos(current_path(), 'admin/structure/forum/add/') === 0;
574
  $is_changed = $is_changed || !empty($fa_values['force_update']);
575
  $form_initial_values = $form; // avoid Coder warning
576
  $form_initial_values = $form_initial_values['forum_access'];
577
  foreach ($fa_values['grants']['col_ids'] as $grant_type) {
578
    $defaults = $form_initial_values['grants']['checkboxes'][$grant_type]['#default_value'];
579
    $defaults = array_flip($defaults);
580
    foreach ($access[$grant_type] as $rid => $checked) {
581
      $is_changed = $is_changed || (empty($form_initial_values['grants']['checkboxes'][$grant_type][$rid]['#disabled']) && (!empty($checked) != isset($defaults[$rid])));
582
    }
583
  }
584
  if (!$is_changed && $fa_values['acl']['user_list'] == $form_initial_values['acl']['user_list']['#default_value'] && (empty($fa_values['interference']) || $fa_values['interference']['advanced']['priority'] == $form_initial_values['interference']['advanced']['priority']['#default_value'])) {
585
    drupal_set_message(t('The content access permissions are unchanged.'));
586
    return;
587
  }
588

    
589
  // Remove and re-create records.
590
  $tid = $form_state['values']['tid'];
591
  db_delete('forum_access')
592
    ->condition('tid', $tid)
593
    ->execute();
594

    
595
  $fa_priority = isset($fa_values['interference']['advanced']['priority']) ? $fa_values['interference']['advanced']['priority'] : 0;
596
  if (array_key_exists('acl', $fa_values)) {
597
    $moderators = unserialize($fa_values['acl']['user_list']);
598
    acl_save_form($fa_values['acl'], $fa_priority);
599
  }
600
  $insert = db_insert('forum_access');
601
  $insert->fields(array('tid', 'rid', 'grant_view', 'grant_update', 'grant_delete', 'grant_create', 'priority'));
602
  foreach ($access['view'] as $rid => $checked) {
603
    if (isset($permissions[$rid]['bypass node access'])) {
604
      // We prefer not to save records for these roles, because they have access anyway.
605
      if (isset($permissions[$rid]['administer forums']) && $access['view'][$rid]) {
606
        // For forum administrators, View needs to be saved, ...
607
      }
608
      else {
609
        // ... otherwise forget View.
610
        $access['view'][$rid] = FALSE;
611
      }
612
      if ($access['view'][$rid] || $access['create'][$rid]) {
613
          $insert->values(array(
614
          'tid'          => $tid,
615
          'rid'          => $rid,
616
          'grant_view'   => (int) (!empty($access['view'][$rid]) && empty($form_initial_values['grants']['checkboxes']['view'][$rid]['#disabled'])),
617
          'grant_create' => (int) (!empty($access['create'][$rid])),
618
          'priority'     => (int) $fa_priority,
619
        ));
620
      }
621
    }
622
    else {
623
      $insert->values(array(
624
        'tid'          => $tid,
625
        'rid'          => $rid,
626
        'grant_view'   => (int) ($checked && empty($form_initial_values['grants']['checkboxes']['view'][$rid]['#disabled'])),
627
        'grant_update' => (int) (!empty($access['update'][$rid]) && empty($form_initial_values['grants']['checkboxes']['update'][$rid]['#disabled'])),
628
        'grant_delete' => (int) (!empty($access['delete'][$rid]) && empty($form_initial_values['grants']['checkboxes']['delete'][$rid]['#disabled'])),
629
        'grant_create' => (int) (!empty($access['create'][$rid])),
630
        'priority'     => (int) $fa_priority,
631
      ));
632
    }
633
  }
634
  $insert->execute();
635
  $tr = 't';
636
  $link = l($tr('edit'), 'admin/structure/forum/edit/forum/' . $tid);
637
  watchdog('access', 'Changed grants for %forum forum.', array('%forum' => $form_state['values']['name']), WATCHDOG_NOTICE, $link);
638

    
639
  if ($form_state['values']['form_id'] != 'forum_form_container') {
640
    if ($is_new) {
641
      $acl_id = acl_create_new_acl('forum_access', NULL, $tid);
642
    }
643
    else {
644
      if (!isset($fa_values['update_choice']) || $fa_values['update_choice'] == 2) {
645
        node_access_needs_rebuild(TRUE);
646
      }
647
      elseif ($fa_values['update_choice'] == 0) {
648
        // update immediately (but use the batch functions anyway
649
        $save_redirect = $form_state['redirect'];
650
        $form_state['redirect'] = current_path();
651
        $context = array();
652
        $pending_error_messages = drupal_get_messages('error', FALSE);
653
        $our_error_message_index = (isset($pending_error_messages['error']) ? count($pending_error_messages['error']) : 0);
654
        _forum_access_update_batch_finished(FALSE, array(), array()); // add our error message (in case we die underway)
655
        _forum_access_update_batch_operation($tid, 999999, 1, $context);
656
        $pending_error_messages = drupal_get_messages('error', TRUE); // still alive, get and clear all 'error' messages
657
        unset($pending_error_messages['error'][$our_error_message_index]); // remove our error message
658
        $drupal_set_message = 'drupal_set_message';
659
        foreach ($pending_error_messages['error'] as $message) { // replay any others
660
          $drupal_set_message($message, 'error');
661
        }
662
        _forum_access_update_batch_finished(TRUE, array(), array());
663
        $form_state['redirect'] = $save_redirect;
664
      }
665
      else {
666
        // mass update in batch mode, modeled after node.module
667
        $limit = $fa_values['update_limit'];
668
        $count = db_query("SELECT COUNT(DISTINCT ti.nid) FROM {taxonomy_index} ti WHERE ti.tid = :tid", array(
669
          ':tid' => (int) $tid,
670
        ))->fetchField();
671
        $batch = array(
672
          'title' => t('Updating content access permissions'),
673
          'file' => drupal_get_path('module', 'forum_access') . '/forum_access.admin.inc',
674
          'operations' => array(
675
            array('_forum_access_update_batch_operation', array($tid, $limit, $count)),
676
          ),
677
          'finished' => '_forum_access_update_batch_finished'
678
        );
679
        batch_set($batch);
680
      }
681
    }
682
  }
683
  variable_del('forum_access_rids'); // clear cache
684
}
685

    
686
/**
687
 * Batch operation for forum_access_form_submit().
688
 *
689
 * This is a multistep operation: we go through all nodes by packs of 20.
690
 * The batch processing engine interrupts processing and sends progress
691
 * feedback after 1 second execution time.
692
 */
693
function _forum_access_update_batch_operation($tid, $limit, $count, &$context) {
694
  if (empty($context['sandbox'])) {
695
    // Initiate multistep processing.
696
    $context['sandbox']['progress'] = 0;
697
    $context['sandbox']['current_node'] = 0;
698
    $context['sandbox']['max'] = $count;
699
  }
700

    
701
  // Process the next $limit nodes.
702
  $nids = db_select('taxonomy_index', 'n')
703
    ->fields('n', array('nid'))
704
    ->distinct()
705
    ->condition('n.nid', $context['sandbox']['current_node'], '>')
706
    ->condition('n.tid', $tid)
707
    ->orderBy('n.nid')
708
    ->range(0, $limit)
709
    ->execute()
710
    ->fetchCol();
711
  $nodes = node_load_multiple($nids);
712
  foreach ($nodes as $node) {
713
    // To preserve database integrity, only aquire grants if the node
714
    // loads successfully.
715
    if (!empty($node)) {
716
      node_access_acquire_grants($node);
717
    }
718
    $context['sandbox']['progress']++;
719
    $context['sandbox']['current_node'] = $node->nid;
720
  }
721

    
722
  // Multistep processing: report progress.
723
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
724
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
725
  }
726
}
727

    
728
/**
729
 * Post-processing for forum_access_form_submit().
730
 */
731
function _forum_access_update_batch_finished($success, $results, $operations) {
732
  if ($success) {
733
    drupal_set_message(t('The content access permissions have been updated.'));
734
    cache_clear_all();
735
  }
736
  else {
737
    drupal_set_message(t('The content access permissions have not been properly updated.'), 'error');
738
  }
739
}
740

    
741
/**
742
 * Helper function to retrieve the settings for a forum.
743
 */
744
function _forum_access_get_settings($tid = NULL) {
745
  $return = array('view' => array(), 'create' => array(), 'update' => array(), 'delete' => array(), 'priority' => 0);
746
  if (!isset($tid)) {
747
    // Default to all users can read; all logged in users can post.
748
    $return['view'] = array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID);
749
    $return['create'] = array(DRUPAL_AUTHENTICATED_RID);
750
  }
751
  else {
752
    $result = db_query("SELECT * FROM {forum_access} where tid = :tid", array(
753
      ':tid' => $tid,
754
    ));
755
    foreach ($result as $access) {
756
      if ($access->grant_view) {
757
        $return['view'][] = $access->rid;
758
      }
759
      if ($access->grant_update) {
760
        $return['update'][] = $access->rid;
761
      }
762
      if ($access->grant_delete) {
763
        $return['delete'][] = $access->rid;
764
      }
765
      if ($access->grant_create) {
766
        $return['create'][] = $access->rid;
767
      }
768
      if ($access->rid == DRUPAL_AUTHENTICATED_RID) { // this is our reference
769
        $return['priority'] = $access->priority;
770
      }
771
    }
772
  }
773
  return $return;
774
}
775

    
776
/**
777
 * Remove unusable 'edit' links from overview form.
778
 */
779
function _forum_access_forum_overview(&$form, &$form_state) {
780
  global $user;
781
  if (user_access('bypass node access', $user)) {
782
    return;
783
  }
784
  foreach ($form as $key => $value) {
785
    if (preg_match('#^tid:(.*):0$#', $key, $matches)) {
786
      if (!forum_access_access('view', $matches[1], NULL, FALSE)) {
787
        $form[$key]['edit']['#access'] = FALSE;
788
        $form[$key]['view'] = array('#markup' => $form[$key]['view']['#title']);
789
      }
790
    }
791
  }
792
}
793

    
794
/**
795
 * Add warnings on Content Access admin forms where CA wants
796
 * to control the same content types as we do.
797
 */
798
function _forum_access_content_access_admin_form($bundle) {
799
  $tr = 't';
800
  $l = 'l';
801
  $variables = array(
802
    '!Content_Access' => 'Content Access',
803
    '!Forum_Access' => 'Forum Access',
804
    '!Forum_Access_link' => $l('Forum Access', 'admin/structure/forum'),
805
    '%anonymous_user' => $tr('anonymous user'),
806
    '%authenticated_user' => $tr('authenticated user'),
807
    '%Advanced' => $tr('Advanced'),
808
  );
809
  $dsm = 'drupal_set_message';
810
  if ($bundle->type == 'forum') {
811
    $dsm(t('Note: In Drupal, access can only be granted, not taken away. Whatever access you grant here will not be reflected in the !Forum_Access_link settings, but !Forum_Access can only allow <i>more</i> access, not less.', $variables)
812
      . '<br /><span class="error">' . t('Specifically, any rights granted to the %anonymous_user and/or the %authenticated_user will <b>override</b> the settings of !Forum_Access!', $variables) . '</span>'
813
      . '<br />' . t('To avoid conflicts with !Forum_Access settings, you may want to lower the priority of !Content_Access (under %Advanced below) below the priority of !Forum_Access for the content types that you want to be controlled by !Forum_Access.', $variables), 'warning');
814
  }
815
  else {
816
    $instances = field_info_instances('node', $bundle->type);
817
    if (isset($instances['taxonomy_forums'])) {
818
      $dsm(t('Note: Nodes of this content type can be put inside forums, where access to them will also be controlled by !Forum_Access.<br />In Drupal, access can only be granted, not taken away. Whatever access you grant here will not be reflected on the !Forum_Access_link settings, and vice versa, but any node access module can only allow <i>more</i> access, not less.', $variables), 'warning');
819
    }
820
  }
821
}
822