Projet

Général

Profil

Paste
Télécharger (47,8 ko) Statistiques
| Branche: | Révision:

root / drupal7 / modules / forum / forum.module @ 01dfd3b5

1
<?php
2

    
3
/**
4
 * @file
5
 * Provides discussion forums.
6
 */
7

    
8
/**
9
 * Implements hook_help().
10
 */
11
function forum_help($path, $arg) {
12
  switch ($path) {
13
    case 'admin/help#forum':
14
      $output = '';
15
      $output .= '<h3>' . t('About') . '</h3>';
16
      $output .= '<p>' . t('The Forum module lets you create threaded discussion forums with functionality similar to other message board systems. Forums are useful because they allow community members to discuss topics with one another while ensuring those conversations are archived for later reference. In a forum, users post topics and threads in nested hierarchies, allowing discussions to be categorized and grouped. The forum hierarchy consists of:') . '</p>';
17
      $output .= '<ul>';
18
      $output .= '<li>' . t('Optional containers (for example, <em>Support</em>), which can hold:') . '</li>';
19
      $output .= '<ul><li>' . t('Forums (for example, <em>Installing Drupal</em>), which can hold:') . '</li>';
20
      $output .= '<ul><li>' . t('Forum topics submitted by users (for example, <em>How to start a Drupal 6 Multisite</em>), which start discussions and are starting points for:') . '</li>';
21
      $output .= '<ul><li>' . t('Threaded comments submitted by users (for example, <em>You have these options...</em>).') . '</li>';
22
      $output .= '</ul>';
23
      $output .= '</ul>';
24
      $output .= '</ul>';
25
      $output .= '</ul>';
26
      $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@forum">Forum module</a>.', array('@forum' => 'http://drupal.org/documentation/modules/forum')) . '</p>';
27
      $output .= '<h3>' . t('Uses') . '</h3>';
28
      $output .= '<dl>';
29
      $output .= '<dt>' . t('Setting up forum structure') . '</dt>';
30
      $output .= '<dd>' . t('Visit the <a href="@forums">Forums page</a> to set up containers and forums to hold your discussion topics.', array('@forums' => url('admin/structure/forum'))) . '</dd>';
31
      $output .= '<dt>' . t('Starting a discussion') . '</dt>';
32
      $output .= '<dd>' . t('The <a href="@create-topic">Forum topic</a> link on the <a href="@content-add">Add new content</a> page creates the first post of a new threaded discussion, or thread.', array('@create-topic' => url('node/add/forum'), '@content-add' => url('node/add'))) . '</dd>';
33
      $output .= '<dt>' . t('Navigation') . '</dt>';
34
      $output .= '<dd>' . t('Enabling the Forum module provides a default <em>Forums</em> menu item in the navigation menu that links to the <a href="@forums">Forums page</a>.', array('@forums' => url('forum'))) . '</dd>';
35
      $output .= '<dt>' . t('Moving forum topics') . '</dt>';
36
      $output .= '<dd>' . t('A forum topic (and all of its comments) may be moved between forums by selecting a different forum while editing a forum topic. When moving a forum topic between forums, the <em>Leave shadow copy</em> option creates a link in the original forum pointing to the new location.') . '</dd>';
37
      $output .= '<dt>' . t('Locking and disabling comments') . '</dt>';
38
      $output .= '<dd>' . t('Selecting <em>Closed</em> under <em>Comment settings</em> while editing a forum topic will lock (prevent new comments on) the thread. Selecting <em>Hidden</em> under <em>Comment settings</em> while editing a forum topic will hide all existing comments on the thread, and prevent new ones.') . '</dd>';
39
      $output .= '</dl>';
40
      return $output;
41
    case 'admin/structure/forum':
42
      $output = '<p>' . t('Forums contain forum topics. Use containers to group related forums.') . '</p>';
43
      $output .= theme('more_help_link', array('url' => 'admin/help/forum'));
44
      return $output;
45
    case 'admin/structure/forum/add/container':
46
      return '<p>' . t('Use containers to group related forums.') . '</p>';
47
    case 'admin/structure/forum/add/forum':
48
      return '<p>' . t('A forum holds related forum topics.') . '</p>';
49
    case 'admin/structure/forum/settings':
50
      return '<p>' . t('Adjust the display of your forum topics. Organize the forums on the <a href="@forum-structure">forum structure page</a>.', array('@forum-structure' => url('admin/structure/forum'))) . '</p>';
51
  }
52
}
53

    
54
/**
55
 * Implements hook_theme().
56
 */
57
function forum_theme() {
58
  return array(
59
    'forums' => array(
60
      'template' => 'forums',
61
      'variables' => array('forums' => NULL, 'topics' => NULL, 'parents' => NULL, 'tid' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
62
    ),
63
    'forum_list' => array(
64
      'template' => 'forum-list',
65
      'variables' => array('forums' => NULL, 'parents' => NULL, 'tid' => NULL),
66
    ),
67
    'forum_topic_list' => array(
68
      'template' => 'forum-topic-list',
69
      'variables' => array('tid' => NULL, 'topics' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
70
    ),
71
    'forum_icon' => array(
72
      'template' => 'forum-icon',
73
      'variables' => array('new_posts' => NULL, 'num_posts' => 0, 'comment_mode' => 0, 'sticky' => 0, 'first_new' => FALSE),
74
    ),
75
    'forum_submitted' => array(
76
      'template' => 'forum-submitted',
77
      'variables' => array('topic' => NULL),
78
    ),
79
    'forum_form' => array(
80
      'render element' => 'form',
81
      'file' => 'forum.admin.inc',
82
    ),
83
  );
84
}
85

    
86
/**
87
 * Implements hook_menu().
88
 */
89
function forum_menu() {
90
  $items['forum'] = array(
91
    'title' => 'Forums',
92
    'page callback' => 'forum_page',
93
    'access arguments' => array('access content'),
94
    'file' => 'forum.pages.inc',
95
  );
96
  $items['forum/%forum_forum'] = array(
97
    'title' => 'Forums',
98
    'page callback' => 'forum_page',
99
    'page arguments' => array(1),
100
    'access arguments' => array('access content'),
101
    'file' => 'forum.pages.inc',
102
  );
103
  $items['admin/structure/forum'] = array(
104
    'title' => 'Forums',
105
    'description' => 'Control forum hierarchy settings.',
106
    'page callback' => 'drupal_get_form',
107
    'page arguments' => array('forum_overview'),
108
    'access arguments' => array('administer forums'),
109
    'file' => 'forum.admin.inc',
110
  );
111
  $items['admin/structure/forum/list'] = array(
112
    'title' => 'List',
113
    'type' => MENU_DEFAULT_LOCAL_TASK,
114
    'weight' => -10,
115
  );
116
  $items['admin/structure/forum/add/container'] = array(
117
    'title' => 'Add container',
118
    'page callback' => 'forum_form_main',
119
    'page arguments' => array('container'),
120
    'access arguments' => array('administer forums'),
121
    'type' => MENU_LOCAL_ACTION,
122
    'parent' => 'admin/structure/forum',
123
    'file' => 'forum.admin.inc',
124
  );
125
  $items['admin/structure/forum/add/forum'] = array(
126
    'title' => 'Add forum',
127
    'page callback' => 'forum_form_main',
128
    'page arguments' => array('forum'),
129
    'access arguments' => array('administer forums'),
130
    'type' => MENU_LOCAL_ACTION,
131
    'parent' => 'admin/structure/forum',
132
    'file' => 'forum.admin.inc',
133
  );
134
  $items['admin/structure/forum/settings'] = array(
135
    'title' => 'Settings',
136
    'page callback' => 'drupal_get_form',
137
    'page arguments' => array('forum_admin_settings'),
138
    'access arguments' => array('administer forums'),
139
    'weight' => 5,
140
    'type' => MENU_LOCAL_TASK,
141
    'parent' => 'admin/structure/forum',
142
    'file' => 'forum.admin.inc',
143
  );
144
  $items['admin/structure/forum/edit/container/%taxonomy_term'] = array(
145
    'title' => 'Edit container',
146
    'page callback' => 'forum_form_main',
147
    'page arguments' => array('container', 5),
148
    'access arguments' => array('administer forums'),
149
    'file' => 'forum.admin.inc',
150
  );
151
  $items['admin/structure/forum/edit/forum/%taxonomy_term'] = array(
152
    'title' => 'Edit forum',
153
    'page callback' => 'forum_form_main',
154
    'page arguments' => array('forum', 5),
155
    'access arguments' => array('administer forums'),
156
    'file' => 'forum.admin.inc',
157
  );
158
  return $items;
159
}
160

    
161
/**
162
 * Implements hook_menu_local_tasks_alter().
163
 */
164
function forum_menu_local_tasks_alter(&$data, $router_item, $root_path) {
165
  global $user;
166

    
167
  // Add action link to 'node/add/forum' on 'forum' sub-pages.
168
  if ($root_path == 'forum' || $root_path == 'forum/%') {
169
    $tid = (isset($router_item['page_arguments'][0]) ? $router_item['page_arguments'][0]->tid : 0);
170
    $forum_term = forum_forum_load($tid);
171
    if ($forum_term) {
172
      $links = array();
173
      // Loop through all bundles for forum taxonomy vocabulary field.
174
      $field = field_info_field('taxonomy_forums');
175
      foreach ($field['bundles']['node'] as $type) {
176
        if (node_access('create', $type)) {
177
          $links[$type] = array(
178
            '#theme' => 'menu_local_action',
179
            '#link' => array(
180
              'title' => t('Add new @node_type', array('@node_type' => node_type_get_name($type))),
181
              'href' => 'node/add/' . str_replace('_', '-', $type) . '/' . $forum_term->tid,
182
            ),
183
          );
184
        }
185
      }
186
      if (empty($links)) {
187
        // Authenticated user does not have access to create new topics.
188
        if ($user->uid) {
189
          $links['disallowed'] = array(
190
            '#theme' => 'menu_local_action',
191
            '#link' => array(
192
              'title' => t('You are not allowed to post new content in the forum.'),
193
            ),
194
          );
195
        }
196
        // Anonymous user does not have access to create new topics.
197
        else {
198
          $links['login'] = array(
199
            '#theme' => 'menu_local_action',
200
            '#link' => array(
201
              'title' => t('<a href="@login">Log in</a> to post new content in the forum.', array(
202
                '@login' => url('user/login', array('query' => drupal_get_destination())),
203
              )),
204
              'localized_options' => array('html' => TRUE),
205
            ),
206
          );
207
        }
208
      }
209
      $data['actions']['output'] = array_merge($data['actions']['output'], $links);
210
    }
211
  }
212
}
213

    
214
/**
215
 * Implements hook_entity_info_alter().
216
 */
217
function forum_entity_info_alter(&$info) {
218
  // Take over URI construction for taxonomy terms that are forums.
219
  if ($vid = variable_get('forum_nav_vocabulary', 0)) {
220
    // Within hook_entity_info(), we can't invoke entity_load() as that would
221
    // cause infinite recursion, so we call taxonomy_vocabulary_get_names()
222
    // instead of taxonomy_vocabulary_load(). All we need is the machine name
223
    // of $vid, so retrieving and iterating all the vocabulary names is somewhat
224
    // inefficient, but entity info is cached across page requests, and an
225
    // iteration of all vocabularies once per cache clearing isn't a big deal,
226
    // and is done as part of taxonomy_entity_info() anyway.
227
    foreach (taxonomy_vocabulary_get_names() as $machine_name => $vocabulary) {
228
      if ($vid == $vocabulary->vid) {
229
        $info['taxonomy_term']['bundles'][$machine_name]['uri callback'] = 'forum_uri';
230
      }
231
    }
232
  }
233
}
234

    
235
/**
236
 * Implements callback_entity_info_uri().
237
 *
238
 * Entity URI callback used in forum_entity_info_alter().
239
 */
240
function forum_uri($forum) {
241
  return array(
242
    'path' => 'forum/' . $forum->tid,
243
  );
244
}
245

    
246
/**
247
 * Checks whether a node can be used in a forum, based on its content type.
248
 *
249
 * @param $node
250
 *   A node object.
251
 *
252
 * @return
253
 *   Boolean indicating if the node can be assigned to a forum.
254
 */
255
function _forum_node_check_node_type($node) {
256
  // Fetch information about the forum field.
257
  $field = field_info_instance('node', 'taxonomy_forums', $node->type);
258

    
259
  return is_array($field);
260
}
261

    
262
/**
263
 * Implements hook_node_view().
264
 */
265
function forum_node_view($node, $view_mode) {
266
  if (_forum_node_check_node_type($node)) {
267
    if ($view_mode == 'full' && node_is_page($node)) {
268
      $vid = variable_get('forum_nav_vocabulary', 0);
269
      $vocabulary = taxonomy_vocabulary_load($vid);
270
      // Breadcrumb navigation
271
      $breadcrumb[] = l(t('Home'), NULL);
272
      $breadcrumb[] = l($vocabulary->name, 'forum');
273
      if ($parents = taxonomy_get_parents_all($node->forum_tid)) {
274
        $parents = array_reverse($parents);
275
        foreach ($parents as $parent) {
276
          $breadcrumb[] = l($parent->name, 'forum/' . $parent->tid);
277
        }
278
      }
279
      drupal_set_breadcrumb($breadcrumb);
280

    
281
    }
282
  }
283
}
284

    
285
/**
286
 * Implements hook_node_validate().
287
 *
288
 * Checks in particular that the node is assigned only a "leaf" term in the
289
 * forum taxonomy.
290
 */
291
function forum_node_validate($node, $form) {
292
  if (_forum_node_check_node_type($node)) {
293
    $langcode = $form['taxonomy_forums']['#language'];
294
    // vocabulary is selected, not a "container" term.
295
    if (!empty($node->taxonomy_forums[$langcode])) {
296
      // Extract the node's proper topic ID.
297
      $containers = variable_get('forum_containers', array());
298
      foreach ($node->taxonomy_forums[$langcode] as $delta => $item) {
299
        // If no term was selected (e.g. when no terms exist yet), remove the
300
        // item.
301
        if (empty($item['tid'])) {
302
          unset($node->taxonomy_forums[$langcode][$delta]);
303
          continue;
304
        }
305
        $term = taxonomy_term_load($item['tid']);
306
        if (!$term) {
307
          form_set_error('taxonomy_forums', t('Select a forum.'));
308
          continue;
309
        }
310
        $used = db_query_range('SELECT 1 FROM {taxonomy_term_data} WHERE tid = :tid AND vid = :vid',0 , 1, array(
311
          ':tid' => $term->tid,
312
          ':vid' => $term->vid,
313
        ))->fetchField();
314
        if ($used && in_array($term->tid, $containers)) {
315
          form_set_error('taxonomy_forums', t('The item %forum is a forum container, not a forum. Select one of the forums below instead.', array('%forum' => $term->name)));
316
        }
317
      }
318
    }
319
  }
320
}
321

    
322
/**
323
 * Implements hook_node_presave().
324
 *
325
 * Assigns the forum taxonomy when adding a topic from within a forum.
326
 */
327
function forum_node_presave($node) {
328
  if (_forum_node_check_node_type($node)) {
329
    // Make sure all fields are set properly:
330
    $node->icon = !empty($node->icon) ? $node->icon : '';
331
    reset($node->taxonomy_forums);
332
    $langcode = key($node->taxonomy_forums);
333
    if (!empty($node->taxonomy_forums[$langcode])) {
334
      $node->forum_tid = $node->taxonomy_forums[$langcode][0]['tid'];
335
      if (isset($node->nid)) {
336
        $old_tid = db_query_range("SELECT f.tid FROM {forum} f INNER JOIN {node} n ON f.vid = n.vid WHERE n.nid = :nid ORDER BY f.vid DESC", 0, 1, array(':nid' => $node->nid))->fetchField();
337
        if ($old_tid && isset($node->forum_tid) && ($node->forum_tid != $old_tid) && !empty($node->shadow)) {
338
          // A shadow copy needs to be created. Retain new term and add old term.
339
          $node->taxonomy_forums[$langcode][] = array('tid' => $old_tid);
340
        }
341
      }
342
    }
343
  }
344
}
345

    
346
/**
347
 * Implements hook_node_update().
348
 */
349
function forum_node_update($node) {
350
  if (_forum_node_check_node_type($node)) {
351
    if (empty($node->revision) && db_query('SELECT tid FROM {forum} WHERE nid=:nid', array(':nid' => $node->nid))->fetchField()) {
352
      if (!empty($node->forum_tid)) {
353
        db_update('forum')
354
          ->fields(array('tid' => $node->forum_tid))
355
          ->condition('vid', $node->vid)
356
          ->execute();
357
      }
358
      // The node is removed from the forum.
359
      else {
360
        db_delete('forum')
361
          ->condition('nid', $node->nid)
362
          ->execute();
363
      }
364
    }
365
    else {
366
      if (!empty($node->forum_tid)) {
367
        db_insert('forum')
368
          ->fields(array(
369
            'tid' => $node->forum_tid,
370
            'vid' => $node->vid,
371
            'nid' => $node->nid,
372
          ))
373
          ->execute();
374
      }
375
    }
376
    // If the node has a shadow forum topic, update the record for this
377
    // revision.
378
    if (!empty($node->shadow)) {
379
      db_delete('forum')
380
        ->condition('nid', $node->nid)
381
        ->condition('vid', $node->vid)
382
        ->execute();
383
      db_insert('forum')
384
        ->fields(array(
385
          'nid' => $node->nid,
386
          'vid' => $node->vid,
387
          'tid' => $node->forum_tid,
388
        ))
389
        ->execute();
390
     }
391
  }
392
}
393

    
394
/**
395
 * Implements hook_node_insert().
396
 */
397
function forum_node_insert($node) {
398
  if (_forum_node_check_node_type($node)) {
399
    if (!empty($node->forum_tid)) {
400
      $nid = db_insert('forum')
401
        ->fields(array(
402
          'tid' => $node->forum_tid,
403
          'vid' => $node->vid,
404
          'nid' => $node->nid,
405
        ))
406
        ->execute();
407
    }
408
  }
409
}
410

    
411
/**
412
 * Implements hook_node_delete().
413
 */
414
function forum_node_delete($node) {
415
  if (_forum_node_check_node_type($node)) {
416
    db_delete('forum')
417
      ->condition('nid', $node->nid)
418
      ->execute();
419
    db_delete('forum_index')
420
      ->condition('nid', $node->nid)
421
      ->execute();
422
  }
423
}
424

    
425
/**
426
 * Implements hook_node_load().
427
 */
428
function forum_node_load($nodes) {
429
  $node_vids = array();
430
  foreach ($nodes as $node) {
431
    if (_forum_node_check_node_type($node)) {
432
      $node_vids[] = $node->vid;
433
    }
434
  }
435
  if (!empty($node_vids)) {
436
    $query = db_select('forum', 'f');
437
    $query
438
      ->fields('f', array('nid', 'tid'))
439
      ->condition('f.vid', $node_vids);
440
    $result = $query->execute();
441
    foreach ($result as $record) {
442
      $nodes[$record->nid]->forum_tid = $record->tid;
443
    }
444
  }
445
}
446

    
447
/**
448
 * Implements hook_node_info().
449
 */
450
function forum_node_info() {
451
  return array(
452
    'forum' => array(
453
      'name' => t('Forum topic'),
454
      'base' => 'forum',
455
      'description' => t('A <em>forum topic</em> starts a new discussion thread within a forum.'),
456
      'title_label' => t('Subject'),
457
    )
458
  );
459
}
460

    
461
/**
462
 * Implements hook_permission().
463
 */
464
function forum_permission() {
465
  $perms = array(
466
    'administer forums' => array(
467
      'title' => t('Administer forums'),
468
    ),
469
  );
470
  return $perms;
471
}
472

    
473
/**
474
 * Implements hook_taxonomy_term_delete().
475
 */
476
function forum_taxonomy_term_delete($term) {
477
  // For containers, remove the tid from the forum_containers variable.
478
  $containers = variable_get('forum_containers', array());
479
  $key = array_search($term->tid, $containers);
480
  if ($key !== FALSE) {
481
    unset($containers[$key]);
482
  }
483
  variable_set('forum_containers', $containers);
484
}
485

    
486
/**
487
 * Implements hook_comment_publish().
488
 *
489
 * This actually handles the insertion and update of published nodes since
490
 * comment_save() calls hook_comment_publish() for all published comments.
491
 */
492
function forum_comment_publish($comment) {
493
  _forum_update_forum_index($comment->nid);
494
}
495

    
496
/**
497
 * Implements hook_comment_update().
498
 *
499
 * The Comment module doesn't call hook_comment_unpublish() when saving
500
 * individual comments, so we need to check for those here.
501
 */
502
function forum_comment_update($comment) {
503
  // comment_save() calls hook_comment_publish() for all published comments,
504
  // so we need to handle all other values here.
505
  if (!$comment->status) {
506
    _forum_update_forum_index($comment->nid);
507
  }
508
}
509

    
510
/**
511
 * Implements hook_comment_unpublish().
512
 */
513
function forum_comment_unpublish($comment) {
514
  _forum_update_forum_index($comment->nid);
515
}
516

    
517
/**
518
 * Implements hook_comment_delete().
519
 */
520
function forum_comment_delete($comment) {
521
  _forum_update_forum_index($comment->nid);
522
}
523

    
524
/**
525
 * Implements hook_field_storage_pre_insert().
526
 */
527
function forum_field_storage_pre_insert($entity_type, $entity, &$skip_fields) {
528
  if ($entity_type == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
529
    $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
530
    foreach ($entity->taxonomy_forums as $language) {
531
      foreach ($language as $item) {
532
        $query->values(array(
533
          'nid' => $entity->nid,
534
          'title' => $entity->title,
535
          'tid' => $item['tid'],
536
          'sticky' => $entity->sticky,
537
          'created' => $entity->created,
538
          'comment_count' => 0,
539
          'last_comment_timestamp' => $entity->created,
540
        ));
541
      }
542
    }
543
    $query->execute();
544
  }
545
}
546

    
547
/**
548
 * Implements hook_field_storage_pre_update().
549
 */
550
function forum_field_storage_pre_update($entity_type, $entity, &$skip_fields) {
551
  $first_call = &drupal_static(__FUNCTION__, array());
552

    
553
  if ($entity_type == 'node' && _forum_node_check_node_type($entity)) {
554

    
555
    // If the node is published, update the forum index.
556
    if ($entity->status) {
557

    
558
      // We don't maintain data for old revisions, so clear all previous values
559
      // from the table. Since this hook runs once per field, per object, make
560
      // sure we only wipe values once.
561
      if (!isset($first_call[$entity->nid])) {
562
        $first_call[$entity->nid] = FALSE;
563
        db_delete('forum_index')->condition('nid', $entity->nid)->execute();
564
      }
565
      $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
566
      foreach ($entity->taxonomy_forums as $language) {
567
        foreach ($language as $item) {
568
          $query->values(array(
569
            'nid' => $entity->nid,
570
            'title' => $entity->title,
571
            'tid' => $item['tid'],
572
            'sticky' => $entity->sticky,
573
            'created' => $entity->created,
574
            'comment_count' => 0,
575
            'last_comment_timestamp' => $entity->created,
576
          ));
577
        }
578
      }
579
      $query->execute();
580
      // The logic for determining last_comment_count is fairly complex, so
581
      // call _forum_update_forum_index() too.
582
      _forum_update_forum_index($entity->nid);
583
    }
584

    
585
    // When a forum node is unpublished, remove it from the forum_index table.
586
    else {
587
      db_delete('forum_index')->condition('nid', $entity->nid)->execute();
588
    }
589

    
590
  }
591
}
592

    
593
/**
594
 * Implements hook_form_FORM_ID_alter() for taxonomy_form_vocabulary().
595
 */
596
function forum_form_taxonomy_form_vocabulary_alter(&$form, &$form_state, $form_id) {
597
  $vid = variable_get('forum_nav_vocabulary', 0);
598
  if (isset($form['vid']['#value']) && $form['vid']['#value'] == $vid) {
599
    $form['help_forum_vocab'] = array(
600
      '#markup' => t('This is the designated forum vocabulary. Some of the normal vocabulary options have been removed.'),
601
      '#weight' => -1,
602
    );
603
    // Forum's vocabulary always has single hierarchy. Forums and containers
604
    // have only one parent or no parent for root items. By default this value
605
    // is 0.
606
    $form['hierarchy']['#value'] = 1;
607
    // Do not allow to delete forum's vocabulary.
608
    $form['actions']['delete']['#access'] = FALSE;
609
  }
610
}
611

    
612
/**
613
 * Implements hook_form_FORM_ID_alter() for taxonomy_form_term().
614
 */
615
function forum_form_taxonomy_form_term_alter(&$form, &$form_state, $form_id) {
616
   $vid = variable_get('forum_nav_vocabulary', 0);
617
   if (isset($form['vid']['#value']) && $form['vid']['#value'] == $vid) {
618
    // Hide multiple parents select from forum terms.
619
    $form['relations']['parent']['#access'] = FALSE;
620
  }
621
}
622

    
623
/**
624
 * Implements hook_form_BASE_FORM_ID_alter() for node_form().
625
 */
626
function forum_form_node_form_alter(&$form, &$form_state, $form_id) {
627
  if (isset($form['taxonomy_forums'])) {
628
    $langcode = $form['taxonomy_forums']['#language'];
629
    // Make the vocabulary required for 'real' forum-nodes.
630
    $form['taxonomy_forums'][$langcode]['#required'] = TRUE;
631
    $form['taxonomy_forums'][$langcode]['#multiple'] = FALSE;
632
    if (empty($form['taxonomy_forums'][$langcode]['#default_value'])) {
633
      // If there is no default forum already selected, try to get the forum
634
      // ID from the URL (e.g., if we are on a page like node/add/forum/2, we
635
      // expect "2" to be the ID of the forum that was requested).
636
      $requested_forum_id = arg(3);
637
      $form['taxonomy_forums'][$langcode]['#default_value'] = is_numeric($requested_forum_id) ? $requested_forum_id : '';
638
    }
639
  }
640
}
641

    
642
/**
643
 * Implements hook_block_info().
644
 */
645
function forum_block_info() {
646
  $blocks['active'] = array(
647
    'info' => t('Active forum topics'),
648
    'cache' => DRUPAL_CACHE_CUSTOM,
649
    'properties' => array('administrative' => TRUE),
650
  );
651
  $blocks['new'] = array(
652
    'info' => t('New forum topics'),
653
    'cache' => DRUPAL_CACHE_CUSTOM,
654
    'properties' => array('administrative' => TRUE),
655
  );
656
  return $blocks;
657
}
658

    
659
/**
660
 * Implements hook_block_configure().
661
 */
662
function forum_block_configure($delta = '') {
663
  $form['forum_block_num_' . $delta] = array(
664
    '#type' => 'select',
665
    '#title' => t('Number of topics'),
666
    '#default_value' => variable_get('forum_block_num_' . $delta, '5'),
667
    '#options' => drupal_map_assoc(range(2, 20))
668
  );
669
  return $form;
670
}
671

    
672
/**
673
 * Implements hook_block_save().
674
 */
675
function forum_block_save($delta = '', $edit = array()) {
676
  variable_set('forum_block_num_' . $delta, $edit['forum_block_num_' . $delta]);
677
}
678

    
679
/**
680
 * Implements hook_block_view().
681
 *
682
 * Generates a block containing the currently active forum topics and the most
683
 * recently added forum topics.
684
 */
685
function forum_block_view($delta = '') {
686
  $query = db_select('forum_index', 'f')
687
    ->fields('f')
688
    ->addTag('node_access');
689
  switch ($delta) {
690
    case 'active':
691
      $title = t('Active forum topics');
692
      $query
693
        ->orderBy('f.last_comment_timestamp', 'DESC')
694
        ->range(0, variable_get('forum_block_num_active', '5'));
695
      break;
696

    
697
    case 'new':
698
      $title = t('New forum topics');
699
      $query
700
        ->orderBy('f.created', 'DESC')
701
        ->range(0, variable_get('forum_block_num_new', '5'));
702
      break;
703
  }
704

    
705
  $block['subject'] = $title;
706
  // Cache based on the altered query. Enables us to cache with node access enabled.
707
  $block['content'] = drupal_render_cache_by_query($query, 'forum_block_view');
708
  $block['content']['#access'] = user_access('access content');
709
  return $block;
710
}
711

    
712
/**
713
 * Render API callback: Lists nodes based on the element's #query property.
714
 *
715
 * This function can be used as a #pre_render callback.
716
 *
717
 * @see forum_block_view()
718
 */
719
function forum_block_view_pre_render($elements) {
720
  $result = $elements['#query']->execute();
721
  if ($node_title_list = node_title_list($result)) {
722
    $elements['forum_list'] = $node_title_list;
723
    $elements['forum_more'] = array('#theme' => 'more_link', '#url' => 'forum', '#title' => t('Read the latest forum topics.'));
724
  }
725
  return $elements;
726
}
727

    
728
/**
729
 * Implements hook_form().
730
 */
731
function forum_form($node, $form_state) {
732
  $type = node_type_get_type($node);
733
  $form['title'] = array(
734
    '#type' => 'textfield',
735
    '#title' => check_plain($type->title_label),
736
    '#default_value' => !empty($node->title) ? $node->title : '',
737
    '#required' => TRUE, '#weight' => -5
738
  );
739

    
740
  if (!empty($node->nid)) {
741
    $forum_terms = $node->taxonomy_forums;
742
    // If editing, give option to leave shadows.
743
    $shadow = (count($forum_terms) > 1);
744
    $form['shadow'] = array('#type' => 'checkbox', '#title' => t('Leave shadow copy'), '#default_value' => $shadow, '#description' => t('If you move this topic, you can leave a link in the old forum to the new forum.'));
745
    $form['forum_tid'] = array('#type' => 'value', '#value' => $node->forum_tid);
746
  }
747

    
748
  return $form;
749
}
750

    
751
/**
752
 * Returns a tree of all forums for a given taxonomy term ID.
753
 *
754
 * @param $tid
755
 *   (optional) Taxonomy term ID of the forum. If not given all forums will be
756
 *   returned.
757
 *
758
 * @return
759
 *   A tree of taxonomy objects, with the following additional properties:
760
 *   - num_topics: Number of topics in the forum.
761
 *   - num_posts: Total number of posts in all topics.
762
 *   - last_post: Most recent post for the forum.
763
 *   - forums: An array of child forums.
764
 */
765
function forum_forum_load($tid = NULL) {
766
  $cache = &drupal_static(__FUNCTION__, array());
767

    
768
  // Return a cached forum tree if available.
769
  if (!isset($tid)) {
770
    $tid = 0;
771
  }
772
  if (isset($cache[$tid])) {
773
    return $cache[$tid];
774
  }
775

    
776
  $vid = variable_get('forum_nav_vocabulary', 0);
777

    
778
  // Load and validate the parent term.
779
  if ($tid) {
780
    $forum_term = taxonomy_term_load($tid);
781
    if (!$forum_term || ($forum_term->vid != $vid)) {
782
      return $cache[$tid] = FALSE;
783
    }
784
  }
785
  // If $tid is 0, create an empty object to hold the child terms.
786
  elseif ($tid === 0) {
787
    $forum_term = (object) array(
788
      'tid' => 0,
789
    );
790
  }
791

    
792
  // Determine if the requested term is a container.
793
  if (!$forum_term->tid || in_array($forum_term->tid, variable_get('forum_containers', array()))) {
794
    $forum_term->container = 1;
795
  }
796

    
797
  // Load parent terms.
798
  $forum_term->parents = taxonomy_get_parents_all($forum_term->tid);
799

    
800
  // Load the tree below.
801
  $forums = array();
802
  $_forums = taxonomy_get_tree($vid, $tid);
803

    
804
  if (count($_forums)) {
805
    $query = db_select('node', 'n');
806
    $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
807
    $query->join('forum', 'f', 'n.vid = f.vid');
808
    $query->addExpression('COUNT(n.nid)', 'topic_count');
809
    $query->addExpression('SUM(ncs.comment_count)', 'comment_count');
810
    $counts = $query
811
      ->fields('f', array('tid'))
812
      ->condition('n.status', 1)
813
      ->groupBy('tid')
814
      ->addTag('node_access')
815
      ->execute()
816
      ->fetchAllAssoc('tid');
817
  }
818

    
819
  foreach ($_forums as $forum) {
820
    // Determine if the child term is a container.
821
    if (in_array($forum->tid, variable_get('forum_containers', array()))) {
822
      $forum->container = 1;
823
    }
824

    
825
    // Merge in the topic and post counters.
826
    if (!empty($counts[$forum->tid])) {
827
      $forum->num_topics = $counts[$forum->tid]->topic_count;
828
      $forum->num_posts = $counts[$forum->tid]->topic_count + $counts[$forum->tid]->comment_count;
829
    }
830
    else {
831
      $forum->num_topics = 0;
832
      $forum->num_posts = 0;
833
    }
834

    
835
    // Query "Last Post" information for this forum.
836
    $query = db_select('node', 'n');
837
    $query->join('users', 'u1', 'n.uid = u1.uid');
838
    $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $forum->tid));
839
    $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
840
    $query->join('users', 'u2', 'ncs.last_comment_uid = u2.uid');
841
    $query->addExpression('CASE ncs.last_comment_uid WHEN 0 THEN ncs.last_comment_name ELSE u2.name END', 'last_comment_name');
842

    
843
    $topic = $query
844
      ->fields('ncs', array('last_comment_timestamp', 'last_comment_uid'))
845
      ->condition('n.status', 1)
846
      ->orderBy('last_comment_timestamp', 'DESC')
847
      ->range(0, 1)
848
      ->addTag('node_access')
849
      ->execute()
850
      ->fetchObject();
851

    
852
    // Merge in the "Last Post" information.
853
    $last_post = new stdClass();
854
    if (!empty($topic->last_comment_timestamp)) {
855
      $last_post->created = $topic->last_comment_timestamp;
856
      $last_post->name = $topic->last_comment_name;
857
      $last_post->uid = $topic->last_comment_uid;
858
    }
859
    $forum->last_post = $last_post;
860

    
861
    $forums[$forum->tid] = $forum;
862
  }
863

    
864
  // Cache the result, and return the tree.
865
  $forum_term->forums = $forums;
866
  $cache[$tid] = $forum_term;
867
  return $forum_term;
868
}
869

    
870
/**
871
 * Calculates the number of new posts in a forum that the user has not yet read.
872
 *
873
 * Nodes are new if they are newer than NODE_NEW_LIMIT.
874
 *
875
 * @param $term
876
 *   The term ID of the forum.
877
 * @param $uid
878
 *   The user ID.
879
 *
880
 * @return
881
 *   The number of new posts in the forum that have not been read by the user.
882
 */
883
function _forum_topics_unread($term, $uid) {
884
  $query = db_select('node', 'n');
885
  $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $term));
886
  $query->leftJoin('history', 'h', 'n.nid = h.nid AND h.uid = :uid', array(':uid' => $uid));
887
  $query->addExpression('COUNT(n.nid)', 'count');
888
  return $query
889
    ->condition('status', 1)
890
    ->condition('n.created', NODE_NEW_LIMIT, '>')
891
    ->isNull('h.nid')
892
    ->addTag('node_access')
893
    ->execute()
894
    ->fetchField();
895
}
896

    
897
/**
898
 * Gets all the topics in a forum.
899
 *
900
 * @param $tid
901
 *   The term ID of the forum.
902
 * @param $sortby
903
 *   One of the following integers indicating the sort criteria:
904
 *   - 1: Date - newest first.
905
 *   - 2: Date - oldest first.
906
 *   - 3: Posts with the most comments first.
907
 *   - 4: Posts with the least comments first.
908
 * @param $forum_per_page
909
 *   The maximum number of topics to display per page.
910
 *
911
 * @return
912
 *   A list of all the topics in a forum.
913
 */
914
function forum_get_topics($tid, $sortby, $forum_per_page) {
915
  global $user, $forum_topic_list_header;
916

    
917
  $forum_topic_list_header = array(
918
    NULL,
919
    array('data' => t('Topic'), 'field' => 'f.title'),
920
    array('data' => t('Replies'), 'field' => 'f.comment_count'),
921
    array('data' => t('Last reply'), 'field' => 'f.last_comment_timestamp'),
922
  );
923

    
924
  $order = _forum_get_topic_order($sortby);
925
  // Skip element with index 0 which is NULL.
926
  for ($i = 1; $i < count($forum_topic_list_header); $i++) {
927
    if ($forum_topic_list_header[$i]['field'] == $order['field']) {
928
      $forum_topic_list_header[$i]['sort'] = $order['sort'];
929
    }
930
  }
931

    
932
  $query = db_select('forum_index', 'f')->extend('PagerDefault')->extend('TableSort');
933
  $query->fields('f');
934
  $query
935
    ->condition('f.tid', $tid)
936
    ->addTag('node_access')
937
    ->orderBy('f.sticky', 'DESC')
938
    ->orderByHeader($forum_topic_list_header)
939
    ->limit($forum_per_page);
940

    
941
  $count_query = db_select('forum_index', 'f');
942
  $count_query->condition('f.tid', $tid);
943
  $count_query->addExpression('COUNT(*)');
944
  $count_query->addTag('node_access');
945

    
946
  $query->setCountQuery($count_query);
947
  $result = $query->execute();
948
  $nids = array();
949
  foreach ($result as $record) {
950
    $nids[] = $record->nid;
951
  }
952
  if ($nids) {
953
    $query = db_select('node', 'n')->extend('TableSort');
954
    $query->fields('n', array('title', 'nid', 'type', 'sticky', 'created', 'uid'));
955
    $query->addField('n', 'comment', 'comment_mode');
956

    
957
    $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
958
    $query->fields('ncs', array('cid', 'last_comment_uid', 'last_comment_timestamp', 'comment_count'));
959

    
960
    $query->join('forum_index', 'f', 'f.nid = ncs.nid');
961
    $query->addField('f', 'tid', 'forum_tid');
962

    
963
    $query->join('users', 'u', 'n.uid = u.uid');
964
    $query->addField('u', 'name');
965

    
966
    $query->join('users', 'u2', 'ncs.last_comment_uid = u2.uid');
967

    
968
    $query->addExpression('CASE ncs.last_comment_uid WHEN 0 THEN ncs.last_comment_name ELSE u2.name END', 'last_comment_name');
969

    
970
    $query
971
      ->orderBy('f.sticky', 'DESC')
972
      ->orderByHeader($forum_topic_list_header)
973
      ->condition('n.nid', $nids);
974

    
975
    $result = $query->execute();
976
  }
977
  else {
978
    $result = array();
979
  }
980

    
981
  $topics = array();
982
  $first_new_found = FALSE;
983
  foreach ($result as $topic) {
984
    if ($user->uid) {
985
      // A forum is new if the topic is new, or if there are new comments since
986
      // the user's last visit.
987
      if ($topic->forum_tid != $tid) {
988
        $topic->new = 0;
989
      }
990
      else {
991
        $history = _forum_user_last_visit($topic->nid);
992
        $topic->new_replies = comment_num_new($topic->nid, $history);
993
        $topic->new = $topic->new_replies || ($topic->last_comment_timestamp > $history);
994
      }
995
    }
996
    else {
997
      // Do not track "new replies" status for topics if the user is anonymous.
998
      $topic->new_replies = 0;
999
      $topic->new = 0;
1000
    }
1001

    
1002
    // Make sure only one topic is indicated as the first new topic.
1003
    $topic->first_new = FALSE;
1004
    if ($topic->new != 0 && !$first_new_found) {
1005
      $topic->first_new = TRUE;
1006
      $first_new_found = TRUE;
1007
    }
1008

    
1009
    if ($topic->comment_count > 0) {
1010
      $last_reply = new stdClass();
1011
      $last_reply->created = $topic->last_comment_timestamp;
1012
      $last_reply->name = $topic->last_comment_name;
1013
      $last_reply->uid = $topic->last_comment_uid;
1014
      $topic->last_reply = $last_reply;
1015
    }
1016
    $topics[] = $topic;
1017
  }
1018

    
1019
  return $topics;
1020
}
1021

    
1022
/**
1023
 * Preprocesses variables for forums.tpl.php.
1024
 *
1025
 * @param $variables
1026
 *   An array containing the following elements:
1027
 *   - forums: An array of all forum objects to display for the given taxonomy
1028
 *     term ID. If tid = 0 then all the top-level forums are displayed.
1029
 *   - topics: An array of all the topics in the current forum.
1030
 *   - parents: An array of taxonomy term objects that are ancestors of the
1031
 *     current term ID.
1032
 *   - tid: Taxonomy term ID of the current forum.
1033
 *   - sortby: One of the following integers indicating the sort criteria:
1034
 *     - 1: Date - newest first.
1035
 *     - 2: Date - oldest first.
1036
 *     - 3: Posts with the most comments first.
1037
 *     - 4: Posts with the least comments first.
1038
 *   - forum_per_page: The maximum number of topics to display per page.
1039
 *
1040
 * @see forums.tpl.php
1041
 */
1042
function template_preprocess_forums(&$variables) {
1043
  global $user;
1044

    
1045
  $vid = variable_get('forum_nav_vocabulary', 0);
1046
  $vocabulary = taxonomy_vocabulary_load($vid);
1047
  $title = !empty($vocabulary->name) ? $vocabulary->name : '';
1048

    
1049
  // Breadcrumb navigation:
1050
  $breadcrumb[] = l(t('Home'), NULL);
1051
  if ($variables['tid']) {
1052
    $breadcrumb[] = l($vocabulary->name, 'forum');
1053
  }
1054
  if ($variables['parents']) {
1055
    $variables['parents'] = array_reverse($variables['parents']);
1056
    foreach ($variables['parents'] as $p) {
1057
      if ($p->tid == $variables['tid']) {
1058
        $title = $p->name;
1059
      }
1060
      else {
1061
        $breadcrumb[] = l($p->name, 'forum/' . $p->tid);
1062
      }
1063
    }
1064
  }
1065
  drupal_set_breadcrumb($breadcrumb);
1066
  drupal_set_title($title);
1067

    
1068
  if ($variables['forums_defined'] = count($variables['forums']) || count($variables['parents'])) {
1069
    if (!empty($variables['forums'])) {
1070
      $variables['forums'] = theme('forum_list', $variables);
1071
    }
1072
    else {
1073
      $variables['forums'] = '';
1074
    }
1075

    
1076
    if ($variables['tid'] && !in_array($variables['tid'], variable_get('forum_containers', array()))) {
1077
      $variables['topics'] = theme('forum_topic_list', $variables);
1078
      drupal_add_feed('taxonomy/term/' . $variables['tid'] . '/feed', 'RSS - ' . $title);
1079
    }
1080
    else {
1081
      $variables['topics'] = '';
1082
    }
1083

    
1084
    // Provide separate template suggestions based on what's being output. Topic id is also accounted for.
1085
    // Check both variables to be safe then the inverse. Forums with topic ID's take precedence.
1086
    if ($variables['forums'] && !$variables['topics']) {
1087
      $variables['theme_hook_suggestions'][] = 'forums__containers';
1088
      $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid'];
1089
      $variables['theme_hook_suggestions'][] = 'forums__containers__' . $variables['tid'];
1090
    }
1091
    elseif (!$variables['forums'] && $variables['topics']) {
1092
      $variables['theme_hook_suggestions'][] = 'forums__topics';
1093
      $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid'];
1094
      $variables['theme_hook_suggestions'][] = 'forums__topics__' . $variables['tid'];
1095
    }
1096
    else {
1097
      $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid'];
1098
    }
1099

    
1100
  }
1101
  else {
1102
    drupal_set_title(t('No forums defined'));
1103
    $variables['forums'] = '';
1104
    $variables['topics'] = '';
1105
  }
1106
}
1107

    
1108
/**
1109
 * Preprocesses variables for forum-list.tpl.php.
1110
 *
1111
 * @param $variables
1112
 *   An array containing the following elements:
1113
 *   - forums: An array of all forum objects to display for the given taxonomy
1114
 *     term ID. If tid = 0 then all the top-level forums are displayed.
1115
 *   - parents: An array of taxonomy term objects that are ancestors of the
1116
 *     current term ID.
1117
 *   - tid: Taxonomy term ID of the current forum.
1118
 *
1119
 * @see forum-list.tpl.php
1120
 * @see theme_forum_list()
1121
 */
1122
function template_preprocess_forum_list(&$variables) {
1123
  global $user;
1124
  $row = 0;
1125
  // Sanitize each forum so that the template can safely print the data.
1126
  foreach ($variables['forums'] as $id => $forum) {
1127
    $variables['forums'][$id]->description = !empty($forum->description) ? filter_xss_admin($forum->description) : '';
1128
    $variables['forums'][$id]->link = url("forum/$forum->tid");
1129
    $variables['forums'][$id]->name = check_plain($forum->name);
1130
    $variables['forums'][$id]->is_container = !empty($forum->container);
1131
    $variables['forums'][$id]->zebra = $row % 2 == 0 ? 'odd' : 'even';
1132
    $row++;
1133

    
1134
    $variables['forums'][$id]->new_text = '';
1135
    $variables['forums'][$id]->new_url = '';
1136
    $variables['forums'][$id]->new_topics = 0;
1137
    $variables['forums'][$id]->old_topics = $forum->num_topics;
1138
    $variables['forums'][$id]->icon_class = 'default';
1139
    $variables['forums'][$id]->icon_title = t('No new posts');
1140
    if ($user->uid) {
1141
      $variables['forums'][$id]->new_topics = _forum_topics_unread($forum->tid, $user->uid);
1142
      if ($variables['forums'][$id]->new_topics) {
1143
        $variables['forums'][$id]->new_text = format_plural($variables['forums'][$id]->new_topics, '1 new', '@count new');
1144
        $variables['forums'][$id]->new_url = url("forum/$forum->tid", array('fragment' => 'new'));
1145
        $variables['forums'][$id]->icon_class = 'new';
1146
        $variables['forums'][$id]->icon_title = t('New posts');
1147
      }
1148
      $variables['forums'][$id]->old_topics = $forum->num_topics - $variables['forums'][$id]->new_topics;
1149
    }
1150
    $variables['forums'][$id]->last_reply = theme('forum_submitted', array('topic' => $forum->last_post));
1151
  }
1152
  // Give meaning to $tid for themers. $tid actually stands for term id.
1153
  $variables['forum_id'] = $variables['tid'];
1154
  unset($variables['tid']);
1155
}
1156

    
1157
/**
1158
 * Preprocesses variables for forum-topic-list.tpl.php.
1159
 *
1160
 * @param $variables
1161
 *   An array containing the following elements:
1162
 *   - tid: Taxonomy term ID of the current forum.
1163
 *   - topics: An array of all the topics in the current forum.
1164
 *   - forum_per_page: The maximum number of topics to display per page.
1165
 *
1166
 * @see forum-topic-list.tpl.php
1167
 * @see theme_forum_topic_list()
1168
 */
1169
function template_preprocess_forum_topic_list(&$variables) {
1170
  global $forum_topic_list_header;
1171

    
1172
  // Create the tablesorting header.
1173
  $ts = tablesort_init($forum_topic_list_header);
1174
  $header = '';
1175
  foreach ($forum_topic_list_header as $cell) {
1176
    $cell = tablesort_header($cell, $forum_topic_list_header, $ts);
1177
    $header .= _theme_table_cell($cell, TRUE);
1178
  }
1179
  $variables['header'] = $header;
1180

    
1181
  if (!empty($variables['topics'])) {
1182
    $row = 0;
1183
    foreach ($variables['topics'] as $id => $topic) {
1184
      $variables['topics'][$id]->icon = theme('forum_icon', array('new_posts' => $topic->new, 'num_posts' => $topic->comment_count, 'comment_mode' => $topic->comment_mode, 'sticky' => $topic->sticky, 'first_new' => $topic->first_new));
1185
      $variables['topics'][$id]->zebra = $row % 2 == 0 ? 'odd' : 'even';
1186
      $row++;
1187

    
1188
      // We keep the actual tid in forum table, if it's different from the
1189
      // current tid then it means the topic appears in two forums, one of
1190
      // them is a shadow copy.
1191
      if ($variables['tid'] != $topic->forum_tid) {
1192
        $variables['topics'][$id]->moved = TRUE;
1193
        $variables['topics'][$id]->title = check_plain($topic->title);
1194
        $variables['topics'][$id]->message = l(t('This topic has been moved'), "forum/$topic->forum_tid");
1195
      }
1196
      else {
1197
        $variables['topics'][$id]->moved = FALSE;
1198
        $variables['topics'][$id]->title = l($topic->title, "node/$topic->nid");
1199
        $variables['topics'][$id]->message = '';
1200
      }
1201
      $variables['topics'][$id]->created = theme('forum_submitted', array('topic' => $topic));
1202
      $variables['topics'][$id]->last_reply = theme('forum_submitted', array('topic' => isset($topic->last_reply) ? $topic->last_reply : NULL));
1203

    
1204
      $variables['topics'][$id]->new_text = '';
1205
      $variables['topics'][$id]->new_url = '';
1206
      if ($topic->new_replies) {
1207
        $variables['topics'][$id]->new_text = format_plural($topic->new_replies, '1 new', '@count new');
1208
        $variables['topics'][$id]->new_url = url("node/$topic->nid", array('query' => comment_new_page_count($topic->comment_count, $topic->new_replies, $topic), 'fragment' => 'new'));
1209
      }
1210

    
1211
    }
1212
  }
1213
  else {
1214
    // Make this safe for the template.
1215
    $variables['topics'] = array();
1216
  }
1217
  // Give meaning to $tid for themers. $tid actually stands for term id.
1218
  $variables['topic_id'] = $variables['tid'];
1219
  unset($variables['tid']);
1220

    
1221
  $variables['pager'] = theme('pager');
1222
}
1223

    
1224
/**
1225
 * Preprocesses variables for forum-icon.tpl.php.
1226
 *
1227
 * @param $variables
1228
 *   An array containing the following elements:
1229
 *   - new_posts: Indicates whether or not the topic contains new posts.
1230
 *   - num_posts: The total number of posts in all topics.
1231
 *   - comment_mode: An integer indicating whether comments are open, closed,
1232
 *     or hidden.
1233
 *   - sticky: Indicates whether the topic is sticky.
1234
 *   - first_new: Indicates whether this is the first topic with new posts.
1235
 *
1236
 * @see forum-icon.tpl.php
1237
 * @see theme_forum_icon()
1238
 */
1239
function template_preprocess_forum_icon(&$variables) {
1240
  $variables['hot_threshold'] = variable_get('forum_hot_topic', 15);
1241
  if ($variables['num_posts'] > $variables['hot_threshold']) {
1242
    $variables['icon_class'] = $variables['new_posts'] ? 'hot-new' : 'hot';
1243
    $variables['icon_title'] = $variables['new_posts'] ? t('Hot topic, new comments') : t('Hot topic');
1244
  }
1245
  else {
1246
    $variables['icon_class'] = $variables['new_posts'] ? 'new' : 'default';
1247
    $variables['icon_title'] = $variables['new_posts'] ? t('New comments') : t('Normal topic');
1248
  }
1249

    
1250
  if ($variables['comment_mode'] == COMMENT_NODE_CLOSED || $variables['comment_mode'] == COMMENT_NODE_HIDDEN) {
1251
    $variables['icon_class'] = 'closed';
1252
    $variables['icon_title'] = t('Closed topic');
1253
  }
1254

    
1255
  if ($variables['sticky'] == 1) {
1256
    $variables['icon_class'] = 'sticky';
1257
    $variables['icon_title'] = t('Sticky topic');
1258
  }
1259
}
1260

    
1261
/**
1262
 * Preprocesses variables for forum-submitted.tpl.php.
1263
 *
1264
 * The submission information will be displayed in the forum list and topic
1265
 * list.
1266
 *
1267
 * @param $variables
1268
 *   An array containing the following elements:
1269
 *   - topic: The topic object.
1270
 *
1271
 * @see forum-submitted.tpl.php
1272
 * @see theme_forum_submitted()
1273
 */
1274
function template_preprocess_forum_submitted(&$variables) {
1275
  $variables['author'] = isset($variables['topic']->uid) ? theme('username', array('account' => $variables['topic'])) : '';
1276
  $variables['time'] = isset($variables['topic']->created) ? format_interval(REQUEST_TIME - $variables['topic']->created) : '';
1277
}
1278

    
1279
/**
1280
 * Gets the last time the user viewed a node.
1281
 *
1282
 * @param $nid
1283
 *   The node ID.
1284
 *
1285
 * @return
1286
 *   The timestamp when the user last viewed this node, if the user has
1287
 *   previously viewed the node; otherwise NODE_NEW_LIMIT.
1288
 */
1289
function _forum_user_last_visit($nid) {
1290
  global $user;
1291
  $history = &drupal_static(__FUNCTION__, array());
1292

    
1293
  if (empty($history)) {
1294
    $result = db_query('SELECT nid, timestamp FROM {history} WHERE uid = :uid', array(':uid' => $user->uid));
1295
    foreach ($result as $t) {
1296
      $history[$t->nid] = $t->timestamp > NODE_NEW_LIMIT ? $t->timestamp : NODE_NEW_LIMIT;
1297
    }
1298
  }
1299
  return isset($history[$nid]) ? $history[$nid] : NODE_NEW_LIMIT;
1300
}
1301

    
1302
/**
1303
 * Gets topic sorting information based on an integer code.
1304
 *
1305
 * @param $sortby
1306
 *   One of the following integers indicating the sort criteria:
1307
 *   - 1: Date - newest first.
1308
 *   - 2: Date - oldest first.
1309
 *   - 3: Posts with the most comments first.
1310
 *   - 4: Posts with the least comments first.
1311
 *
1312
 * @return
1313
 *   An array with the following values:
1314
 *   - field: A field for an SQL query.
1315
 *   - sort: 'asc' or 'desc'.
1316
 */
1317
function _forum_get_topic_order($sortby) {
1318
  switch ($sortby) {
1319
    case 1:
1320
      return array('field' => 'f.last_comment_timestamp', 'sort' => 'desc');
1321
      break;
1322
    case 2:
1323
      return array('field' => 'f.last_comment_timestamp', 'sort' => 'asc');
1324
      break;
1325
    case 3:
1326
      return array('field' => 'f.comment_count', 'sort' => 'desc');
1327
      break;
1328
    case 4:
1329
      return array('field' => 'f.comment_count', 'sort' => 'asc');
1330
      break;
1331
  }
1332
}
1333

    
1334
/**
1335
 * Updates the taxonomy index for a given node.
1336
 *
1337
 * @param $nid
1338
 *   The ID of the node to update.
1339
 */
1340
function _forum_update_forum_index($nid) {
1341
  $count = db_query('SELECT COUNT(cid) FROM {comment} c INNER JOIN {forum_index} i ON c.nid = i.nid WHERE c.nid = :nid AND c.status = :status', array(
1342
    ':nid' => $nid,
1343
    ':status' => COMMENT_PUBLISHED,
1344
  ))->fetchField();
1345

    
1346
  if ($count > 0) {
1347
    // Comments exist.
1348
    $last_reply = db_query_range('SELECT cid, name, created, uid FROM {comment} WHERE nid = :nid AND status = :status ORDER BY cid DESC', 0, 1, array(
1349
      ':nid' => $nid,
1350
      ':status' => COMMENT_PUBLISHED,
1351
    ))->fetchObject();
1352
    db_update('forum_index')
1353
      ->fields( array(
1354
        'comment_count' => $count,
1355
        'last_comment_timestamp' => $last_reply->created,
1356
      ))
1357
      ->condition('nid', $nid)
1358
      ->execute();
1359
  }
1360
  else {
1361
    // Comments do not exist.
1362
    $node = db_query('SELECT uid, created FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchObject();
1363
    db_update('forum_index')
1364
      ->fields( array(
1365
        'comment_count' => 0,
1366
        'last_comment_timestamp' => $node->created,
1367
      ))
1368
      ->condition('nid', $nid)
1369
      ->execute();
1370
  }
1371
}
1372

    
1373
/**
1374
 * Implements hook_rdf_mapping().
1375
 */
1376
function forum_rdf_mapping() {
1377
  return array(
1378
    array(
1379
      'type' => 'node',
1380
      'bundle' => 'forum',
1381
      'mapping' => array(
1382
        'rdftype' => array('sioc:Post', 'sioct:BoardPost'),
1383
        'taxonomy_forums' => array(
1384
          'predicates' => array('sioc:has_container'),
1385
          'type' => 'rel',
1386
        ),
1387
      ),
1388
    ),
1389
    array(
1390
      'type' => 'taxonomy_term',
1391
      'bundle' => 'forums',
1392
      'mapping' => array(
1393
        'rdftype' => array('sioc:Container', 'sioc:Forum'),
1394
      ),
1395
    ),
1396
  );
1397
}