You are here

advanced_forum.module in Advanced Forum 5

Enables the look and feel of other popular forum software.

File

advanced_forum.module
View source
<?php

/**
 * @file
 * Enables the look and feel of other popular forum software.
 */

// DRUPAL HOOKS **************************************************************/

/**
 * Implementation of hook_perm().
 */
function advanced_forum_perm() {
  return array(
    'administer advanced forum',
    'view forum statistics',
  );
}

/**
 * Implementation of hook_menu().
 */
function advanced_forum_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'forum',
      'title' => t('Forums'),
      'callback' => 'advanced_forum_page',
      'access' => user_access('access content'),
      'type' => MENU_SUGGESTED_ITEM,
    );
    global $user;
    $items[] = array(
      'path' => "forum/markasread",
      'title' => 'Helper page to mark forums read.',
      'callback' => 'advanced_forum_markasread',
      'access' => advanced_forum_markasread_access(),
      'type' => MENU_CALLBACK,
    );

    // Add menu entry for settings page
    $items[] = array(
      'path' => "admin/settings/advanced-forum",
      'title' => t('Advanced Forum'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'advanced_forum_settings_page',
      ),
      'access' => user_access('administer advanced forum'),
    );
  }
  return $items;
}

// SETTINGS PAGE *************************************************************/

/**
 * Defines the Advanced Forum settings form.
 */
function advanced_forum_settings_page() {

  /* General settings */
  $form['advanced_forum_general'] = array(
    '#type' => 'fieldset',
    '#title' => t('General'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );

  // Choose style
  $form['advanced_forum_general']['advanced_forum_style'] = array(
    '#type' => 'textfield',
    '#title' => t('Advanced forum style'),
    '#size' => 25,
    '#description' => t('Name of advanced forum style. Defaults to "naked" Don\'t forget to copy this style to your theme.'),
    '#default_value' => variable_get('advanced_forum_style', 'naked'),
  );

  // Choose image directory
  $form['advanced_forum_general']['advanced_forum_image_directory'] = array(
    '#type' => 'textfield',
    '#title' => t('Image directory'),
    '#size' => 50,
    '#description' => t('Images are assumed to be in the "images" subdirectory of your style. If you need images to be somewhere else, put the full path here.'),
    '#default_value' => variable_get('advanced_forum_image_directory', ''),
  );

  // Use buttons for links
  $form['advanced_forum_general']['advanced_forum_button_links'] = array(
    '#type' => 'checkbox',
    '#title' => t('Use graphical buttons for links'),
    '#default_value' => variable_get('advanced_forum_button_links', 1),
    '#description' => t('Included buttons are in English. Uncheck this to use translatable links instead.'),
  );

  // Theme all site comments as forums
  $form['advanced_forum_general']['advanced_forum_theme_all_comments'] = array(
    '#type' => 'checkbox',
    '#title' => t('Treat all site comments like forum posts'),
    '#default_value' => variable_get('advanced_forum_theme_all_comments', 0),
    '#description' => t('Choosing yes causes advanced forum to consider every comment a forum comment and attempt to theme it that way. Some changes to your theme may be required.'),
  );

  /* Forum / topic list settings */
  $form['advanced_forum_lists'] = array(
    '#type' => 'fieldset',
    '#title' => t('Forum and topic lists'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );

  // Hide 'created' column
  $form['advanced_forum_lists']['advanced_forum_hide_created'] = array(
    '#type' => 'checkbox',
    '#title' => t('Hide the created column on the topic list'),
    '#default_value' => variable_get('advanced_forum_hide_created', 0),
    '#description' => t('This allows you to hide the created column on the topic list. Useful if you want to move the created by information to underneath the topic title.'),
  );

  // Pager max
  $form['advanced_forum_lists']['advanced_forum_topic_pager_max'] = array(
    '#type' => 'textfield',
    '#title' => t('Maximum number of pages to show on per title pager'),
    '#size' => 5,
    '#description' => t('Used on the pager under topic titles in topic list. e.g. entering 5 will get you 1,2,3,4 ... 10'),
    '#default_value' => variable_get('advanced_forum_topic_pager_max', 5),
  );

  // Retrieve new comments on forum listing
  $form['advanced_forum_lists']['advanced_forum_get_new_comments'] = array(
    '#type' => 'checkbox',
    '#title' => t('Get the number of new comments per forum on the forum list'),
    '#default_value' => variable_get('advanced_forum_get_new_comments', 0),
    '#description' => t('Core forum shows the number of new topics. If checked, Advanced Forum will get the number of new comments as well and show it under "posts" on the forum overview. Slow query not recommended on large forums.'),
  );

  // Title length max
  $form['advanced_forum_lists']['advanced_forum_topic_title_length'] = array(
    '#type' => 'textfield',
    '#title' => t('Number of characters to display for the topic title'),
    '#size' => 5,
    '#description' => t('Used on main forum page. Enter 0 to use the full title.'),
    '#default_value' => variable_get('advanced_forum_topic_title_length', 15),
  );

  // Time ago cutoff
  $form['advanced_forum_lists']['advanced_forum_time_ago_cutoff'] = array(
    '#type' => 'textfield',
    '#title' => t('Number of hours before switching to date posted in displays'),
    '#size' => 5,
    '#description' => t('Will show "time ago" until this cutoff and then switch to actual date posted .'),
    '#default_value' => variable_get('advanced_forum_time_ago_cutoff', 48),
  );

  /* Topic settings */
  $form['advanced_forum_topics'] = array(
    '#type' => 'fieldset',
    '#title' => t('Topics'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );

  // Use topic navigation
  $form['advanced_forum_topics']['advanced_forum_use_topic_navigation'] = array(
    '#type' => 'checkbox',
    '#title' => t('Use topic navigation'),
    '#default_value' => variable_get('advanced_forum_use_topic_navigation', 0),
    '#description' => t('Core forum gets the next and previous topics and shows links to them under the top post. This is turned off by default as the query has performance issues and the placement of the links is poor.'),
  );
  if (module_exists('imagecache') && function_exists('imagecache_presets')) {
    $options = array(
      '' => '',
    );
    $presets = imagecache_presets();
    foreach ($presets as $preset) {
      $options[$preset['presetname']] = $preset['presetname'];
    }
    $form['advanced_forum_topics']['advanced_forum_user_picture_preset'] = array(
      '#type' => 'select',
      '#title' => t('User picture preset'),
      '#options' => $options,
      '#description' => t('Imagecache preset to use for forum avatars. Leave blank to not use this feature.'),
      '#default_value' => variable_get('advanced_forum_user_picture_preset', ''),
    );
  }

  // Send our form to Drupal to make a settings page
  return system_settings_form($form);
}

// THEME TO PREPROCESS CONVERTER *********************************************/

/**
 * Provides D5 compatability wrapper for node/comment preprocess functions.
 */
function advanced_forum_addvars($hook, $variables) {
  switch ($hook) {
    case 'node':
    case 'comment':
      advanced_forum_call_preprocess($hook, $variables);
  }
  return $variables;
}

// Add in the D6 backports from the forum, taxonomy, and comment modules
include_once drupal_get_path('module', 'advanced_forum') . '/d6_compat.inc';

/**
 * Implementation of theme_forum_display().
 */
function phptemplate_forum_display($forums, $topics, $parents, $tid, $sortby, $forum_per_page) {

  // Create a $variables array from the parameters
  $variables['forums'] = $forums;
  $variables['topics'] = $topics;
  $variables['parents'] = $parents;
  $variables['tid'] = $tid;
  $variables['sortby'] = $sortby;
  $variables['forum_per_page'] = $forum_per_page;

  // Pass the parameters into our preprocess function
  advanced_forum_call_preprocess('forums', $variables);

  // Set the template file
  $forum_style = advanced_forum_get_current_style();
  return _phptemplate_callback('advf_forums', $variables, array(
    "{$forum_style}/advf-forums",
  ));
}

/**
 * Theme function for advf_forum_statistics().
 */
function theme_advf_forum_statistics() {
  $variables = array();

  // Pass the parameters into our preprocess function
  advanced_forum_call_preprocess('advf_forum_statistics', $variables);

  // Set the template file
  $forum_style = advanced_forum_get_current_style();
  return _phptemplate_callback("advf_forum_statistics", $variables, array(
    "{$forum_style}/advf-forum-statistics",
  ));
}

/**
 * Theme function for advanced_forum_forum_legend().
 */
function theme_advanced_forum_forum_legend() {
  $variables = array();

  // Pass the parameters into our preprocess function
  advanced_forum_call_preprocess('advanced_forum_forum_legend', $variables);

  // Set the template file
  $forum_style = advanced_forum_get_current_style();
  return _phptemplate_callback("advanced_forum_forum_legend", $variables, array(
    "{$forum_style}/advanced_forum-forum-legend",
  ));
}

/**
 * Theme function for advanced_forum_topic_legend().
 */
function theme_advanced_forum_topic_legend() {
  $variables = array();

  // Pass the parameters into our preprocess function
  advanced_forum_call_preprocess('advanced_forum_topic_legend', $variables);

  // Set the template file
  $forum_style = advanced_forum_get_current_style();
  return _phptemplate_callback("advanced_forum_topic_legend", $variables, array(
    "{$forum_style}/advanced_forum-topic-legend",
  ));
}

/**
 * Theme function for advanced_forum_forum_legend().
 */
function theme_advanced_forum_topic_header($node, $comment_count) {
  $variables = array();
  $variables['node'] = $node;
  $variables['comment_count'] = $comment_count;

  // Pass the parameters into our preprocess function
  advanced_forum_call_preprocess('advanced_forum_topic_header', $variables);

  // Set the template file
  $forum_style = advanced_forum_get_current_style();
  return _phptemplate_callback("advanced_forum_topic_header", $variables, array(
    "{$forum_style}/advanced_forum-topic-header",
  ));
}

/**
 * Theme function to format the reply link at the top/bottom of topic.
 */
function theme_advanced_forum_reply_link($node) {
  if (user_access('post comments')) {
    if ($node->comment == COMMENT_NODE_READ_WRITE) {
      if (variable_get('comment_form_location', COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {

        // Comment form on seperate page
        $links['topic_reply'] = array(
          'title' => t('Post Reply'),
          'href' => "comment/reply/{$node->nid}",
          'attributes' => array(
            'title' => t('Share your thoughts and opinions related to this posting.'),
          ),
          'fragment' => 'comment-form',
        );
      }
      else {

        // Comment form is already on the page
        $links['topic_reply'] = array(
          'title' => t('Post Reply'),
          'href' => $_GET['q'],
          'attributes' => array(
            'title' => t('Share your thoughts and opinions related to this posting.'),
          ),
          'fragment' => 'comment-form',
          'query' => $_GET['page'] ? "page=" . $_GET['page'] : NULL,
        );
      }

      // Change to using a button if so set
      if (variable_get('advanced_forum_button_links', 1)) {
        $links['topic_reply']['title'] = advanced_forum_theme_image(t('post-reply.png'), t('Post reply'));
        $links['topic_reply']['html'] = TRUE;
      }
    }
    else {

      // Post is locked
      $links['topic_locked'] = array(
        'title' => t('Topic Locked'),
        'attributes' => array(
          'title' => t('This topic is locked'),
        ),
      );

      // Change to using a button if so set
      if (variable_get('advanced_forum_button_links', 1)) {
        $links['topic_locked']['title'] = advanced_forum_theme_image(t('locked-topic.png'), t('Locked'));
        $links['topic_locked']['html'] = TRUE;
      }
    }
  }
  else {

    // User does not have rights to post
    $links['comment_forbidden']['title'] = theme('comment_post_forbidden', $node->nid);
    $links['comment_forbidden']['html'] = TRUE;
  }
  return theme('links', $links, array(
    'class' => 'forum-links',
  ));
}

/**
 * Implementation of theme_forum_list().
 */
function phptemplate_forum_list($forums, $parents, $tid) {

  // Create a $variables array from the parameters
  $variables['forums'] = $forums;
  $variables['parents'] = $parents;
  $variables['tid'] = $tid;

  // Pass the parameters into our preprocess function
  advanced_forum_call_preprocess('forum_list', $variables);

  // Set the template file
  $forum_style = advanced_forum_get_current_style();
  return _phptemplate_callback("advf_forum_list", $variables, array(
    "{$forum_style}/advf-forum-list",
  ));
}

/*
 * Provides D5 compatability wrapper for theme_forum_submitted.
 * Note: this function does not exist at all in D5's forum.module
 */
function phptemplate_forum_submitted($topic) {

  // Create a $variables array from the parameters
  $variables['topic'] = $topic;

  // Pass the parameters into our preprocess function
  advanced_forum_call_preprocess('forum_submitted', $variables);

  // Set the template file
  $forum_style = advanced_forum_get_current_style();
  return _phptemplate_callback("advf_forum_submitted", $variables, array(
    "{$forum_style}/advf-forum-submitted",
  ));
}

/**
 * Implementation of theme_forum_topic_list().
 */
function phptemplate_forum_topic_list($tid, $topics, $sortby, $forum_per_page) {

  // Create a $variables array from the parameters
  $variables['tid'] = $tid;
  $variables['topics'] = $topics;
  $variables['sortby'] = $sortby;
  $variables['forum_per_page'] = $forum_per_page;

  // Pass the parameters into our preprocess function
  advanced_forum_call_preprocess('forum_topic_list', $variables);

  // Set the template file
  $forum_style = advanced_forum_get_current_style();
  return _phptemplate_callback("advf_forum_topic_list", $variables, array(
    "{$forum_style}/advf-forum-topic-list",
  ));
}

/**
 * Implementation of theme_forum_topic_navigation
 */
function phptemplate_forum_topic_navigation($node) {

  // Removes post title navigation from forum posts.
  return;
}

/**
 * Implementation of theme_forum_icon
 */
function phptemplate_forum_icon($new_posts, $num_posts = 0, $comment_mode = 0, $sticky = 0, $topic_id = 0) {

  // Create a $variables array from the parameters
  $variables['new_posts'] = $new_posts;
  $variables['num_posts'] = $num_posts;
  $variables['comment_mode'] = $comment_mode;
  $variables['sticky'] = $sticky;
  $variables['topic_id'] = $topic_id;

  // Pass the parameters into our preprocess function
  advanced_forum_call_preprocess('forum_icon', $variables);

  // Set the template file
  $forum_style = advanced_forum_get_current_style();
  return _phptemplate_callback("advf_forum_icon", $variables, array(
    "{$forum_style}/advf-forum-icon",
  ));
}

// TEMPLATE PREPROCESS *******************************************************/

/**
 * Preprocesses template variables for the author pane.
 */
function advanced_forum_preprocess_author_pane(&$variables) {

  // Author pane is used in various places. The only way to be sure to only
  // hit it when we are using it is to check the template suggestion.
  if (strpos($variables['template_suggestion'], 'advf-author-pane')) {

    // Attempt to override the user picture fetched by Author Pane from core.
    $sized_picture = theme('advanced_forum_user_picture', $variables['account']);
    if (!empty($sized_picture)) {
      $variables['picture'] = $sized_picture;
    }
  }
}

/**
 * Preprocesses template variables for the topic header template.
 */
function advanced_forum_preprocess_advanced_forum_topic_header(&$variables) {

  // Build the reply link / button
  $variables['reply_link'] = theme('advanced_forum_reply_link', $variables['node'], $variables['comment_count']);

  // Total & new replies
  if (!empty($variables['comment_count'])) {
    $variables['total_posts'] = format_plural($variables['comment_count'], '1 reply', '@count replies');
    $variables['last_post'] = advanced_forum_last_post_link($variables['node']);
  }
  else {
    $variables['total_posts'] = t('No replies');
  }
  $variables['new_posts'] = advanced_forum_first_new_post_link($variables['node'], $variables['comment_count']);
}

/**
 * Preprocesses template variables for the node template.
 */
function advanced_forum_preprocess_node(&$variables) {
  if (!advanced_forum_treat_as_forum_post('node', $variables)) {

    // We only deal with forum posts.
    return;
  }

  // Set the static variables of the comment wrapper theme function.
  if (function_exists('phptemplate_comment_wrapper')) {
    phptemplate_comment_wrapper(NULL, 'forum', $variables['node']);
  }
  $forum_style = advanced_forum_get_current_style();
  if ($_GET['page'] > 0) {

    // This is the repeated node on the top of subsequent pages.
    // We send this to a special .tpl so people can wipe it out or whatever
    $variables['template_files'][] = "{$forum_style}/advf-forum-repeat-post";
  }
  else {

    // Use our combined node/comment template file
    $variables['template_files'][] = "{$forum_style}/advf-forum-post";
  }

  // The node is the first post, aka topic
  $variables['top_post'] = TRUE;

  // Build the topic header
  $variables['topic_header'] = theme('advanced_forum_topic_header', $variables['node'], $variables['comment_count']);

  /* Node links changes */

  // Change first post from "add comment" to "Reply"
  if (!empty($variables['node']->links) && !empty($variables['node']->links['comment_add'])) {
    $variables['node']->links['comment_add']['title'] = t('Reply');
  }

  // Change links over to buttons when possible and wanted.
  _advanced_forum_buttonify_links($variables['node']->links);

  // Remake the links with our changes
  $variables['links'] = theme('links', $variables['node']->links, array(
    'class' => 'links inline forum-links',
  ));

  // Make an array version of $links
  $variables['links_array'] = $variables['node']->links;

  // User information / author pane
  $variables['account'] = user_load(array(
    'uid' => $variables['node']->uid,
  ));
  $variables['author_pane'] = theme('author_pane', $variables['account'], advanced_forum_path_to_images(), "{$forum_style}/advf-author-pane");
}

/**
 * Preprocesses template variables for the comment template.
 */
function advanced_forum_preprocess_comment(&$variables) {
  if (advanced_forum_treat_as_forum_post('comment', $variables)) {

    // Use our combined node/comment template file
    // D5 won't find templates in subdirectories so we need to give it that
    $forum_style = advanced_forum_get_current_style();
    $variables['template_files'][] = "{$forum_style}/advf-forum-post";

    // Thread is being shown in the forum (not on the front page or in a view)
    $variables['is_forum'] = TRUE;

    // This is a comment, not the node.
    $variables['top_post'] = FALSE;

    // We need some information from the parent node so load it here
    $node = node_load($variables['comment']->nid);
    $variables['node'] = $node;

    // Title
    if (variable_get('comment_subject_field', 1) == 0) {

      // if comment titles are disabled, don't display it.
      $variables['title'] = '';
    }
    else {

      // Assign the subject to the title variable for consistancy with nodes.
      $variables['title'] = check_plain($variables['comment']->subject);
    }

    // Just use the date for the submitted on.
    $variables['submitted'] = format_date($variables['comment']->timestamp);

    // Assign the comment to the content variable for consistancy with nodes.
    $variables['content'] = $variables['comment']->comment;

    // User information
    $account_id = $variables['comment']->uid;
    if ($account_id == 0) {

      // Anonymous user. Make a fake user object for theme_username
      $variables['account']->name = $variables['comment']->name;
      $variables['account']->homepage = $variables['comment']->homepage;
    }
    else {

      // Load up the real user object
      $variables['account'] = user_load(array(
        'uid' => $variables['comment']->uid,
      ));
    }

    // Create the author pane
    $variables['author_pane'] = theme('author_pane', $variables['account'], advanced_forum_path_to_images(), "{$forum_style}/advf-author-pane");

    // Because the $links array isn't available here, we recreate it
    if (arg(1) != 'reply') {
      $links = module_invoke_all('link', 'comment', $variables['comment']);
      foreach (module_implements('link_alter') as $module) {
        $function = $module . '_link_alter';
        $function($node, $links);
      }
      unset($links['comment_parent']);

      // Iconify common links (optional to avoid translation issues)
      _advanced_forum_buttonify_links($links);

      // Remake the links with our changes
      $variables['links'] = theme('links', $links, array(
        'class' => 'links forum-links',
      ));
      $variables['links_array'] = $links;
    }

    // Comment number with link
    if (!isset($post_number)) {
      static $post_number = 0;
    }
    _advanced_forum_topic_nid($variables['node']->nid);
    $post_per_page = _comment_get_display_setting('comments_per_page', $variables['node']);
    $page_number = $_GET['page'];
    if (!$page_number) {
      $page_number = 0;
    }
    $post_number++;
    $fragment = 'comment-' . $variables['comment']->cid;
    $query = $page_number ? 'page=' . $page_number : NULL;
    $linktext = '#' . ($page_number * $post_per_page + $post_number);
    $linkpath = 'node/' . _advanced_forum_topic_nid();
    $variables['comment_link'] = l($linktext, $linkpath, NULL, $query, $fragment);

    // Link to page created by Comment Page module, if it exists
    if (!empty($variables['comment']->page_url) && !(arg(0) == 'comment' && arg(1) == $variables['comment']->cid)) {
      $variables['page_link'] = l('(permalink)', $variables['comment']->page_url);
    }
  }
}

/**
 * Preprocesses template variables for the comment wrapper template.
 */
function advanced_forum_preprocess_comment_wrapper(&$variables) {
  if (advanced_forum_treat_as_forum_post('comment-wrapper', $variables)) {
    $variables['reply_link'] = '';
    if ($variables['node']->comment != COMMENT_NODE_READ_WRITE || variable_get('comment_form_location', COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {

      // If the post is locked or the comment form is on a seperate page, build
      // the reply/locked link / button
      $variables['reply_link'] = theme('advanced_forum_reply_link', $variables['node']);
    }
  }
}

/**
 * Preprocesses template variables for the forum template.
 */
function advanced_forum_preprocess_forums(&$variables) {
  if (empty($variables['topics'])) {

    // We don't want the links on the top of the forum overview
    $variables['links_orig'] = $variables['links'];
    $variables['links'] = array();
    $variables['forum_description'] = '';
  }
  else {

    // Grab the forum description and make it available to the template file
    $forum = taxonomy_get_term($variables['tid']);
    $variables['forum_description'] = $forum->description;

    // To avoid translation issues, make the button version optional
    if (variable_get('advanced_forum_button_links', 1)) {
      if (isset($variables['links']['forum'])) {
        $title_image = advanced_forum_theme_image(t('new-topic.png'), t('New topic'));
        $variables['links']['forum']['title'] = $title_image;
        $variables['links']['forum']['html'] = TRUE;
      }
      if (isset($variables['links']['poll'])) {
        $title_image = advanced_forum_theme_image(t('new-poll.png'), t('New poll'));
        $variables['links']['poll']['title'] = $title_image;
        $variables['links']['poll']['html'] = TRUE;
      }
    }
  }

  // Add in the mark as read link if user has access
  if (advanced_forum_markasread_access()) {
    $tid = $variables['tid'];
    if ($tid) {
      $title = t('Mark all topics read');
      $link = "forum/markasread/{$tid}";
    }
    else {
      $title = t('Mark all forums read');
      $link = "forum/markasread";
    }

    // To avoid translation issues, make the button version optional
    if (variable_get('advanced_forum_button_links', 1)) {
      $title_image = advanced_forum_theme_image(t('mark-read.png'), t('Mark read'));
      $variables['links']['markasread'] = array(
        'title' => $title_image,
        'href' => $link,
        'html' => TRUE,
      );
      $variables['links_orig']['markasread'] = array(
        'title' => $title_image,
        'href' => $link,
        'html' => TRUE,
      );
    }
    else {
      $variables['links']['markasread'] = array(
        'title' => $title,
        'href' => $link,
      );
      $variables['links_orig']['markasread'] = array(
        'title' => $title,
        'href' => $link,
      );
    }
  }
}

/**
 * Preprocesses template variables for the forum statistics template.
 */
function advanced_forum_preprocess_advf_forum_statistics(&$variables) {
  $variables['template_files'][] = 'advf-forum-statistics';
  $variables['topics'] = advanced_forum_statistics_topics();
  $variables['posts'] = advanced_forum_statistics_comments() + $variables['topics'];
  $variables['users'] = advanced_forum_statistics_users();
  $authenticated_users = advanced_forum_statistics_online_users();
  $variables['online_users'] = implode(', ', $authenticated_users);
  $variables['current_users'] = count($authenticated_users);
  $variables['current_guests'] = sess_count(time() - variable_get('user_block_seconds_online', 900));
  $variables['current_total'] = $variables['current_users'] + $variables['current_guests'];
  $latest_user = advanced_forum_statistics_latest_user();
  $variables['latest_user'] = theme('username', $latest_user);
}

/**
 * Preprocesses template variables for the forum statistics template.
 */
function advanced_forum_preprocess_advanced_forum_forum_legend(&$variables) {
  $variables['folder_new_posts'] = advanced_forum_theme_image(t('forum-folder-new-posts.png'), t('Forum Contains New Posts'));
  $variables['folder_default'] = advanced_forum_theme_image(t('forum-folder.png'), t('Forum Contains No New Posts'));
  $variables['folder_locked'] = advanced_forum_theme_image(t('forum-folder-locked.png'), t('Forum is Locked'));
}
function advanced_forum_preprocess_advanced_forum_topic_legend(&$variables) {
  $variables['topic_new'] = advanced_forum_theme_image(t('topic-new.png'), t('New Posts'));
  $variables['topic_hot_new'] = advanced_forum_theme_image(t('topic-hot-new.png'), t('Hot Thread (New)'));
  $variables['topic_hot'] = advanced_forum_theme_image(t('topic-hot.png'), t('Hot Thread (No New)'));
  $variables['topic_default'] = advanced_forum_theme_image(t('topic-default.png'), t('No New Posts'));
  $variables['topic_sticky'] = advanced_forum_theme_image(t('topic-sticky.png'), t('Sticky Thread'));
  $variables['topic_closed'] = advanced_forum_theme_image(t('topic-closed.png'), t('Locked Thread'));
}

/**
 * Preprocesses template variables for the forum list template.
 */
function advanced_forum_preprocess_forum_list(&$variables) {
  global $user;
  $number_of_forums = count($variables['forums']);
  $forum_counter = 0;
  foreach ($variables['forums'] as $id => $forum) {

    // Counter to label the rows by position
    $forum_counter++;
    switch ($forum_counter) {
      case "1":
        $row_classes = 'first-row';
        break;
      case $number_of_forums:
        $row_classes = 'last-row';
        break;
      default:
        $row_classes = 'middle-row';
    }
    if ($forum->is_container) {
      $row_classes .= ' container';
    }
    $variables['forums'][$id]->row_classes = $row_classes;
    $variables['forums'][$id]->new_posts = 0;
    $variables['forums'][$id]->new_text_posts = '';
    $variables['forums'][$id]->new_url_posts = '';
    $variables['forums'][$id]->old_posts = $forum->num_posts;
    $variables['forums'][$id]->position = $position;
    $current_container = $current_container_depth = 0;
    if ($forum->is_container) {
      $current_container = $forum->tid;
      $current_container_depth = $forum->depth;
      $variables['forums'][$id]->container_id = $current_container;
    }
    else {
      if ($forum->depth > $current_container_depth) {
        $variables['forums'][$id]->container_id = $current_container;
      }
      else {
        $variables['forums'][$id]->container_id = 0;
      }
    }
    if ($user->uid) {

      // Show number of new posts as well as topics
      if (variable_get('advanced_forum_get_new_comments', 0)) {

        // This can cause performance issues, so allow it to be turned off
        $number_of_new_comments = advanced_forum_unread_comments_in_forum($forum->tid, $user->uid);
        $variables['forums'][$id]->new_posts = $number_of_new_comments + $variables['forums'][$id]->new_topics;
        if ($variables['forums'][$id]->new_posts) {
          $variables['forums'][$id]->new_text_posts = format_plural($variables['forums'][$id]->new_posts, '1 new', '@count new');
          $variables['forums'][$id]->new_url_posts = url("forum/{$forum->tid}", NULL, 'new');
        }
        $variables['forums'][$id]->old_posts = $forum->num_posts - $variables['forums'][$id]->new_posts;
      }
    }

    // If there are new topics/posts, change the icon
    if ($forum->new_topics || $forum->new_posts) {
      $variables['forums'][$id]->icon = advanced_forum_theme_image(t('forum-folder-new-posts.png'), t('New posts folder'));
    }
    else {
      $variables['forums'][$id]->icon = advanced_forum_theme_image(t('forum-folder.png'), t('Folder'));
    }

    // Make container description more accessable
    if (!empty($variables['parents']['0'])) {
      $variables['container_description'] = $variables['parents']['0']->description;
    }
  }
  if (user_access('view forum statistics')) {
    $variables['forum_statistics'] = theme('advf_forum_statistics');
  }

  // Set a variable for displaying the forum legend.
  $variables['forum_legend'] = theme('advanced_forum_forum_legend');
}

/**
 * Preprocesses template variables for the submitted by/in template.
 */
function advanced_forum_preprocess_forum_submitted(&$variables) {
  if ($variables['topic']->node_title) {
    $nid = $variables['topic']->nid;

    // Make a fake node object to avoid the node load
    $node = new stdClass();
    $node->nid = $nid;
    $node->type = $variables['topic']->type;

    // Find the page of the first unread comment, if any
    $num_comments = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE nid = %d', $nid));
    $new_replies = comment_num_new($nid);
    $query = comment_new_page_count($num_comments, $new_replies, $node);

    // Format the node title with a link
    $title_length = variable_get('advanced_forum_topic_title_length', 15);
    if ($title_length == 0) {
      $short_topic_title = $variables['topic']->node_title;
    }
    else {
      $short_topic_title = truncate_utf8($variables['topic']->node_title, $title_length, TRUE, TRUE);
    }
    $fragment = $new_replies ? 'new' : NULL;
    $variables['topic_link'] = l($short_topic_title, "node/{$nid}", NULL, $query, $fragment);
  }

  // For items posted more than $cutoff hours ago, offer an actual date.
  $cutoff = variable_get('advanced_forum_time_ago_cutoff', 48) * 60 * 60;
  if (isset($variables['topic']->timestamp)) {
    $timestamp = $variables['topic']->timestamp;
    if (time() - $timestamp > $cutoff) {
      $variables['date_posted'] = format_date($timestamp, 'small');
    }
  }
}

/**
 * Preprocesses template variables for the topic list template.
 */
function advanced_forum_preprocess_forum_topic_list(&$variables) {

  // Redo the table header so we can add in views.
  global $forum_topic_list_header;
  $forum_topic_list_header = array();
  $forum_topic_list_header[] = array(
    'data' => '&nbsp;',
    'class' => 'column_icon',
  );
  $forum_topic_list_header[] = array(
    'data' => t('Topic'),
    'field' => 'n.title',
    'class' => 'column_topic',
  );
  $forum_topic_list_header[] = array(
    'data' => t('Replies'),
    'field' => 'l.comment_count',
    'class' => 'column_replies',
  );

  // Topic views require the statistics module so don't show if it's not enabled
  if (module_exists('statistics')) {
    $forum_topic_list_header[] = array(
      'data' => t('Views'),
      'class' => 'column_views',
    );
  }

  // Allow admins to turn off the created column
  if (!variable_get('advanced_forum_hide_created', 0)) {
    $forum_topic_list_header[] = array(
      'data' => t('Created'),
      'field' => 'n.created',
      'class' => 'column_created',
    );
  }
  $forum_topic_list_header[] = array(
    'data' => t('Last reply'),
    'field' => 'l.last_comment_timestamp',
    'class' => 'column_lreply',
  );

  // Create the tablesorting header.
  $ts = tablesort_init($forum_topic_list_header);
  $header = '';
  foreach ($forum_topic_list_header as $cell) {
    $cell = tablesort_header($cell, $forum_topic_list_header, $ts);
    $header .= _theme_table_cell($cell, TRUE);
  }
  $variables['header'] = $header;

  // Grab the forum description and make it available to the template file
  $forum = taxonomy_get_term($variables['topic_id']);
  $variables['forum_description'] = $forum->description;

  // Do our own topic processing.
  if (!empty($variables['topics'])) {
    $row = 0;

    // Find out how many pages to show on the topic pager. We do this outside
    // the loop because it will be the same for all topics.
    $max_pages_to_display = variable_get('advanced_forum_topic_pager_max', 5);
    foreach ($variables['topics'] as $id => $topic) {

      // Get a pager to add to the topic, if there is one
      $variables['topics'][$id]->pager = _advanced_forum_create_topic_pager($max_pages_to_display, $topic);

      // Make shadow copy point to actual thread and tell you new the forum name
      if ($variables['topics'][$id]->moved == TRUE) {
        $term = taxonomy_get_term($topic->tid);
        $variables['topics'][$id]->message = l(t('This topic has been moved to !forum', array(
          '!forum' => $term->name,
        )), "node/{$topic->nid}");
      }

      // Send the NID into the icon theme code so it can be linked to the topic
      $variables['topics'][$id]->icon = theme('forum_icon', $topic->new, $topic->num_comments, $topic->comment_mode, $topic->sticky, $topic->nid);

      // Add in the number of views
      if (module_exists('statistics')) {
        $variables['topics'][$id]->views = _advanced_forum_get_topic_views_count($topic->nid);
      }

      // Set classes based on stickiness. This allows themers to seperate out
      // all the sticky posts into their own section.
      if ($topic->sticky) {
        $sticky_class = 'sticky-topic';
        $was_sticky = TRUE;
      }
      elseif ($was_sticky) {
        $sticky_class = 'first-not-sticky not-sticky';
        $was_sticky = FALSE;
      }
      else {
        $sticky_class = 'not-sticky';
      }
      $topic->sticky_class = $sticky_class;
    }

    // Set a variable for displaying the topic legend.
    $variables['topic_legend'] = theme('advanced_forum_topic_legend');
  }
}

/**
 * Preprocesses template variables for the forum icon template.
 */
function advanced_forum_preprocess_forum_icon(&$variables) {
  $variables['iconpath'] = advanced_forum_path_to_images();
  return;
}

// CORE FORUM PAGE OVERRIDE **************************************************/

/**
 * Menu callback; prints a forum listing.
 *
 * This is copied from the forum module and adapted.
 */
function advanced_forum_page($tid = 0) {
  if (!is_numeric($tid)) {
    return MENU_NOT_FOUND;
  }
  $tid = (int) $tid;
  drupal_add_css(drupal_get_path('module', 'forum') . '/forum.css');
  _advanced_forum_add_files();
  $forum_per_page = variable_get('forum_per_page', 25);
  $sortby = variable_get('forum_order', 1);
  $forums = advanced_forum_get_forums($tid);
  $parents = taxonomy_get_parents_all($tid);
  if ($tid && !in_array($tid, variable_get('forum_containers', array()))) {
    $topics = forum_get_topics($tid, $sortby, $forum_per_page);
  }
  return theme('forum_display', $forums, $topics, $parents, $tid, $sortby, $forum_per_page);
}

/**
 * Returns a list of all forums for a given taxonomy id
 *
 * This is copied from the forum module and adapted.
 *
 * Forum objects contain the following fields
 * -num_topics Number of topics in the forum
 * -num_posts Total number of posts in all topics
 * -last_post Most recent post for the forum
 *
 * @param $tid
 *   Taxonomy ID of the vocabulary that holds the forum list.
 * @return
 *   Array of object containing the forum information.
 */
function advanced_forum_get_forums($tid = 0) {
  $forums = array();
  $_forums = taxonomy_get_tree(variable_get('forum_nav_vocabulary', ''), $tid);
  if (count($_forums)) {
    $counts = array();
    $sql = "SELECT r.tid, COUNT(n.nid) AS topic_count, SUM(l.comment_count) AS comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {term_node} r ON n.nid = r.nid WHERE n.status = 1 AND n.type = 'forum' GROUP BY r.tid";
    $sql = db_rewrite_sql($sql);
    $_counts = db_query($sql, $forum->tid);
    while ($count = db_fetch_object($_counts)) {
      $counts[$count->tid] = $count;
    }
  }
  foreach ($_forums as $forum) {
    if (in_array($forum->tid, variable_get('forum_containers', array()))) {
      $forum->container = 1;
    }
    if ($counts[$forum->tid]) {
      $forum->num_topics = $counts[$forum->tid]->topic_count;
      $forum->num_posts = $counts[$forum->tid]->topic_count + $counts[$forum->tid]->comment_count;
    }
    else {
      $forum->num_topics = 0;
      $forum->num_posts = 0;
    }

    // This query does not use full ANSI syntax since MySQL 3.x does not support
    // table1 INNER JOIN table2 INNER JOIN table3 ON table2_criteria ON table3_criteria
    // used to join node_comment_statistics to users.
    $sql = "SELECT n.nid, n.title, n.type, ncs.last_comment_timestamp, IF (ncs.last_comment_uid != 0, u2.name, ncs.last_comment_name) AS last_comment_name, ncs.last_comment_uid FROM {node} n INNER JOIN {users} u1 ON n.uid = u1.uid INNER JOIN {term_node} tn ON n.nid = tn.nid INNER JOIN {node_comment_statistics} ncs ON n.nid = ncs.nid INNER JOIN {users} u2 ON ncs.last_comment_uid=u2.uid WHERE n.status = 1 AND n.type='forum' AND tn.tid = %d ORDER BY ncs.last_comment_timestamp DESC";
    $sql = db_rewrite_sql($sql);
    $topic = db_fetch_object(db_query_range($sql, $forum->tid, 0, 1));
    $last_post = new stdClass();
    $last_post->timestamp = $topic->last_comment_timestamp;
    $last_post->name = $topic->last_comment_name;
    $last_post->uid = $topic->last_comment_uid;
    $last_post->nid = $topic->nid;

    // Note, we call it "node_title" otherwise it gets picked up on the
    // topic list as well.
    $last_post->node_title = $topic->title;
    $last_post->type = $topic->type;
    $forum->last_post = $last_post;
    $forums[$forum->tid] = $forum;
  }
  return $forums;
}

// MARK AS READ **************************************************************/

/**
 * Marks all posts in forums or in a given forum as read by the current user.
 */
function advanced_forum_markasread() {
  global $user;

  // See if we're on a forum or on the forum overview
  // Path will be /forum/markasread or /forum/markasread/tid
  $current_forum_id = arg(2);
  if ($current_forum_id) {

    // Delete the current history entries so already visited nodes get updated.
    $sql = "DELETE h\n            FROM {history} AS h\n              INNER JOIN {term_node} AS tn ON (h.nid = tn.nid)\n            WHERE h.uid = %d AND tn.tid = %d";
    db_query($sql, $user->uid, $current_forum_id);

    // Update the history table with all forum nodes newer than the cutoff.
    $sql = "INSERT INTO {history} (uid, nid, timestamp)\n            SELECT DISTINCT %d, n.nid, %d\n            FROM {node} AS n\n              INNER JOIN {term_node} AS tn ON n.nid = tn.nid\n              INNER JOIN {node_comment_statistics} AS ncs ON ncs.nid = n.nid\n            WHERE (n.changed > %d OR ncs.last_comment_timestamp > %d) AND tn.tid = %d";
    $args = array(
      $user->uid,
      time(),
      NODE_NEW_LIMIT,
      NODE_NEW_LIMIT,
      $current_forum_id,
    );
    db_query($sql, $args);

    // Readpath integration
    if (module_exists('readpath')) {
      readpath_clear_readpath();
    }
    drupal_set_message(t('All content in this forum has been marked as read'));
    drupal_goto('forum/' . $current_forum_id);
  }
  else {

    // We are on the forum overview, requesting all forums be marked read
    $forum_vocabulary_id = variable_get('forum_nav_vocabulary', '');

    // Delete the current history entries so already visited nodes get updated.
    $sql = "DELETE h\n            FROM {history} AS h\n              INNER JOIN {term_node} AS tn ON (h.nid = tn.nid)\n              INNER JOIN {term_data} AS td ON (td.tid = tn.tid)\n            WHERE h.uid = %d AND td.vid = %d";
    db_query($sql, $user->uid, $forum_vocabulary_id);

    // Update the history table with all forum nodes newer than the cutoff.
    $sql = "INSERT INTO {history} (uid, nid, timestamp)\n            SELECT DISTINCT %d, n.nid, %d\n            FROM {node} AS n\n              INNER JOIN {term_node} AS tn ON n.nid=tn.nid\n              INNER JOIN {node_comment_statistics} AS ncs ON ncs.nid = n.nid\n              INNER JOIN {term_data} AS td ON tn.tid = td.tid\n            WHERE (n.changed > %d OR ncs.last_comment_timestamp > %d) AND td.vid = %d";
    $args = array(
      $user->uid,
      time(),
      NODE_NEW_LIMIT,
      NODE_NEW_LIMIT,
      $forum_vocabulary_id,
    );
    db_query($sql, $args);

    // Readpath integration
    if (module_exists('readpath')) {
      readpath_clear_readpath();
    }
    drupal_set_message(t('All forum content been marked as read'));
    drupal_goto('forum');
  }
}

/**
 * Helper function to tell whether a user should be able to mark forums read.
 */
function advanced_forum_markasread_access() {
  global $user;
  return user_access('access content') && $user->uid;
}

// STYLE UTILITY FUNCTIONS ***************************************************/

/**
 * Returns the name of the forum style to use.
 */
function advanced_forum_get_current_style() {
  return variable_get('advanced_forum_style', 'naked');
}

/**
 * Returns the full path to the advanced forum style, including the style name
 */
function advanced_forum_path_to_style() {
  static $style_path;
  if (!$style_path) {

    // Retrieve the style name
    $style = advanced_forum_get_current_style();

    // Check first if there is a subtheme. This function is defined in Zen
    // and potentially may exist in other themes with subthemes.
    init_theme();
    if (function_exists("path_to_subtheme")) {
      $path_to_subtheme = path_to_subtheme();
      $style_path = $path_to_subtheme . '/' . $style;
    }

    // If we had no luck finding the style in a subtheme, find the style path
    // the normal way.
    if (empty($path_to_subtheme) || empty($style_path) || !file_exists($style_path)) {
      $style_path = path_to_theme() . '/' . $style;
    }
  }
  return $style_path;
}

/**
 * Returns the path to the advanced forum image directory
 */
function advanced_forum_path_to_images() {
  static $image_path;
  if (!$image_path) {
    $image_path = advanced_forum_path_to_style() . '/images';
  }
  return $image_path;
}

/** 
 * Wrapper around theme_image that automatically includes the path.
 */
function advanced_forum_theme_image($image_file, $alt = NULL, $title = NULL) {
  $image = advanced_forum_path_to_images() . "/{$image_file}";
  if ($title = NULL) {
    $title = $alt;
  }
  if (file_exists($image)) {
    return theme('image', $image, $alt, $title);
  }
  else {
    return false;
  }
}

/**
 * Theme function to return imagecached version of the user picture.
 */
function theme_advanced_forum_user_picture($account) {

  // Get the imagecache preset to use, if any.
  $preset = variable_get('advanced_forum_user_picture_preset', '');

  // Only proceed if user pictures are enabled, and there is a preset set, and
  // imagecache is enabled. For performace reasons, we return nothing if these
  // critera aren't met because we only call this function when the non
  // imagecached version is already created. If you use this function elsewhere
  // you will need to provide a picture when imagecache is not used.
  if (variable_get('user_pictures', 0) && !empty($preset) && module_exists('imagecache') && function_exists('imagecache_presets')) {
    if ($account->picture && file_exists($account->picture)) {

      // User has picture, so use that
      $alt = t("@user's picture", array(
        '@user' => $account->name ? $account->name : 'Visitor',
      ));
      $picture = theme('imagecache', $preset, $account->picture);
    }
    else {

      // User doesn't have a picture so use the default, if any
      $default_picture = variable_get('user_picture_default', '');
      if ($default_picture) {
        $picture = theme('imagecache', $preset, $default_picture);
      }
    }
    if (!empty($picture)) {

      // If the user has access to view profiles, link the picture
      if (!empty($account->uid) && user_access('access user profiles')) {
        $picture = l($picture, "user/{$account->uid}", array(
          'title' => t('View user profile.'),
        ), NULL, NULL, FALSE, TRUE);
      }
    }
    return '<div class="picture">' . $picture . '</div>';
  }
}
function _advanced_forum_buttonify_links(&$links) {
  $buttonify = variable_get('advanced_forum_button_links', 1);
  if ($buttonify && !empty($links)) {
    foreach ($links as $link_key => $link) {
      switch ($link_key) {

        // First, take care of the special cases
        case 'comment_add':
          $links[$link_key]['title'] = advanced_forum_theme_image(t('reply.png'), t('reply'));
          $links[$link_key]['html'] = TRUE;
          break;
        case 'comment_reply':
          $links[$link_key]['title'] = advanced_forum_theme_image(t('reply.png'), t('reply'));
          $links[$link_key]['html'] = TRUE;
          break;
        case 'comment_edit':
          $links[$link_key]['title'] = advanced_forum_theme_image(t('edit.png'), t('edit'));
          $links[$link_key]['html'] = TRUE;
          break;
        case 'comment_delete':
          $links[$link_key]['title'] = advanced_forum_theme_image(t('delete.png'), t('delete'));
          $links[$link_key]['html'] = TRUE;
          break;
        default:

          // For all other links, attempt to buttonify by key name.
          $image = advanced_forum_theme_image($link_key . '.png', $link['title']);
          if (!$image) {

            // If no luck there, try the adjusted title.
            $title = strtolower(str_replace(' ', '-', $link['title']));
            $image = advanced_forum_theme_image($title . '.png', $link['title']);
          }
          if ($image) {

            // If we found an image, use it. Otherwise leave the title alone.
            $links[$link_key]['title'] = $image;
            $links[$link_key]['html'] = TRUE;
          }
      }
    }
  }
}

/**
 * This function returns true if the node/comment should be themed and
 * otherwise treated as a forum post.
 */
function advanced_forum_treat_as_forum_post($hook, $variables) {

  // Setting this static means the check only needs to be done once per page
  // as long as we haven't hit a non forum node on the page
  static $forum_node_id;

  // Start out assuming it's not one of ours.
  $decision = FALSE;
  if ($hook == 'node') {

    // Make sure this is a forum node type and that it's not being
    // shown on some other page (like a view or promoted to front page)
    if ($variables['node']->type == 'forum' && (arg(0) == 'node' && is_numeric(arg(1)) && arg(1) == $variables['node']->nid || arg(0) == 'comment')) {
      $forum_node_id = $variables['node']->nid;
      $decision = TRUE;
    }
    else {

      // In D5, only forum types allowed in the forums so
      // we know this isn't a forum thread (or that it's not in the forums).
      unset($forum_node_id);
      $decision = FALSE;
    }
  }
  if ($hook == 'comment') {
    if (isset($forum_node_id) && $variables['comment']->nid == $forum_node_id) {

      // We already know this comment is either part of a forum thread
      // or that comments on this thread are known exceptions.
      $decision = TRUE;
    }
    else {

      // Not part of a forum thread. Check if this site wants all comments to
      // use the forum comment template or the comment is being shown alone via
      // the comment_page module
      if (variable_get("advanced_forum_theme_all_comments", 0) == 1 || arg(0) == 'comment' && is_numeric(arg(1))) {

        // This site wants all comments to use the forum comment template
        $forum_node_id = $variables['comment']->nid;
        $decision = TRUE;
      }
      else {

        // If we are on a comment reply page, check if the node/comment gets styled.
        if (arg(0) == 'comment' && arg(1) == 'reply') {
          $nid = arg(2);
          $variables['node'] = node_load($nid);
          $decision = advanced_forum_treat_as_forum_post('node', $variables);
        }
      }
    }
  }
  if ($hook == 'comment-wrapper') {
    if (isset($forum_node_id) && $variables['node']->nid == $forum_node_id) {

      // We only want to use our comment wrapper if we are on a node that
      // passed the tests.
      $decision = TRUE;
    }
  }

  // Call a hook with our decisions and the available information. If it returns
  // a number, override our decision and set the static variable to that number.
  $override_nids = module_invoke_all('treat_as_forum_post_alter', $decision, $hook, $variables);
  if (!empty($override_nids)) {

    // At least one module has returned an nid. We are making an assumption here
    // that if there was more than one the nids are the same and just grabbing
    // the first one.
    $forum_node_id = $override_nids[0];
    if ($forum_node_id == -1) {
      unset($forum_node_id);
      $decision = FALSE;
    }
    else {
      $decision = TRUE;
    }
  }
  if ($decision) {
    _advanced_forum_add_files();
  }
  return $decision;
}

// GENERAL UTILITY FUNCTIONS *************************************************/

/**
 * Adds extra files needed when we have a post that is to be treated as a forum
 * post. Mostly CSS but could be JS and others in the future.
 */
function _advanced_forum_add_files() {

  // This could get called more than once on a page and we only need to do it
  // once.
  static $added;
  if (!$added) {
    $added = TRUE;
    $style_directory = advanced_forum_path_to_style();
    drupal_add_css("{$style_directory}/advanced_forum-structure.css");
    drupal_add_css("{$style_directory}/advanced_forum.css");
  }
}

/**
 * Holds the node ID of the thread we are on.
 *
 * Used for linking the comment numbers.
 *
 * @param $nid
 *   Node ID
 */
function _advanced_forum_topic_nid($nodeid = 0) {
  static $nid;
  if (!empty($nodeid)) {
    $nid = $nodeid;
  }
  return $nid;
}

/**
 * Returns the cid of the last post in a topic.
 *
 * @param $node
 *   Node object
 * @return
 *   cid of last post.
 */
function advanced_forum_last_post_in_topic($nid) {

  // Retrieve the newest comment.
  $query = 'SELECT c.cid
            FROM {comments} c
            WHERE c.nid = %d AND c.status = %d
            ORDER BY c.cid DESC';
  $result = db_result(db_query_range($query, $nid, COMMENT_PUBLISHED, 0, 1));
  return $result;
}
function advanced_forum_last_post_link($node) {
  $last_comment_id = advanced_forum_last_post_in_topic($node->nid);
  $last_page = advanced_forum_get_last_page($node);
  $query = $last_page > 0 ? "page={$last_page}" : NULL;
  return l(t('Last post'), "node/{$node->nid}", NULL, $query, "comment-{$last_comment_id}");
}

/**
 * Returns the page number of the last page starting at 0 like the pager does.
 */
function advanced_forum_get_last_page($node) {
  $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
  $comment_count = $node->comment_count;
  $last_page = ceil($comment_count / $comments_per_page) - 1;
  return $last_page;
}

/**
 * Returns the ID of the first unread comment.
 *
 * @param $nid
 *   Node ID
 * @param $timestamp
 *   Date/time used to override when the user last viewed the node.
 * @return
 *   Comment ID
 */
function advanced_forum_first_new_comment($nid, $timestamp = 0) {
  global $user;
  if ($user->uid) {

    // Retrieve the timestamp at which the current user last viewed the
    // specified node.
    if (!$timestamp) {
      $timestamp = node_last_viewed($nid);
    }

    // Set the timestamp to the limit if the node was last read past the cutoff
    $timestamp = $timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT;

    // Use the timestamp to retrieve the oldest new comment.
    $query = 'SELECT c.cid
              FROM {node} n
              INNER JOIN {comments} c ON n.nid = c.nid
              WHERE n.nid = %d AND timestamp > %d AND c.status = %d ORDER BY c.cid';
    $result = db_result(db_query_range($query, $nid, $timestamp, COMMENT_PUBLISHED, 0, 1));
    return $result;
  }
  else {
    return 0;
  }
}

/**
 * Returns a link directly to the first new post in a topic.
 *
 * @param $node
 *   Node object
 * @param $comment_count
 *   Number of comments on passed node.
 * @return
 *   Link to the first unread comment with text as "42 new".
 */
function advanced_forum_first_new_post_link($node, $comment_count) {
  $nid = $node->nid;
  $current_page = $_GET['page'];
  $number_new_comments = comment_num_new($nid);
  if ($number_new_comments > 0) {

    // Note that we are linking to the cid anchor rather than "new" because
    // the new links will be gone if we go to another page.
    $page_of_first_new = comment_new_page_count($comment_count, $number_new_comments, $node);
    $cid_of_first_new = advanced_forum_first_new_comment($nid);
    return l($number_new_comments . ' ' . t('new'), "node/{$nid}", NULL, $page_of_first_new, "comment-{$cid_of_first_new}");
  }
}

/**
 * Creates a pager to place on each multi-page topic of the topic listing page.
 *
 * @param $max_pages_to_display
 *   Number of pages to include on the pager.
 * @param $topic
 *   Topic object to create a pager for.
 * @return
 *   Asembled pager.
 */
function _advanced_forum_create_topic_pager($max_pages_to_display, $topic) {

  // Find the number of comments per page for the node type of the topic.
  // It's the same for all types in D5, but varies in D6.
  $comments_per_page = _comment_get_display_setting('comments_per_page', $topic);
  if ($max_pages_to_display > 0 && $topic->num_comments > $comments_per_page) {

    // Topic has more than one page and a pager is wanted. Start off the
    // first page because that doesn't have a query.
    $pager_array = array();
    $current_display_page = 1;
    $pager_array[] = l(t('1'), "node/{$topic->nid}");

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

    // Add pages until we run out or until we hit the max to show.
    while ($current_display_page < $last_display_page && $current_display_page < $max_pages_to_display) {

      // Move to the next page
      $current_display_page++;

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

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

    // Move to the next page
    $current_display_page++;
    if ($current_display_page == $last_display_page) {

      // We are one past the max to display, but it's the last page,
      // so putting the ...last is silly. Just display it normally.
      $link_to_page = $current_display_page - 1;
      $pager_array[] = l($current_display_page, "node/{$topic->nid}", NULL, 'page=' . $link_to_page);
    }
    $pager_last = '';
    if ($current_display_page < $last_display_page) {

      // We are one past the max to display and still aren't
      // on the last page, so put in ... Last Page(N)
      $text = t('Last Page') . '(' . $last_display_page . ')';
      $pager_last = ' &hellip; ' . l($text, "node/{$topic->nid}", NULL, 'page=' . $last_pager_page);
    }

    // Put it all together
    return '[' . t('Page') . ' ' . implode(", ", $pager_array) . $pager_last . ']';
  }
}

/**
 * Retrieves a forum topic's "views count".
 *
 * @param $nid
 *   Node ID
 * @return
 *   Total number of times that node has been viewed.
 */
function _advanced_forum_get_topic_views_count($nid) {
  if ($nid > 0) {
    $views_count = db_result(db_query('SELECT totalcount FROM {node_counter} WHERE nid = %d', $nid));
  }

  // Make sure it's 0, not blank, for better display.
  if (empty($views_count)) {
    $views_count = 0;
  }
  return $views_count;
}

/**
 * Returns the number of unread posts in a given forum for a given user
 */
function advanced_forum_unread_comments_in_forum($tid, $uid) {
  static $result_cache = NULL;
  if (is_null($result_cache)) {
    $result_cache = array();
    $vid = variable_get('forum_nav_vocabulary', '');
    $sql = "SELECT COUNT(c.cid) AS count, tn.tid\n            FROM {comments} c\n            INNER JOIN {term_node} tn ON c.nid = tn.nid\n            INNER JOIN {term_data} td ON td.tid = tn.tid AND td.vid = %d\n            LEFT JOIN {history} h ON c.nid = h.nid AND h.uid = %d\n            WHERE c.status = 0 AND c.timestamp > %d AND (c.timestamp > h.timestamp OR h.timestamp IS NULL)\n            GROUP BY tn.tid";
    $sql = db_rewrite_sql($sql, 'c', 'cid');
    $result = db_query($sql, $vid, $uid, NODE_NEW_LIMIT);
    while ($row = db_fetch_array($result)) {
      $result_cache[$row['tid']] = $row['count'];
    }
  }
  return isset($result_cache[$tid]) ? $result_cache[$tid] : 0;
}
function advanced_forum_get_forum_list($tid = 0) {
  $forums = array();
  $vid = variable_get('forum_nav_vocabulary', '');
  $forums = taxonomy_get_tree($vid, $tid);
  return $forums;
}

// STATISTICS ****************************************************************/

/**
 * Count total amount of forum threads.
 */
function advanced_forum_statistics_topics() {
  return db_result(db_query('SELECT COUNT(DISTINCT(nid)) FROM {forum}'));
}

/**
 * Count total amount of comments on forum threads.
 */
function advanced_forum_statistics_comments() {
  return db_result(db_query('SELECT COUNT(cid) FROM {comments} c INNER JOIN {forum} f ON (f.nid = c.nid)'));
}

/**
 * Count total amount of active users.
 */
function advanced_forum_statistics_users() {
  return db_result(db_query('SELECT COUNT(uid) FROM {users} WHERE status = 1'));
}

/**
 * Return the latest active user.
 */
function advanced_forum_statistics_latest_user() {
  return db_fetch_object(db_query('SELECT uid, name FROM {users} WHERE status = 1 AND access > 0 ORDER BY created DESC'));
}

/**
 * Return an array of online usernames, linked to their profiles.
 */
function advanced_forum_statistics_online_users() {
  $list = array();
  $interval = time() - variable_get('user_block_seconds_online', 900);
  $sql = 'SELECT DISTINCT u.uid, u.name, MAX(s.timestamp) as maxtime
            FROM {users} u
              INNER JOIN {sessions} s ON u.uid = s.uid
            WHERE s.timestamp >= %d AND s.uid > 0
            GROUP BY u.uid, u.name
           ORDER BY maxtime DESC';
  $authenticated_users = db_query($sql, $interval);
  while ($account = db_fetch_object($authenticated_users)) {
    $list[] = theme('username', $account);
  }
  return $list;
}

Functions

Namesort descending Description
advanced_forum_addvars Provides D5 compatability wrapper for node/comment preprocess functions.
advanced_forum_first_new_comment Returns the ID of the first unread comment.
advanced_forum_first_new_post_link Returns a link directly to the first new post in a topic.
advanced_forum_get_current_style Returns the name of the forum style to use.
advanced_forum_get_forums Returns a list of all forums for a given taxonomy id
advanced_forum_get_forum_list
advanced_forum_get_last_page Returns the page number of the last page starting at 0 like the pager does.
advanced_forum_last_post_in_topic Returns the cid of the last post in a topic.
advanced_forum_last_post_link
advanced_forum_markasread Marks all posts in forums or in a given forum as read by the current user.
advanced_forum_markasread_access Helper function to tell whether a user should be able to mark forums read.
advanced_forum_menu Implementation of hook_menu().
advanced_forum_page Menu callback; prints a forum listing.
advanced_forum_path_to_images Returns the path to the advanced forum image directory
advanced_forum_path_to_style Returns the full path to the advanced forum style, including the style name
advanced_forum_perm Implementation of hook_perm().
advanced_forum_preprocess_advanced_forum_forum_legend Preprocesses template variables for the forum statistics template.
advanced_forum_preprocess_advanced_forum_topic_header Preprocesses template variables for the topic header template.
advanced_forum_preprocess_advanced_forum_topic_legend
advanced_forum_preprocess_advf_forum_statistics Preprocesses template variables for the forum statistics template.
advanced_forum_preprocess_author_pane Preprocesses template variables for the author pane.
advanced_forum_preprocess_comment Preprocesses template variables for the comment template.
advanced_forum_preprocess_comment_wrapper Preprocesses template variables for the comment wrapper template.
advanced_forum_preprocess_forums Preprocesses template variables for the forum template.
advanced_forum_preprocess_forum_icon Preprocesses template variables for the forum icon template.
advanced_forum_preprocess_forum_list Preprocesses template variables for the forum list template.
advanced_forum_preprocess_forum_submitted Preprocesses template variables for the submitted by/in template.
advanced_forum_preprocess_forum_topic_list Preprocesses template variables for the topic list template.
advanced_forum_preprocess_node Preprocesses template variables for the node template.
advanced_forum_settings_page Defines the Advanced Forum settings form.
advanced_forum_statistics_comments Count total amount of comments on forum threads.
advanced_forum_statistics_latest_user Return the latest active user.
advanced_forum_statistics_online_users Return an array of online usernames, linked to their profiles.
advanced_forum_statistics_topics Count total amount of forum threads.
advanced_forum_statistics_users Count total amount of active users.
advanced_forum_theme_image Wrapper around theme_image that automatically includes the path.
advanced_forum_treat_as_forum_post This function returns true if the node/comment should be themed and otherwise treated as a forum post.
advanced_forum_unread_comments_in_forum Returns the number of unread posts in a given forum for a given user
phptemplate_forum_display Implementation of theme_forum_display().
phptemplate_forum_icon Implementation of theme_forum_icon
phptemplate_forum_list Implementation of theme_forum_list().
phptemplate_forum_submitted
phptemplate_forum_topic_list Implementation of theme_forum_topic_list().
phptemplate_forum_topic_navigation Implementation of theme_forum_topic_navigation
theme_advanced_forum_forum_legend Theme function for advanced_forum_forum_legend().
theme_advanced_forum_reply_link Theme function to format the reply link at the top/bottom of topic.
theme_advanced_forum_topic_header Theme function for advanced_forum_forum_legend().
theme_advanced_forum_topic_legend Theme function for advanced_forum_topic_legend().
theme_advanced_forum_user_picture Theme function to return imagecached version of the user picture.
theme_advf_forum_statistics Theme function for advf_forum_statistics().
_advanced_forum_add_files Adds extra files needed when we have a post that is to be treated as a forum post. Mostly CSS but could be JS and others in the future.
_advanced_forum_buttonify_links
_advanced_forum_create_topic_pager Creates a pager to place on each multi-page topic of the topic listing page.
_advanced_forum_get_topic_views_count Retrieves a forum topic's "views count".
_advanced_forum_topic_nid Holds the node ID of the thread we are on.