Code coverage for /20080809/modules/forum/forum.module

Line #Times calledCode
1
<?php
2
// $Id: forum.module,v 1.460 2008/08/03 18:29:29 dries Exp $
3
4
/**
5
 * @file
6
 * Provides discussion forums.
7
 */
8
9
/**
10
 * Implementation of hook_help().
11
 */
12169
function forum_help($path, $arg) {
13
  switch ($path) {
14136
    case 'admin/help#forum':
1510
      $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. The <a href="@create-topic">forum topic</a> menu item
(under <em>Create content</em> on the Navigation menu) creates the initial
post of a new threaded discussion, or thread.', array('@create-topic' =>
url('node/add/forum'))) . '</p>';
1610
      $output .= '<p>' . t('A threaded discussion occurs as people leave
comments on a forum topic (or on other comments within that topic). A forum
topic is contained within a forum, which may hold many similar or related
forum topics. Forums are (optionally) nested within a container, which may
hold many similar or related forums. Both containers and forums may be
nested within other containers and forums, and provide structure for your
message board. By carefully planning this structure, you make it easier for
users to find and comment on a specific forum topic.') . '</p>';
1710
      $output .= '<p>' . t('When administering a forum, note that:') .
'</p>';
1810
      $output .= '<ul><li>' . t('a forum topic (and all of its comments)
may be moved between forums by selecting a different forum while editing a
forum topic.') . '</li>';
1910
      $output .= '<li>' . t('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.') . '</li>';
2010
      $output .= '<li>' . t('selecting <em>Read only</em> under <em>Comment
settings</em> while editing a forum topic will lock (prevent new comments)
on the thread.') . '</li>';
2110
      $output .= '<li>' . t('selecting <em>Disabled</em> under <em>Comment
settings</em> while editing a forum topic will hide all existing comments
on the thread, and prevent new ones.') . '</li></ul>';
2210
      $output .= '<p>' . t('For more information, see the online handbook
entry for <a href="@forum">Forum module</a>.', array('@forum' =>
'http://drupal.org/handbook/modules/forum/')) . '</p>';
2310
      return $output;
24136
    case 'admin/build/forum':
253
      return '<p>' . t('This page displays a list of existing forums and
containers. Containers (optionally) hold forums, and forums hold forum
topics (a forum topic is the initial post to a threaded discussion). To
provide structure, both containers and forums may be placed inside other
containers and forums. To rearrange forums and containers, grab a
drag-and-drop handle under the <em>Name</em> column and drag the forum or
container to a new location in the list. (Grab a handle by clicking and
holding the mouse while hovering over a handle icon.) Remember that your
changes will not be saved until you click the <em>Save</em> button at the
bottom of the page.') . '</p>';
26133
    case 'admin/build/forum/add/container':
271
      return '<p>' . t('By grouping related or similar forums, containers
help organize forums. For example, a container named "Food" may hold two
forums named "Fruit" and "Vegetables", respectively.') . '</p>';
28132
    case 'admin/build/forum/add/forum':
292
      return '<p>' . t('A forum holds related or similar forum topics (a
forum topic is the initial post to a threaded discussion). For example, a
forum named "Fruit" may contain forum topics titled "Apples" and "Bananas",
respectively.') . '</p>';
30130
    case 'admin/build/forum/settings':
310
      return '<p>' . t('These settings allow you to adjust the display of
your forum topics. The content types available for use within a forum may
be selected by editing the <em>Content types</em> on the <a
href="@forum-vocabulary">forum vocabulary page</a>.',
array('@forum-vocabulary' => url('admin/content/taxonomy/edit/vocabulary/'
. variable_get('forum_nav_vocabulary', '')))) . '</p>';
320
  }
33130
}
34
35
/**
36
 * Implementation of hook_theme().
37
 */
38169
function forum_theme() {
39
  return array(
40
    'forums' => array(
411
      'template' => 'forums',
421
      'arguments' => array('forums' => NULL, 'topics' => NULL, 'parents' =>
NULL, 'tid' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
431
    ),
44
    'forum_list' => array(
451
      'template' => 'forum-list',
461
      'arguments' => array('forums' => NULL, 'parents' => NULL, 'tid' =>
NULL),
471
    ),
48
    'forum_topic_list' => array(
491
      'template' => 'forum-topic-list',
501
      'arguments' => array('tid' => NULL, 'topics' => NULL, 'sortby' =>
NULL, 'forum_per_page' => NULL),
511
    ),
52
    'forum_icon' => array(
531
      'template' => 'forum-icon',
541
      'arguments' => array('new_posts' => NULL, 'num_posts' => 0,
'comment_mode' => 0, 'sticky' => 0),
551
    ),
56
    'forum_topic_navigation' => array(
571
      'template' => 'forum-topic-navigation',
581
      'arguments' => array('node' => NULL),
591
    ),
60
    'forum_submitted' => array(
611
      'template' => 'forum-submitted',
621
      'arguments' => array('topic' => NULL),
631
    ),
641
  );
650
}
66
67
/**
68
 * Fetch a forum term.
69
 *
70
 * @param $tid
71
 *   The ID of the term which should be loaded.
72
 *
73
 * @return
74
 *   An associative array containing the term data or FALSE if the term
cannot be loaded, or is not part of the forum vocabulary.
75
 */
76169
function forum_term_load($tid) {
770
  $result = db_query(db_rewrite_sql('SELECT t.tid, t.vid, t.name,
t.description, t.weight FROM {term_data} t WHERE t.tid = %d AND t.vid =
%d', 't', 'tid'), $tid, variable_get('forum_nav_vocabulary', ''));
780
  return db_fetch_array($result);
790
}
80
81
/**
82
 * Implementation of hook_menu().
83
 */
84169
function forum_menu() {
853
  $items['forum'] = array(
863
    'title' => 'Forums',
873
    'page callback' => 'forum_page',
883
    'access arguments' => array('access content'),
893
    'type' => MENU_SUGGESTED_ITEM,
90
  );
913
  $items['admin/build/forum'] = array(
923
    'title' => 'Forums',
933
    'description' => 'Control forums and their hierarchy and change forum
settings.',
943
    'page callback' => 'drupal_get_form',
953
    'page arguments' => array('forum_overview'),
963
    'access arguments' => array('administer forums'),
97
  );
983
  $items['admin/build/forum/list'] = array(
993
    'title' => 'List',
1003
    'type' => MENU_DEFAULT_LOCAL_TASK,
1013
    'weight' => -10,
102
  );
1033
  $items['admin/build/forum/add/container'] = array(
1043
    'title' => 'Add container',
1053
    'page callback' => 'forum_form_main',
1063
    'page arguments' => array('container'),
1073
    'access arguments' => array('administer forums'),
1083
    'type' => MENU_LOCAL_TASK,
1093
    'parent' => 'admin/build/forum',
110
  );
1113
  $items['admin/build/forum/add/forum'] = array(
1123
    'title' => 'Add forum',
1133
    'page callback' => 'forum_form_main',
1143
    'page arguments' => array('forum'),
1153
    'access arguments' => array('administer forums'),
1163
    'type' => MENU_LOCAL_TASK,
1173
    'parent' => 'admin/build/forum',
118
  );
1193
  $items['admin/build/forum/settings'] = array(
1203
    'title' => 'Settings',
1213
    'page callback' => 'drupal_get_form',
1223
    'page arguments' => array('forum_admin_settings'),
1233
    'access arguments' => array('administer forums'),
1243
    'weight' => 5,
1253
    'type' => MENU_LOCAL_TASK,
1263
    'parent' => 'admin/build/forum',
127
  );
1283
  $items['admin/build/forum/edit/%forum_term'] = array(
1293
    'page callback' => 'forum_form_main',
1303
    'access arguments' => array('administer forums'),
1313
    'type' => MENU_CALLBACK,
132
  );
1333
  $items['admin/build/forum/edit/container/%forum_term'] = array(
1343
    'title' => 'Edit container',
1353
    'page callback' => 'forum_form_main',
1363
    'page arguments' => array('container', 5),
1373
    'access arguments' => array('administer forums'),
1383
    'type' => MENU_CALLBACK,
139
  );
1403
  $items['admin/build/forum/edit/forum/%forum_term'] = array(
1413
    'title' => 'Edit forum',
1423
    'page callback' => 'forum_form_main',
1433
    'page arguments' => array('forum', 5),
1443
    'access arguments' => array('administer forums'),
1453
    'type' => MENU_CALLBACK,
146
  );
1473
  return $items;
1480
}
149
150
151
/**
152
 * Implementation of hook_init().
153
 */
154169
function forum_init() {
155168
  drupal_add_css(drupal_get_path('module', 'forum') . '/forum.css');
156168
}
157
158
/**
159
 * Implementation of hook_nodeapi().
160
 */
161169
function forum_nodeapi(&$node, $op, $teaser, $page) {
162
  // We are going to return if $node->type is not one of the node
163
  // types assigned to the forum vocabulary.  If forum_nav_vocabulary
164
  // is undefined or the vocabulary does not exist, it clearly cannot
165
  // be assigned to $node->type, so return to avoid E_ALL warnings.
16694
  $vid = variable_get('forum_nav_vocabulary', '');
16794
  $vocabulary = taxonomy_vocabulary_load($vid);
16894
  if (empty($vocabulary)) {
1690
    return;
1700
  }
171
172
  // Operate only on node types assigned for the forum vocabulary.
17394
  if (!in_array($node->type, $vocabulary->nodes)) {
1740
    return;
1750
  }
176
177
  switch ($op) {
17894
    case 'view':
17955
      if ($page && taxonomy_node_get_terms_by_vocabulary($node, $vid) &&
$tree = taxonomy_get_tree($vid)) {
180
        // Get the forum terms from the (cached) tree
18155
        foreach ($tree as $term) {
18255
          $forum_terms[] = $term->tid;
18355
        }
18455
        foreach ($node->taxonomy as $term_id => $term) {
18555
          if (in_array($term_id, $forum_terms)) {
18655
            $node->tid = $term_id;
18755
          }
18855
        }
189
        // Breadcrumb navigation
19055
        $breadcrumb[] = l(t('Home'), NULL);
19155
        $breadcrumb[] = l($vocabulary->name, 'forum');
19255
        if ($parents = taxonomy_get_parents_all($node->tid)) {
19355
          $parents = array_reverse($parents);
19455
          foreach ($parents as $p) {
19555
            $breadcrumb[] = l($p->name, 'forum/' . $p->tid);
19655
          }
19755
        }
19855
        drupal_set_breadcrumb($breadcrumb);
199
20055
        if (!$teaser) {
20155
          $node->content['forum_navigation'] = array(
20255
            '#markup' => theme('forum_topic_navigation', $node),
20355
            '#weight' => 100,
204
          );
20555
        }
20655
      }
20755
      break;
208
20994
    case 'prepare':
21017
      if (empty($node->nid)) {
211
        // New topic
21211
        $node->taxonomy[arg(3)]->vid = $vid;
21311
        $node->taxonomy[arg(3)]->tid = arg(3);
21411
      }
21517
      break;
216
217
    // Check in particular that only a "leaf" term in the associated
taxonomy
218
    // vocabulary is selected, not a "container" term.
21983
    case 'validate':
22014
      if ($node->taxonomy) {
221
        // Extract the node's proper topic ID.
22214
        $vocabulary = $vid;
22314
        $containers = variable_get('forum_containers', array());
22414
        foreach ($node->taxonomy as $term) {
22514
          if (db_result(db_query('SELECT COUNT(*) FROM {term_data} WHERE
tid = %d AND vid = %d', $term, $vocabulary))) {
22614
            if (in_array($term, $containers)) {
2272
              $term = taxonomy_get_term($term);
2282
              form_set_error('taxonomy', t('The item %forum is only a
container for forums. Please select one of the forums below it.',
array('%forum' => $term->name)));
2292
            }
23014
          }
23114
        }
23214
      }
23314
      break;
234
235
    // Assign forum taxonomy when adding a topic from within a forum.
23681
    case 'presave':
237
      // Make sure all fields are set properly:
23812
      $node->icon = !empty($node->icon) ? $node->icon : '';
239
24012
      if ($node->taxonomy && $tree = taxonomy_get_tree($vid)) {
241
        // Get the forum terms from the (cached) tree if we have a
taxonomy.
24212
        foreach ($tree as $term) {
24312
          $forum_terms[] = $term->tid;
24412
        }
24512
        foreach ($node->taxonomy as $term_id) {
24612
          if (in_array($term_id, $forum_terms)) {
24712
            $node->tid = $term_id;
24812
          }
24912
        }
25012
        $old_tid = db_result(db_query_range("SELECT t.tid FROM {term_node}
t INNER JOIN {node} n ON t.vid = n.vid WHERE n.nid = %d ORDER BY t.vid
DESC", $node->nid, 0, 1));
25112
        if ($old_tid && isset($node->tid) && ($node->tid != $old_tid) &&
!empty($node->shadow)) {
252
          // A shadow copy needs to be created. Retain new term and add old
term.
2533
          $node->taxonomy[] = $old_tid;
2543
        }
25512
      }
25612
      break;
257
25881
    case 'update':
2593
      if (empty($node->revision) && db_result(db_query('SELECT tid FROM
{forum} WHERE nid=%d', $node->nid))) {
2603
        if (!empty($node->tid)) {
2613
          db_query('UPDATE {forum} SET tid = %d WHERE vid = %d',
$node->tid, $node->vid);
2623
        }
263
        // The node is removed from the forum.
264
        else {
2650
          db_query('DELETE FROM {forum} WHERE nid = %d', $node->nid);
266
        }
2673
        break;
2680
      }
269
      // Deliberate no break -- for new revisions and for previously
unassigned terms we need an insert.
270
27181
    case 'insert':
2729
      if (!empty($node->tid)) {
2739
        db_query('INSERT INTO {forum} (tid, vid, nid) VALUES (%d, %d, %d)',
$node->tid, $node->vid, $node->nid);
2749
      }
2759
      break;
276
27772
    case 'delete':
2783
      db_query('DELETE FROM {forum} WHERE nid = %d', $node->nid);
2793
      break;
280
28172
    case 'load':
28272
      return db_fetch_array(db_query('SELECT tid AS forum_tid FROM {forum}
WHERE vid = %d', $node->vid));
2830
  }
284
28589
  return;
2860
}
287
288
/**
289
 * Implementation of hook_node_info().
290
 */
291169
function forum_node_info() {
292
  return array(
293
    'forum' => array(
294155
      'name' => t('Forum topic'),
295155
      'module' => 'forum',
296155
      'description' => t('A <em>forum topic</em> is the initial post to a
new discussion thread within a forum.'),
297155
      'title_label' => t('Subject'),
298
    )
299155
  );
3000
}
301
302
/**
303
 * Implementation of hook_access().
304
 */
305169
function forum_access($op, $node, $account) {
306
  switch ($op) {
307152
    case 'create':
308146
      return user_access('create forum content', $account);
30971
    case 'update':
31065
      return user_access('edit any forum content', $account) ||
(user_access('edit own forum content', $account) && ($account->uid ==
$node->uid));
31167
    case 'delete':
31212
      return user_access('delete any forum content', $account) ||
(user_access('delete own forum content', $account) && ($account->uid ==
$node->uid));
3130
  }
31461
}
315
316
/**
317
 * Implementation of hook_perm().
318
 */
319169
function forum_perm() {
320
  $perms = array(
3211
    'administer forums' => t('Manage forums and configure forum
administration settings.'),
3221
  );
3231
  $perms += node_list_permissions('forum');
3241
  return $perms;
3250
}
326
327
/**
328
 * Implementation of hook_taxonomy().
329
 */
330169
function forum_taxonomy($op, $type, $term = NULL) {
3316
  if ($op == 'delete' && $term['vid'] ==
variable_get('forum_nav_vocabulary', '')) {
332
    switch ($type) {
3330
      case 'term':
3340
        $results = db_query('SELECT tn.nid FROM {term_node} tn WHERE tn.tid
= %d', $term['tid']);
3350
        while ($node = db_fetch_object($results)) {
336
          // node_delete will also remove any association with non-forum
vocabularies.
3370
          node_delete($node->nid);
3380
        }
339
340
        // For containers, remove the tid from the forum_containers
variable.
3410
        $containers = variable_get('forum_containers', array());
3420
        $key = array_search($term['tid'], $containers);
3430
        if ($key !== FALSE) {
3440
          unset($containers[$key]);
3450
        }
3460
        variable_set('forum_containers', $containers);
3470
        break;
3480
      case 'vocabulary':
3490
        variable_del('forum_nav_vocabulary');
3500
    }
3510
  }
3526
}
353
354
/**
355
 * Implementation of hook_form_alter().
356
 */
357169
function forum_form_alter(&$form, $form_state, $form_id) {
35866
  $vid = variable_get('forum_nav_vocabulary', '');
35966
  if (isset($form['vid']) && $form['vid']['#value'] == $vid) {
360
    // Hide critical options from forum vocabulary.
36110
    if ($form_id == 'taxonomy_form_vocabulary') {
3624
      $form['help_forum_vocab'] = array(
3634
        '#markup' => t('This is the designated forum vocabulary. Some of
the normal vocabulary options have been removed.'),
3644
        '#weight' => -1,
365
      );
3664
      $form['content_types']['nodes']['#required'] = TRUE;
3674
      $form['hierarchy'] = array('#type' => 'value', '#value' => 1);
3684
      $form['settings']['required'] = array('#type' => 'value', '#value' =>
FALSE);
3694
      $form['settings']['relations'] = array('#type' => 'value', '#value'
=> FALSE);
3704
      $form['settings']['tags'] = array('#type' => 'value', '#value' =>
FALSE);
3714
      $form['settings']['multiple'] = array('#type' => 'value', '#value' =>
FALSE);
3724
      unset($form['delete']);
3734
    }
374
    // Hide multiple parents select from forum terms.
3756
    elseif ($form_id == 'taxonomy_form_term') {
3760
      $form['advanced']['parent']['#access'] = FALSE;
3770
    }
37810
  }
37966
  if ($form_id == 'forum_node_form') {
380
    // Make the vocabulary required for 'real' forum-nodes.
38117
    $vid = variable_get('forum_nav_vocabulary', '');
38217
    $form['taxonomy'][$vid]['#required'] = TRUE;
38317
    $form['taxonomy'][$vid]['#options'][''] = t('- Please choose -');
38417
  }
38566
}
386
387
/**
388
 * Implementation of hook_load().
389
 */
390169
function forum_load($node) {
39172
  $forum = db_fetch_object(db_query('SELECT * FROM {term_node} WHERE vid =
%d', $node->vid));
392
39372
  return $forum;
3940
}
395
396
/**
397
 * Implementation of hook_block().
398
 *
399
 * Generates a block containing the currently active forum topics and the
400
 * most recently added forum topics.
401
 */
402169
function forum_block($op = 'list', $delta = '', $edit = array()) {
403
  switch ($op) {
404135
    case 'list':
4056
      $blocks['active']['info'] = t('Active forum topics');
4066
      $blocks['new']['info'] = t('New forum topics');
4076
      return $blocks;
408
409132
    case 'configure':
4100
      $form['forum_block_num_' . $delta] = array('#type' => 'select',
'#title' => t('Number of topics'), '#default_value' =>
variable_get('forum_block_num_' . $delta, '5'), '#options' =>
drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20)));
4110
      return $form;
412
413132
    case 'save':
4140
      variable_set('forum_block_num_' . $delta, $edit['forum_block_num_' .
$delta]);
4150
      break;
416
417132
    case 'view':
418132
      if (user_access('access content')) {
419
        switch ($delta) {
420132
          case 'active':
421132
            $title = t('Active forum topics');
422132
            $sql = db_rewrite_sql("SELECT n.nid, n.title, l.comment_count,
l.last_comment_timestamp FROM {node} n INNER JOIN {term_node} tn ON tn.vid
= n.vid INNER JOIN {term_data} td ON td.tid = tn.tid INNER JOIN
{node_comment_statistics} l ON n.nid = l.nid WHERE n.status = 1 AND td.vid
= %d ORDER BY l.last_comment_timestamp DESC");
423132
            $result = db_query_range($sql,
variable_get('forum_nav_vocabulary', ''), 0,
variable_get('forum_block_num_active', '5'));
424132
            $content = node_title_list($result);
425132
            break;
426
427130
          case 'new':
428130
            $title = t('New forum topics');
429130
            $sql = db_rewrite_sql("SELECT n.nid, n.title, l.comment_count
FROM {node} n INNER JOIN {term_node} tn ON tn.vid = n.vid INNER JOIN
{term_data} td ON td.tid = tn.tid INNER JOIN {node_comment_statistics} l ON
n.nid = l.nid WHERE n.status = 1 AND td.vid = %d ORDER BY n.nid DESC");
430130
            $result = db_query_range($sql,
variable_get('forum_nav_vocabulary', ''), 0,
variable_get('forum_block_num_new', '5'));
431130
            $content = node_title_list($result);
432130
            break;
4330
        }
434
435132
        if (!empty($content)) {
436116
          $block['subject'] = $title;
437116
          $block['content'] = $content . theme('more_link', url('forum'),
t('Read the latest forum topics.'));
438116
          return $block;
4390
        }
44016
      }
44116
  }
44216
}
443
444
/**
445
 * Implementation of hook_form().
446
 */
447169
function forum_form(&$node, $form_state) {
44817
  $type = node_get_types('type', $node);
44917
  $form['title'] = array('#type' => 'textfield', '#title' =>
check_plain($type->title_label), '#default_value' => !empty($node->title) ?
$node->title : '', '#required' => TRUE, '#weight' => -5);
450
45117
  if (!empty($node->nid)) {
4526
    $vid = variable_get('forum_nav_vocabulary', '');
4536
    $forum_terms = taxonomy_node_get_terms_by_vocabulary($node, $vid);
454
    // if editing, give option to leave shadows
4556
    $shadow = (count($forum_terms) > 1);
4566
    $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.'));
4576
  }
458
45917
  $form['body_field'] = node_body_field($node, $type->body_label, 1);
460
46117
  $form['#submit'][] = 'forum_submit';
462
  // Assign the forum topic submit handler.
463
46417
  return $form;
4650
}
466
467169
function forum_link_alter(&$links, $node) {
46855
  foreach ($links as $module => $link) {
46955
    if (strstr($module, 'taxonomy_term')) {
470
      // Link back to the forum and not the taxonomy term page. We'll only
471
      // do this if the taxonomy term in question belongs to forums.
47255
      $tid = str_replace('taxonomy/term/', '', $link['href']);
47355
      $vid = variable_get('forum_nav_vocabulary', '');
47455
      $term = taxonomy_get_term($tid);
47555
      if ($term->vid == $vid) {
47655
        $links[$module]['href'] = str_replace('taxonomy/term', 'forum',
$link['href']);
47755
      }
47855
    }
47955
  }
48055
}
481
482
/**
483
 * Returns a list of all forums for a given taxonomy id
484
 *
485
 * Forum objects contain the following fields
486
 * -num_topics Number of topics in the forum
487
 * -num_posts Total number of posts in all topics
488
 * -last_post Most recent post for the forum
489
 *
490
 * @param $tid
491
 *   Taxonomy ID of the vocabulary that holds the forum list.
492
 * @return
493
 *   Array of object containing the forum information.
494
 */
495169
function forum_get_forums($tid = 0) {
496
49712
  $forums = array();
49812
  $vid = variable_get('forum_nav_vocabulary', '');
49912
  $_forums = taxonomy_get_tree($vid, $tid);
500
50112
  if (count($_forums)) {
502
5034
    $counts = array();
504
5054
    $sql = "SELECT r.tid, COUNT(n.nid) AS topic_count, SUM(l.comment_count)
AS comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON
n.nid = l.nid INNER JOIN {term_node} r ON n.vid = r.vid WHERE n.status = 1
GROUP BY r.tid";
5064
    $sql = db_rewrite_sql($sql);
5074
    $_counts = db_query($sql);
5084
    while ($count = db_fetch_object($_counts)) {
5094
      $counts[$count->tid] = $count;
5104
    }
5114
  }
512
51312
  foreach ($_forums as $forum) {
5144
    if (in_array($forum->tid, variable_get('forum_containers', array())))
{
5150
      $forum->container = 1;
5160
    }
517
5184
    if (!empty($counts[$forum->tid])) {
5194
      $forum->num_topics = $counts[$forum->tid]->topic_count;
5204
      $forum->num_posts = $counts[$forum->tid]->topic_count +
$counts[$forum->tid]->comment_count;
5214
    }
522
    else {
5230
      $forum->num_topics = 0;
5240
      $forum->num_posts = 0;
525
    }
526
527
    // This query does not use full ANSI syntax since MySQL 3.x does not
support
528
    // table1 INNER JOIN table2 INNER JOIN table3 ON table2_criteria ON
table3_criteria
529
    // used to join node_comment_statistics to users.
5304
    $sql = "SELECT ncs.last_comment_timestamp, IF (ncs.last_comment_uid !=
0, u2.name, ncs.last_comment_name) AS last_comment_name,
ncs.last_comment_uid FROM {node} n INNER JOIN {users} u1 ON n.uid = u1.uid
INNER JOIN {term_node} tn ON n.vid = tn.vid INNER JOIN
{node_comment_statistics} ncs ON n.nid = ncs.nid INNER JOIN {users} u2 ON
ncs.last_comment_uid=u2.uid WHERE n.status = 1 AND tn.tid = %d ORDER BY
ncs.last_comment_timestamp DESC";
5314
    $sql = db_rewrite_sql($sql);
5324
    $topic = db_fetch_object(db_query_range($sql, $forum->tid, 0, 1));
533
5344
    $last_post = new stdClass();
5354
    if (!empty($topic->last_comment_timestamp)) {
5364
      $last_post->timestamp = $topic->last_comment_timestamp;
5374
      $last_post->name = $topic->last_comment_name;
5384
      $last_post->uid = $topic->last_comment_uid;
5394
    }
5404
    $forum->last_post = $last_post;
541
5424
    $forums[$forum->tid] = $forum;
5434
  }
544
54512
  return $forums;
5460
}
547
548
/**
549
 * Calculate the number of nodes the user has not yet read and are newer
550
 * than NODE_NEW_LIMIT.
551
 */
552169
function _forum_topics_unread($term, $uid) {
5534
  $sql = "SELECT COUNT(n.nid) FROM {node} n INNER JOIN {term_node} tn ON
n.vid = tn.vid AND tn.tid = %d LEFT JOIN {history} h ON n.nid = h.nid AND
h.uid = %d WHERE n.status = 1 AND n.created > %d AND h.nid IS NULL";
5544
  $sql = db_rewrite_sql($sql);
5554
  return db_result(db_query($sql, $term, $uid, NODE_NEW_LIMIT));
5560
}
557
558169
function forum_get_topics($tid, $sortby, $forum_per_page) {
5598
  global $user, $forum_topic_list_header;
560
561
  $forum_topic_list_header = array(
5628
    array('data' => '&nbsp;', 'field' => NULL),
5638
    array('data' => t('Topic'), 'field' => 'n.title'),
5648
    array('data' => t('Replies'), 'field' => 'l.comment_count'),
5658
    array('data' => t('Created'), 'field' => 'n.created'),
5668
    array('data' => t('Last reply'), 'field' =>
'l.last_comment_timestamp'),
5678
  );
568
5698
  $order = _forum_get_topic_order($sortby);
5708
  for ($i = 0; $i < count($forum_topic_list_header); $i++) {
5718
    if ($forum_topic_list_header[$i]['field'] == $order['field']) {
5728
      $forum_topic_list_header[$i]['sort'] = $order['sort'];
5738
    }
5748
  }
575
5768
  $term = taxonomy_get_term($tid);
577
5788
  $sql = db_rewrite_sql("SELECT n.nid, r.tid, n.title, n.type, n.sticky,
u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode,
l.last_comment_timestamp, IF(l.last_comment_uid != 0, cu.name,
l.last_comment_name) AS last_comment_name, l.last_comment_uid,
l.comment_count AS num_comments, f.tid AS forum_tid FROM
{node_comment_statistics} l INNER JOIN {node} n ON n.nid = l.nid INNER JOIN
{users} cu ON l.last_comment_uid = cu.uid INNER JOIN {term_node} r ON n.vid
= r.vid INNER JOIN {users} u ON n.uid = u.uid INNER JOIN {forum} f ON n.vid
= f.vid WHERE n.status = 1 AND r.tid = %d");
5798
  $sql .= tablesort_sql($forum_topic_list_header, 'n.sticky DESC,');
5808
  $sql .= ', n.created DESC';  // Always add a secondary sort order so that
the news forum topics are on top.
581
5828
  $sql_count = db_rewrite_sql("SELECT COUNT(n.nid) FROM {node} n INNER JOIN
{term_node} r ON n.vid = r.vid AND r.tid = %d WHERE n.status = 1");
583
5848
  $result = pager_query($sql, $forum_per_page, 0, $sql_count, $tid);
5858
  $topics = array();
5868
  while ($topic = db_fetch_object($result)) {
5874
    if ($user->uid) {
588
      // folder is new if topic is new or there are new comments since last
visit
5894
      if ($topic->tid != $tid) {
5900
        $topic->new = 0;
5910
      }
592
      else {
5934
        $history = _forum_user_last_visit($topic->nid);
5944
        $topic->new_replies = comment_num_new($topic->nid, $history);
5954
        $topic->new = $topic->new_replies || ($topic->timestamp >
$history);
596
      }
5974
    }
598
    else {
599
      // Do not track "new replies" status for topics if the user is
anonymous.
6000
      $topic->new_replies = 0;
6010
      $topic->new = 0;
602
    }
603
6044
    if ($topic->num_comments > 0) {
6050
      $last_reply = new stdClass();
6060
      $last_reply->timestamp = $topic->last_comment_timestamp;
6070
      $last_reply->name = $topic->last_comment_name;
6080
      $last_reply->uid = $topic->last_comment_uid;
6090
      $topic->last_reply = $last_reply;
6100
    }
6114
    $topics[] = $topic;
6124
  }
613
6148
  return $topics;
6150
}
616
617
/**
618
 * Finds the first unread node for a given forum.
619
 */
620169
function _forum_new($tid) {
6210
  global $user;
622
6230
  $sql = "SELECT n.nid FROM {node} n LEFT JOIN {history} h ON n.nid = h.nid
AND h.uid = %d INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d
WHERE n.status = 1 AND h.nid IS NULL AND n.created > %d ORDER BY created";
6240
  $sql = db_rewrite_sql($sql);
6250
  $nid = db_result(db_query_range($sql, $user->uid, $tid, NODE_NEW_LIMIT,
0, 1));
626
6270
  return $nid ? $nid : 0;
6280
}
629
630
/**
631
 * Process variables for forums.tpl.php
632
 *
633
 * The $variables array contains the following arguments:
634
 * - $forums
635
 * - $topics
636
 * - $parents
637
 * - $tid
638
 * - $sortby
639
 * - $forum_per_page
640
 *
641
 * @see forums.tpl.php
642
 */
643169
function template_preprocess_forums(&$variables) {
64412
  global $user;
645
64612
  $vid = variable_get('forum_nav_vocabulary', '');
64712
  $vocabulary = taxonomy_vocabulary_load($vid);
64812
  $title = !empty($vocabulary->name) ? $vocabulary->name : '';
649
650
  // Breadcrumb navigation:
65112
  $breadcrumb[] = l(t('Home'), NULL);
65212
  if ($variables['tid']) {
65312
    $breadcrumb[] = l($vocabulary->name, 'forum');
65412
  }
65512
  if ($variables['parents']) {
65612
    $variables['parents'] = array_reverse($variables['parents']);
65712
    foreach ($variables['parents'] as $p) {
65812
      if ($p->tid == $variables['tid']) {
65912
        $title = $p->name;
66012
      }
661
      else {
6624
        $breadcrumb[] = l($p->name, 'forum/' . $p->tid);
663
      }
66412
    }
66512
  }
66612
  drupal_set_breadcrumb($breadcrumb);
66712
  drupal_set_title(check_plain($title));
668
66912
  if ($variables['forums_defined'] = count($variables['forums']) ||
count($variables['parents'])) {
670
    // Format the "post new content" links listing.
67112
    $forum_types = array();
672
673
    // Loop through all node types for forum vocabulary.
67412
    foreach ($vocabulary->nodes as $type) {
675
      // Check if the current user has the 'create' permission for this
node type.
67612
      if (node_access('create', $type)) {
677
        // Fetch the "General" name of the content type;
678
        // Push the link with title and url to the array.
67912
        $forum_types[$type] = array('title' => t('Post new @node_type',
array('@node_type' => node_get_types('name', $type))), 'href' =>
'node/add/' . str_replace('_', '-', $type) . '/' . $variables['tid']);
68012
      }
68112
    }
682
68312
    if (empty($forum_types)) {
684
      // The user is logged-in; but denied access to create any new forum
content type.
6850
      if ($user->uid) {
6860
        $forum_types['disallowed'] = array('title' => t('You are not
allowed to post new content in forum.'));
6870
      }
688
      // The user is not logged-in; and denied access to create any new
forum content type.
689
      else {
6900
        $forum_types['login'] = array('title' => t('<a
href="@login">Login</a> to post new content in forum.', array('@login' =>
url('user/login', array('query' => drupal_get_destination())))), 'html' =>
TRUE);
691
      }
6920
    }
69312
    $variables['links'] = $forum_types;
694
69512
    if (!empty($variables['forums'])) {
6964
      $variables['forums'] = theme('forum_list', $variables['forums'],
$variables['parents'], $variables['tid']);
6974
    }
698
    else {
6998
      $variables['forums'] = '';
700
    }
701
70212
    if ($variables['tid'] && !in_array($variables['tid'],
variable_get('forum_containers', array()))) {
7038
      $variables['topics'] = theme('forum_topic_list', $variables['tid'],
$variables['topics'], $variables['sortby'], $variables['forum_per_page']);
7048
      drupal_add_feed(url('taxonomy/term/' . $variables['tid'] .
'/0/feed'), 'RSS - ' . $title);
7058
    }
706
    else {
7074
      $variables['topics'] = '';
708
    }
709
710
    // Provide separate template suggestions based on what's being output.
Topic id is also accounted for.
711
    // Check both variables to be safe then the inverse. Forums with topic
ID's take precedence.
71212
    if ($variables['forums'] && !$variables['topics']) {
7134
      $variables['template_files'][] = 'forums-containers';
7144
      $variables['template_files'][] = 'forums-' . $variables['tid'];
7154
      $variables['template_files'][] = 'forums-containers-' .
$variables['tid'];
7164
    }
7178
    elseif (!$variables['forums'] && $variables['topics']) {
7188
      $variables['template_files'][] = 'forums-topics';
7198
      $variables['template_files'][] = 'forums-' . $variables['tid'];
7208
      $variables['template_files'][] = 'forums-topics-' .
$variables['tid'];
7218
    }
722
    else {
7230
      $variables['template_files'][] = 'forums-' . $variables['tid'];
724
    }
725
72612
  }
727
  else {
7280
    drupal_set_title(t('No forums defined'));
7290
    $variables['links'] = array();
7300
    $variables['forums'] = '';
7310
    $variables['topics'] = '';
732
  }
73312
}
734
735
/**
736
 * Process variables to format a forum listing.
737
 *
738
 * $variables contains the following information:
739
 * - $forums
740
 * - $parents
741
 * - $tid
742
 *
743
 * @see forum-list.tpl.php
744
 * @see theme_forum_list()
745
 */
746169
function template_preprocess_forum_list(&$variables) {
7474
  global $user;
7484
  $row = 0;
749
  // Sanitize each forum so that the template can safely print the data.
7504
  foreach ($variables['forums'] as $id => $forum) {
7514
    $variables['forums'][$id]->description = !empty($forum->description) ?
filter_xss_admin($forum->description) : '';
7524
    $variables['forums'][$id]->link = url("forum/$forum->tid");
7534
    $variables['forums'][$id]->name = check_plain($forum->name);
7544
    $variables['forums'][$id]->is_container = !empty($forum->container);
7554
    $variables['forums'][$id]->zebra = $row % 2 == 0 ? 'odd' : 'even';
7564
    $row++;
757
7584
    $variables['forums'][$id]->new_text = '';
7594
    $variables['forums'][$id]->new_url = '';
7604
    $variables['forums'][$id]->new_topics = 0;
7614
    $variables['forums'][$id]->old_topics = $forum->num_topics;
7624
    if ($user->uid) {
7634
      $variables['forums'][$id]->new_topics =
_forum_topics_unread($forum->tid, $user->uid);
7644
      if ($variables['forums'][$id]->new_topics) {
7654
        $variables['forums'][$id]->new_text =
format_plural($variables['forums'][$id]->new_topics, '1 new', '@count
new');
7664
        $variables['forums'][$id]->new_url = url("forum/$forum->tid",
array('fragment' => 'new'));
7674
      }
7684
      $variables['forums'][$id]->old_topics = $forum->num_topics -
$variables['forums'][$id]->new_topics;
7694
    }
7704
    $variables['forums'][$id]->last_reply = theme('forum_submitted',
$forum->last_post);
7714
  }
772
  // Give meaning to $tid for themers. $tid actually stands for term id.
7734
  $variables['forum_id'] = $variables['tid'];
7744
  unset($variables['tid']);
7754
}
776
777
/**
778
 * Preprocess variables to format the topic listing.
779
 *
780
 * $variables contains the following data:
781
 * - $tid
782
 * - $topics
783
 * - $sortby
784
 * - $forum_per_page
785
 *
786
 * @see forum-topic-list.tpl.php
787
 * @see theme_forum_topic_list()
788
 */
789169
function template_preprocess_forum_topic_list(&$variables) {
7908
  global $forum_topic_list_header;
791
792
  // Create the tablesorting header.
7938
  $ts = tablesort_init($forum_topic_list_header);
7948
  $header = '';
7958
  foreach ($forum_topic_list_header as $cell) {
7968
    $cell = tablesort_header($cell, $forum_topic_list_header, $ts);
7978
    $header .= _theme_table_cell($cell, TRUE);
7988
  }
7998
  $variables['header'] = $header;
800
8018
  if (!empty($variables['topics'])) {
8024
    $row = 0;
8034
    foreach ($variables['topics'] as $id => $topic) {
8044
      $variables['topics'][$id]->icon = theme('forum_icon', $topic->new,
$topic->num_comments, $topic->comment_mode, $topic->sticky);
8054
      $variables['topics'][$id]->zebra = $row % 2 == 0 ? 'odd' : 'even';
8064
      $row++;
807
808
      // We keep the actual tid in forum table, if it's different from the
809
      // current tid then it means the topic appears in two forums, one of
810
      // them is a shadow copy.
8114
      if ($topic->forum_tid != $variables['tid']) {
8120
        $variables['topics'][$id]->moved = TRUE;
8130
        $variables['topics'][$id]->title = check_plain($topic->title);
8140
        $variables['topics'][$id]->message = l(t('This topic has been
moved'), "forum/$topic->forum_tid");
8150
      }
816
      else {
8174
        $variables['topics'][$id]->moved = FALSE;
8184
        $variables['topics'][$id]->title = l($topic->title,
"node/$topic->nid");
8194
        $variables['topics'][$id]->message = '';
820
      }
8214
      $variables['topics'][$id]->created = theme('forum_submitted',
$topic);
8224
      $variables['topics'][$id]->last_reply = theme('forum_submitted',
isset($topic->last_reply) ? $topic->last_reply : NULL);
823
8244
      $variables['topics'][$id]->new_text = '';
8254
      $variables['topics'][$id]->new_url = '';
8264
      if ($topic->new_replies) {
8270
        $variables['topics'][$id]->new_text =
format_plural($topic->new_replies, '1 new', '@count new');
8280
        $variables['topics'][$id]->new_url = url("node/$topic->nid",
array('query' => comment_new_page_count($topic->num_comments,
$topic->new_replies, $topic), 'fragment' => 'new'));
8290
      }
830
8314
    }
8324
  }
833
  else {
834
    // Make this safe for the template
8354
    $variables['topics'] = array();
836
  }
837
  // Give meaning to $tid for themers. $tid actually stands for term id.
8388
  $variables['topic_id'] = $variables['tid'];
8398
  unset($variables['tid']);
840
8418
  $variables['pager'] = theme('pager', NULL, $variables['forum_per_page'],
0);
8428
}
843
844
/**
845
 * Process variables to format the icon for each individual topic.
846
 *
847
 * $variables contains the following data:
848
 * - $new_posts
849
 * - $num_posts = 0
850
 * - $comment_mode = 0
851
 * - $sticky = 0
852
 *
853
 * @see forum-icon.tpl.php
854
 * @see theme_forum_icon()
855
 */
856169
function template_preprocess_forum_icon(&$variables) {
8574
  $variables['hot_threshold'] = variable_get('forum_hot_topic', 15);
8584
  if ($variables['num_posts'] > $variables['hot_threshold']) {
8590
    $variables['icon'] = $variables['new_posts'] ? 'hot-new' : 'hot';
8600
  }
861
  else {
8624
    $variables['icon'] = $variables['new_posts'] ? 'new' : 'default';
863
  }
864
8654
  if ($variables['comment_mode'] == COMMENT_NODE_READ_ONLY ||
$variables['comment_mode'] == COMMENT_NODE_DISABLED) {
8660
    $variables['icon'] = 'closed';
8670
  }
868
8694
  if ($variables['sticky'] == 1) {
8700
    $variables['icon'] = 'sticky';
8710
  }
8724
}
873
874
/**
875
 * Preprocess variables to format the next/previous forum topic navigation
links.
876
 *
877
 * $variables contains $node.
878
 *
879
 * @see forum-topic-navigation.tpl.php
880
 * @see theme_forum_topic_navigation()
881
 */
882169
function template_preprocess_forum_topic_navigation(&$variables) {
88355
  $output = '';
884
885
  // get previous and next topic
88655
  $sql = "SELECT n.nid, n.title, n.sticky, l.comment_count,
l.last_comment_timestamp FROM {node} n INNER JOIN {node_comment_statistics}
l ON n.nid = l.nid INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d
WHERE n.status = 1 ORDER BY n.sticky DESC, " .
_forum_get_topic_order_sql(variable_get('forum_order', 1));
88755
  $result = db_query(db_rewrite_sql($sql), isset($variables['node']->tid) ?
$variables['node']->tid : 0);
888
88955
  $stop = $variables['prev'] = $variables['next'] = 0;
890
89155
  while ($topic = db_fetch_object($result)) {
89255
    if ($stop == 1) {
89347
      $variables['next'] = $topic->nid;
89447
      $variables['next_title'] = check_plain($topic->title);
89547
      $variables['next_url'] = url("node/$topic->nid");
89647
      break;
8970
    }
89855
    if ($topic->nid == $variables['node']->nid) {
89955
      $stop = 1;
90055
    }
901
    else {
90224
      $variables['prev'] = $topic->nid;
90324
      $variables['prev_title'] = check_plain($topic->title);
90424
      $variables['prev_url'] = url("node/$topic->nid");
905
    }
90655
  }
90755
}
908
909
/**
910
 * Process variables to format submission info for display in the forum
list and topic list.
911
 *
912
 * $variables will contain: $topic
913
 *
914
 * @see forum-submitted.tpl.php
915
 * @see theme_forum_submitted()
916
 */
917169
function template_preprocess_forum_submitted(&$variables) {
9188
  $variables['author'] = isset($variables['topic']->uid) ?
theme('username', $variables['topic']) : '';
9198
  $variables['time'] = isset($variables['topic']->timestamp) ?
format_interval(time() - $variables['topic']->timestamp) : '';
9208
}
921
922169
function _forum_user_last_visit($nid) {
9234
  global $user;
9244
  static $history = array();
925
9264
  if (empty($history)) {
9274
    $result = db_query('SELECT nid, timestamp FROM {history} WHERE uid =
%d', $user->uid);
9284
    while ($t = db_fetch_object($result)) {
9294
      $history[$t->nid] = $t->timestamp > NODE_NEW_LIMIT ? $t->timestamp :
NODE_NEW_LIMIT;
9304
    }
9314
  }
9324
  return isset($history[$nid]) ? $history[$nid] : NODE_NEW_LIMIT;
9330
}
934
935169
function _forum_get_topic_order($sortby) {
936
  switch ($sortby) {
93763
    case 1:
93863
      return array('field' => 'l.last_comment_timestamp', 'sort' =>
'desc');
9390
      break;
9400
    case 2:
9410
      return array('field' => 'l.last_comment_timestamp', 'sort' =>
'asc');
9420
      break;
9430
    case 3:
9440
      return array('field' => 'l.comment_count', 'sort' => 'desc');
9450
      break;
9460
    case 4:
9470
      return array('field' => 'l.comment_count', 'sort' => 'asc');
9480
      break;
9490
  }
9500
}
951
952169
function _forum_get_topic_order_sql($sortby) {
95355
  $order = _forum_get_topic_order($sortby);
95455
  return $order['field'] . ' ' . strtoupper($order['sort']);
9550
}
956169