Projet

Général

Profil

Paste
Télécharger (43,1 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / advanced_forum / advanced_forum.module @ 651307cd

1
<?php
2

    
3
/**
4
 * @file
5
 * Enables the look and feel of other popular forum software.
6
 */
7

    
8
/**
9
 * Implementation of hook_perm().
10
 */
11
function advanced_forum_permission() {
12
  return array(
13
    'administer advanced forum' => array(
14
      'title' => t('Administer Advanced Forum'),
15
    ),
16
    'view forum statistics' => array(
17
      'title' => t('View Advanced Forum statistics'),
18
    ),
19
    'view last edited notice' => array(
20
      'title' => t('View last edited notice'),
21
    )
22
  );
23
}
24

    
25
/**
26
 * Implements hook_menu().
27
 */
28
function advanced_forum_menu() {
29
  $items['admin/config/content/advanced-forum'] = array(
30
    'access arguments' => array('administer advanced forum'),
31
    'description' => 'Configure Advanced Forum with these settings.',
32
    'page arguments' => array('advanced_forum_settings_page'),
33
    'page callback' => 'drupal_get_form',
34
    'title' => 'Advanced Forum',
35
    'file' => 'includes/settings.inc',
36
  );
37

    
38
  $items['forum/markasread'] = array(
39
    'access callback' => 'advanced_forum_markasread_access',
40
    'page callback' => 'advanced_forum_markasread',
41
    'type' => MENU_CALLBACK,
42
  );
43

    
44
  if (variable_get('advanced_forum_add_local_task', TRUE)) {
45
    $items['forum/view'] = array(
46
      'title' => 'View Forums',
47
      'page callback' => 'advanced_forum_page',
48
      'type' => MENU_DEFAULT_LOCAL_TASK,
49
      'weight' => -100,
50
    );
51
  }
52

    
53
  return $items;
54
}
55

    
56
/**
57
 * Implements hook_cron().
58
 */
59
function advanced_forum_cron() {
60
  // Ensure the reply stats are up-to-date.
61
  advanced_forum_statistics_replies(NULL, TRUE);
62
}
63

    
64
/**
65
 * Implements hook_menu_alter().
66
 */
67
function advanced_forum_menu_alter(&$items) {
68
  // Take over the forum page creation so we can add more information.
69
  $items['forum']['page callback'] = 'advanced_forum_page';
70
  $items['forum']['module'] = 'advanced_forum';
71
  unset($items['forum']['file']);
72

    
73
  // Take over forum/%forum_forum page because we want advanced_forum_forum_load
74
  // is called instead of core forum_forum_load
75
  $items['forum/%advanced_forum_forum'] = $items['forum/%forum_forum'];
76
  $items['forum/%advanced_forum_forum']['page callback'] = 'advanced_forum_page';
77
  $items['forum/%advanced_forum_forum']['module'] = 'advanced_forum';
78
  unset($items['forum/%advanced_forum_forum']['file']);
79
  unset($items['forum/%forum_forum']);
80
}
81

    
82
/**
83
 * Implements hook_menu_local_tasks_alter().
84
 *
85
 * Unset all items set in core forum_menu_local_tasks_alter
86
 */
87
function advanced_forum_menu_local_tasks_alter(&$data, $router_item, $root_path) {
88
  if ($root_path == 'forum' || $root_path == 'forum/%') {
89
    $data['actions']['output'] = array();
90
  }
91
}
92

    
93
/**
94
 * Implements hook_module_implements_alter().
95
 *
96
 * We don't want forum_menu_local_tasks_alter() to be called and mess with our links.
97
 */
98
function advanced_forum_module_implements_alter(&$implementations, $hook) {
99
  if ($hook == 'menu_local_tasks_alter') {
100
    unset($implementations['forum']);
101
  }
102
}
103

    
104
/**
105
 * Implements hook_theme().
106
 */
107
function advanced_forum_theme() {
108
  advanced_forum_load_style_includes();
109

    
110
  // Bulk read all available (active) style templates.
111
  $existing_items = advanced_forum_find_style_templates();
112

    
113
  $items['advanced_forum_l'] = array(
114
    'variables' => array(
115
      'text' => NULL,
116
      'path' => NULL,
117
      'options' => array(),
118
      'button_class' => NULL,
119
    ),
120
  );
121

    
122
  $items['advanced_forum_topic_header'] = array(
123
    'variables' => array(
124
      'node' => NULL,
125
      'comment_count' => NULL,
126
    ),
127
  );
128

    
129
  $items['advanced_forum_active_poster'] = array(
130
    'variables' => array(
131
      'forum' => NULL,
132
      'account' => NULL,
133
      'posts' => NULL,
134
      'topics' => NULL,
135
      'last_post' => NULL,
136
    ),
137
  );
138

    
139
  $items['advanced_forum_user_picture'] = array(
140
    'variables' => array(
141
      'account' => NULL,
142
    ),
143
  );
144

    
145
  $items['advanced_forum_reply_link'] = array(
146
    'variables' => array(
147
      'node' => NULL,
148
    ),
149
  );
150

    
151
  $items['advanced_forum_topic_pager'] = array(
152
    'variables' => array(
153
      'pagecount' => NULL,
154
      'topic' => NULL,
155
    ),
156
  );
157

    
158
  $items['advanced_forum_shadow_topic'] = array(
159
    'variables' => array(
160
      'title' => NULL,
161
      'nid' => NULL,
162
      'new_forum' => NULL,
163
    ),
164
  );
165

    
166
  $items['advanced_forum_subforum_list'] = array(
167
    'variables' => array(
168
      'subforum_list' => NULL,
169
    ),
170
  );
171

    
172
  $items['advanced_forum_subcontainer_list'] = array(
173
    'variables' => array(
174
      'subcontainer_list' => NULL,
175
    ),
176
  );
177

    
178
  $items['advanced_forum_simple_author_pane'] = array(
179
    'variables' => array(
180
      'context' => NULL,
181
    ),
182
  );
183

    
184
  $items['advanced_forum_post_edited'] = array(
185
    'variables' => array(
186
      'who' => NULL,
187
      'when' => NULL,
188
      'why' => NULL,
189
    ),
190
  );
191

    
192
  $items['advanced_forum_node_type_create_list'] = array(
193
    'variables' => array(
194
      'forum_id' => NULL,
195
    ),
196
  );
197
  /*
198
  // Templates for features added by Views
199

    
200
  // style
201
  $items['views_view_forum_topic_list__advanced_forum_topic_list'] = array(
202
  'variables' => array('view' => NULL, 'options' => NULL, 'rows' => NULL, 'title' => NULL),
203
  'template' => 'advanced-forum-topic-list-view',
204
  //'base hook' => 'views_view_forum_topic_list',
205
  );
206
   */
207
  // Display.
208
  $items['views_view__advanced_forum_topic_list'] = array(
209
    'variables' => array('view' => NULL),
210
    'template' => 'advanced-forum-topic-list-outer-view',
211
  );
212

    
213
  // Display group.
214
  $items['views_view__advanced_forum_group_topic_list'] = array(
215
    'variables' => array('view' => NULL),
216
    'template' => 'advanced-forum-group-topic-list-outer-view',
217
  );
218

    
219
  // Return merged items found in style folder with new ones
220
  // array_merge_recursive doesn't work as desired.
221
  foreach ($items as $key => $item) {
222
    if (array_key_exists($key, $existing_items)) {
223
      $existing_items[$key] += $item;
224
    }
225
    else {
226
      $existing_items[$key] = $item;
227
    }
228
  }
229

    
230
  return $existing_items;
231
}
232

    
233
/**
234
 * Implements hook_theme_registry_alter().
235
 */
236
function advanced_forum_theme_registry_alter(&$theme_registry) {
237
  advanced_forum_load_style_includes();
238

    
239
  // Don't let core do its basic preprocess for forums, as we want to do
240
  // other stuff now.
241
  if (isset($theme_registry['forums']['preprocess functions'])) {
242
    foreach ($theme_registry['forums']['preprocess functions'] as $key => $value) {
243
      if ($value == 'template_preprocess_forums') {
244
        unset($theme_registry['forums']['preprocess functions'][$key]);
245
      }
246
    }
247
  }
248

    
249
  // We duplicate all of core's forum list preprocessing so no need to run
250
  // it twice. Running twice also causes problems with & in forum name.
251
  if (isset($theme_registry['forum_list']['preprocess functions'])) {
252
    foreach ($theme_registry['forum_list']['preprocess functions'] as $key => $value) {
253
      if ($value == 'template_preprocess_forum_list') {
254
        unset($theme_registry['forum_list']['preprocess functions'][$key]);
255
      }
256
    }
257
  }
258

    
259
  // Views handles the topic list pages so remove the core template preprocess.
260
  if (isset($theme_registry['forum_topic_list']['preprocess functions'])) {
261
    foreach ($theme_registry['forum_topic_list']['preprocess functions'] as $key => $value) {
262
      if ($value == 'template_preprocess_forum_topic_list') {
263
        unset($theme_registry['forum_topic_list']['preprocess functions'][$key]);
264
      }
265
    }
266
  }
267

    
268
  // --- The following section manipulates the theme registry so the .tpl files
269
  // --- for the given templates can be found first in the (sub)theme directory
270
  // --- then in ancestor themes, if any, then in the active style directory
271
  // --- for advanced forum or any ancestor styles.
272
  // Affected templates.
273
  $templates = array(
274
    'node',
275
    'comment',
276
    'comment_wrapper',
277
    'forums',
278
    'forum_list',
279
    'forum_topic_list',
280
    'forum_icon',
281
    'forum_submitted',
282
    'author_pane',
283
      /*
284
        'advanced_forum_statistics',
285
        'advanced_forum_topic_list_view',
286
        'advanced_forum_topic_legend',
287
        'advanced_forum_forum_legend',
288
        'advanced_forum_topic_header',
289
        'advanced_forum_active_poster', */
290
  );
291

    
292
  // Get the sequence of styles to look in for templates.
293
  $lineage = advanced_forum_style_lineage();
294

    
295
  if (!array_key_exists('naked', $lineage)) {
296
    // Add naked in at the end of the line to prevent problems if a style
297
    // doesn't include all needed templates.
298
    $lineage['naked'] = drupal_get_path('module', 'advanced_forum') . '/styles/naked';
299
  }
300

    
301
  // Get theme engine extension.
302
  global $theme_engine;
303
  $extension = '.tpl.php';
304
  if (isset($theme_engine)) {
305
    $extension_function = $theme_engine . '_extension';
306
    if (function_exists($extension_function)) {
307
      $extension = $extension_function();
308
    }
309
  }
310

    
311
  foreach ($templates as $template) {
312
    // Sanity check in case the template is not being used.
313
    if (!empty($theme_registry[$template])) {
314
      // There are preprocess functions to add, so figure out where we want to add
315
      // them.
316
      if (!empty($preprocess)) {
317
        $position = 0;
318
        foreach ($theme_registry[$template]['preprocess functions'] as $function) {
319
          $position++;
320
          // If we see either of these items, that means we can place our
321
          // preprocess functions after this.
322
          if (substr($function, 0, 25) == 'advanced_forum_preprocess' || substr($function, 0, 34) == 'template_preprocess_advanced_forum') {
323
            break;
324
          }
325
        }
326
        // Add in our new preprocess functions:
327
        if (isset($theme_registry[$template]['preprocess functions'])) {
328
          array_splice($theme_registry[$template]['preprocess functions'], $position, 0, $preprocess);
329
        }
330
      }
331
    }
332
  }
333

    
334
  // Temp workaround.
335
  if (isset($theme_registry['views_view__advanced_forum_topic_list']['preprocess functions'])) {
336
    array_splice($theme_registry['views_view__advanced_forum_topic_list']['preprocess functions'], 1, 0, 'template_preprocess_views_view');
337
  }
338
  if (isset($theme_registry['views_view__advanced_forum_group_topic_list']['preprocess functions'])) {
339
    array_splice($theme_registry['views_view__advanced_forum_group_topic_list']['preprocess functions'], 1, 0, 'template_preprocess_views_view');
340
  }
341
}
342

    
343
/**
344
 * Own link alteration implementation because there are no hook_link nor hook_link_alter in D7.
345
 *
346
 * Name changed intentionally to avoid confusion with hook_link_alter!
347
 */
348
function advanced_forum_links_alter(&$object, $view_mode, $object_type = 'node') {
349
  // Don't alter anything if in preview mode.
350
  if (!empty($object->in_preview)) {
351
    return;
352
  }
353

    
354
  if (!advanced_forum_is_styled($object, ($view_mode == 'teaser'), $object_type)) {
355
    return;
356
  }
357

    
358
  if (!empty($object->content['links']['comment'])) {
359
    $comment_links = $object->content['links']['comment'];
360
    $links = empty($comment_links['#links']) ? array() : $comment_links['#links'];
361
  }
362
  else {
363
    $comment_links = array();
364
    $links = array();
365
  }
366

    
367
  if ($object_type == 'node') {
368

    
369
    $node = $object;
370
    // Add edit / delete links to the node links to match replies.
371
    if (node_access('update', $node)) {
372
      $links['post-edit'] = array(
373
        'title' => t('edit'),
374
        'href' => 'node/' . $node->nid . '/edit',
375
        'query' => drupal_get_destination(),
376
      );
377
    }
378

    
379
    if (node_access('delete', $node)) {
380
      $links['post-delete'] = array(
381
        'title' => t('delete'),
382
        'href' => 'node/' . $node->nid . '/delete',
383
      );
384
    }
385
  }
386

    
387
  // Change first post from "add comment" to "reply" if it isn't already.
388
  if (!empty($links['comment-add'])) {
389
    $links['comment-add']['title'] = t('reply');
390
    $links['comment-add']['href'] = "comment/reply/$node->nid";
391
  }
392

    
393
  // List the keys we are interested in.
394
  $affected_keys = array(
395
    'post-edit',
396
    'comment-edit',
397
    'post-delete',
398
    'comment-delete',
399
    'quote',
400
    'comment-add',
401
    'comment-reply',
402
  );
403

    
404
  // Add extra span tags for image replacement.
405
  foreach ($links as $key => $link) {
406
    if (in_array($key, $affected_keys)) {
407
      $links[$key]['attributes']['class'][] = "af-button-small";
408
      $links[$key]['title'] = '<span>' . $links[$key]['title'] . '</span>';
409
      $links[$key]['html'] = TRUE;
410
    }
411
  }
412

    
413
  // Put the links in a consistent order.
414
  foreach ($affected_keys as $key) {
415
    if (isset($links[$key])) {
416
      $temp = $links[$key];
417
      unset($links[$key]);
418
      $links[$key] = $temp;
419
    }
420
  }
421

    
422
  // We want to put comment links last.
423
  unset($object->content['links']['comment']);
424
  $object->content['links']['comment'] = $comment_links;
425

    
426
  // Put links back.
427
  $object->content['links']['comment']['#links'] = $links;
428
}
429

    
430
/**
431
 * Implements hook_node_view().
432
 *
433
 * hook_link() and hook_link_alter() functionality implemented here
434
 */
435
function advanced_forum_node_view($node, $view_mode, $langcode) {
436
  advanced_forum_links_alter($node, $view_mode, 'node');
437
}
438

    
439
/**
440
 * Implements hook_comment_view().
441
 */
442
function advanced_forum_comment_view($comment, $view_mode, $langcode) {
443
  advanced_forum_links_alter($comment, $view_mode, 'comment');
444
}
445

    
446
/**
447
 * Implements hook_comment_delete().
448
 */
449
function advanced_forum_comment_delete($comment) {
450
  if (!empty($comment->node_type) && ($comment->node_type == "comment_node_forum")) {
451
    advanced_forum_statistics_replies(-1);
452
  }
453
}
454

    
455
/**
456
 * Implements hook_comment_update().
457
 */
458
function advanced_forum_comment_update($comment) {
459
  if (!empty($comment->node_type) && ($comment->node_type == "comment_node_forum")) {
460
    // Comment unpublished?
461
    if (!$comment->status) {
462
      advanced_forum_statistics_replies(-1);
463
    }
464
  }
465
}
466

    
467
/**
468
 * Implements hook_comment_publish().
469
 */
470
function advanced_forum_comment_publish($comment) {
471
  if (!empty($comment->node_type) && ($comment->node_type == "comment_node_forum")) {
472
    advanced_forum_statistics_replies(1);
473
  }
474
}
475

    
476
/**
477
 * Implements hook_form_alter().
478
 */
479
function advanced_forum_form_alter(&$form, &$form_state, $form_id) {
480
  if (!empty($form['#node']->type) && advanced_forum_type_is_in_forum($form['#node']) && isset($form['body_field']) && isset($form['body_field']['#after_build'])) {
481
    // Remove the teaser splitter.
482
    $teaser_js_build = array_search('node_teaser_js', $form['body_field']['#after_build']);
483
    unset($form['body_field']['#after_build'][$teaser_js_build]);
484
    $form['body_field']['teaser_js']['#access'] = FALSE;
485
    $form['body_field']['teaser_include']['#access'] = FALSE;
486
  }
487

    
488
  // Add our OG view as a potential RON for organic groups.
489
  if (!empty($form['og_settings']['group_details']['og_home_page_view'])) {
490
    $form['og_settings']['group_details']['og_home_page_view']['#options']['advanced_forum_group_topic_list'] = 'advanced_forum_group_topic_list';
491
  }
492
}
493

    
494
/**
495
 * Implements hook_form_form_id_alter().
496
 */
497
function advanced_forum_form_forum_form_forum_alter(&$form, $form_state) {
498
  if (variable_get('advanced_forum_forum_user_term_fields')) {
499
    // Show fields from taxonomy term for forum.
500
    if (!isset($form['tid'])) {
501
      $voc = taxonomy_vocabulary_load($form['vid']['#value']);
502
      $defaults = array(
503
        'name' => '',
504
        'description' => '',
505
        'format' => NULL,
506
        'vocabulary_machine_name' => $voc->machine_name,
507
        'tid' => NULL,
508
        'weight' => 0,
509
      );
510

    
511
      $term = (object) $defaults;
512
    }
513
    else {
514
      $term = taxonomy_term_load($form['tid']['#value']);
515
    }
516

    
517
    $form['#term'] = (array) $term;
518
    $form_state['term'] = $term;
519
    field_attach_form('taxonomy_term', $term, $form, $form_state);
520
  }
521
}
522

    
523
/**
524
 * Implements hook_views_api().
525
 */
526
function advanced_forum_views_api() {
527
  return array(
528
    'api' => '3.0-alpha1',
529
    'path' => drupal_get_path('module', 'advanced_forum') . '/includes/views',
530
  );
531
}
532

    
533
/**
534
 * Tell CTools about what plugins we support.
535
 */
536
function advanced_forum_ctools_plugin_directory($module, $plugin) {
537
  if ($module == 'advanced_forum') {
538
    return 'styles';
539
  }
540

    
541
  if ($module == 'page_manager' || $module == 'ctools') {
542
    return 'plugins/' . $plugin;
543
  }
544
}
545

    
546
/**
547
 * Implements hook_ctools_plugin_api().
548
 */
549
function advanced_forum_ctools_plugin_api($module, $api) {
550
  if ($module == 'page_manager' && $api = 'pages_default') {
551
    return array(
552
      'version' => 1,
553
      'path' => drupal_get_path('module', 'advanced_forum') . '/includes/panels',
554
    );
555
  }
556
}
557

    
558
// THEME FUNCTIONS AND TEMPLATE PREPROCESSES **********************************/
559
module_load_include('inc', 'advanced_forum', 'includes/theme');
560

    
561
// STYLE RELATED FUNCTIONS ****************************************************/
562
module_load_include('inc', 'advanced_forum', 'includes/style');
563

    
564
// CORE FORUM PAGE OVERRIDES **************************************************/
565
module_load_include('inc', 'advanced_forum', 'includes/core-overrides');
566

    
567
// MARK AS READ ***************************************************************/
568
module_load_include('inc', 'advanced_forum', 'includes/mark-read');
569

    
570
// VIEWS RELATED GOODIES ******************************************************/
571
/**
572
 * Post render a view and replace any advanced forum tokens.
573
 */
574
function advanced_forum_views_post_render(&$view, &$output) {
575
  if (empty($view->style_plugin) || !$view->style_plugin->uses_row_plugin()) {
576
    return;
577
  }
578

    
579
  $plugin = $view->display_handler->get_option('row_plugin');
580
  if ($plugin == 'node') {
581
    // Look for token matches in the output:
582
    $matches = array();
583
    $tokens = array();
584

    
585
    // We want to change the look of the 'new' marker from the default, slightly:
586
    $tokens['<span class="new">' . t('new') . '</span>'] = '<span class="new">(' . t('new') . ')</span>';
587

    
588
    // Replace the Author Pane token with the actual Author Pane.
589
    if (preg_match_all('/<!--post:author-pane-([\d]+)-->/us', $output, $matches)) {
590
      foreach ($matches[1] as $match => $uid) {
591
        // This is the exact string that matched.
592
        $token = $matches[0][$match];
593
        if (!isset($tokens[$token])) {
594
          $account = user_load($uid);
595
          $tokens[$token] = theme('author_pane', array(
596
            'account' => $account,
597
            'caller' => 'advanced_forum',
598
            'picture_preset' => variable_get('advanced_forum_user_picture_preset', ''),
599
            'context' => NULL,
600
            'disable_css' => TRUE,
601
            'join_date_type' => variable_get('advanced_forum_author_pane_join_date_type', 'short'),
602
          ));
603
        }
604
      }
605
    }
606

    
607
    // Replace the Post edited token.
608
    if (preg_match_all('/<!--post:post-edited-([\d]+)-->/us', $output, $matches)) {
609
      foreach ($matches[1] as $match => $nid) {
610
        // This is the exact string that matched.
611
        $token = $matches[0][$match];
612
        if (!isset($tokens[$token])) {
613
          if (user_access('view last edited notice')) {
614
            $sql = 'SELECT uid, log, timestamp FROM {node_revision} WHERE nid = %d ORDER BY timestamp DESC';
615
            $row = db_fetch_object(db_query($sql, $nid));
616
            $tokens[$token] = theme('advanced_forum_post_edited', array(
617
              'who' => $row->uid,
618
              'when' => $row->timestamp,
619
              'why' => $row->log
620
            ));
621
          }
622
          else {
623
            // No access; remove token.
624
            $tokens[$token] = '';
625
          }
626
        }
627
      }
628
    }
629

    
630
    // Replace the core Signature token.
631
    if (preg_match_all('/<!--post:signature-core-([\d]+)-->/us', $output, $matches)) {
632
      foreach ($matches[1] as $match => $uid) {
633
        // This is the exact string that matched.
634
        $token = $matches[0][$match];
635
        if (!isset($tokens[$token])) {
636
          $account = user_load($uid);
637
          if ($account->signature) {
638
            $tokens[$token] = check_markup($account->signature, $account->signature_format, FALSE);
639
          }
640
        }
641
      }
642
    }
643

    
644
    // Perform replacements.
645
    $output = strtr($output, $tokens);
646
  }
647
}
648

    
649
/**
650
 * Display the "sort" widget.
651
 *
652
 * This is a specially hacked widget that only
653
 * works with tablesorting. Tablesorting MUST be on for these widgets
654
 * to appear.
655
 */
656
function advanced_forum_forum_topic_list_sort() {
657
  $form_state = array(
658
    'method' => 'get',
659
    'no_redirect' => TRUE,
660
    'rerender' => TRUE,
661
    'input' => $_GET,
662
    'drop tokens' => TRUE,
663
  );
664

    
665
  $form = drupal_build_form('advanced_forum_forum_topic_list_sort_form', $form_state);
666
  return drupal_render($form);
667
}
668

    
669
/**
670
 * Sort form.
671
 */
672
function advanced_forum_forum_topic_list_sort_form($form_state) {
673
  $view = views_get_view('advanced_forum_topic_list');
674
  $view->set_display('default');
675
  $view->init_handlers();
676
  $view->init_style();
677

    
678
  // Work up a list of possible fields.
679
  $handler = &$view->style_plugin;
680
  $fields = &$view->field;
681
  $columns = $handler->sanitize_columns($handler->options['columns'], $fields);
682

    
683
  $options = array();
684
  foreach ($columns as $field => $column) {
685
    if ($field == $column && empty($fields[$field]->options['exclude'])) {
686
      if (empty($handler->options['info'][$field]['sortable']) || !$fields[$field]->click_sortable()) {
687
        continue;
688
      }
689
      $label = check_plain(!empty($fields[$field]) ? $fields[$field]->label() : '');
690
      $options[$field] = $label;
691
    }
692
  }
693

    
694
  $form['inline'] = array(
695
    '#prefix' => '<div class="container-inline">',
696
    '#suffix' => '</div>',
697
  );
698
  $form['inline']['order'] = array(
699
    '#type' => 'select',
700
    '#title' => t('Order by'),
701
    '#title_display' => 'invisible',
702
    '#options' => $options,
703
    '#default_value' => $handler->options['default'],
704
  );
705

    
706
  $form['inline']['sort'] = array(
707
    '#type' => 'select',
708
    '#title' => t('Sort'),
709
    '#title_display' => 'invisible',
710
    '#options' => array(
711
      'asc' => t('Up'),
712
      'desc' => t('Down'),
713
    ),
714
    '#default_value' => 'desc',
715
  );
716

    
717
  $form['inline']['submit'] = array(
718
    '#id' => 'sort-topic-submit',
719
    '#name' => '',
720
    '#type' => 'submit',
721
    '#value' => t('Sort'),
722
  );
723

    
724
  if (isset($_GET['page'])) {
725
    $form['page'] = array(
726
      '#type' => 'hidden',
727
      '#default_value' => $_GET['page'],
728
    );
729
  }
730

    
731
  if (!variable_get('clean_url', FALSE)) {
732
    $form['q'] = array(
733
      '#type' => 'hidden',
734
      '#value' => $_GET['q'],
735
    );
736
  }
737

    
738
  $view->destroy();
739
  return $form;
740
}
741

    
742
// STATISTICS *****************************************************************/
743
/**
744
 * Count total amount of forum threads.
745
 */
746
function advanced_forum_statistics_topics() {
747
  return db_query('SELECT COUNT(DISTINCT(nid)) FROM {forum}')->fetchField();
748
}
749

    
750
/**
751
 * Counts total amount of replies.
752
 *
753
 * Initial posts are added to this total in the calling function.
754
 *
755
 * @param int|null $delta
756
 *   if not NULL, a numerical delta which should be applied to the count
757
 *
758
 * @param bool $refresh
759
 *   TRUE if the stored count should be updated.
760
 *
761
 * @return int
762
 *   Total number of replies in the forum.
763
 */
764
function advanced_forum_statistics_replies($delta = NULL, $refresh = FALSE) {
765

    
766
  if ($refresh || !($cache = cache_get('advanced_forum_stats_replies'))) {
767
    $total_replies = db_query('SELECT SUM(comment_count) FROM {forum_index}')->fetchField();
768
    cache_set('advanced_forum_stats_replies', $total_replies);
769
  }
770
  else {
771
    $total_replies = $cache->data;
772
  }
773

    
774
  if (!empty($delta) && is_numeric($delta)) {
775
    $total_replies += $delta;
776
    cache_set('advanced_forum_stats_replies', $total_replies);
777
  }
778

    
779
  return $total_replies;
780
}
781

    
782
/**
783
 * Count total amount of active users.
784
 */
785
function advanced_forum_statistics_users() {
786
  return db_query('SELECT COUNT(uid) FROM {users} WHERE status = 1')->fetchField();
787
}
788

    
789
/**
790
 * Return the newest X active (not blocked) users, linked to their profiles.
791
 */
792
function advanced_forum_statistics_latest_users() {
793
  // @TODO: Make this a setting.
794
  $number_to_fetch = 5;
795

    
796
  $query = db_select("users", "u")
797
      ->fields("u", array("uid", "name"))
798
      ->condition("status", 0, "<>")
799
      ->condition("access", 0, "<>")
800
      ->orderBy("created", "DESC");
801

    
802
  $latest_users = $query->range(NULL, $number_to_fetch)->execute();
803
  while ($account = $latest_users->fetchObject()) {
804
    $list[] = theme('username', array('account' => $account));
805
  }
806

    
807
  return $list;
808
}
809

    
810
/**
811
 * Returns session count.
812
 */
813
function advanced_forum_session_count($anonymous = TRUE) {
814

    
815
  $interval = REQUEST_TIME - variable_get('user_block_seconds_online', 900);
816

    
817
  $query = db_select("sessions", "s")
818
      ->fields("s", array("uid"))
819
      ->distinct()
820
      ->condition('s.timestamp', $interval, '>=')
821
      ->condition('s.uid', 0, $anonymous ? '=' : '>')
822
      ->countQuery();
823

    
824
  return $query->execute()->fetchField();
825
}
826

    
827
/**
828
 * Return an array of online usernames, linked to their profiles.
829
 */
830
function advanced_forum_statistics_online_users() {
831
  $list = array();
832

    
833
  $interval = REQUEST_TIME - variable_get('user_block_seconds_online', 900);
834

    
835
  $query = db_select("users", "u")->distinct()->fields("u", array("uid", "name"));
836
  $s_alias = $query->join("sessions", "s", "u.uid = s.uid");
837

    
838
  $query->addExpression("MAX({$s_alias}.timestamp)", "maxtime");
839
  $query
840
      ->condition("{$s_alias}.timestamp", $interval, ">=")
841
      ->condition("{$s_alias}.uid", "0", ">")
842
      ->groupBy("u.uid, u.name")
843
      ->orderBy("maxtime", "DESC");
844

    
845
  $authenticated_users = $query->execute();
846

    
847
  while ($account = $authenticated_users->fetchObject()) {
848
    $list[] = theme('username', array('account' => $account));
849
  }
850

    
851
  return $list;
852
}
853

    
854
/**
855
 * Calculating links - New, Last, Etc.
856
 */
857
function advanced_forum_get_reply_link($node) {
858
  $reply_link = array();
859

    
860
  $comment_setting = $node->comment;
861
  $fragment = 'comment-form';
862

    
863
  if ($comment_setting == COMMENT_NODE_OPEN) {
864

    
865
    $allowed = FALSE;
866
    if (module_exists('forum_access')) {
867
      // Get tid.
868
      if (!empty($node->taxonomy_forums)) {
869
        reset($node->taxonomy_forums);
870
        $langcode = key($node->taxonomy_forums);
871
        if (!empty($node->taxonomy_forums[$langcode])) {
872
          $tid = $node->taxonomy_forums[$langcode][0]['tid'];
873
          if (forum_access_access('create', $tid)) {
874
            $allowed = TRUE;
875
          }
876
        }
877
      }
878
    }
879
    else {
880
      $allowed = user_access('post comments');
881
    }
882

    
883
    if ($allowed) {
884
      if (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {
885
        // Reply form is on separate page. Grab the href from the node links
886
        // so it's automatically corrected for Node Comments if needed.
887
        $reply_link['href'] = "comment/reply/$node->nid";
888
        $reply_link['options']['fragment'] = $fragment;
889

    
890
        $reply_link['class'] = 'reply-allowed';
891
        $reply_link['title'] = t('Post reply');
892

    
893
        return $reply_link;
894
      }
895
      else {
896
        // Reply form is on same page. The reply button should jump down to it
897
        // rather than going to a new page.
898
        $reply_link['href'] = $_GET['q'];
899
        $reply_link['options']['fragment'] = $fragment;
900
        $current_page = isset($_GET['page']) ? $_GET['page'] : 0;
901
        if ($current_page) {
902
          $reply_link['options']['query'] = array('page' => $current_page);
903
        }
904

    
905
        $reply_link['class'] = 'reply-allowed';
906
        $reply_link['title'] = t('Quick reply');
907

    
908
        return $reply_link;
909
      }
910
    }
911
    else {
912
      // User does not have access to post replies on this node.
913
      return 'reply-forbidden';
914
    }
915
  }
916
  else {
917
    // Topic is locked.
918
    return 'reply-locked';
919
  }
920
}
921

    
922
/**
923
 * Get a link to the last post in a topic.
924
 *
925
 * @param object $node
926
 *   Node object
927
 *
928
 * @return string
929
 *   Text linking to the last post in a topic.
930
 */
931
function advanced_forum_last_post_link($node) {
932
  $last_comment_id = advanced_forum_last_post_in_topic($node->nid);
933
  // Return empty link if post doesn't have comments.
934
  if (empty($last_comment_id)) {
935
    return '';
936
  }
937

    
938
  $last_page = advanced_forum_get_last_page($node);
939

    
940
  if ($last_page > 0) {
941
    $query = array('page' => $last_page);
942
  }
943

    
944
  $options = array(
945
    'html' => TRUE,
946
    'query' => empty($query) ? array() : $query,
947
    'fragment' => "comment-$last_comment_id",
948
  );
949

    
950
  return theme('advanced_forum_l', array(
951
    'text' => t('Last post'),
952
    'path' => "node/$node->nid",
953
    'options' => $options,
954
    'button_class' => 'large'
955
  ));
956
}
957

    
958
/**
959
 * Returns a link directly to the first new post in a topic.
960
 *
961
 * @param object $node
962
 *   Node object
963
 *
964
 * @param int $comment_count
965
 *   Number of comments on passed node.
966
 *
967
 * @return string
968
 *   Link to the first unread post.
969
 */
970
function advanced_forum_first_new_post_link($node, $comment_count) {
971
  $nid = $node->nid;
972

    
973
  $current_page = isset($_GET['page']) ? $_GET['page'] : 0;
974
  $number_new_comments = advanced_forum_reply_num_new($nid);
975

    
976
  if ($number_new_comments > 0) {
977
    $page_of_first_new = advanced_forum_page_first_new($comment_count, $number_new_comments, $node);
978

    
979
    // Note that we are linking to the cid anchor rather than "new" because
980
    // the new links will be gone if we go to another page.
981
    $cid_of_first_new = advanced_forum_first_new_comment($nid);
982

    
983
    $number_new = t("(!new new)", array('!new' => $number_new_comments));
984

    
985
    $options = array(
986
      'html' => TRUE,
987
      'query' => $page_of_first_new,
988
      'fragment' => "comment-$cid_of_first_new",
989
    );
990

    
991
    return theme('advanced_forum_l', array(
992
      'text' => t('First unread'),
993
      'path' => "node/$nid",
994
      'options' => $options,
995
      'button_class' => 'large',
996
    ));
997
  }
998
}
999

    
1000
/**
1001
 * Get the page number with the first new post.
1002
 */
1003
function advanced_forum_page_first_new($comment_count, $new_replies, $node) {
1004
  return comment_new_page_count($comment_count, $new_replies, $node);
1005
}
1006

    
1007
/**
1008
 * Get the number of new posts on a topic.
1009
 */
1010
function advanced_forum_reply_num_new($nid, $timestamp = 0) {
1011
  // Make a static cache because this function is called twice from the topic
1012
  // header. Once to display the number and once to make the link to first new.
1013
  static $number_new_for_node = array();
1014

    
1015
  // $nid is empty if new topic in preview.
1016
  if (empty($nid)) {
1017
    return 0;
1018
  }
1019

    
1020
  if (empty($number_new_for_node[$nid])) {
1021
    global $user;
1022

    
1023
    $node = node_load($nid);
1024

    
1025
    // We must also check the forum post itself to see if we have viewed it.
1026
    // If not told otherwise, it has been viewed before.
1027
    $viewed = 0;
1028
    if ($user->uid) {
1029
      $viewed = node_last_viewed($nid);
1030
      // Set it to 1 if it has not been viewed before.
1031
      $viewed = ($viewed == 0 ? 1 : 0);
1032
    }
1033

    
1034
    $number_new_for_node[$nid] = comment_num_new($nid, $timestamp) + $viewed;
1035
  }
1036

    
1037
  return $number_new_for_node[$nid];
1038
}
1039

    
1040
/**
1041
 * Get the comment id of the last post in a topic.
1042
 *
1043
 * @param int $nid
1044
 *   Node id.
1045
 *
1046
 * @return int
1047
 *   cid of last post.
1048
 */
1049
function advanced_forum_last_post_in_topic($nid) {
1050
  // $nid is empty if new topic in preview.
1051
  if (empty($nid)) {
1052
    return NULL;
1053
  }
1054

    
1055
  $node = node_load($nid);
1056

    
1057
  // Comment module version.
1058
  $query = 'SELECT c.cid
1059
            FROM {comment} c
1060
            WHERE c.nid = :nid AND c.status = :status
1061
            ORDER BY c.cid DESC';
1062
  $result = db_query_range($query, 0, 1, array(':nid' => $nid, ':status' => COMMENT_PUBLISHED))->fetchField();
1063

    
1064
  return $result;
1065
}
1066

    
1067
/**
1068
 * Returns the page number of the last page starting at 0 like the pager does.
1069
 */
1070
function advanced_forum_get_last_page($node) {
1071
  $comments_per_page = variable_get('comment_default_per_page_' . $node->type, 50);
1072
  $comment_count = isset($node->comment_count) ? $node->comment_count : 0;
1073
  $last_page = ceil($comment_count / $comments_per_page) - 1;
1074
  return $last_page;
1075
}
1076

    
1077
/**
1078
 * Returns the ID of the first unread comment.
1079
 *
1080
 * @param int $nid
1081
 *   Node ID
1082
 *
1083
 * @param int $timestamp
1084
 *   Date/time used to override when the user last viewed the node.
1085
 *
1086
 * @return int
1087
 *   Comment ID
1088
 */
1089
function advanced_forum_first_new_comment($nid, $timestamp = 0) {
1090
  global $user;
1091

    
1092
  if ($user->uid) {
1093
    // Retrieve the timestamp at which the current user last viewed the
1094
    // specified node.
1095
    if (!$timestamp) {
1096
      $timestamp = node_last_viewed($nid);
1097
    }
1098

    
1099
    // Set the timestamp to the limit if the node was last read past the cutoff.
1100
    $timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT);
1101

    
1102
    // Use the timestamp to retrieve the oldest new comment.
1103
    $query = db_select('comment', 'c')
1104
        ->fields('c', array('cid'))
1105
        ->condition('nid', $nid)
1106
        ->condition('changed', $timestamp, '>')
1107
        ->condition('status', COMMENT_PUBLISHED)
1108
        ->range(0, 1)
1109
        ->execute();
1110
    return $query->fetchField();
1111
  }
1112
  else {
1113
    return 0;
1114
  }
1115
}
1116

    
1117
// GENERAL UTILITY FUNCTIONS *************************************************/
1118
/**
1119
 * Return an array of node types allowed in a given vocabulary or term ID.
1120
 */
1121
function advanced_forum_allowed_node_types($tid = 0) {
1122
  if (module_exists('forum_access')) {
1123
    // Check with forum access to see if this forum allows node creation.
1124
    // If it doesn't, send back an empty list.
1125
    if (!forum_access_access('create', $tid)) {
1126
      return array();
1127
    }
1128
  }
1129

    
1130
  $field = field_info_field('taxonomy_forums');
1131
  if (!empty($field['bundles']['node'])) {
1132
    return $field['bundles']['node'];
1133
  }
1134
  else {
1135
    return array();
1136
  }
1137
}
1138

    
1139
/**
1140
 * Return whether a given node type is allowed in the whole forum or given forum.
1141
 */
1142
function advanced_forum_type_is_in_forum($node, $tid = 0) {
1143
  $vid = (empty($vid)) ? variable_get('forum_nav_vocabulary', 0) : $vid;
1144
  if (!empty($node->taxonomy_forums)) {
1145
    // Check for language used.
1146
    if (!isset($node->taxonomy_forums[$node->language])) {
1147
      $langcode = LANGUAGE_NONE;
1148
    }
1149
    else {
1150
      $langcode = $node->language;
1151
    }
1152

    
1153
    foreach ($node->taxonomy_forums[$langcode] as $tforum) {
1154
      if (!isset($tforum['taxonomy_term'])) {
1155
        continue;
1156
      }
1157
      if (($tforum['taxonomy_term']->vid == $vid) || ($tforum['taxonomy_term']->tid == $tid)) {
1158
        return TRUE;
1159
      }
1160
    }
1161
  }
1162
  return FALSE;
1163
}
1164

    
1165
/**
1166
 * Generate a list of node creation links for a forum.
1167
 *
1168
 * This is used on the forum list, allowing us to have direct
1169
 * links to create new nodes in the forum.
1170
 */
1171
function advanced_forum_node_type_create_list($tid) {
1172
  $allowed_types = advanced_forum_allowed_node_types($tid);
1173

    
1174
  // Ensure "new topic" is first.
1175
  if (isset($allowed_types['forum'])) {
1176
    unset($allowed_types['forum']);
1177
    array_unshift($allowed_types, 'forum');
1178
  }
1179
  // Loop through all node types allowed in this forum.
1180
  foreach ($allowed_types as $type) {
1181
    // Check if this node type can be created by current user.
1182
    if (node_access('create', $type)) {
1183
      // Fetch the "General" name of the content type.
1184
      $node_type = t(node_type_get_name($type));
1185

    
1186
      // Remove the word "Forum" out of "Forum topic" to shorten it.
1187
      // @TODO: this is a little dodgy and may not work right with
1188
      // translations. Should be replaced if there's a better way.
1189
      $node_type = str_replace('Forum', '', $node_type);
1190

    
1191
      // Push the link with title and url to the array.
1192
      $forum_types[$type] = array(
1193
        'name' => $node_type,
1194
        'href' => 'node/add/' . str_replace('_', '-', $type) . '/' . $tid,
1195
      );
1196
    }
1197
  }
1198

    
1199
  if (empty($forum_types)) {
1200
    // The user is logged-in; but denied access to create any new forum content type.
1201
    global $user;
1202
    if ($user->uid) {
1203
      return t('You are not allowed to post new content in the forum.');
1204
    }
1205
    // The user is not logged-in; and denied access to create any new forum content type.
1206
    else {
1207
      return t('<a href="@login">Log in</a> to post new content in the forum.', array(
1208
        '@login' => url('user/login', array('query' => drupal_get_destination())),
1209
      ));
1210
    }
1211
  }
1212
  else {
1213
    return $forum_types;
1214
  }
1215
}
1216

    
1217
/**
1218
 * Create a drop down list of forum actions.
1219
 */
1220
function advanced_forum_forum_tools($tid = 0) {
1221
  global $user;
1222
  $options = array();
1223

    
1224
  ctools_include('jump-menu');
1225
  if ($tid > 0) {
1226
    $select[url("forum/active", array('query' => array('forum[]' => $tid)))] = t("View active posts in this forum");
1227
    $select[url("forum/unanswered", array('query' => array('forum[]' => $tid)))] = t("View unanswered posts in this forum");
1228

    
1229
    if ($user->uid) {
1230
      $select[url("forum/new", array('query' => array('forum[]' => $tid)))] = t("View new posts in this forum");
1231
    }
1232
  }
1233
  else {
1234
    $select[url("forum/active")] = t("View active forum posts");
1235
    $select[url("forum/unanswered")] = t("View unanswered forum posts");
1236

    
1237
    if ($user->uid) {
1238
      $select[url("forum/new")] = t("View new forum posts");
1239
    }
1240
  }
1241

    
1242
  // Add mark as read to the jump list.
1243
  // This code is a little odd and needs explaining. The return value of
1244
  // the mark_as_read function is already formed HTML and so is unsuitable
1245
  // for the jump list. The function already has built in the ability
1246
  // to add to an existing $links array, which has the URL and title text
1247
  // separated. Rather than add a third method just for the jump menu, I
1248
  // reused that functionality here.
1249
  $mark_as_read = array();
1250
  advanced_forum_get_mark_read_link($tid, $mark_as_read);
1251
  if (!empty($mark_as_read['mark-read']['href'])) {
1252
    $select[url($mark_as_read['mark-read']['href'])] = $mark_as_read['mark-read']['title'];
1253
  }
1254

    
1255
  $options['choose'] = t("- Forum Tools -");
1256

    
1257
  // Create and return the jump menu.
1258
  $form = drupal_get_form('ctools_jump_menu', $select, $options);
1259
  return drupal_render($form);
1260
}
1261

    
1262
/**
1263
 * Creates a pager to place on each multi-page topic of the topic listing page.
1264
 *
1265
 * @param int $max_pages_to_display
1266
 *   Number of pages to include on the pager.
1267
 *
1268
 * @param object $topic
1269
 *   Topic object to create a pager for.
1270
 *
1271
 * @return object
1272
 *   Object containing the linked pages ready assembly by the theme function.
1273
 */
1274
function advanced_forum_create_topic_pager($max_pages_to_display, $topic) {
1275
  // Find the number of comments per page for the node type of the topic.
1276
  $comments_per_page = variable_get('comment_default_per_page_' . $topic->type, 50);
1277

    
1278
  if ($max_pages_to_display > 0 && $topic->comment_count > $comments_per_page) {
1279
    // Topic has more than one page and a pager is wanted. Start off the
1280
    // first page because that doesn't have a query.
1281
    $pager_array = array();
1282
    $current_display_page = 1;
1283
    // @codingStandardsIgnoreStart
1284
    $pager_array[0] = l('1', "node/$topic->nid");
1285
    // @codingStandardsIgnoreEnd
1286

    
1287
    // Find the ending point. The pager URL is always 1 less than
1288
    // the number being displayed because the first page is 0.
1289
    $last_display_page = ceil($topic->comment_count / $comments_per_page);
1290
    $last_pager_page = $last_display_page - 1;
1291

    
1292
    // Add pages until we run out or until we hit the max to show.
1293
    while (($current_display_page < $last_display_page) && ($current_display_page < $max_pages_to_display)) {
1294
      // Move to the next page.
1295
      $current_display_page++;
1296

    
1297
      // The page number we link to is 1 less than what's displayed.
1298
      $link_to_page = $current_display_page - 1;
1299

    
1300
      // Add the link to the array.
1301
      $pager_array[$link_to_page] = l($current_display_page, "node/$topic->nid", array('query' => array('page' => $link_to_page)));
1302
    }
1303

    
1304
    // Move to the next page.
1305
    $current_display_page++;
1306

    
1307
    if ($current_display_page == $last_display_page) {
1308
      // We are one past the max to display, but it's the last page,
1309
      // so putting the ...last is silly. Just display it normally.
1310
      $link_to_page = $current_display_page - 1;
1311
      $pager_array[$link_to_page] = l($current_display_page, "node/$topic->nid", array('query' => array('page' => $link_to_page)));
1312
    }
1313

    
1314
    if ($current_display_page < $last_display_page) {
1315
      // We are one past the max to display and still aren't
1316
      // on the last page, so put in ... Last Page(N)
1317
      $text = t('Last Page');
1318
      $pager_last_text = l($text, "node/$topic->nid", array('query' => array('page' => $last_pager_page)));
1319
      $pager_last_number = l($last_display_page, "node/$topic->nid", array('query' => array('page' => $last_pager_page)));
1320
      // Create last page array to enable more customization for themers.
1321
      $pager_last = array(
1322
        'number' => $last_display_page,
1323
        'link' => "node/$topic->nid",
1324
        'options' => array('query' => array('page' => $last_pager_page)),
1325
      );
1326
    }
1327

    
1328
    $topic_pager = new stdClass();
1329
    $topic_pager->initial_pages = (empty($pager_array)) ? array() : $pager_array;
1330
    $topic_pager->last_page_text = (empty($pager_last_text)) ? '' : $pager_last_text;
1331
    $topic_pager->last_page_number = (empty($pager_last_number)) ? '' : $pager_last_number;
1332
    $topic_pager->last_page = (empty($pager_last)) ? array() : $pager_last;
1333

    
1334
    return $topic_pager;
1335
  }
1336
}
1337

    
1338
/**
1339
 * Create a drop down list of forum hierarchy.
1340
 */
1341
function advanced_forum_forum_jump($tid = 0) {
1342
  global $user;
1343
  ctools_include('jump-menu');
1344
  $select = array();
1345
  $options = array();
1346
  $vid = variable_get('forum_nav_vocabulary', 0);
1347
  if ($tid > 0) {
1348
    $forum_tree = taxonomy_get_tree($vid);
1349
    foreach ($forum_tree as $forum) {
1350
      $select[url("forum/" . $forum->tid)] = str_repeat("-", $forum->depth) . $forum->name;
1351
    }
1352
  }
1353

    
1354
  $options['choose'] = t("- Select a forum -");
1355

    
1356
  // Create and return the jump menu.
1357
  $form = drupal_get_form('ctools_jump_menu', $select, $options);
1358
  return drupal_render($form);
1359
}
1360

    
1361
/**
1362
 * Calculates the number of unread replies for each forum and returns the count for the requested forum.
1363
 */
1364
function advanced_forum_unread_replies_in_forum($tid, $uid) {
1365
  static $result_cache = NULL;
1366

    
1367
  if (is_null($result_cache)) {
1368
    $result_cache = array();
1369

    
1370
    $query = db_select("comment", "c");
1371
    $f_alias = $query->join("forum", "f", "c.nid = f.nid");
1372
    $h_alias = $query->leftJoin("history", "h", "c.nid = h.nid AND h.uid = :uid", array(":uid" => $uid));
1373
    $query->addExpression("COUNT(DISTINCT(c.cid))", "count");
1374
    $query->addField($f_alias, "tid");
1375

    
1376
    $query->condition("c.status", COMMENT_PUBLISHED)
1377
        ->condition("c.changed", NODE_NEW_LIMIT, ">")
1378
        ->condition(db_or()->where("c.changed > {$h_alias}.timestamp")->isNull("h.timestamp"))
1379
        ->groupBy("{$f_alias}.tid")
1380
        ->addTag("node_access");
1381

    
1382
    $result = $query->execute();
1383
    foreach ($result as $row) {
1384
      $result_cache[$row->tid] = $row->count;
1385
    }
1386
  }
1387

    
1388
  return (isset($result_cache[$tid])) ? $result_cache[$tid] : 0;
1389
}
1390

    
1391
/**
1392
 * Returns the display position of a given reply post ID on a given node.
1393
 */
1394
function advanced_forum_post_position($node, $comment) {
1395
  static $post_order = array();
1396

    
1397
  if (empty($node) || empty($comment)) {
1398
    return 0;
1399
  }
1400

    
1401
  $node_id = $node->nid;
1402
  $post_id = $comment->cid;
1403

    
1404
  if (!isset($post_order[$node_id])) {
1405
    // Initialize the spot for this node's list.
1406
    $post_order[$node_id] = array();
1407

    
1408
    $mode = variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED);
1409

    
1410
    // Get the list of CIDs from the database in order of oldest first.
1411
    // We are going to make that assumption for now for simplicity but may
1412
    // revisit in the future if there are requests for newest first.
1413
    $query = db_select('comment', 'c')
1414
        ->fields('c', array('cid'))
1415
        ->condition('c.nid', $node_id)
1416
        ->addTag('node_access')
1417
        ->addTag('comment_filter');
1418

    
1419
    if ($mode === COMMENT_MODE_FLAT) {
1420
      $query->orderBy('c.cid', 'ASC');
1421
    }
1422
    else {
1423
      $query->addExpression('SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))', 'torder');
1424
      $query->orderBy('torder', 'ASC');
1425
    }
1426

    
1427
    $query = $query->execute();
1428
    // Cycle through the results and fill in the array.
1429
    while ($post = $query->fetchAssoc()) {
1430
      $post_order[$node_id][] = reset($post);
1431
    }
1432
  }
1433

    
1434
  // Find the position of the passed in post ID.
1435
  $post_position = 0;
1436
  if (is_array($post_order[$node_id])) {
1437
    if (($index = array_search($post_id, $post_order[$node_id])) !== FALSE) {
1438
      $post_position = $index;
1439

    
1440
      $advanced_forum_styled_node_types = variable_get('advanced_forum_styled_node_types', array('forum'));
1441
      // We need to add 1 because the topic node is post #1 on display but is not included in the index.
1442
      if (in_array($node->type, $advanced_forum_styled_node_types)) {
1443
        $post_position = $post_position + 1;
1444
      }
1445
    }
1446
  }
1447

    
1448
  return $post_position;
1449
}