Projet

Général

Profil

Paste
Télécharger (42,3 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / advanced_forum / advanced_forum.module @ 13c3c9b4

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_views_api().
496
 */
497
function advanced_forum_views_api() {
498
  return array(
499
    'api' => '3.0-alpha1',
500
    'path' => drupal_get_path('module', 'advanced_forum') . '/includes/views',
501
  );
502
}
503

    
504
/**
505
 * Tell CTools about what plugins we support.
506
 */
507
function advanced_forum_ctools_plugin_directory($module, $plugin) {
508
  if ($module == 'advanced_forum') {
509
    return 'styles';
510
  }
511

    
512
  if ($module == 'page_manager' || $module == 'ctools') {
513
    return 'plugins/' . $plugin;
514
  }
515
}
516

    
517
/**
518
 * Implements hook_ctools_plugin_api().
519
 */
520
function advanced_forum_ctools_plugin_api($module, $api) {
521
  if ($module == 'page_manager' && $api = 'pages_default') {
522
    return array(
523
      'version' => 1,
524
      'path' => drupal_get_path('module', 'advanced_forum') . '/includes/panels',
525
    );
526
  }
527
}
528

    
529
// THEME FUNCTIONS AND TEMPLATE PREPROCESSES **********************************/
530
module_load_include('inc', 'advanced_forum', 'includes/theme');
531

    
532
// STYLE RELATED FUNCTIONS ****************************************************/
533
module_load_include('inc', 'advanced_forum', 'includes/style');
534

    
535
// CORE FORUM PAGE OVERRIDES **************************************************/
536
module_load_include('inc', 'advanced_forum', 'includes/core-overrides');
537

    
538
// MARK AS READ ***************************************************************/
539
module_load_include('inc', 'advanced_forum', 'includes/mark-read');
540

    
541
// VIEWS RELATED GOODIES ******************************************************/
542
/**
543
 * Post render a view and replace any advanced forum tokens.
544
 */
545
function advanced_forum_views_post_render(&$view, &$output) {
546
  if (empty($view->style_plugin) || !$view->style_plugin->uses_row_plugin()) {
547
    return;
548
  }
549

    
550
  $plugin = $view->display_handler->get_option('row_plugin');
551
  if ($plugin == 'node') {
552
    // Look for token matches in the output:
553
    $matches = array();
554
    $tokens = array();
555

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

    
559
    // Replace the Author Pane token with the actual Author Pane.
560
    if (preg_match_all('/<!--post:author-pane-([\d]+)-->/us', $output, $matches)) {
561
      foreach ($matches[1] as $match => $uid) {
562
        // This is the exact string that matched.
563
        $token = $matches[0][$match];
564
        if (!isset($tokens[$token])) {
565
          $account = user_load($uid);
566
          $tokens[$token] = theme('author_pane', array(
567
            'account' => $account,
568
            'caller' => 'advanced_forum',
569
            'picture_preset' => variable_get('advanced_forum_user_picture_preset', ''),
570
            'context' => NULL,
571
            'disable_css' => TRUE,
572
            'join_date_type' => variable_get('advanced_forum_author_pane_join_date_type', 'short'),
573
          ));
574
        }
575
      }
576
    }
577

    
578
    // Replace the Post edited token.
579
    if (preg_match_all('/<!--post:post-edited-([\d]+)-->/us', $output, $matches)) {
580
      foreach ($matches[1] as $match => $nid) {
581
        // This is the exact string that matched.
582
        $token = $matches[0][$match];
583
        if (!isset($tokens[$token])) {
584
          if (user_access('view last edited notice')) {
585
            $sql = 'SELECT uid, log, timestamp FROM {node_revision} WHERE nid = %d ORDER BY timestamp DESC';
586
            $row = db_fetch_object(db_query($sql, $nid));
587
            $tokens[$token] = theme('advanced_forum_post_edited', array(
588
              'who' => $row->uid,
589
              'when' => $row->timestamp,
590
              'why' => $row->log
591
            ));
592
          }
593
          else {
594
            // No access; remove token.
595
            $tokens[$token] = '';
596
          }
597
        }
598
      }
599
    }
600

    
601
    // Replace the core Signature token.
602
    if (preg_match_all('/<!--post:signature-core-([\d]+)-->/us', $output, $matches)) {
603
      foreach ($matches[1] as $match => $uid) {
604
        // This is the exact string that matched.
605
        $token = $matches[0][$match];
606
        if (!isset($tokens[$token])) {
607
          $account = user_load($uid);
608
          if ($account->signature) {
609
            $tokens[$token] = check_markup($account->signature, $account->signature_format, FALSE);
610
          }
611
        }
612
      }
613
    }
614

    
615
    // Perform replacements.
616
    $output = strtr($output, $tokens);
617
  }
618
}
619

    
620
/**
621
 * Display the "sort" widget.
622
 *
623
 * This is a specially hacked widget that only
624
 * works with tablesorting. Tablesorting MUST be on for these widgets
625
 * to appear.
626
 */
627
function advanced_forum_forum_topic_list_sort() {
628
  $form_state = array(
629
    'method' => 'get',
630
    'no_redirect' => TRUE,
631
    'rerender' => TRUE,
632
    'input' => $_GET,
633
    'drop tokens' => TRUE,
634
  );
635

    
636
  $form = drupal_build_form('advanced_forum_forum_topic_list_sort_form', $form_state);
637
  return drupal_render($form);
638
}
639

    
640
/**
641
 * Sort form.
642
 */
643
function advanced_forum_forum_topic_list_sort_form($form_state) {
644
  $view = views_get_view('advanced_forum_topic_list');
645
  $view->set_display('default');
646
  $view->init_handlers();
647
  $view->init_style();
648

    
649
  // Work up a list of possible fields.
650
  $handler = &$view->style_plugin;
651
  $fields = &$view->field;
652
  $columns = $handler->sanitize_columns($handler->options['columns'], $fields);
653

    
654
  $options = array();
655
  foreach ($columns as $field => $column) {
656
    if ($field == $column && empty($fields[$field]->options['exclude'])) {
657
      if (empty($handler->options['info'][$field]['sortable']) || !$fields[$field]->click_sortable()) {
658
        continue;
659
      }
660
      $label = check_plain(!empty($fields[$field]) ? $fields[$field]->label() : '');
661
      $options[$field] = $label;
662
    }
663
  }
664

    
665
  $form['inline'] = array(
666
    '#prefix' => '<div class="container-inline">',
667
    '#suffix' => '</div>',
668
  );
669
  $form['inline']['order'] = array(
670
    '#type' => 'select',
671
    '#title' => t('Order by'),
672
    '#title_display' => 'invisible',
673
    '#options' => $options,
674
    '#default_value' => $handler->options['default'],
675
  );
676

    
677
  $form['inline']['sort'] = array(
678
    '#type' => 'select',
679
    '#title' => t('Sort'),
680
    '#title_display' => 'invisible',
681
    '#options' => array(
682
      'asc' => t('Up'),
683
      'desc' => t('Down'),
684
    ),
685
    '#default_value' => 'desc',
686
  );
687

    
688
  $form['inline']['submit'] = array(
689
    '#id' => 'sort-topic-submit',
690
    '#name' => '',
691
    '#type' => 'submit',
692
    '#value' => t('Sort'),
693
  );
694

    
695
  if (isset($_GET['page'])) {
696
    $form['page'] = array(
697
      '#type' => 'hidden',
698
      '#default_value' => $_GET['page'],
699
    );
700
  }
701

    
702
  if (!variable_get('clean_url', FALSE)) {
703
    $form['q'] = array(
704
      '#type' => 'hidden',
705
      '#value' => $_GET['q'],
706
    );
707
  }
708

    
709
  $view->destroy();
710
  return $form;
711
}
712

    
713
// STATISTICS *****************************************************************/
714
/**
715
 * Count total amount of forum threads.
716
 */
717
function advanced_forum_statistics_topics() {
718
  return db_query('SELECT COUNT(DISTINCT(nid)) FROM {forum}')->fetchField();
719
}
720

    
721
/**
722
 * Counts total amount of replies.
723
 *
724
 * Initial posts are added to this total in the calling function.
725
 *
726
 * @param int|null $delta
727
 *   if not NULL, a numerical delta which should be applied to the count
728
 *
729
 * @param bool $refresh
730
 *   TRUE if the stored count should be updated.
731
 *
732
 * @return int
733
 *   Total number of replies in the forum.
734
 */
735
function advanced_forum_statistics_replies($delta = NULL, $refresh = FALSE) {
736

    
737
  if ($refresh || !($cache = cache_get('advanced_forum_stats_replies'))) {
738
    $total_replies = db_query('SELECT SUM(comment_count) FROM {forum_index}')->fetchField();
739
    cache_set('advanced_forum_stats_replies', $total_replies);
740
  }
741
  else {
742
    $total_replies = $cache->data;
743
  }
744

    
745
  if (!empty($delta) && is_numeric($delta)) {
746
    $total_replies += $delta;
747
    cache_set('advanced_forum_stats_replies', $total_replies);
748
  }
749

    
750
  return $total_replies;
751
}
752

    
753
/**
754
 * Count total amount of active users.
755
 */
756
function advanced_forum_statistics_users() {
757
  return db_query('SELECT COUNT(uid) FROM {users} WHERE status = 1')->fetchField();
758
}
759

    
760
/**
761
 * Return the newest X active (not blocked) users, linked to their profiles.
762
 */
763
function advanced_forum_statistics_latest_users() {
764
  // @TODO: Make this a setting.
765
  $number_to_fetch = 5;
766

    
767
  $query = db_select("users", "u")
768
      ->fields("u", array("uid", "name"))
769
      ->condition("status", 0, "<>")
770
      ->condition("access", 0, "<>")
771
      ->orderBy("created", "DESC");
772

    
773
  $latest_users = $query->range(NULL, $number_to_fetch)->execute();
774
  while ($account = $latest_users->fetchObject()) {
775
    $list[] = theme('username', array('account' => $account));
776
  }
777

    
778
  return $list;
779
}
780

    
781
/**
782
 * Returns session count.
783
 */
784
function advanced_forum_session_count($anonymous = TRUE) {
785

    
786
  $interval = REQUEST_TIME - variable_get('user_block_seconds_online', 900);
787

    
788
  $query = db_select("sessions", "s")
789
      ->fields("s", array("uid"))
790
      ->distinct()
791
      ->condition('s.timestamp', $interval, '>=')
792
      ->condition('s.uid', 0, $anonymous ? '=' : '>')
793
      ->countQuery();
794

    
795
  return $query->execute()->fetchField();
796
}
797

    
798
/**
799
 * Return an array of online usernames, linked to their profiles.
800
 */
801
function advanced_forum_statistics_online_users() {
802
  $list = array();
803

    
804
  $interval = REQUEST_TIME - variable_get('user_block_seconds_online', 900);
805

    
806
  $query = db_select("users", "u")->distinct()->fields("u", array("uid", "name"));
807
  $s_alias = $query->join("sessions", "s", "u.uid = s.uid");
808

    
809
  $query->addExpression("MAX({$s_alias}.timestamp)", "maxtime");
810
  $query
811
      ->condition("{$s_alias}.timestamp", $interval, ">=")
812
      ->condition("{$s_alias}.uid", "0", ">")
813
      ->groupBy("u.uid, u.name")
814
      ->orderBy("maxtime", "DESC");
815

    
816
  $authenticated_users = $query->execute();
817

    
818
  while ($account = $authenticated_users->fetchObject()) {
819
    $list[] = theme('username', array('account' => $account));
820
  }
821

    
822
  return $list;
823
}
824

    
825
/**
826
 * Calculating links - New, Last, Etc.
827
 */
828
function advanced_forum_get_reply_link($node) {
829
  $reply_link = array();
830

    
831
  $comment_setting = $node->comment;
832
  $fragment = 'comment-form';
833

    
834
  if ($comment_setting == COMMENT_NODE_OPEN) {
835

    
836
    $allowed = FALSE;
837
    if (module_exists('forum_access')) {
838
      // Get tid.
839
      if (!empty($node->taxonomy_forums)) {
840
        reset($node->taxonomy_forums);
841
        $langcode = key($node->taxonomy_forums);
842
        if (!empty($node->taxonomy_forums[$langcode])) {
843
          $tid = $node->taxonomy_forums[$langcode][0]['tid'];
844
          if (forum_access_access('create', $tid)) {
845
            $allowed = TRUE;
846
          }
847
        }
848
      }
849
    }
850
    else {
851
      $allowed = user_access('post comments');
852
    }
853

    
854
    if ($allowed) {
855
      if (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {
856
        // Reply form is on separate page. Grab the href from the node links
857
        // so it's automatically corrected for Node Comments if needed.
858
        $reply_link['href'] = "comment/reply/$node->nid";
859
        $reply_link['options']['fragment'] = $fragment;
860

    
861
        $reply_link['class'] = 'reply-allowed';
862
        $reply_link['title'] = t('Post reply');
863

    
864
        return $reply_link;
865
      }
866
      else {
867
        // Reply form is on same page. The reply button should jump down to it
868
        // rather than going to a new page.
869
        $reply_link['href'] = $_GET['q'];
870
        $reply_link['options']['fragment'] = $fragment;
871
        $current_page = isset($_GET['page']) ? $_GET['page'] : 0;
872
        if ($current_page) {
873
          $reply_link['options']['query'] = array('page' => $current_page);
874
        }
875

    
876
        $reply_link['class'] = 'reply-allowed';
877
        $reply_link['title'] = t('Quick reply');
878

    
879
        return $reply_link;
880
      }
881
    }
882
    else {
883
      // User does not have access to post replies on this node.
884
      return 'reply-forbidden';
885
    }
886
  }
887
  else {
888
    // Topic is locked.
889
    return 'reply-locked';
890
  }
891
}
892

    
893
/**
894
 * Get a link to the last post in a topic.
895
 *
896
 * @param object $node
897
 *   Node object
898
 *
899
 * @return string
900
 *   Text linking to the last post in a topic.
901
 */
902
function advanced_forum_last_post_link($node) {
903
  $last_comment_id = advanced_forum_last_post_in_topic($node->nid);
904
  // Return empty link if post doesn't have comments.
905
  if (empty($last_comment_id)) {
906
    return '';
907
  }
908

    
909
  $last_page = advanced_forum_get_last_page($node);
910

    
911
  if ($last_page > 0) {
912
    $query = array('page' => $last_page);
913
  }
914

    
915
  $options = array(
916
    'html' => TRUE,
917
    'query' => empty($query) ? array() : $query,
918
    'fragment' => "comment-$last_comment_id",
919
  );
920

    
921
  return theme('advanced_forum_l', array(
922
    'text' => t('Last post'),
923
    'path' => "node/$node->nid",
924
    'options' => $options,
925
    'button_class' => 'large'
926
  ));
927
}
928

    
929
/**
930
 * Returns a link directly to the first new post in a topic.
931
 *
932
 * @param object $node
933
 *   Node object
934
 *
935
 * @param int $comment_count
936
 *   Number of comments on passed node.
937
 *
938
 * @return string
939
 *   Link to the first unread post.
940
 */
941
function advanced_forum_first_new_post_link($node, $comment_count) {
942
  $nid = $node->nid;
943

    
944
  $current_page = isset($_GET['page']) ? $_GET['page'] : 0;
945
  $number_new_comments = advanced_forum_reply_num_new($nid);
946

    
947
  if ($number_new_comments > 0) {
948
    $page_of_first_new = advanced_forum_page_first_new($comment_count, $number_new_comments, $node);
949

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

    
954
    $number_new = t("(!new new)", array('!new' => $number_new_comments));
955

    
956
    $options = array(
957
      'html' => TRUE,
958
      'query' => $page_of_first_new,
959
      'fragment' => "comment-$cid_of_first_new",
960
    );
961

    
962
    return theme('advanced_forum_l', array(
963
      'text' => t('First unread'),
964
      'path' => "node/$nid",
965
      'options' => $options,
966
      'button_class' => 'large',
967
    ));
968
  }
969
}
970

    
971
/**
972
 * Get the page number with the first new post.
973
 */
974
function advanced_forum_page_first_new($comment_count, $new_replies, $node) {
975
  return comment_new_page_count($comment_count, $new_replies, $node);
976
}
977

    
978
/**
979
 * Get the number of new posts on a topic.
980
 */
981
function advanced_forum_reply_num_new($nid, $timestamp = 0) {
982
  // Make a static cache because this function is called twice from the topic
983
  // header. Once to display the number and once to make the link to first new.
984
  static $number_new_for_node = array();
985

    
986
  // $nid is empty if new topic in preview.
987
  if (empty($nid)) {
988
    return 0;
989
  }
990

    
991
  if (empty($number_new_for_node[$nid])) {
992
    global $user;
993

    
994
    $node = node_load($nid);
995

    
996
    // We must also check the forum post itself to see if we have viewed it.
997
    // If not told otherwise, it has been viewed before.
998
    $viewed = 0;
999
    if ($user->uid) {
1000
      $viewed = node_last_viewed($nid);
1001
      // Set it to 1 if it has not been viewed before.
1002
      $viewed = ($viewed == 0 ? 1 : 0);
1003
    }
1004

    
1005
    $number_new_for_node[$nid] = comment_num_new($nid, $timestamp) + $viewed;
1006
  }
1007

    
1008
  return $number_new_for_node[$nid];
1009
}
1010

    
1011
/**
1012
 * Get the comment id of the last post in a topic.
1013
 *
1014
 * @param int $nid
1015
 *   Node id.
1016
 *
1017
 * @return int
1018
 *   cid of last post.
1019
 */
1020
function advanced_forum_last_post_in_topic($nid) {
1021
  // $nid is empty if new topic in preview.
1022
  if (empty($nid)) {
1023
    return NULL;
1024
  }
1025

    
1026
  $node = node_load($nid);
1027

    
1028
  // Comment module version.
1029
  $query = 'SELECT c.cid
1030
            FROM {comment} c
1031
            WHERE c.nid = :nid AND c.status = :status
1032
            ORDER BY c.cid DESC';
1033
  $result = db_query_range($query, 0, 1, array(':nid' => $nid, ':status' => COMMENT_PUBLISHED))->fetchField();
1034

    
1035
  return $result;
1036
}
1037

    
1038
/**
1039
 * Returns the page number of the last page starting at 0 like the pager does.
1040
 */
1041
function advanced_forum_get_last_page($node) {
1042
  $comments_per_page = variable_get('comment_default_per_page_' . $node->type, 50);
1043
  $comment_count = isset($node->comment_count) ? $node->comment_count : 0;
1044
  $last_page = ceil($comment_count / $comments_per_page) - 1;
1045
  return $last_page;
1046
}
1047

    
1048
/**
1049
 * Returns the ID of the first unread comment.
1050
 *
1051
 * @param int $nid
1052
 *   Node ID
1053
 *
1054
 * @param int $timestamp
1055
 *   Date/time used to override when the user last viewed the node.
1056
 *
1057
 * @return int
1058
 *   Comment ID
1059
 */
1060
function advanced_forum_first_new_comment($nid, $timestamp = 0) {
1061
  global $user;
1062

    
1063
  if ($user->uid) {
1064
    // Retrieve the timestamp at which the current user last viewed the
1065
    // specified node.
1066
    if (!$timestamp) {
1067
      $timestamp = node_last_viewed($nid);
1068
    }
1069

    
1070
    // Set the timestamp to the limit if the node was last read past the cutoff.
1071
    $timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT);
1072

    
1073
    // Use the timestamp to retrieve the oldest new comment.
1074
    $query = db_select('comment', 'c')
1075
        ->fields('c', array('cid'))
1076
        ->condition('nid', $nid)
1077
        ->condition('changed', $timestamp, '>')
1078
        ->condition('status', COMMENT_PUBLISHED)
1079
        ->range(0, 1)
1080
        ->execute();
1081
    return $query->fetchField();
1082
  }
1083
  else {
1084
    return 0;
1085
  }
1086
}
1087

    
1088
// GENERAL UTILITY FUNCTIONS *************************************************/
1089
/**
1090
 * Return an array of node types allowed in a given vocabulary or term ID.
1091
 */
1092
function advanced_forum_allowed_node_types($tid = 0) {
1093
  if (module_exists('forum_access')) {
1094
    // Check with forum access to see if this forum allows node creation.
1095
    // If it doesn't, send back an empty list.
1096
    if (!forum_access_access('create', $tid)) {
1097
      return array();
1098
    }
1099
  }
1100

    
1101
  $field = field_info_field('taxonomy_forums');
1102
  if (!empty($field['bundles']['node'])) {
1103
    return $field['bundles']['node'];
1104
  }
1105
  else {
1106
    return array();
1107
  }
1108
}
1109

    
1110
/**
1111
 * Return whether a given node type is allowed in the whole forum or given forum.
1112
 */
1113
function advanced_forum_type_is_in_forum($node, $tid = 0) {
1114
  $vid = (empty($vid)) ? variable_get('forum_nav_vocabulary', 0) : $vid;
1115
  if (!empty($node->taxonomy_forums)) {
1116
    // Check for language used.
1117
    if (!isset($node->taxonomy_forums[$node->language])) {
1118
      $langcode = LANGUAGE_NONE;
1119
    }
1120
    else {
1121
      $langcode = $node->language;
1122
    }
1123

    
1124
    foreach ($node->taxonomy_forums[$langcode] as $tforum) {
1125
      if (!isset($tforum['taxonomy_term'])) {
1126
        continue;
1127
      }
1128
      if (($tforum['taxonomy_term']->vid == $vid) || ($tforum['taxonomy_term']->tid == $tid)) {
1129
        return TRUE;
1130
      }
1131
    }
1132
  }
1133
  return FALSE;
1134
}
1135

    
1136
/**
1137
 * Generate a list of node creation links for a forum.
1138
 *
1139
 * This is used on the forum list, allowing us to have direct
1140
 * links to create new nodes in the forum.
1141
 */
1142
function advanced_forum_node_type_create_list($tid) {
1143
  $allowed_types = advanced_forum_allowed_node_types($tid);
1144

    
1145
  // Ensure "new topic" is first.
1146
  if (isset($allowed_types['forum'])) {
1147
    unset($allowed_types['forum']);
1148
    array_unshift($allowed_types, 'forum');
1149
  }
1150
  // Loop through all node types allowed in this forum.
1151
  foreach ($allowed_types as $type) {
1152
    // Check if this node type can be created by current user.
1153
    if (node_access('create', $type)) {
1154
      // Fetch the "General" name of the content type.
1155
      $node_type = t(node_type_get_name($type));
1156

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

    
1162
      // Push the link with title and url to the array.
1163
      $forum_types[$type] = array(
1164
        'name' => $node_type,
1165
        'href' => 'node/add/' . str_replace('_', '-', $type) . '/' . $tid,
1166
      );
1167
    }
1168
  }
1169

    
1170
  if (empty($forum_types)) {
1171
    // The user is logged-in; but denied access to create any new forum content type.
1172
    global $user;
1173
    if ($user->uid) {
1174
      return t('You are not allowed to post new content in the forum.');
1175
    }
1176
    // The user is not logged-in; and denied access to create any new forum content type.
1177
    else {
1178
      return t('<a href="@login">Log in</a> to post new content in the forum.', array(
1179
        '@login' => url('user/login', array('query' => drupal_get_destination())),
1180
      ));
1181
    }
1182
  }
1183
  else {
1184
    return $forum_types;
1185
  }
1186
}
1187

    
1188
/**
1189
 * Create a drop down list of forum actions.
1190
 */
1191
function advanced_forum_forum_tools($tid = 0) {
1192
  global $user;
1193
  $options = array();
1194

    
1195
  ctools_include('jump-menu');
1196
  if ($tid > 0) {
1197
    $select[url("forum/active", array('query' => array('forum[]' => $tid)))] = t("View active posts in this forum");
1198
    $select[url("forum/unanswered", array('query' => array('forum[]' => $tid)))] = t("View unanswered posts in this forum");
1199

    
1200
    if ($user->uid) {
1201
      $select[url("forum/new", array('query' => array('forum[]' => $tid)))] = t("View new posts in this forum");
1202
    }
1203
  }
1204
  else {
1205
    $select[url("forum/active")] = t("View active forum posts");
1206
    $select[url("forum/unanswered")] = t("View unanswered forum posts");
1207

    
1208
    if ($user->uid) {
1209
      $select[url("forum/new")] = t("View new forum posts");
1210
    }
1211
  }
1212

    
1213
  // Add mark as read to the jump list.
1214
  // This code is a little odd and needs explaining. The return value of
1215
  // the mark_as_read function is already formed HTML and so is unsuitable
1216
  // for the jump list. The function already has built in the ability
1217
  // to add to an existing $links array, which has the URL and title text
1218
  // separated. Rather than add a third method just for the jump menu, I
1219
  // reused that functionality here.
1220
  $mark_as_read = array();
1221
  advanced_forum_get_mark_read_link($tid, $mark_as_read);
1222
  if (!empty($mark_as_read['mark-read']['href'])) {
1223
    $select[url($mark_as_read['mark-read']['href'])] = $mark_as_read['mark-read']['title'];
1224
  }
1225

    
1226
  $options['choose'] = t("- Forum Tools -");
1227

    
1228
  // Create and return the jump menu.
1229
  $form = drupal_get_form('ctools_jump_menu', $select, $options);
1230
  return drupal_render($form);
1231
}
1232

    
1233
/**
1234
 * Creates a pager to place on each multi-page topic of the topic listing page.
1235
 *
1236
 * @param int $max_pages_to_display
1237
 *   Number of pages to include on the pager.
1238
 *
1239
 * @param object $topic
1240
 *   Topic object to create a pager for.
1241
 *
1242
 * @return object
1243
 *   Object containing the linked pages ready assembly by the theme function.
1244
 */
1245
function advanced_forum_create_topic_pager($max_pages_to_display, $topic) {
1246
  // Find the number of comments per page for the node type of the topic.
1247
  $comments_per_page = variable_get('comment_default_per_page_' . $topic->type, 50);
1248

    
1249
  if ($max_pages_to_display > 0 && $topic->comment_count > $comments_per_page) {
1250
    // Topic has more than one page and a pager is wanted. Start off the
1251
    // first page because that doesn't have a query.
1252
    $pager_array = array();
1253
    $current_display_page = 1;
1254
    // @codingStandardsIgnoreStart
1255
    $pager_array[0] = l('1', "node/$topic->nid");
1256
    // @codingStandardsIgnoreEnd
1257

    
1258
    // Find the ending point. The pager URL is always 1 less than
1259
    // the number being displayed because the first page is 0.
1260
    $last_display_page = ceil($topic->comment_count / $comments_per_page);
1261
    $last_pager_page = $last_display_page - 1;
1262

    
1263
    // Add pages until we run out or until we hit the max to show.
1264
    while (($current_display_page < $last_display_page) && ($current_display_page < $max_pages_to_display)) {
1265
      // Move to the next page.
1266
      $current_display_page++;
1267

    
1268
      // The page number we link to is 1 less than what's displayed.
1269
      $link_to_page = $current_display_page - 1;
1270

    
1271
      // Add the link to the array.
1272
      $pager_array[$link_to_page] = l($current_display_page, "node/$topic->nid", array('query' => array('page' => $link_to_page)));
1273
    }
1274

    
1275
    // Move to the next page.
1276
    $current_display_page++;
1277

    
1278
    if ($current_display_page == $last_display_page) {
1279
      // We are one past the max to display, but it's the last page,
1280
      // so putting the ...last is silly. Just display it normally.
1281
      $link_to_page = $current_display_page - 1;
1282
      $pager_array[$link_to_page] = l($current_display_page, "node/$topic->nid", array('query' => array('page' => $link_to_page)));
1283
    }
1284

    
1285
    if ($current_display_page < $last_display_page) {
1286
      // We are one past the max to display and still aren't
1287
      // on the last page, so put in ... Last Page(N)
1288
      $text = t('Last Page');
1289
      $pager_last_text = l($text, "node/$topic->nid", array('query' => array('page' => $last_pager_page)));
1290
      $pager_last_number = l($last_display_page, "node/$topic->nid", array('query' => array('page' => $last_pager_page)));
1291
      // Create last page array to enable more customization for themers.
1292
      $pager_last = array(
1293
        'number' => $last_display_page,
1294
        'link' => "node/$topic->nid",
1295
        'options' => array('query' => array('page' => $last_pager_page)),
1296
      );
1297
    }
1298

    
1299
    $topic_pager = new stdClass();
1300
    $topic_pager->initial_pages = (empty($pager_array)) ? array() : $pager_array;
1301
    $topic_pager->last_page_text = (empty($pager_last_text)) ? '' : $pager_last_text;
1302
    $topic_pager->last_page_number = (empty($pager_last_number)) ? '' : $pager_last_number;
1303
    $topic_pager->last_page = (empty($pager_last)) ? array() : $pager_last;
1304

    
1305
    return $topic_pager;
1306
  }
1307
}
1308

    
1309
/**
1310
 * Create a drop down list of forum hierarchy.
1311
 */
1312
function advanced_forum_forum_jump($tid = 0) {
1313
  global $user;
1314
  ctools_include('jump-menu');
1315
  $select = array();
1316
  $options = array();
1317
  $vid = variable_get('forum_nav_vocabulary', 0);
1318
  if ($tid > 0) {
1319
    $forum_tree = taxonomy_get_tree($vid);
1320
    foreach ($forum_tree as $forum) {
1321
      $select[url("forum/" . $forum->tid)] = str_repeat("-", $forum->depth) . $forum->name;
1322
    }
1323
  }
1324

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

    
1327
  // Create and return the jump menu.
1328
  $form = drupal_get_form('ctools_jump_menu', $select, $options);
1329
  return drupal_render($form);
1330
}
1331

    
1332
/**
1333
 * Calculates the number of unread replies for each forum and returns the count for the requested forum.
1334
 */
1335
function advanced_forum_unread_replies_in_forum($tid, $uid) {
1336
  static $result_cache = NULL;
1337

    
1338
  if (is_null($result_cache)) {
1339
    $result_cache = array();
1340

    
1341
    $query = db_select("comment", "c");
1342
    $f_alias = $query->join("forum", "f", "c.nid = f.nid");
1343
    $h_alias = $query->leftJoin("history", "h", "c.nid = h.nid AND h.uid = :uid", array(":uid" => $uid));
1344
    $query->addExpression("COUNT(DISTINCT(c.cid))", "count");
1345
    $query->addField($f_alias, "tid");
1346

    
1347
    $query->condition("c.status", COMMENT_PUBLISHED)
1348
        ->condition("c.changed", NODE_NEW_LIMIT, ">")
1349
        ->condition(db_or()->where("c.changed > {$h_alias}.timestamp")->isNull("h.timestamp"))
1350
        ->groupBy("{$f_alias}.tid")
1351
        ->addTag("node_access");
1352

    
1353
    $result = $query->execute();
1354
    foreach ($result as $row) {
1355
      $result_cache[$row->tid] = $row->count;
1356
    }
1357
  }
1358

    
1359
  return (isset($result_cache[$tid])) ? $result_cache[$tid] : 0;
1360
}
1361

    
1362
/**
1363
 * Returns the display position of a given reply post ID on a given node.
1364
 */
1365
function advanced_forum_post_position($node, $comment) {
1366
  static $post_order = array();
1367

    
1368
  if (empty($node) || empty($comment)) {
1369
    return 0;
1370
  }
1371

    
1372
  $node_id = $node->nid;
1373
  $post_id = $comment->cid;
1374

    
1375
  if (!isset($post_order[$node_id])) {
1376
    // Initialize the spot for this node's list.
1377
    $post_order[$node_id] = array();
1378

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

    
1381
    // Get the list of CIDs from the database in order of oldest first.
1382
    // We are going to make that assumption for now for simplicity but may
1383
    // revisit in the future if there are requests for newest first.
1384
    $query = db_select('comment', 'c')
1385
        ->fields('c', array('cid'))
1386
        ->condition('c.nid', $node_id)
1387
        ->addTag('node_access')
1388
        ->addTag('comment_filter');
1389

    
1390
    if ($mode === COMMENT_MODE_FLAT) {
1391
      $query->orderBy('c.cid', 'ASC');
1392
    }
1393
    else {
1394
      $query->addExpression('SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))', 'torder');
1395
      $query->orderBy('torder', 'ASC');
1396
    }
1397

    
1398
    $query = $query->execute();
1399
    // Cycle through the results and fill in the array.
1400
    while ($post = $query->fetchAssoc()) {
1401
      $post_order[$node_id][] = reset($post);
1402
    }
1403
  }
1404

    
1405
  // Find the position of the passed in post ID.
1406
  $post_position = 0;
1407
  if (is_array($post_order[$node_id])) {
1408
    if (($index = array_search($post_id, $post_order[$node_id])) !== FALSE) {
1409
      $post_position = $index;
1410

    
1411
      $advanced_forum_styled_node_types = variable_get('advanced_forum_styled_node_types', array('forum'));
1412
      // We need to add 1 because the topic node is post #1 on display but is not included in the index.
1413
      if (in_array($node->type, $advanced_forum_styled_node_types)) {
1414
        $post_position = $post_position + 1;
1415
      }
1416
    }
1417
  }
1418

    
1419
  return $post_position;
1420
}