Projet

Général

Profil

Paste
Télécharger (24,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / forum_access / forum_access.module @ 136a805a

1
<?php
2

    
3
/**
4
 * @file
5
 * forum_access.module
6
 *
7
 * This module uses form_alter to add permissions and moderator settings to
8
 * forums.
9
 *
10
 */
11

    
12
/**
13
 * Implements hook_requirements().
14
 *
15
 * Remind the user to upgrade to Chain Menu Access API 2.x.
16
 */
17
function forum_access_requirements($phase) {
18
  require_once DRUPAL_ROOT . '/includes/install.inc';
19
  $result = array();
20
  switch ( $phase ) {
21
    case 'update':
22
    case 'runtime':
23
      $t = get_t();
24
      $path = drupal_get_filename('module', 'chain_menu_access');
25
      $path = substr($path, 0, strlen($path) - 7) . '.info';
26
      $info = drupal_parse_info_file($path);
27
      $version = (isset($info['version']) ? $info['version'] : $t('Unknown'));
28
      $found = preg_match('/7\.x-([0-9]*)\./', $version, $matches);
29
      if ($found && $matches[1] == 1 || !$found) {
30
        $cma = 'Chain Menu Access API';
31
        $variables = array('@Chain_Menu_Access_API' => $cma, '@module' => url('admin/modules'));
32
        $result[] = array(
33
          'title'       => $t('@Chain_Menu_Access_API module', $variables),
34
          'value'       => $version,
35
          'description' => $t('Version 1.x is obsolete. Upgrade to version 2.x as soon as <em>all</em> installed client modules support that version.') . '<br />' .
36
                           $t('Check the <a href="@module">module administration page</a> to find out which of your modules depend on @Chain_Menu_Access_API.', $variables),
37
          'severity'    => REQUIREMENT_WARNING,
38
        );
39
      }
40
  }
41
  return $result;
42
}
43

    
44
/**
45
 * Implements hook_menu_alter().
46
 *
47
 * Remove the 'Forum' menu item if no forums are visible.
48
 */
49
function forum_access_menu_alter(&$items) {
50
  $requirements = forum_access_requirements('runtime');
51
  if (!empty($requirements) && $requirements[0]['value'] != t('Unknown')) {
52
    // Fall back to obsolete Chain Menu Access API version 7.x-1.x,
53
    // because that's what's installed.
54
    chain_menu_access_chain($items['forum'],                 'forum_access_view_any_forum', array());
55
    chain_menu_access_chain($items['node/%node'],            '_forum_access_node_access_callback', array(1, 'view'));
56
    chain_menu_access_chain($items['comment/%comment/edit'], '_forum_access_comment_edit_callback', array(1));
57
    chain_menu_access_chain($items['comment/%comment/edit'], '_forum_access_comment_access_callback', array(1, 2), TRUE);
58
    chain_menu_access_chain($items['comment/%/delete'],      '_forum_access_comment_access_callback', array(1, 2), TRUE);
59
    chain_menu_access_chain($items['comment/%/approve'],     '_forum_access_comment_access_callback', array(1, 2), TRUE);
60
    chain_menu_access_chain($items['comment/reply/%node'],   '_forum_access_comment_access_callback', array(2, 1));
61
  }
62
  else {
63
    chain_menu_access_chain($items, 'forum',                 'forum_access_view_any_forum');
64
    chain_menu_access_chain($items, 'node/%node',            '_forum_access_node_access_callback', array(1, 'view'));
65
    chain_menu_access_chain($items, 'comment/%comment/edit', '_forum_access_comment_edit_callback', array(1));
66
    chain_menu_access_chain($items, 'comment/%comment/edit', '_forum_access_comment_access_callback', array(1, 2), TRUE);
67
    chain_menu_access_chain($items, 'comment/%/delete',      '_forum_access_comment_access_callback', array(1, 2), TRUE);
68
    chain_menu_access_chain($items, 'comment/%/approve',     '_forum_access_comment_access_callback', array(1, 2), TRUE);
69
    chain_menu_access_chain($items, 'comment/reply/%node',   '_forum_access_comment_access_callback', array(2, 1));
70
  }
71
}
72

    
73
function _forum_access_node_access_callback($node, $op) {
74
  global $user;
75

    
76
  if ($op == 'view' && ($tid = _forum_access_get_tid($node))) {
77
    if (forum_access_access('update', $tid) || forum_access_access('delete', $tid)) {
78
      // Add the 'administer comments' permission to the user so that he can
79
      // see unpublished comments.
80
      forum_access_enable_moderator();
81
    }
82
    // The 'view' access check will be done by core.
83
  }
84
  return TRUE;
85
}
86

    
87
function _forum_access_comment_edit_callback($comment) {
88
  global $user;
89

    
90
  // This callback is governed by AND, return TRUE by default.
91
  $node = node_load($comment->nid);
92
  if ($tid = _forum_access_get_tid($node)) {
93
    if (!forum_access_is_moderator($user, $tid) && !forum_access_access('update', $tid, $user) && !user_access('administer comments') && !($user->uid == $comment->uid && user_access('edit own forum content'))) {
94
      return FALSE;
95
    }
96
  }
97
  return TRUE;
98
}
99

    
100
function _forum_access_comment_access_callback($comment, $op) {
101
  global $user;
102

    
103
  if ($op == 'reply') {
104
    // 'reply' is governed by AND, return TRUE by default.
105
    $node = $comment;
106
    if ($tid = _forum_access_get_tid($node)) {
107
      return forum_access_access('create', $tid);
108
    }
109
    return TRUE;
110
  }
111

    
112
  if (is_numeric($comment)) {
113
    $comment = comment_load($comment);
114
    if (empty($comment)) {
115
      return FALSE;
116
    }
117
  }
118
  $node = node_load($comment->nid);
119

    
120
  // The remaining $ops are governed by OR, return FALSE by default.
121
  if ($tid = _forum_access_get_tid($node)) {
122
    if ($op == 'approve') {
123
      return $user->uid == 1 || forum_access_is_moderator($user, $tid);
124
    }
125
    if (!user_access('administer comments')) {
126
      if ($op == 'edit' && (forum_access_access('update', $tid) || user_access('edit any forum content') || ($user->uid == $comment->uid && user_access('edit own forum content')))) {
127
        forum_access_enable_moderator();
128
        return TRUE;
129
      }
130
      if ($op == 'delete' && (forum_access_access('delete', $tid) || user_access('delete any forum content') || ($user->uid == $comment->uid && user_access('delete own forum content')))) {
131
        return TRUE;
132
      }
133
    }
134
  }
135
  return FALSE;
136
}
137

    
138
/**
139
 * Access callback for the 'forum' menu item.
140
 *
141
 * Returns 1 if the user has at least one role that can access
142
 * at least one forum, 2 if the user is moderator in at least one forum,
143
 * FALSE otherwise.
144
 */
145
function forum_access_view_any_forum($account = NULL) {
146
  global $user;
147
  $returns = &drupal_static(__FUNCTION__, array());
148

    
149
  if (!isset($account)) {
150
    $account = $user;
151
  }
152

    
153
  if (!isset($returns[$account->uid])) {
154
    if (user_access('bypass node access', $account)) {
155
      return $returns[$account->uid] = 1;
156
    }
157
    if (!user_access('access content')) {
158
      return $returns[$account->uid] = FALSE;
159
    }
160
    $rids = variable_get('forum_access_rids', NULL);
161
    if (!isset($rids)) {
162
      $rids = db_query("SELECT fa.rid FROM {forum_access} fa WHERE fa.grant_view > 0 GROUP BY fa.rid")->fetchCol();
163
      variable_set('forum_access_rids', $rids);
164
    }
165
    foreach ($rids as $rid) {
166
      if (isset($account->roles[$rid])) {
167
        return $returns[$account->uid] = 1;
168
      }
169
    }
170
    // Check moderator, too.
171
    $query = db_select('acl', 'acl');
172
    $query->join('acl_user', 'aclu', "acl.acl_id = aclu.acl_id");
173
    $count = $query
174
      ->fields('acl', array('number'))
175
      ->condition('acl.module', 'forum_access')
176
      ->condition('aclu.uid', $account->uid)
177
      ->countQuery()
178
      ->execute()
179
      ->fetchField();
180
    $returns[$account->uid] = ($count > 0 ? 2 : FALSE);
181
  }
182
  return $returns[$account->uid];
183
}
184

    
185
/**
186
 * Implements hook_node_grants().
187
 *
188
 * This function supplies the forum access grants. forum_access simply uses
189
 * roles as ACLs, so rids translate directly to gids.
190
 */
191
function forum_access_node_grants($user, $op) {
192
  $grants['forum_access'] = array_keys($user->roles);
193
  return $grants;
194
}
195

    
196
/**
197
 * Implements hook_node_access_records().
198
 *
199
 * Returns a list of grant records for the passed in node object.
200
 * Checks to see if maybe we're being disabled.
201
 */
202
function forum_access_node_access_records($node) {
203
  if (!forum_access_enabled()) {
204
    return;
205
  }
206

    
207
  static $seers;
208

    
209
  $grants = &drupal_static(__FUNCTION__, array());
210
  $seers = &drupal_static(__FUNCTION__ . '__seers');
211
  $tid = _forum_access_get_tid($node);
212

    
213
  // Set proper grants for nodecomment comment nodes.
214
  //if (isset($node->comment_target_nid)) {
215
  //  if ($changed_tid = _forum_access_changed_tid()) {
216
  //    $tid = $changed_tid; // the topic node hasn't been saved yet!
217
  //  }
218
  //  else {
219
  //    $node = node_load($node->comment_target_nid);
220
  //    $tid = _forum_access_get_tid($node);
221
  //  }
222
  //}
223

    
224
  if ($tid) {
225
    if (!isset($grants[$tid])) {
226
      if (!isset($seers)) {
227
        $seers = user_roles(FALSE, 'bypass node access');
228
      }
229
      $result = db_query('SELECT * FROM {forum_access} WHERE tid = :tid', array(
230
        ':tid' => $tid
231
      ));
232
      foreach ($result as $grant) {
233
        if (isset($seers[$grant->rid])) {
234
          continue; // Don't provide any useless grants!
235
        }
236
        $grants[$tid][] = array(
237
          'realm'        => 'forum_access',
238
          'gid'          => $grant->rid,
239
          'grant_view'   => $grant->grant_view,
240
          'grant_update' => $grant->grant_update,
241
          'grant_delete' => $grant->grant_delete,
242
          'priority'     => $grant->priority,
243
        );
244
      }
245
      //dsm("forum_access_node_access_records($node->nid) (tid=$tid) returns ". var_export($grants[$tid], TRUE), 'status');
246
    }
247
    if (isset($grants[$tid])) {
248
      return $grants[$tid];
249
    }
250
  }
251
}
252

    
253
/**
254
 * Implements hook_form_alter().
255
 *
256
 * Alter the node/comment create/edit forms and various admin forms.
257
 */
258
function forum_access_form_alter(&$form, &$form_state, $form_id) {
259
  //dpm($form, "form_id($form_id)");
260
  if ($form_id == 'forum_node_form' && !empty($form['#node_edit_form'])) {
261
    _forum_access_module_load_include('node.inc');
262
    _forum_access_node_form($form, $form_state);
263
  }
264
  elseif ($form['#id'] == 'comment-form') {
265
    _forum_access_module_load_include('node.inc');
266
    _forum_access_comment_form($form, $form_state);
267
  }
268
  elseif ($form_id == 'forum_overview') {
269
    _forum_access_module_load_include('admin.inc');
270
    _forum_access_forum_overview($form, $form_state);
271
  }
272
  elseif ($form_id == 'forum_form_container') {
273
    _forum_access_module_load_include('admin.inc');
274
    _forum_access_forum_form($form, $form_state, TRUE);
275
  }
276
  elseif ($form_id == 'forum_form_forum') {
277
    _forum_access_module_load_include('admin.inc');
278
    _forum_access_forum_form($form, $form_state, FALSE);
279
  }
280
  elseif ($form_id == 'content_access_admin_settings' && empty($_POST)) {
281
    _forum_access_module_load_include('admin.inc');
282
    _forum_access_content_access_admin_form($form_state['build_info']['args'][0]);
283
  }
284
}
285

    
286
/**
287
 * Implements hook_comment_load().
288
 */
289
function forum_access_comment_load($comments) {
290
  //TODO: Investigate usefulness of this hook.
291
  return;
292
}
293

    
294
/**
295
 * Implements hook_query_alter().
296
 */
297
function forum_access_query_alter($p1, $p2, $p3) {
298
  //TODO: Investigate usefulness of this hook.
299
  return;
300
}
301

    
302
function forum_access_query_term_access_alter(QueryAlterableInterface $query) {
303
  global $user;
304

    
305
  // Read meta-data from query, if provided.
306
  if (!$account = $query->getMetaData('account')) {
307
    $account = $user;
308
  }
309
  if (!$op = $query->getMetaData('op')) {
310
    $op = 'view';
311
  }
312

    
313
  // If $account can bypass node access, or there are no node access
314
  // modules, we don't need to alter the query.
315
  if (user_access('bypass node access', $account)) {
316
    return;
317
  }
318

    
319
  // Prevent duplicate records.
320
  $query->distinct();
321

    
322
  // Find all instances of the {taxonomy_term_data} table being joined --
323
  // could appear more than once in the query, and could be aliased.
324
  // Join each one to the forum_access table.
325

    
326
  $tables = $query->getTables();
327
  $rids = array_keys($account->roles);
328
  foreach ($tables as $talias => $tableinfo) {
329
    $table = $tableinfo['table'];
330
    if (!($table instanceof SelectQueryInterface) && $table == 'taxonomy_term_data') {
331
      // The node_access table has the access grants for any given node.
332
      $access_alias = $query->leftJoin('forum_access', 'fa', '%alias.tid = ' . $talias . '.tid');
333
      $acl_alias = $query->leftJoin('acl', 'acl', "%alias.number = $talias.tid AND %alias.module = 'forum_access'");
334
      $aclu_alias = $query->leftJoin('acl_user', 'aclu', "%alias.acl_id = $acl_alias.acl_id AND %alias.uid = $account->uid");
335
      $query->condition(db_or()
336
        ->isNull("$access_alias.rid")
337
        ->condition(db_and()
338
          ->condition("$access_alias.rid", $rids, 'IN')
339
          ->condition("$access_alias.grant_$op", 1, '>='))
340
        ->condition("$aclu_alias.uid", $account->uid));
341
    }
342
  }
343
}
344

    
345
/**
346
 * Implements hook_node_presave().
347
 */
348
function forum_access_node_presave($node, $return_old_tid = FALSE) {
349
  $old_tid = &drupal_static('forum_access_node_presave');
350
  if (_forum_node_check_node_type($node)) {
351
    $old_tid = db_query('SELECT tid FROM {forum} WHERE nid = :nid', array(
352
      ':nid' => $node->nid,
353
    ))->fetchField();
354
  }
355
}
356

    
357
/**
358
 * Implements hook_node_update().
359
 */
360
function forum_access_node_update($node) {
361
  $old_tid = &drupal_static('forum_access_node_presave');
362
  if (_forum_node_check_node_type($node)) {
363
    $tid = _forum_access_get_tid($node);
364
    if (isset($old_tid)) {
365
      if ($tid == $old_tid) {
366
        return;
367
      }
368
      acl_node_clear_acls($node->nid, 'forum_access');
369

    
370
      /*
371
      if (module_exists('nodecomment')) {
372
        _forum_access_changed_tid($tid);
373
        $result = db_query('SELECT cid FROM {node_comments} WHERE nid = :nid', array(
374
          ':nid' => $node->nid,
375
        ));
376
        foreach ($result as $row) {
377
          acl_node_clear_acls($row->cid, 'forum_access');
378
        }
379
      }
380
      */
381
    }
382
    // For changed and for previously unassigned terms we need to fake an insert.
383
    forum_access_node_insert($node);
384
  }
385
}
386

    
387
/**
388
 * Implements hook_node_insert().
389
 */
390
function forum_access_node_insert($node) {
391
  $old_tid = &drupal_static('forum_access_node_presave');
392
  if (_forum_node_check_node_type($node)) {
393
    if ($tid = _forum_access_get_tid($node)) {
394
      $acl_id = _forum_access_get_acl($tid);
395
      acl_node_add_acl($node->nid, $acl_id, 1, 1, 1);
396

    
397
      /*
398
      if (isset($old_tid) && module_exists('nodecomment')) {
399
        $result = db_query('SELECT cid FROM {node_comments} WHERE nid = :nid', array(
400
          ':nid' => $node->nid,
401
        ));
402
        foreach ($result as $row) {
403
          acl_node_add_acl($row->cid, $acl_id, 1, 1, 1);
404
          node_access_acquire_grants(node_load($row->cid)); //TODO use node_load_multiple() here
405
        }
406
      }
407
      */
408
    }
409
    $old_tid = NULL;
410
  }
411
  /*
412
  elseif (isset($node->comment_target_nid)) {
413
    // Set moderator on nodecomment.
414
    $topic_node = node_load($node->comment_target_nid);
415
    if (_forum_node_check_node_type($topic_node) && $topic_tid = _forum_access_get_tid($topic_node)) {
416
      $acl_id = _forum_access_get_acl($topic_tid);
417
      acl_node_add_acl($node->nid, $acl_id, 1, 1, 1);
418
    }
419
  }
420
  */
421
}
422

    
423
function forum_access_enable_moderator($enable = TRUE) {
424
  global $user;
425

    
426
  if (!$enable || $enable && !user_access('administer comments')) {
427
    $perm = &drupal_static('user_access');
428
    if ($enable) {
429
      $perm[$user->uid]['administer comments'] = $enable;
430
      $user->_forum_access_moderator = $enable;
431
    }
432
    else {
433
      unset($perm[$user->uid]['administer comments']);
434
      unset($user->_forum_access_moderator);
435
    }
436
  }
437
}
438

    
439
/**
440
 * Get an array of moderator UIDs or NULL.
441
 */
442
function forum_access_get_moderator_uids($tid) {
443
  $acl_id = _forum_access_get_acl($tid);
444
  if ($uids = acl_get_uids($acl_id)) {
445
    return $uids;
446
  }
447
}
448

    
449
/**
450
 * Implements hook_custom_theme().
451
 */
452
function forum_access_custom_theme() {
453
}
454

    
455
/**
456
 * Implements hook_menu_get_item_alter().
457
 *
458
 * Saves the tid on the forum-specific pages.
459
 */
460
function forum_access_menu_get_item_alter(&$router_item, $path, $original_map) {
461
  if (forum_access_current_tid() == 0) {
462
    switch ($original_map[0]) {
463
      case 'forum':
464
        if (isset($original_map[1]) && is_numeric($original_map[1])) {
465
          forum_access_current_tid($original_map[1]);
466
        }
467
        break;
468
      case 'node':
469
        if (isset($original_map[1]) && is_numeric($nid = $original_map[1]) && ($node = node_load($nid)) && ($tid = _forum_access_get_tid($node))) {
470
          forum_access_current_tid($tid);
471
        }
472
        break;
473
    }
474
  }
475
}
476

    
477
/**
478
 * Saves and returns the forum TID, if we're on a forum-specific page.
479
 */
480
function forum_access_current_tid($tid = NULL) {
481
  static $saved_tid = 0;
482
  if (isset($tid)) {
483
    $saved_tid = $tid;
484
  }
485
  return $saved_tid;
486
}
487

    
488
/**
489
 * Implements $modulename_preprocess_$hook() for forum_list.
490
 *
491
 * Add forum_access_moderators to each forum,
492
 * containing a list of user objects.
493
 *
494
 * Note: On a site with many moderators, this function is expensive,
495
 * and thus it is disabled by default. Set the variable to TRUE to enable.
496
 */
497
function forum_access_preprocess_forum_list(&$variables) {
498
  if (variable_get('forum_access_provide_moderators_template_variable', FALSE)) {
499
    foreach ($variables['forums'] as $tid => $forum) {
500
      $forum->forum_access_moderators = NULL;
501
      if ($uids = forum_access_get_moderator_uids($tid)) {
502
        $forum->forum_access_moderators = user_load_multiple($uids);
503
      }
504
    }
505
  }
506
}
507

    
508
/**
509
 * Implements hook_node_view_alter().
510
 *
511
 * Remove 'Add new comment' link if it shouldn't be there.
512
 */
513
function forum_access_node_view_alter(&$build) {
514
  if ($tid = _forum_access_get_tid($build['#node'])) {
515
    _forum_access_module_load_include('node.inc');
516
    _forum_access_node_view_alter($build, $tid);
517
  }
518
}
519

    
520
/**
521
 * Implements hook_comment_view_alter().
522
 */
523
function forum_access_comment_view_alter(&$build) {
524
  if ($tid = _forum_access_get_tid($build['#node'])) {
525
    _forum_access_module_load_include('node.inc');
526
    _forum_access_comment_view_alter($build, $tid);
527
  }
528
}
529

    
530
/**
531
 * This is also required by ACL module.
532
 */
533
function forum_access_enabled($set = NULL) {
534
  static $enabled = TRUE; // not drupal_static!
535
  if ($set !== NULL) {
536
    $enabled = $set;
537
  }
538
  return $enabled;
539
}
540

    
541
/**
542
 * Implements hook_node_access().
543
 */
544
function forum_access_node_access($node, $op, $account) {
545
  $cache = &drupal_static(__FUNCTION__, array());
546

    
547
  $type = is_string($node) ? $node : $node->type;
548
  if ($op == 'create') {
549
    if ($type == 'forum') {
550
      $tid = forum_access_current_tid();
551
      if (isset($cache[$account->uid][$op][$tid])) {
552
        return $cache[$account->uid][$op][$tid];
553
      }
554
      if (!forum_access_access('create', $tid, $account)) {
555
        return $cache[$account->uid][$op][$tid] = NODE_ACCESS_DENY;
556
      }
557
      return $cache[$account->uid][$op][$tid] = NODE_ACCESS_IGNORE;
558
    }
559
  }
560
  else {
561
    $nid = $node->nid;
562
    if (!isset($cache[$account->uid][$op][$nid])) {
563
      if ($tid = _forum_access_get_tid($node)) {
564
        if (!forum_access_access('view', $tid, $account)) {
565
          return $cache[$account->uid][$op][$nid] = NODE_ACCESS_DENY;
566
        }
567
        if ($op == 'update' && (user_access('edit any forum content', $account) || ($node->uid == $account->uid && user_access('edit own forum content', $account)))
568
          || $op == 'delete' && (user_access('delete any forum content', $account) || ($node->uid == $account->uid && user_access('delete own forum content', $account)))) {
569
          return $cache[$account->uid][$op][$nid] = forum_access_node_access($node, 'view', $account);
570
        }
571
        $access = forum_access_access($op, $tid, $account);
572
        if ($op == 'view' && $access == 1 && !$node->status) {
573
          if (user_access('view own unpublished content', $account) && $account->uid && $account->uid == $node->uid) {
574
            // Allow access to own unpublished node.
575
          }
576
          elseif (forum_access_is_moderator($account, $tid)) {
577
            // Allow access to moderator.
578
          }
579
          else {
580
            $access = FALSE;
581
          }
582
        }
583
        return $cache[$account->uid][$op][$nid] = ($access ? NODE_ACCESS_IGNORE : NODE_ACCESS_DENY);
584
      }
585
      else {
586
        return $cache[$account->uid][$op][$nid] = NODE_ACCESS_IGNORE;
587
      }
588
    }
589
    return $cache[$account->uid][$op][$nid];
590
  }
591
  return NODE_ACCESS_IGNORE;
592
}
593

    
594
/**
595
 * Implements access checking.
596
 *
597
 * $op -- view, update, delete or create
598
 * $tid -- the tid of the forum
599
 * $account -- the account to test for; if NULL use current user
600
 * $administer_nodes_sees_everything -- pass FALSE to ignore the 'administer nodes' permission
601
 *
602
 * Return:
603
 *   FALSE - access not granted
604
 *   1     - access granted
605
 *   2     - access granted for forum moderator
606
 */
607
function forum_access_access($op, $tid, $account = NULL, $administer_nodes_sees_everything = TRUE) {
608
  $cache = &drupal_static(__FUNCTION__, array());
609
  if (!$account) {
610
    global $user;
611
    $account = $user;
612
  }
613

    
614
  if (user_access('bypass node access', $account)) {
615
//TODO revise (including comment above)
616
//      $administer_nodes_sees_everything && user_access('administer nodes', $account) && array_search($type, array('view', 'update', 'delete')) !== FALSE) {
617
    return 1;
618
  }
619

    
620
  if ($op == 'delete' && user_access('delete any forum content', $account)) {
621
    return 1;
622
  }
623

    
624
  if ($op == 'update' && user_access('edit any forum content', $account)) {
625
    return 1;
626
  }
627

    
628
  if (!isset($cache[$account->uid][$tid][$op])) {
629
    $query = db_select('forum_access', 'fa')
630
      ->fields('fa', array('tid'))
631
      ->condition("fa.grant_$op", 1, '>=')
632
      ->condition('fa.rid', array_keys($account->roles), 'IN');
633
    if ($tid != 0) {
634
      $query = $query
635
        ->condition('fa.tid', $tid, '=');
636
    }
637
    $result = $query
638
      ->execute()
639
      ->fetchField();
640

    
641
    if ($result) {
642
      $cache[$account->uid][$tid][$op] = 1;
643
    }
644
    else {
645
      // check our moderators too
646
      $result = forum_access_is_moderator($account, $tid);
647
      $cache[$account->uid][$tid][$op] = ($result ? 2 : FALSE);
648
    }
649
  }
650
  return $cache[$account->uid][$tid][$op];
651
}
652

    
653
/**
654
 * Implements hook_taxonomy_term_delete().
655
 *
656
 * Delete {forum_access} records when forums are deleted.
657
 */
658
function forum_access_taxonomy_term_delete($term) {
659
  //dpm($array, "hook_taxonomy_term_delete($term->tid)");
660
  if ($term->vid == _forum_access_get_vid()) {
661
    db_delete('forum_access')
662
      ->condition('tid', $term->tid)
663
      ->execute();
664
    variable_del('forum_access_rids'); // clear cache
665
  }
666
}
667

    
668
/**
669
 * Implements hook_user_role_delete().
670
 */
671
function forum_access_user_role_delete($role) {
672
  db_delete('forum_access')
673
    ->condition('rid', $role->rid)
674
    ->execute();
675
  db_delete('node_access')
676
    ->condition('gid', $role->rid)
677
    ->condition('realm', 'forum_access')
678
    ->execute();
679
}
680

    
681
/**
682
 * Check whether the given user is a moderator.
683
 *
684
 * @param $account
685
 *   The user or user ID to check.
686
 * @param $tid
687
 *   ID of the forum to check or NULL to check whether the user is moderator
688
 *   in any forum at all.
689
 */
690
function forum_access_is_moderator($account, $tid = NULL) {
691
  $uid = (is_object($account) ? $account->uid : $account);
692
  return (bool) acl_get_ids_by_user('forum_access', $uid, NULL, ($tid ? $tid : NULL));
693
}
694

    
695
/**
696
 * Return forum.module's forum vocabulary ID.
697
 */
698
function _forum_access_get_vid() {
699
  return variable_get('forum_nav_vocabulary', '');
700
}
701

    
702
/**
703
 * Returns the forum tid or FALSE.
704
 */
705
function _forum_access_get_tid($node) {
706
  return (isset($node->forum_tid) ? $node->forum_tid : FALSE);
707
}
708

    
709
/**
710
 * Saves and returns the $tid.
711
 */
712
function _forum_access_changed_tid($tid = NULL) {
713
  $saved_tid = &drupal_static(__FUNCTION__);
714
  if (!empty($tid)) {
715
    $saved_tid = $tid;
716
  }
717
  return $saved_tid;
718
}
719

    
720
/**
721
 * Returns the ACL ID of the forum.
722
 */
723
function _forum_access_get_acl($tid) {
724
  $acl_id = acl_get_id_by_number('forum_access', $tid);
725
  if (!$acl_id) { // create one
726
    $acl_id = acl_create_new_acl('forum_access', NULL, $tid);
727
    $subselect = db_select('taxonomy_index', 'n');
728
    $subselect
729
      ->fields('n', array('nid'))
730
      ->condition('n.tid', $tid);
731
    acl_add_nodes($subselect, $acl_id, 1, 1, 1);
732
  }
733
  return $acl_id;
734
}
735

    
736
function _forum_access_module_load_include($type) {
737
  static $loaded = array();
738

    
739
  if (!isset($loaded[$type])) {
740
    $path = module_load_include($type, 'forum_access');
741
    $loaded[$type] = drupal_get_path('module', 'forum_access') . "/forum_access.$type";
742
  }
743
  return $loaded[$type];
744
}
745

    
746
/**
747
 * Implements hook_theme().
748
 */
749
function forum_access_theme() {
750
  return array(
751
    'forum_access_table' => array(
752
      'render element' => 'form',
753
      'file' => 'forum_access.admin.inc',
754
    ),
755
  );
756
}
757

    
758
/**
759
 * Implements hook_node_access_explain().
760
 */
761
function forum_access_node_access_explain($row) {
762
  $roles = &drupal_static(__FUNCTION__);
763
  if ($row->realm == 'forum_access') {
764
    if (!isset($roles)) {
765
      $roles = user_roles();
766
    }
767
    if (isset($roles[$row->gid])) {
768
      return array($roles[$row->gid]);
769
    }
770
    return array('(unknown gid)');
771
  }
772
}
773

    
774
/**
775
 * Implements hook_acl_explain().
776
 */
777
function forum_access_acl_explain($acl_id, $name, $number, $users = NULL) {
778
  if (empty($users)) {
779
    return "ACL (id=$acl_id) would grant access to nodes in forum/$number.";
780
  }
781
  return "ACL (id=$acl_id) grants access to nodes in forum/$number to the listed user(s).";
782
}
783