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
|
elseif (is_string($comment)) {
|
119
|
return FALSE;
|
120
|
}
|
121
|
$node = node_load($comment->nid);
|
122
|
|
123
|
// The remaining $ops are governed by OR, return FALSE by default.
|
124
|
if ($tid = _forum_access_get_tid($node)) {
|
125
|
if ($op == 'approve') {
|
126
|
return $user->uid == 1 || forum_access_is_moderator($user, $tid);
|
127
|
}
|
128
|
if (!user_access('administer comments')) {
|
129
|
if ($op == 'edit' && (forum_access_access('update', $tid) || user_access('edit any forum content') || ($user->uid == $comment->uid && user_access('edit own forum content')))) {
|
130
|
forum_access_enable_moderator();
|
131
|
return TRUE;
|
132
|
}
|
133
|
if ($op == 'delete' && (forum_access_access('delete', $tid) || user_access('delete any forum content') || ($user->uid == $comment->uid && user_access('delete own forum content')))) {
|
134
|
return TRUE;
|
135
|
}
|
136
|
}
|
137
|
}
|
138
|
return FALSE;
|
139
|
}
|
140
|
|
141
|
/**
|
142
|
* Access callback for the 'forum' menu item.
|
143
|
*
|
144
|
* Returns 1 if the user has at least one role that can access
|
145
|
* at least one forum, 2 if the user is moderator in at least one forum,
|
146
|
* FALSE otherwise.
|
147
|
*/
|
148
|
function forum_access_view_any_forum($account = NULL) {
|
149
|
global $user;
|
150
|
$returns = &drupal_static(__FUNCTION__, array());
|
151
|
|
152
|
if (!isset($account)) {
|
153
|
$account = $user;
|
154
|
}
|
155
|
|
156
|
if (!isset($returns[$account->uid])) {
|
157
|
if (user_access('bypass node access', $account)) {
|
158
|
return $returns[$account->uid] = 1;
|
159
|
}
|
160
|
if (!user_access('access content')) {
|
161
|
return $returns[$account->uid] = FALSE;
|
162
|
}
|
163
|
$rids = variable_get('forum_access_rids', NULL);
|
164
|
if (!isset($rids)) {
|
165
|
$rids = db_query("SELECT fa.rid FROM {forum_access} fa WHERE fa.grant_view > 0 GROUP BY fa.rid")->fetchCol();
|
166
|
variable_set('forum_access_rids', $rids);
|
167
|
}
|
168
|
foreach ($rids as $rid) {
|
169
|
if (isset($account->roles[$rid])) {
|
170
|
return $returns[$account->uid] = 1;
|
171
|
}
|
172
|
}
|
173
|
// Check moderator, too.
|
174
|
$query = db_select('acl', 'acl');
|
175
|
$query->join('acl_user', 'aclu', "acl.acl_id = aclu.acl_id");
|
176
|
$count = $query
|
177
|
->fields('acl', array('number'))
|
178
|
->condition('acl.module', 'forum_access')
|
179
|
->condition('aclu.uid', $account->uid)
|
180
|
->countQuery()
|
181
|
->execute()
|
182
|
->fetchField();
|
183
|
$returns[$account->uid] = ($count > 0 ? 2 : FALSE);
|
184
|
}
|
185
|
return $returns[$account->uid];
|
186
|
}
|
187
|
|
188
|
/**
|
189
|
* Implements hook_node_grants().
|
190
|
*
|
191
|
* This function supplies the forum access grants. forum_access simply uses
|
192
|
* roles as ACLs, so rids translate directly to gids.
|
193
|
*/
|
194
|
function forum_access_node_grants($user, $op) {
|
195
|
$grants['forum_access'] = array_keys($user->roles);
|
196
|
return $grants;
|
197
|
}
|
198
|
|
199
|
/**
|
200
|
* Implements hook_node_access_records().
|
201
|
*
|
202
|
* Returns a list of grant records for the passed in node object.
|
203
|
* Checks to see if maybe we're being disabled.
|
204
|
*/
|
205
|
function forum_access_node_access_records($node) {
|
206
|
if (!forum_access_enabled()) {
|
207
|
return;
|
208
|
}
|
209
|
|
210
|
static $seers;
|
211
|
|
212
|
$grants = &drupal_static(__FUNCTION__, array());
|
213
|
$seers = &drupal_static(__FUNCTION__ . '__seers');
|
214
|
$tid = _forum_access_get_tid($node);
|
215
|
|
216
|
// Set proper grants for nodecomment comment nodes.
|
217
|
//if (isset($node->comment_target_nid)) {
|
218
|
// if ($changed_tid = _forum_access_changed_tid()) {
|
219
|
// $tid = $changed_tid; // the topic node hasn't been saved yet!
|
220
|
// }
|
221
|
// else {
|
222
|
// $node = node_load($node->comment_target_nid);
|
223
|
// $tid = _forum_access_get_tid($node);
|
224
|
// }
|
225
|
//}
|
226
|
|
227
|
if ($tid) {
|
228
|
if (!isset($grants[$tid])) {
|
229
|
if (!isset($seers)) {
|
230
|
$seers = user_roles(FALSE, 'bypass node access');
|
231
|
}
|
232
|
$result = db_query('SELECT * FROM {forum_access} WHERE tid = :tid', array(
|
233
|
':tid' => $tid
|
234
|
));
|
235
|
foreach ($result as $grant) {
|
236
|
if (isset($seers[$grant->rid])) {
|
237
|
continue; // Don't provide any useless grants!
|
238
|
}
|
239
|
$grants[$tid][] = array(
|
240
|
'realm' => 'forum_access',
|
241
|
'gid' => $grant->rid,
|
242
|
'grant_view' => $grant->grant_view,
|
243
|
'grant_update' => $grant->grant_update,
|
244
|
'grant_delete' => $grant->grant_delete,
|
245
|
'priority' => $grant->priority,
|
246
|
);
|
247
|
}
|
248
|
//dsm("forum_access_node_access_records($node->nid) (tid=$tid) returns ". var_export($grants[$tid], TRUE), 'status');
|
249
|
}
|
250
|
if (isset($grants[$tid])) {
|
251
|
return $grants[$tid];
|
252
|
}
|
253
|
}
|
254
|
}
|
255
|
|
256
|
/**
|
257
|
* Implements hook_form_alter().
|
258
|
*
|
259
|
* Alter the node/comment create/edit forms and various admin forms.
|
260
|
*/
|
261
|
function forum_access_form_alter(&$form, &$form_state, $form_id) {
|
262
|
//dpm($form, "form_id($form_id)");
|
263
|
if ($form_id == 'forum_node_form' && !empty($form['#node_edit_form'])) {
|
264
|
_forum_access_module_load_include('node.inc');
|
265
|
_forum_access_node_form($form, $form_state);
|
266
|
}
|
267
|
elseif ($form['#id'] == 'comment-form') {
|
268
|
_forum_access_module_load_include('node.inc');
|
269
|
_forum_access_comment_form($form, $form_state);
|
270
|
}
|
271
|
elseif ($form_id == 'forum_overview') {
|
272
|
_forum_access_module_load_include('admin.inc');
|
273
|
_forum_access_forum_overview($form, $form_state);
|
274
|
}
|
275
|
elseif ($form_id == 'forum_form_container') {
|
276
|
_forum_access_module_load_include('admin.inc');
|
277
|
_forum_access_forum_form($form, $form_state, TRUE);
|
278
|
}
|
279
|
elseif ($form_id == 'forum_form_forum') {
|
280
|
_forum_access_module_load_include('admin.inc');
|
281
|
_forum_access_forum_form($form, $form_state, FALSE);
|
282
|
}
|
283
|
elseif ($form_id == 'content_access_admin_settings' && empty($_POST)) {
|
284
|
_forum_access_module_load_include('admin.inc');
|
285
|
_forum_access_content_access_admin_form($form_state['build_info']['args'][0]);
|
286
|
}
|
287
|
}
|
288
|
|
289
|
/**
|
290
|
* Implements hook_form_node_form_alter().
|
291
|
*
|
292
|
* Allows the forum field to be optional on non-forum nodes.
|
293
|
*/
|
294
|
function forum_access_form_node_form_alter(&$form, &$form_state, $form_id) {
|
295
|
if (isset($form['taxonomy_forums'])) {
|
296
|
$field = field_info_instance('node', 'taxonomy_forums', $form['type']['#value']);
|
297
|
// Make the vocabulary required for 'real' forum-nodes.
|
298
|
if (!$field['required']) {
|
299
|
$langcode = $form['taxonomy_forums']['#language'];
|
300
|
$form['taxonomy_forums'][$langcode]['#required'] = FALSE;
|
301
|
}
|
302
|
}
|
303
|
}
|
304
|
|
305
|
/**
|
306
|
* Implement hook_node_load().
|
307
|
*
|
308
|
* Sets $node->forum_tid to avoid confusing forum_node_view().
|
309
|
*/
|
310
|
function forum_access_node_load($nodes, $types) {
|
311
|
foreach ($nodes as $node) {
|
312
|
if (isset($node->taxonomy_forums) && empty($node->taxonomy_forums) && !isset($node->forum_tid)) {
|
313
|
$node->forum_tid = NULL;
|
314
|
}
|
315
|
}
|
316
|
}
|
317
|
|
318
|
/**
|
319
|
* Implements hook_comment_load().
|
320
|
*/
|
321
|
function forum_access_comment_load($comments) {
|
322
|
//TODO: Investigate usefulness of this hook.
|
323
|
return;
|
324
|
}
|
325
|
|
326
|
/**
|
327
|
* Implements hook_query_alter().
|
328
|
*/
|
329
|
function forum_access_query_alter($p1, $p2, $p3) {
|
330
|
//TODO: Investigate usefulness of this hook.
|
331
|
return;
|
332
|
}
|
333
|
|
334
|
/**
|
335
|
* Implements hook_query_term_access_alter().
|
336
|
*
|
337
|
* @param QueryAlterableInterface $query
|
338
|
*/
|
339
|
function forum_access_query_term_access_alter(QueryAlterableInterface $query) {
|
340
|
global $user;
|
341
|
|
342
|
// Read meta-data from query, if provided.
|
343
|
if (!$account = $query->getMetaData('account')) {
|
344
|
$account = $user;
|
345
|
}
|
346
|
if (!$op = $query->getMetaData('op')) {
|
347
|
$op = 'view';
|
348
|
}
|
349
|
|
350
|
// If $account can bypass node access, we let them administer the full forum
|
351
|
// structure and see the nodes, i.e. we don't restrict the query.
|
352
|
if (user_access('bypass node access', $account) && (strpos(current_path(), 'admin/structure/forum') === 0 || strpos(current_path(), 'node/') === 0)) {
|
353
|
return;
|
354
|
}
|
355
|
|
356
|
// Prevent duplicate records.
|
357
|
$query->distinct();
|
358
|
|
359
|
// Find all instances of the {taxonomy_term_data} table being joined --
|
360
|
// could appear more than once in the query, and could be aliased.
|
361
|
// Join each one to the forum_access table.
|
362
|
|
363
|
$tables = $query->getTables();
|
364
|
$rids = array_keys($account->roles);
|
365
|
foreach ($tables as $talias => $tableinfo) {
|
366
|
$table = $tableinfo['table'];
|
367
|
if (!($table instanceof SelectQueryInterface) && $table == 'taxonomy_term_data') {
|
368
|
// The node_access table has the access grants for any given node.
|
369
|
$access_alias = $query->leftJoin('forum_access', 'fa', '%alias.tid = ' . $talias . '.tid');
|
370
|
$acl_alias = $query->leftJoin('acl', 'acl', "%alias.number = $talias.tid AND %alias.module = 'forum_access'");
|
371
|
if (user_access('bypass node access', $account)) {
|
372
|
// If $account can bypass node access, we allow access if any role or
|
373
|
// account has access.
|
374
|
$aclu_alias = $query->leftJoin('acl_user', 'aclu', "%alias.acl_id = $acl_alias.acl_id");
|
375
|
$query->condition(db_or()
|
376
|
->isNull("$access_alias.rid")
|
377
|
->condition("$access_alias.grant_$op", 1, '>=')
|
378
|
->isNotNull("$aclu_alias.uid")
|
379
|
);
|
380
|
}
|
381
|
else {
|
382
|
$aclu_alias = $query->leftJoin('acl_user', 'aclu', "%alias.acl_id = $acl_alias.acl_id AND %alias.uid = $account->uid");
|
383
|
$query->condition(db_or()
|
384
|
->isNull("$access_alias.rid")
|
385
|
->condition(db_and()
|
386
|
->condition("$access_alias.rid", $rids, 'IN')
|
387
|
->condition("$access_alias.grant_$op", 1, '>='))
|
388
|
->condition("$aclu_alias.uid", $account->uid));
|
389
|
}
|
390
|
}
|
391
|
}
|
392
|
}
|
393
|
|
394
|
/**
|
395
|
* Implements hook_node_presave().
|
396
|
*/
|
397
|
function forum_access_node_presave($node, $return_old_tid = FALSE) {
|
398
|
$old_tid = &drupal_static('forum_access_node_presave');
|
399
|
if (_forum_node_check_node_type($node)) {
|
400
|
if (empty($node->nid)) {
|
401
|
// Added for migrations, which log errors due to no nid during presave, see #3003279.
|
402
|
$old_tid = NULL;
|
403
|
}
|
404
|
else {
|
405
|
$old_tid = db_query('SELECT tid FROM {forum} WHERE nid = :nid', array(
|
406
|
':nid' => $node->nid,
|
407
|
))->fetchField();
|
408
|
}
|
409
|
}
|
410
|
if (!empty($old_tid) && empty($node->taxonomy_forums['und'])) {
|
411
|
$node->forum_tid = null;
|
412
|
}
|
413
|
}
|
414
|
|
415
|
/**
|
416
|
* Implements hook_node_update().
|
417
|
*/
|
418
|
function forum_access_node_update($node) {
|
419
|
$old_tid = &drupal_static('forum_access_node_presave');
|
420
|
if (_forum_node_check_node_type($node)) {
|
421
|
$tid = _forum_access_get_tid($node);
|
422
|
if (isset($old_tid)) {
|
423
|
if ($tid == $old_tid) {
|
424
|
return;
|
425
|
}
|
426
|
acl_node_clear_acls($node->nid, 'forum_access');
|
427
|
|
428
|
/*
|
429
|
if (module_exists('nodecomment')) {
|
430
|
_forum_access_changed_tid($tid);
|
431
|
$result = db_query('SELECT cid FROM {node_comments} WHERE nid = :nid', array(
|
432
|
':nid' => $node->nid,
|
433
|
));
|
434
|
foreach ($result as $row) {
|
435
|
acl_node_clear_acls($row->cid, 'forum_access');
|
436
|
}
|
437
|
}
|
438
|
*/
|
439
|
}
|
440
|
// For changed and for previously unassigned terms we need to fake an insert.
|
441
|
forum_access_node_insert($node);
|
442
|
}
|
443
|
}
|
444
|
|
445
|
/**
|
446
|
* Implements hook_node_insert().
|
447
|
*/
|
448
|
function forum_access_node_insert($node) {
|
449
|
$old_tid = &drupal_static('forum_access_node_presave');
|
450
|
if (_forum_node_check_node_type($node)) {
|
451
|
if ($tid = _forum_access_get_tid($node)) {
|
452
|
$acl_id = _forum_access_get_acl($tid);
|
453
|
acl_node_add_acl($node->nid, $acl_id, 1, 1, 1);
|
454
|
|
455
|
/*
|
456
|
if (isset($old_tid) && module_exists('nodecomment')) {
|
457
|
$result = db_query('SELECT cid FROM {node_comments} WHERE nid = :nid', array(
|
458
|
':nid' => $node->nid,
|
459
|
));
|
460
|
foreach ($result as $row) {
|
461
|
acl_node_add_acl($row->cid, $acl_id, 1, 1, 1);
|
462
|
node_access_acquire_grants(node_load($row->cid)); //TODO use node_load_multiple() here
|
463
|
}
|
464
|
}
|
465
|
*/
|
466
|
}
|
467
|
$old_tid = NULL;
|
468
|
}
|
469
|
/*
|
470
|
elseif (isset($node->comment_target_nid)) {
|
471
|
// Set moderator on nodecomment.
|
472
|
$topic_node = node_load($node->comment_target_nid);
|
473
|
if (_forum_node_check_node_type($topic_node) && $topic_tid = _forum_access_get_tid($topic_node)) {
|
474
|
$acl_id = _forum_access_get_acl($topic_tid);
|
475
|
acl_node_add_acl($node->nid, $acl_id, 1, 1, 1);
|
476
|
}
|
477
|
}
|
478
|
*/
|
479
|
}
|
480
|
|
481
|
function forum_access_enable_moderator($enable = TRUE) {
|
482
|
global $user;
|
483
|
|
484
|
if (!$enable || !user_access('administer comments')) {
|
485
|
$perm = &drupal_static('user_access');
|
486
|
if ($enable) {
|
487
|
$perm[$user->uid]['administer comments'] = $enable;
|
488
|
$user->_forum_access_moderator = $enable;
|
489
|
}
|
490
|
else {
|
491
|
unset($perm[$user->uid]['administer comments']);
|
492
|
unset($user->_forum_access_moderator);
|
493
|
}
|
494
|
}
|
495
|
}
|
496
|
|
497
|
/**
|
498
|
* Get an array of moderator UIDs or NULL.
|
499
|
*/
|
500
|
function forum_access_get_moderator_uids($tid) {
|
501
|
$acl_id = _forum_access_get_acl($tid);
|
502
|
if ($uids = acl_get_uids($acl_id)) {
|
503
|
return $uids;
|
504
|
}
|
505
|
}
|
506
|
|
507
|
/**
|
508
|
* Implements hook_custom_theme().
|
509
|
*/
|
510
|
function forum_access_custom_theme() {
|
511
|
}
|
512
|
|
513
|
/**
|
514
|
* Implements hook_menu_get_item_alter().
|
515
|
*
|
516
|
* Saves the tid on the forum-specific pages.
|
517
|
*/
|
518
|
function forum_access_menu_get_item_alter(&$router_item, $path, $original_map) {
|
519
|
if (forum_access_current_tid() == 0) {
|
520
|
switch ($original_map[0]) {
|
521
|
case 'forum':
|
522
|
if (isset($original_map[1]) && is_numeric($original_map[1])) {
|
523
|
forum_access_current_tid($original_map[1]);
|
524
|
}
|
525
|
break;
|
526
|
case 'node':
|
527
|
if (isset($original_map[1]) && is_numeric($nid = $original_map[1]) && ($node = node_load($nid)) && ($tid = _forum_access_get_tid($node))) {
|
528
|
forum_access_current_tid($tid);
|
529
|
}
|
530
|
break;
|
531
|
}
|
532
|
}
|
533
|
}
|
534
|
|
535
|
/**
|
536
|
* Saves and returns the forum TID, if we're on a forum-specific page.
|
537
|
*/
|
538
|
function forum_access_current_tid($tid = NULL) {
|
539
|
static $saved_tid = 0;
|
540
|
if (isset($tid)) {
|
541
|
$saved_tid = $tid;
|
542
|
}
|
543
|
return $saved_tid;
|
544
|
}
|
545
|
|
546
|
/**
|
547
|
* Implements $modulename_preprocess_$hook() for forum_list.
|
548
|
*
|
549
|
* Add forum_access_moderators to each forum,
|
550
|
* containing a list of user objects.
|
551
|
*
|
552
|
* Note: On a site with many moderators, this function is expensive,
|
553
|
* and thus it is disabled by default. Set the variable to TRUE to enable.
|
554
|
*/
|
555
|
function forum_access_preprocess_forum_list(&$variables) {
|
556
|
if (variable_get('forum_access_provide_moderators_template_variable', FALSE)) {
|
557
|
foreach ($variables['forums'] as $tid => $forum) {
|
558
|
$forum->forum_access_moderators = NULL;
|
559
|
if ($uids = forum_access_get_moderator_uids($tid)) {
|
560
|
$forum->forum_access_moderators = user_load_multiple($uids);
|
561
|
}
|
562
|
}
|
563
|
}
|
564
|
}
|
565
|
|
566
|
/**
|
567
|
* Implements hook_node_view_alter().
|
568
|
*
|
569
|
* Remove 'Add new comment' link and breadcrumb if they shouldn't be there.
|
570
|
*/
|
571
|
function forum_access_node_view_alter(&$build) {
|
572
|
if ($tid = _forum_access_get_tid($build['#node'])) {
|
573
|
_forum_access_module_load_include('node.inc');
|
574
|
_forum_access_node_view_alter($build, $tid);
|
575
|
}
|
576
|
|
577
|
$breadcrumb = drupal_get_breadcrumb();
|
578
|
if (!$tid && count($breadcrumb) == 2 && strpos($breadcrumb[0], '<a href="/">') === 0 && strpos($breadcrumb[1], '<a href="/forum">') === 0) {
|
579
|
// We must hack away the bogus breadcrumb set by forum module
|
580
|
// (empty taxonomy_forums).
|
581
|
$stored_breadcrumb = &drupal_static('drupal_set_breadcrumb');
|
582
|
$stored_breadcrumb = NULL;
|
583
|
}
|
584
|
}
|
585
|
|
586
|
/**
|
587
|
* Implements hook_comment_view_alter().
|
588
|
*/
|
589
|
function forum_access_comment_view_alter(&$build) {
|
590
|
if ($tid = _forum_access_get_tid($build['#node'])) {
|
591
|
_forum_access_module_load_include('node.inc');
|
592
|
_forum_access_comment_view_alter($build, $tid);
|
593
|
}
|
594
|
}
|
595
|
|
596
|
/**
|
597
|
* This is also required by ACL module.
|
598
|
*/
|
599
|
function forum_access_enabled($set = NULL) {
|
600
|
static $enabled = TRUE; // not drupal_static!
|
601
|
if ($set !== NULL) {
|
602
|
$enabled = $set;
|
603
|
}
|
604
|
return $enabled;
|
605
|
}
|
606
|
|
607
|
/**
|
608
|
* Implements hook_node_access().
|
609
|
*/
|
610
|
function forum_access_node_access($node, $op, $account) {
|
611
|
$cache = &drupal_static(__FUNCTION__, array());
|
612
|
|
613
|
$type = is_string($node) ? $node : $node->type;
|
614
|
if ($op == 'create') {
|
615
|
if ($type == 'forum') {
|
616
|
$tid = forum_access_current_tid();
|
617
|
if (isset($cache[$account->uid][$op][$tid])) {
|
618
|
return $cache[$account->uid][$op][$tid];
|
619
|
}
|
620
|
if (!forum_access_access('create', $tid, $account)) {
|
621
|
return $cache[$account->uid][$op][$tid] = NODE_ACCESS_DENY;
|
622
|
}
|
623
|
return $cache[$account->uid][$op][$tid] = NODE_ACCESS_IGNORE;
|
624
|
}
|
625
|
}
|
626
|
else {
|
627
|
$nid = $node->nid;
|
628
|
if (!isset($cache[$account->uid][$op][$nid])) {
|
629
|
if ($tid = _forum_access_get_tid($node)) {
|
630
|
if (!forum_access_access('view', $tid, $account)) {
|
631
|
return $cache[$account->uid][$op][$nid] = NODE_ACCESS_DENY;
|
632
|
}
|
633
|
if (($op == 'update' && (user_access('edit any forum content', $account) || ($node->uid == $account->uid && user_access('edit own forum content', $account))))
|
634
|
|| ($op == 'delete' && (user_access('delete any forum content', $account) || ($node->uid == $account->uid && user_access('delete own forum content', $account))))) {
|
635
|
return $cache[$account->uid][$op][$nid] = forum_access_node_access($node, 'view', $account);
|
636
|
}
|
637
|
$access = forum_access_access($op, $tid, $account);
|
638
|
if ($op == 'view' && $access == 1 && !$node->status) {
|
639
|
if (user_access('view own unpublished content', $account) && $account->uid && $account->uid == $node->uid) {
|
640
|
// Allow access to own unpublished node.
|
641
|
}
|
642
|
elseif (forum_access_is_moderator($account, $tid)) {
|
643
|
// Allow access to moderator.
|
644
|
}
|
645
|
else {
|
646
|
$access = FALSE;
|
647
|
}
|
648
|
}
|
649
|
return $cache[$account->uid][$op][$nid] = ($access ? NODE_ACCESS_IGNORE : NODE_ACCESS_DENY);
|
650
|
}
|
651
|
else {
|
652
|
return $cache[$account->uid][$op][$nid] = NODE_ACCESS_IGNORE;
|
653
|
}
|
654
|
}
|
655
|
return $cache[$account->uid][$op][$nid];
|
656
|
}
|
657
|
return NODE_ACCESS_IGNORE;
|
658
|
}
|
659
|
|
660
|
/**
|
661
|
* Implements access checking.
|
662
|
*
|
663
|
* $op -- view, update, delete or create
|
664
|
* $tid -- the tid of the forum
|
665
|
* $account -- the account to test for; if NULL use current user
|
666
|
* $administer_nodes_sees_everything -- pass FALSE to ignore the 'administer nodes' permission
|
667
|
*
|
668
|
* Return:
|
669
|
* FALSE - access not granted
|
670
|
* 1 - access granted
|
671
|
* 2 - access granted for forum moderator
|
672
|
*/
|
673
|
function forum_access_access($op, $tid, $account = NULL, $administer_nodes_sees_everything = TRUE) {
|
674
|
$cache = &drupal_static(__FUNCTION__, array());
|
675
|
if (!$account) {
|
676
|
global $user;
|
677
|
$account = $user;
|
678
|
}
|
679
|
|
680
|
if (user_access('bypass node access', $account)) {
|
681
|
//TODO revise (including comment above)
|
682
|
// $administer_nodes_sees_everything && user_access('administer nodes', $account) && array_search($type, array('view', 'update', 'delete')) !== FALSE) {
|
683
|
return 1;
|
684
|
}
|
685
|
|
686
|
if ($op == 'delete' && user_access('delete any forum content', $account)) {
|
687
|
return 1;
|
688
|
}
|
689
|
|
690
|
if ($op == 'update' && user_access('edit any forum content', $account)) {
|
691
|
return 1;
|
692
|
}
|
693
|
|
694
|
if (!isset($cache[$account->uid][$tid][$op])) {
|
695
|
$query = db_select('forum_access', 'fa')
|
696
|
->fields('fa', array('tid'))
|
697
|
->condition("fa.grant_$op", 1, '>=')
|
698
|
->condition('fa.rid', array_keys($account->roles), 'IN');
|
699
|
if ($tid != 0) {
|
700
|
$query = $query
|
701
|
->condition('fa.tid', $tid, '=');
|
702
|
}
|
703
|
$result = $query
|
704
|
->execute()
|
705
|
->fetchField();
|
706
|
|
707
|
if ($result) {
|
708
|
$cache[$account->uid][$tid][$op] = 1;
|
709
|
}
|
710
|
else {
|
711
|
// check our moderators too
|
712
|
$result = forum_access_is_moderator($account, $tid);
|
713
|
$cache[$account->uid][$tid][$op] = ($result ? 2 : FALSE);
|
714
|
}
|
715
|
}
|
716
|
return $cache[$account->uid][$tid][$op];
|
717
|
}
|
718
|
|
719
|
/**
|
720
|
* Implements hook_taxonomy_term_delete().
|
721
|
*
|
722
|
* Delete {forum_access} records when forums are deleted.
|
723
|
*/
|
724
|
function forum_access_taxonomy_term_delete($term) {
|
725
|
//dpm($array, "hook_taxonomy_term_delete($term->tid)");
|
726
|
if ($term->vid == _forum_access_get_vid()) {
|
727
|
db_delete('forum_access')
|
728
|
->condition('tid', $term->tid)
|
729
|
->execute();
|
730
|
variable_del('forum_access_rids'); // clear cache
|
731
|
}
|
732
|
}
|
733
|
|
734
|
/**
|
735
|
* Implements hook_user_role_delete().
|
736
|
*/
|
737
|
function forum_access_user_role_delete($role) {
|
738
|
db_delete('forum_access')
|
739
|
->condition('rid', $role->rid)
|
740
|
->execute();
|
741
|
db_delete('node_access')
|
742
|
->condition('gid', $role->rid)
|
743
|
->condition('realm', 'forum_access')
|
744
|
->execute();
|
745
|
}
|
746
|
|
747
|
/**
|
748
|
* Check whether the given user is a moderator.
|
749
|
*
|
750
|
* @param $account
|
751
|
* The user or user ID to check.
|
752
|
* @param $tid
|
753
|
* ID of the forum to check or NULL to check whether the user is moderator
|
754
|
* in any forum at all.
|
755
|
*/
|
756
|
function forum_access_is_moderator($account, $tid = NULL) {
|
757
|
$uid = (is_object($account) ? $account->uid : $account);
|
758
|
return (bool) acl_get_ids_by_user('forum_access', $uid, NULL, ($tid ? $tid : NULL));
|
759
|
}
|
760
|
|
761
|
/**
|
762
|
* Return forum.module's forum vocabulary ID.
|
763
|
*/
|
764
|
function _forum_access_get_vid() {
|
765
|
return variable_get('forum_nav_vocabulary', '');
|
766
|
}
|
767
|
|
768
|
/**
|
769
|
* Returns the forum tid or FALSE.
|
770
|
*/
|
771
|
function _forum_access_get_tid($node) {
|
772
|
return (isset($node->forum_tid) ? $node->forum_tid : FALSE);
|
773
|
}
|
774
|
|
775
|
/**
|
776
|
* Saves and returns the $tid.
|
777
|
*/
|
778
|
function _forum_access_changed_tid($tid = NULL) {
|
779
|
$saved_tid = &drupal_static(__FUNCTION__);
|
780
|
if (!empty($tid)) {
|
781
|
$saved_tid = $tid;
|
782
|
}
|
783
|
return $saved_tid;
|
784
|
}
|
785
|
|
786
|
/**
|
787
|
* Returns the ACL ID of the forum.
|
788
|
*/
|
789
|
function _forum_access_get_acl($tid) {
|
790
|
$acl_id = acl_get_id_by_number('forum_access', $tid);
|
791
|
if (!$acl_id) { // create one
|
792
|
$acl_id = acl_create_new_acl('forum_access', NULL, $tid);
|
793
|
$subselect = db_select('taxonomy_index', 'n');
|
794
|
$subselect
|
795
|
->fields('n', array('nid'))
|
796
|
->condition('n.tid', $tid);
|
797
|
acl_add_nodes($subselect, $acl_id, 1, 1, 1);
|
798
|
}
|
799
|
return $acl_id;
|
800
|
}
|
801
|
|
802
|
function _forum_access_module_load_include($type) {
|
803
|
static $loaded = array();
|
804
|
|
805
|
if (!isset($loaded[$type])) {
|
806
|
$path = module_load_include($type, 'forum_access');
|
807
|
$loaded[$type] = drupal_get_path('module', 'forum_access') . "/forum_access.$type";
|
808
|
}
|
809
|
return $loaded[$type];
|
810
|
}
|
811
|
|
812
|
/**
|
813
|
* Implements hook_theme().
|
814
|
*/
|
815
|
function forum_access_theme() {
|
816
|
return array(
|
817
|
'forum_access_table' => array(
|
818
|
'render element' => 'form',
|
819
|
'file' => 'forum_access.admin.inc',
|
820
|
),
|
821
|
);
|
822
|
}
|
823
|
|
824
|
/**
|
825
|
* Implements hook_node_access_explain().
|
826
|
*/
|
827
|
function forum_access_node_access_explain($row) {
|
828
|
$roles = &drupal_static(__FUNCTION__);
|
829
|
if ($row->realm == 'forum_access') {
|
830
|
if (!isset($roles)) {
|
831
|
$roles = user_roles();
|
832
|
}
|
833
|
if (isset($roles[$row->gid])) {
|
834
|
return array($roles[$row->gid]);
|
835
|
}
|
836
|
return array('(unknown gid)');
|
837
|
}
|
838
|
}
|
839
|
|
840
|
/**
|
841
|
* Implements hook_acl_explain().
|
842
|
*/
|
843
|
function forum_access_acl_explain($acl_id, $name, $number, $users = NULL) {
|
844
|
if (empty($users)) {
|
845
|
return "ACL (id=$acl_id) would grant access to nodes in forum/$number.";
|
846
|
}
|
847
|
return "ACL (id=$acl_id) grants access to nodes in forum/$number to the listed user(s).";
|
848
|
}
|
849
|
|