cms/drupal/modules/forum/forum.module
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * Provides discussion forums.
       
     6  */
       
     7 
       
     8 /**
       
     9  * Implements hook_help().
       
    10  */
       
    11 function forum_help($path, $arg) {
       
    12   switch ($path) {
       
    13     case 'admin/help#forum':
       
    14       $output = '';
       
    15       $output .= '<h3>' . t('About') . '</h3>';
       
    16       $output .= '<p>' . t('The Forum module lets you create threaded discussion forums with functionality similar to other message board systems. Forums are useful because they allow community members to discuss topics with one another while ensuring those conversations are archived for later reference. In a forum, users post topics and threads in nested hierarchies, allowing discussions to be categorized and grouped. The forum hierarchy consists of:') . '</p>';
       
    17       $output .= '<ul>';
       
    18       $output .= '<li>' . t('Optional containers (for example, <em>Support</em>), which can hold:') . '</li>';
       
    19       $output .= '<ul><li>' . t('Forums (for example, <em>Installing Drupal</em>), which can hold:') . '</li>';
       
    20       $output .= '<ul><li>' . t('Forum topics submitted by users (for example, <em>How to start a Drupal 6 Multisite</em>), which start discussions and are starting points for:') . '</li>';
       
    21       $output .= '<ul><li>' . t('Threaded comments submitted by users (for example, <em>You have these options...</em>).') . '</li>';
       
    22       $output .= '</ul>';
       
    23       $output .= '</ul>';
       
    24       $output .= '</ul>';
       
    25       $output .= '</ul>';
       
    26       $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@forum">Forum module</a>.', array('@forum' => 'http://drupal.org/documentation/modules/forum')) . '</p>';
       
    27       $output .= '<h3>' . t('Uses') . '</h3>';
       
    28       $output .= '<dl>';
       
    29       $output .= '<dt>' . t('Setting up forum structure') . '</dt>';
       
    30       $output .= '<dd>' . t('Visit the <a href="@forums">Forums page</a> to set up containers and forums to hold your discussion topics.', array('@forums' => url('admin/structure/forum'))) . '</dd>';
       
    31       $output .= '<dt>' . t('Starting a discussion') . '</dt>';
       
    32       $output .= '<dd>' . t('The <a href="@create-topic">Forum topic</a> link on the <a href="@content-add">Add new content</a> page creates the first post of a new threaded discussion, or thread.', array('@create-topic' => url('node/add/forum'), '@content-add' => url('node/add'))) . '</dd>';
       
    33       $output .= '<dt>' . t('Navigation') . '</dt>';
       
    34       $output .= '<dd>' . t('Enabling the Forum module provides a default <em>Forums</em> menu item in the navigation menu that links to the <a href="@forums">Forums page</a>.', array('@forums' => url('forum'))) . '</dd>';
       
    35       $output .= '<dt>' . t('Moving forum topics') . '</dt>';
       
    36       $output .= '<dd>' . t('A forum topic (and all of its comments) may be moved between forums by selecting a different forum while editing a forum topic. When moving a forum topic between forums, the <em>Leave shadow copy</em> option creates a link in the original forum pointing to the new location.') . '</dd>';
       
    37       $output .= '<dt>' . t('Locking and disabling comments') . '</dt>';
       
    38       $output .= '<dd>' . t('Selecting <em>Closed</em> under <em>Comment settings</em> while editing a forum topic will lock (prevent new comments on) the thread. Selecting <em>Hidden</em> under <em>Comment settings</em> while editing a forum topic will hide all existing comments on the thread, and prevent new ones.') . '</dd>';
       
    39       $output .= '</dl>';
       
    40       return $output;
       
    41     case 'admin/structure/forum':
       
    42       $output = '<p>' . t('Forums contain forum topics. Use containers to group related forums.') . '</p>';
       
    43       $output .= theme('more_help_link', array('url' => 'admin/help/forum'));
       
    44       return $output;
       
    45     case 'admin/structure/forum/add/container':
       
    46       return '<p>' . t('Use containers to group related forums.') . '</p>';
       
    47     case 'admin/structure/forum/add/forum':
       
    48       return '<p>' . t('A forum holds related forum topics.') . '</p>';
       
    49     case 'admin/structure/forum/settings':
       
    50       return '<p>' . t('Adjust the display of your forum topics. Organize the forums on the <a href="@forum-structure">forum structure page</a>.', array('@forum-structure' => url('admin/structure/forum'))) . '</p>';
       
    51   }
       
    52 }
       
    53 
       
    54 /**
       
    55  * Implements hook_theme().
       
    56  */
       
    57 function forum_theme() {
       
    58   return array(
       
    59     'forums' => array(
       
    60       'template' => 'forums',
       
    61       'variables' => array('forums' => NULL, 'topics' => NULL, 'parents' => NULL, 'tid' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
       
    62     ),
       
    63     'forum_list' => array(
       
    64       'template' => 'forum-list',
       
    65       'variables' => array('forums' => NULL, 'parents' => NULL, 'tid' => NULL),
       
    66     ),
       
    67     'forum_topic_list' => array(
       
    68       'template' => 'forum-topic-list',
       
    69       'variables' => array('tid' => NULL, 'topics' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
       
    70     ),
       
    71     'forum_icon' => array(
       
    72       'template' => 'forum-icon',
       
    73       'variables' => array('new_posts' => NULL, 'num_posts' => 0, 'comment_mode' => 0, 'sticky' => 0, 'first_new' => FALSE),
       
    74     ),
       
    75     'forum_submitted' => array(
       
    76       'template' => 'forum-submitted',
       
    77       'variables' => array('topic' => NULL),
       
    78     ),
       
    79     'forum_form' => array(
       
    80       'render element' => 'form',
       
    81       'file' => 'forum.admin.inc',
       
    82     ),
       
    83   );
       
    84 }
       
    85 
       
    86 /**
       
    87  * Implements hook_menu().
       
    88  */
       
    89 function forum_menu() {
       
    90   $items['forum'] = array(
       
    91     'title' => 'Forums',
       
    92     'page callback' => 'forum_page',
       
    93     'access arguments' => array('access content'),
       
    94     'file' => 'forum.pages.inc',
       
    95   );
       
    96   $items['forum/%forum_forum'] = array(
       
    97     'title' => 'Forums',
       
    98     'page callback' => 'forum_page',
       
    99     'page arguments' => array(1),
       
   100     'access arguments' => array('access content'),
       
   101     'file' => 'forum.pages.inc',
       
   102   );
       
   103   $items['admin/structure/forum'] = array(
       
   104     'title' => 'Forums',
       
   105     'description' => 'Control forum hierarchy settings.',
       
   106     'page callback' => 'drupal_get_form',
       
   107     'page arguments' => array('forum_overview'),
       
   108     'access arguments' => array('administer forums'),
       
   109     'file' => 'forum.admin.inc',
       
   110   );
       
   111   $items['admin/structure/forum/list'] = array(
       
   112     'title' => 'List',
       
   113     'type' => MENU_DEFAULT_LOCAL_TASK,
       
   114     'weight' => -10,
       
   115   );
       
   116   $items['admin/structure/forum/add/container'] = array(
       
   117     'title' => 'Add container',
       
   118     'page callback' => 'forum_form_main',
       
   119     'page arguments' => array('container'),
       
   120     'access arguments' => array('administer forums'),
       
   121     'type' => MENU_LOCAL_ACTION,
       
   122     'parent' => 'admin/structure/forum',
       
   123     'file' => 'forum.admin.inc',
       
   124   );
       
   125   $items['admin/structure/forum/add/forum'] = array(
       
   126     'title' => 'Add forum',
       
   127     'page callback' => 'forum_form_main',
       
   128     'page arguments' => array('forum'),
       
   129     'access arguments' => array('administer forums'),
       
   130     'type' => MENU_LOCAL_ACTION,
       
   131     'parent' => 'admin/structure/forum',
       
   132     'file' => 'forum.admin.inc',
       
   133   );
       
   134   $items['admin/structure/forum/settings'] = array(
       
   135     'title' => 'Settings',
       
   136     'page callback' => 'drupal_get_form',
       
   137     'page arguments' => array('forum_admin_settings'),
       
   138     'access arguments' => array('administer forums'),
       
   139     'weight' => 5,
       
   140     'type' => MENU_LOCAL_TASK,
       
   141     'parent' => 'admin/structure/forum',
       
   142     'file' => 'forum.admin.inc',
       
   143   );
       
   144   $items['admin/structure/forum/edit/container/%taxonomy_term'] = array(
       
   145     'title' => 'Edit container',
       
   146     'page callback' => 'forum_form_main',
       
   147     'page arguments' => array('container', 5),
       
   148     'access arguments' => array('administer forums'),
       
   149     'file' => 'forum.admin.inc',
       
   150   );
       
   151   $items['admin/structure/forum/edit/forum/%taxonomy_term'] = array(
       
   152     'title' => 'Edit forum',
       
   153     'page callback' => 'forum_form_main',
       
   154     'page arguments' => array('forum', 5),
       
   155     'access arguments' => array('administer forums'),
       
   156     'file' => 'forum.admin.inc',
       
   157   );
       
   158   return $items;
       
   159 }
       
   160 
       
   161 /**
       
   162  * Implements hook_menu_local_tasks_alter().
       
   163  */
       
   164 function forum_menu_local_tasks_alter(&$data, $router_item, $root_path) {
       
   165   global $user;
       
   166 
       
   167   // Add action link to 'node/add/forum' on 'forum' sub-pages.
       
   168   if ($root_path == 'forum' || $root_path == 'forum/%') {
       
   169     $tid = (isset($router_item['page_arguments'][0]) ? $router_item['page_arguments'][0]->tid : 0);
       
   170     $forum_term = forum_forum_load($tid);
       
   171     if ($forum_term) {
       
   172       $links = array();
       
   173       // Loop through all bundles for forum taxonomy vocabulary field.
       
   174       $field = field_info_field('taxonomy_forums');
       
   175       foreach ($field['bundles']['node'] as $type) {
       
   176         if (node_access('create', $type)) {
       
   177           $links[$type] = array(
       
   178             '#theme' => 'menu_local_action',
       
   179             '#link' => array(
       
   180               'title' => t('Add new @node_type', array('@node_type' => node_type_get_name($type))),
       
   181               'href' => 'node/add/' . str_replace('_', '-', $type) . '/' . $forum_term->tid,
       
   182             ),
       
   183           );
       
   184         }
       
   185       }
       
   186       if (empty($links)) {
       
   187         // Authenticated user does not have access to create new topics.
       
   188         if ($user->uid) {
       
   189           $links['disallowed'] = array(
       
   190             '#theme' => 'menu_local_action',
       
   191             '#link' => array(
       
   192               'title' => t('You are not allowed to post new content in the forum.'),
       
   193             ),
       
   194           );
       
   195         }
       
   196         // Anonymous user does not have access to create new topics.
       
   197         else {
       
   198           $links['login'] = array(
       
   199             '#theme' => 'menu_local_action',
       
   200             '#link' => array(
       
   201               'title' => t('<a href="@login">Log in</a> to post new content in the forum.', array(
       
   202                 '@login' => url('user/login', array('query' => drupal_get_destination())),
       
   203               )),
       
   204               'localized_options' => array('html' => TRUE),
       
   205             ),
       
   206           );
       
   207         }
       
   208       }
       
   209       $data['actions']['output'] = array_merge($data['actions']['output'], $links);
       
   210     }
       
   211   }
       
   212 }
       
   213 
       
   214 /**
       
   215  * Implements hook_entity_info_alter().
       
   216  */
       
   217 function forum_entity_info_alter(&$info) {
       
   218   // Take over URI construction for taxonomy terms that are forums.
       
   219   if ($vid = variable_get('forum_nav_vocabulary', 0)) {
       
   220     // Within hook_entity_info(), we can't invoke entity_load() as that would
       
   221     // cause infinite recursion, so we call taxonomy_vocabulary_get_names()
       
   222     // instead of taxonomy_vocabulary_load(). All we need is the machine name
       
   223     // of $vid, so retrieving and iterating all the vocabulary names is somewhat
       
   224     // inefficient, but entity info is cached across page requests, and an
       
   225     // iteration of all vocabularies once per cache clearing isn't a big deal,
       
   226     // and is done as part of taxonomy_entity_info() anyway.
       
   227     foreach (taxonomy_vocabulary_get_names() as $machine_name => $vocabulary) {
       
   228       if ($vid == $vocabulary->vid) {
       
   229         $info['taxonomy_term']['bundles'][$machine_name]['uri callback'] = 'forum_uri';
       
   230       }
       
   231     }
       
   232   }
       
   233 }
       
   234 
       
   235 /**
       
   236  * Implements callback_entity_info_uri().
       
   237  *
       
   238  * Entity URI callback used in forum_entity_info_alter().
       
   239  */
       
   240 function forum_uri($forum) {
       
   241   return array(
       
   242     'path' => 'forum/' . $forum->tid,
       
   243   );
       
   244 }
       
   245 
       
   246 /**
       
   247  * Checks whether a node can be used in a forum, based on its content type.
       
   248  *
       
   249  * @param $node
       
   250  *   A node object.
       
   251  *
       
   252  * @return
       
   253  *   Boolean indicating if the node can be assigned to a forum.
       
   254  */
       
   255 function _forum_node_check_node_type($node) {
       
   256   // Fetch information about the forum field.
       
   257   $field = field_info_instance('node', 'taxonomy_forums', $node->type);
       
   258 
       
   259   return is_array($field);
       
   260 }
       
   261 
       
   262 /**
       
   263  * Implements hook_node_view().
       
   264  */
       
   265 function forum_node_view($node, $view_mode) {
       
   266   if (_forum_node_check_node_type($node)) {
       
   267     if ($view_mode == 'full' && node_is_page($node)) {
       
   268       $vid = variable_get('forum_nav_vocabulary', 0);
       
   269       $vocabulary = taxonomy_vocabulary_load($vid);
       
   270       // Breadcrumb navigation
       
   271       $breadcrumb[] = l(t('Home'), NULL);
       
   272       $breadcrumb[] = l($vocabulary->name, 'forum');
       
   273       if ($parents = taxonomy_get_parents_all($node->forum_tid)) {
       
   274         $parents = array_reverse($parents);
       
   275         foreach ($parents as $parent) {
       
   276           $breadcrumb[] = l($parent->name, 'forum/' . $parent->tid);
       
   277         }
       
   278       }
       
   279       drupal_set_breadcrumb($breadcrumb);
       
   280 
       
   281     }
       
   282   }
       
   283 }
       
   284 
       
   285 /**
       
   286  * Implements hook_node_validate().
       
   287  *
       
   288  * Checks in particular that the node is assigned only a "leaf" term in the
       
   289  * forum taxonomy.
       
   290  */
       
   291 function forum_node_validate($node, $form) {
       
   292   if (_forum_node_check_node_type($node)) {
       
   293     $langcode = $form['taxonomy_forums']['#language'];
       
   294     // vocabulary is selected, not a "container" term.
       
   295     if (!empty($node->taxonomy_forums[$langcode])) {
       
   296       // Extract the node's proper topic ID.
       
   297       $containers = variable_get('forum_containers', array());
       
   298       foreach ($node->taxonomy_forums[$langcode] as $delta => $item) {
       
   299         // If no term was selected (e.g. when no terms exist yet), remove the
       
   300         // item.
       
   301         if (empty($item['tid'])) {
       
   302           unset($node->taxonomy_forums[$langcode][$delta]);
       
   303           continue;
       
   304         }
       
   305         $term = taxonomy_term_load($item['tid']);
       
   306         if (!$term) {
       
   307           form_set_error('taxonomy_forums', t('Select a forum.'));
       
   308           continue;
       
   309         }
       
   310         $used = db_query_range('SELECT 1 FROM {taxonomy_term_data} WHERE tid = :tid AND vid = :vid',0 , 1, array(
       
   311           ':tid' => $term->tid,
       
   312           ':vid' => $term->vid,
       
   313         ))->fetchField();
       
   314         if ($used && in_array($term->tid, $containers)) {
       
   315           form_set_error('taxonomy_forums', t('The item %forum is a forum container, not a forum. Select one of the forums below instead.', array('%forum' => $term->name)));
       
   316         }
       
   317       }
       
   318     }
       
   319   }
       
   320 }
       
   321 
       
   322 /**
       
   323  * Implements hook_node_presave().
       
   324  *
       
   325  * Assigns the forum taxonomy when adding a topic from within a forum.
       
   326  */
       
   327 function forum_node_presave($node) {
       
   328   if (_forum_node_check_node_type($node)) {
       
   329     // Make sure all fields are set properly:
       
   330     $node->icon = !empty($node->icon) ? $node->icon : '';
       
   331     reset($node->taxonomy_forums);
       
   332     $langcode = key($node->taxonomy_forums);
       
   333     if (!empty($node->taxonomy_forums[$langcode])) {
       
   334       $node->forum_tid = $node->taxonomy_forums[$langcode][0]['tid'];
       
   335       if (isset($node->nid)) {
       
   336         $old_tid = db_query_range("SELECT f.tid FROM {forum} f INNER JOIN {node} n ON f.vid = n.vid WHERE n.nid = :nid ORDER BY f.vid DESC", 0, 1, array(':nid' => $node->nid))->fetchField();
       
   337         if ($old_tid && isset($node->forum_tid) && ($node->forum_tid != $old_tid) && !empty($node->shadow)) {
       
   338           // A shadow copy needs to be created. Retain new term and add old term.
       
   339           $node->taxonomy_forums[$langcode][] = array('tid' => $old_tid);
       
   340         }
       
   341       }
       
   342     }
       
   343   }
       
   344 }
       
   345 
       
   346 /**
       
   347  * Implements hook_node_update().
       
   348  */
       
   349 function forum_node_update($node) {
       
   350   if (_forum_node_check_node_type($node)) {
       
   351     if (empty($node->revision) && db_query('SELECT tid FROM {forum} WHERE nid=:nid', array(':nid' => $node->nid))->fetchField()) {
       
   352       if (!empty($node->forum_tid)) {
       
   353         db_update('forum')
       
   354           ->fields(array('tid' => $node->forum_tid))
       
   355           ->condition('vid', $node->vid)
       
   356           ->execute();
       
   357       }
       
   358       // The node is removed from the forum.
       
   359       else {
       
   360         db_delete('forum')
       
   361           ->condition('nid', $node->nid)
       
   362           ->execute();
       
   363       }
       
   364     }
       
   365     else {
       
   366       if (!empty($node->forum_tid)) {
       
   367         db_insert('forum')
       
   368           ->fields(array(
       
   369             'tid' => $node->forum_tid,
       
   370             'vid' => $node->vid,
       
   371             'nid' => $node->nid,
       
   372           ))
       
   373           ->execute();
       
   374       }
       
   375     }
       
   376     // If the node has a shadow forum topic, update the record for this
       
   377     // revision.
       
   378     if (!empty($node->shadow)) {
       
   379       db_delete('forum')
       
   380         ->condition('nid', $node->nid)
       
   381         ->condition('vid', $node->vid)
       
   382         ->execute();
       
   383       db_insert('forum')
       
   384         ->fields(array(
       
   385           'nid' => $node->nid,
       
   386           'vid' => $node->vid,
       
   387           'tid' => $node->forum_tid,
       
   388         ))
       
   389         ->execute();
       
   390      }
       
   391   }
       
   392 }
       
   393 
       
   394 /**
       
   395  * Implements hook_node_insert().
       
   396  */
       
   397 function forum_node_insert($node) {
       
   398   if (_forum_node_check_node_type($node)) {
       
   399     if (!empty($node->forum_tid)) {
       
   400       $nid = db_insert('forum')
       
   401         ->fields(array(
       
   402           'tid' => $node->forum_tid,
       
   403           'vid' => $node->vid,
       
   404           'nid' => $node->nid,
       
   405         ))
       
   406         ->execute();
       
   407     }
       
   408   }
       
   409 }
       
   410 
       
   411 /**
       
   412  * Implements hook_node_delete().
       
   413  */
       
   414 function forum_node_delete($node) {
       
   415   if (_forum_node_check_node_type($node)) {
       
   416     db_delete('forum')
       
   417       ->condition('nid', $node->nid)
       
   418       ->execute();
       
   419     db_delete('forum_index')
       
   420       ->condition('nid', $node->nid)
       
   421       ->execute();
       
   422   }
       
   423 }
       
   424 
       
   425 /**
       
   426  * Implements hook_node_load().
       
   427  */
       
   428 function forum_node_load($nodes) {
       
   429   $node_vids = array();
       
   430   foreach ($nodes as $node) {
       
   431     if (_forum_node_check_node_type($node)) {
       
   432       $node_vids[] = $node->vid;
       
   433     }
       
   434   }
       
   435   if (!empty($node_vids)) {
       
   436     $query = db_select('forum', 'f');
       
   437     $query
       
   438       ->fields('f', array('nid', 'tid'))
       
   439       ->condition('f.vid', $node_vids);
       
   440     $result = $query->execute();
       
   441     foreach ($result as $record) {
       
   442       $nodes[$record->nid]->forum_tid = $record->tid;
       
   443     }
       
   444   }
       
   445 }
       
   446 
       
   447 /**
       
   448  * Implements hook_node_info().
       
   449  */
       
   450 function forum_node_info() {
       
   451   return array(
       
   452     'forum' => array(
       
   453       'name' => t('Forum topic'),
       
   454       'base' => 'forum',
       
   455       'description' => t('A <em>forum topic</em> starts a new discussion thread within a forum.'),
       
   456       'title_label' => t('Subject'),
       
   457     )
       
   458   );
       
   459 }
       
   460 
       
   461 /**
       
   462  * Implements hook_permission().
       
   463  */
       
   464 function forum_permission() {
       
   465   $perms = array(
       
   466     'administer forums' => array(
       
   467       'title' => t('Administer forums'),
       
   468     ),
       
   469   );
       
   470   return $perms;
       
   471 }
       
   472 
       
   473 /**
       
   474  * Implements hook_taxonomy_term_delete().
       
   475  */
       
   476 function forum_taxonomy_term_delete($term) {
       
   477   // For containers, remove the tid from the forum_containers variable.
       
   478   $containers = variable_get('forum_containers', array());
       
   479   $key = array_search($term->tid, $containers);
       
   480   if ($key !== FALSE) {
       
   481     unset($containers[$key]);
       
   482   }
       
   483   variable_set('forum_containers', $containers);
       
   484 }
       
   485 
       
   486 /**
       
   487  * Implements hook_comment_publish().
       
   488  *
       
   489  * This actually handles the insertion and update of published nodes since
       
   490  * comment_save() calls hook_comment_publish() for all published comments.
       
   491  */
       
   492 function forum_comment_publish($comment) {
       
   493   _forum_update_forum_index($comment->nid);
       
   494 }
       
   495 
       
   496 /**
       
   497  * Implements hook_comment_update().
       
   498  *
       
   499  * The Comment module doesn't call hook_comment_unpublish() when saving
       
   500  * individual comments, so we need to check for those here.
       
   501  */
       
   502 function forum_comment_update($comment) {
       
   503   // comment_save() calls hook_comment_publish() for all published comments,
       
   504   // so we need to handle all other values here.
       
   505   if (!$comment->status) {
       
   506     _forum_update_forum_index($comment->nid);
       
   507   }
       
   508 }
       
   509 
       
   510 /**
       
   511  * Implements hook_comment_unpublish().
       
   512  */
       
   513 function forum_comment_unpublish($comment) {
       
   514   _forum_update_forum_index($comment->nid);
       
   515 }
       
   516 
       
   517 /**
       
   518  * Implements hook_comment_delete().
       
   519  */
       
   520 function forum_comment_delete($comment) {
       
   521   _forum_update_forum_index($comment->nid);
       
   522 }
       
   523 
       
   524 /**
       
   525  * Implements hook_field_storage_pre_insert().
       
   526  */
       
   527 function forum_field_storage_pre_insert($entity_type, $entity, &$skip_fields) {
       
   528   if ($entity_type == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
       
   529     $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
       
   530     foreach ($entity->taxonomy_forums as $language) {
       
   531       foreach ($language as $item) {
       
   532         $query->values(array(
       
   533           'nid' => $entity->nid,
       
   534           'title' => $entity->title,
       
   535           'tid' => $item['tid'],
       
   536           'sticky' => $entity->sticky,
       
   537           'created' => $entity->created,
       
   538           'comment_count' => 0,
       
   539           'last_comment_timestamp' => $entity->created,
       
   540         ));
       
   541       }
       
   542     }
       
   543     $query->execute();
       
   544   }
       
   545 }
       
   546 
       
   547 /**
       
   548  * Implements hook_field_storage_pre_update().
       
   549  */
       
   550 function forum_field_storage_pre_update($entity_type, $entity, &$skip_fields) {
       
   551   $first_call = &drupal_static(__FUNCTION__, array());
       
   552 
       
   553   if ($entity_type == 'node' && _forum_node_check_node_type($entity)) {
       
   554 
       
   555     // If the node is published, update the forum index.
       
   556     if ($entity->status) {
       
   557 
       
   558       // We don't maintain data for old revisions, so clear all previous values
       
   559       // from the table. Since this hook runs once per field, per object, make
       
   560       // sure we only wipe values once.
       
   561       if (!isset($first_call[$entity->nid])) {
       
   562         $first_call[$entity->nid] = FALSE;
       
   563         db_delete('forum_index')->condition('nid', $entity->nid)->execute();
       
   564       }
       
   565       $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
       
   566       foreach ($entity->taxonomy_forums as $language) {
       
   567         foreach ($language as $item) {
       
   568           $query->values(array(
       
   569             'nid' => $entity->nid,
       
   570             'title' => $entity->title,
       
   571             'tid' => $item['tid'],
       
   572             'sticky' => $entity->sticky,
       
   573             'created' => $entity->created,
       
   574             'comment_count' => 0,
       
   575             'last_comment_timestamp' => $entity->created,
       
   576           ));
       
   577         }
       
   578       }
       
   579       $query->execute();
       
   580       // The logic for determining last_comment_count is fairly complex, so
       
   581       // call _forum_update_forum_index() too.
       
   582       _forum_update_forum_index($entity->nid);
       
   583     }
       
   584 
       
   585     // When a forum node is unpublished, remove it from the forum_index table.
       
   586     else {
       
   587       db_delete('forum_index')->condition('nid', $entity->nid)->execute();
       
   588     }
       
   589 
       
   590   }
       
   591 }
       
   592 
       
   593 /**
       
   594  * Implements hook_form_FORM_ID_alter() for taxonomy_form_vocabulary().
       
   595  */
       
   596 function forum_form_taxonomy_form_vocabulary_alter(&$form, &$form_state, $form_id) {
       
   597   $vid = variable_get('forum_nav_vocabulary', 0);
       
   598   if (isset($form['vid']['#value']) && $form['vid']['#value'] == $vid) {
       
   599     $form['help_forum_vocab'] = array(
       
   600       '#markup' => t('This is the designated forum vocabulary. Some of the normal vocabulary options have been removed.'),
       
   601       '#weight' => -1,
       
   602     );
       
   603     // Forum's vocabulary always has single hierarchy. Forums and containers
       
   604     // have only one parent or no parent for root items. By default this value
       
   605     // is 0.
       
   606     $form['hierarchy']['#value'] = 1;
       
   607     // Do not allow to delete forum's vocabulary.
       
   608     $form['actions']['delete']['#access'] = FALSE;
       
   609   }
       
   610 }
       
   611 
       
   612 /**
       
   613  * Implements hook_form_FORM_ID_alter() for taxonomy_form_term().
       
   614  */
       
   615 function forum_form_taxonomy_form_term_alter(&$form, &$form_state, $form_id) {
       
   616    $vid = variable_get('forum_nav_vocabulary', 0);
       
   617    if (isset($form['vid']['#value']) && $form['vid']['#value'] == $vid) {
       
   618     // Hide multiple parents select from forum terms.
       
   619     $form['relations']['parent']['#access'] = FALSE;
       
   620   }
       
   621 }
       
   622 
       
   623 /**
       
   624  * Implements hook_form_BASE_FORM_ID_alter() for node_form().
       
   625  */
       
   626 function forum_form_node_form_alter(&$form, &$form_state, $form_id) {
       
   627   if (isset($form['taxonomy_forums'])) {
       
   628     $langcode = $form['taxonomy_forums']['#language'];
       
   629     // Make the vocabulary required for 'real' forum-nodes.
       
   630     $form['taxonomy_forums'][$langcode]['#required'] = TRUE;
       
   631     $form['taxonomy_forums'][$langcode]['#multiple'] = FALSE;
       
   632     if (empty($form['taxonomy_forums'][$langcode]['#default_value'])) {
       
   633       // If there is no default forum already selected, try to get the forum
       
   634       // ID from the URL (e.g., if we are on a page like node/add/forum/2, we
       
   635       // expect "2" to be the ID of the forum that was requested).
       
   636       $requested_forum_id = arg(3);
       
   637       $form['taxonomy_forums'][$langcode]['#default_value'] = is_numeric($requested_forum_id) ? $requested_forum_id : '';
       
   638     }
       
   639   }
       
   640 }
       
   641 
       
   642 /**
       
   643  * Implements hook_block_info().
       
   644  */
       
   645 function forum_block_info() {
       
   646   $blocks['active'] = array(
       
   647     'info' => t('Active forum topics'),
       
   648     'cache' => DRUPAL_CACHE_CUSTOM,
       
   649     'properties' => array('administrative' => TRUE),
       
   650   );
       
   651   $blocks['new'] = array(
       
   652     'info' => t('New forum topics'),
       
   653     'cache' => DRUPAL_CACHE_CUSTOM,
       
   654     'properties' => array('administrative' => TRUE),
       
   655   );
       
   656   return $blocks;
       
   657 }
       
   658 
       
   659 /**
       
   660  * Implements hook_block_configure().
       
   661  */
       
   662 function forum_block_configure($delta = '') {
       
   663   $form['forum_block_num_' . $delta] = array(
       
   664     '#type' => 'select',
       
   665     '#title' => t('Number of topics'),
       
   666     '#default_value' => variable_get('forum_block_num_' . $delta, '5'),
       
   667     '#options' => drupal_map_assoc(range(2, 20))
       
   668   );
       
   669   return $form;
       
   670 }
       
   671 
       
   672 /**
       
   673  * Implements hook_block_save().
       
   674  */
       
   675 function forum_block_save($delta = '', $edit = array()) {
       
   676   variable_set('forum_block_num_' . $delta, $edit['forum_block_num_' . $delta]);
       
   677 }
       
   678 
       
   679 /**
       
   680  * Implements hook_block_view().
       
   681  *
       
   682  * Generates a block containing the currently active forum topics and the most
       
   683  * recently added forum topics.
       
   684  */
       
   685 function forum_block_view($delta = '') {
       
   686   $query = db_select('forum_index', 'f')
       
   687     ->fields('f')
       
   688     ->addTag('node_access');
       
   689   switch ($delta) {
       
   690     case 'active':
       
   691       $title = t('Active forum topics');
       
   692       $query
       
   693         ->orderBy('f.last_comment_timestamp', 'DESC')
       
   694         ->range(0, variable_get('forum_block_num_active', '5'));
       
   695       break;
       
   696 
       
   697     case 'new':
       
   698       $title = t('New forum topics');
       
   699       $query
       
   700         ->orderBy('f.created', 'DESC')
       
   701         ->range(0, variable_get('forum_block_num_new', '5'));
       
   702       break;
       
   703   }
       
   704 
       
   705   $block['subject'] = $title;
       
   706   // Cache based on the altered query. Enables us to cache with node access enabled.
       
   707   $block['content'] = drupal_render_cache_by_query($query, 'forum_block_view');
       
   708   $block['content']['#access'] = user_access('access content');
       
   709   return $block;
       
   710 }
       
   711 
       
   712 /**
       
   713  * Render API callback: Lists nodes based on the element's #query property.
       
   714  *
       
   715  * This function can be used as a #pre_render callback.
       
   716  *
       
   717  * @see forum_block_view()
       
   718  */
       
   719 function forum_block_view_pre_render($elements) {
       
   720   $result = $elements['#query']->execute();
       
   721   if ($node_title_list = node_title_list($result)) {
       
   722     $elements['forum_list'] = $node_title_list;
       
   723     $elements['forum_more'] = array('#theme' => 'more_link', '#url' => 'forum', '#title' => t('Read the latest forum topics.'));
       
   724   }
       
   725   return $elements;
       
   726 }
       
   727 
       
   728 /**
       
   729  * Implements hook_form().
       
   730  */
       
   731 function forum_form($node, $form_state) {
       
   732   $type = node_type_get_type($node);
       
   733   $form['title'] = array(
       
   734     '#type' => 'textfield',
       
   735     '#title' => check_plain($type->title_label),
       
   736     '#default_value' => !empty($node->title) ? $node->title : '',
       
   737     '#required' => TRUE, '#weight' => -5
       
   738   );
       
   739 
       
   740   if (!empty($node->nid)) {
       
   741     $forum_terms = $node->taxonomy_forums;
       
   742     // If editing, give option to leave shadows.
       
   743     $shadow = (count($forum_terms) > 1);
       
   744     $form['shadow'] = array('#type' => 'checkbox', '#title' => t('Leave shadow copy'), '#default_value' => $shadow, '#description' => t('If you move this topic, you can leave a link in the old forum to the new forum.'));
       
   745     $form['forum_tid'] = array('#type' => 'value', '#value' => $node->forum_tid);
       
   746   }
       
   747 
       
   748   return $form;
       
   749 }
       
   750 
       
   751 /**
       
   752  * Returns a tree of all forums for a given taxonomy term ID.
       
   753  *
       
   754  * @param $tid
       
   755  *   (optional) Taxonomy term ID of the forum. If not given all forums will be
       
   756  *   returned.
       
   757  *
       
   758  * @return
       
   759  *   A tree of taxonomy objects, with the following additional properties:
       
   760  *   - num_topics: Number of topics in the forum.
       
   761  *   - num_posts: Total number of posts in all topics.
       
   762  *   - last_post: Most recent post for the forum.
       
   763  *   - forums: An array of child forums.
       
   764  */
       
   765 function forum_forum_load($tid = NULL) {
       
   766   $cache = &drupal_static(__FUNCTION__, array());
       
   767 
       
   768   // Return a cached forum tree if available.
       
   769   if (!isset($tid)) {
       
   770     $tid = 0;
       
   771   }
       
   772   if (isset($cache[$tid])) {
       
   773     return $cache[$tid];
       
   774   }
       
   775 
       
   776   $vid = variable_get('forum_nav_vocabulary', 0);
       
   777 
       
   778   // Load and validate the parent term.
       
   779   if ($tid) {
       
   780     $forum_term = taxonomy_term_load($tid);
       
   781     if (!$forum_term || ($forum_term->vid != $vid)) {
       
   782       return $cache[$tid] = FALSE;
       
   783     }
       
   784   }
       
   785   // If $tid is 0, create an empty object to hold the child terms.
       
   786   elseif ($tid === 0) {
       
   787     $forum_term = (object) array(
       
   788       'tid' => 0,
       
   789     );
       
   790   }
       
   791 
       
   792   // Determine if the requested term is a container.
       
   793   if (!$forum_term->tid || in_array($forum_term->tid, variable_get('forum_containers', array()))) {
       
   794     $forum_term->container = 1;
       
   795   }
       
   796 
       
   797   // Load parent terms.
       
   798   $forum_term->parents = taxonomy_get_parents_all($forum_term->tid);
       
   799 
       
   800   // Load the tree below.
       
   801   $forums = array();
       
   802   $_forums = taxonomy_get_tree($vid, $tid);
       
   803 
       
   804   if (count($_forums)) {
       
   805     $query = db_select('node', 'n');
       
   806     $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
       
   807     $query->join('forum', 'f', 'n.vid = f.vid');
       
   808     $query->addExpression('COUNT(n.nid)', 'topic_count');
       
   809     $query->addExpression('SUM(ncs.comment_count)', 'comment_count');
       
   810     $counts = $query
       
   811       ->fields('f', array('tid'))
       
   812       ->condition('n.status', 1)
       
   813       ->groupBy('tid')
       
   814       ->addTag('node_access')
       
   815       ->execute()
       
   816       ->fetchAllAssoc('tid');
       
   817   }
       
   818 
       
   819   foreach ($_forums as $forum) {
       
   820     // Determine if the child term is a container.
       
   821     if (in_array($forum->tid, variable_get('forum_containers', array()))) {
       
   822       $forum->container = 1;
       
   823     }
       
   824 
       
   825     // Merge in the topic and post counters.
       
   826     if (!empty($counts[$forum->tid])) {
       
   827       $forum->num_topics = $counts[$forum->tid]->topic_count;
       
   828       $forum->num_posts = $counts[$forum->tid]->topic_count + $counts[$forum->tid]->comment_count;
       
   829     }
       
   830     else {
       
   831       $forum->num_topics = 0;
       
   832       $forum->num_posts = 0;
       
   833     }
       
   834 
       
   835     // Query "Last Post" information for this forum.
       
   836     $query = db_select('node', 'n');
       
   837     $query->join('users', 'u1', 'n.uid = u1.uid');
       
   838     $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $forum->tid));
       
   839     $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
       
   840     $query->join('users', 'u2', 'ncs.last_comment_uid = u2.uid');
       
   841     $query->addExpression('CASE ncs.last_comment_uid WHEN 0 THEN ncs.last_comment_name ELSE u2.name END', 'last_comment_name');
       
   842 
       
   843     $topic = $query
       
   844       ->fields('ncs', array('last_comment_timestamp', 'last_comment_uid'))
       
   845       ->condition('n.status', 1)
       
   846       ->orderBy('last_comment_timestamp', 'DESC')
       
   847       ->range(0, 1)
       
   848       ->addTag('node_access')
       
   849       ->execute()
       
   850       ->fetchObject();
       
   851 
       
   852     // Merge in the "Last Post" information.
       
   853     $last_post = new stdClass();
       
   854     if (!empty($topic->last_comment_timestamp)) {
       
   855       $last_post->created = $topic->last_comment_timestamp;
       
   856       $last_post->name = $topic->last_comment_name;
       
   857       $last_post->uid = $topic->last_comment_uid;
       
   858     }
       
   859     $forum->last_post = $last_post;
       
   860 
       
   861     $forums[$forum->tid] = $forum;
       
   862   }
       
   863 
       
   864   // Cache the result, and return the tree.
       
   865   $forum_term->forums = $forums;
       
   866   $cache[$tid] = $forum_term;
       
   867   return $forum_term;
       
   868 }
       
   869 
       
   870 /**
       
   871  * Calculates the number of new posts in a forum that the user has not yet read.
       
   872  *
       
   873  * Nodes are new if they are newer than NODE_NEW_LIMIT.
       
   874  *
       
   875  * @param $term
       
   876  *   The term ID of the forum.
       
   877  * @param $uid
       
   878  *   The user ID.
       
   879  *
       
   880  * @return
       
   881  *   The number of new posts in the forum that have not been read by the user.
       
   882  */
       
   883 function _forum_topics_unread($term, $uid) {
       
   884   $query = db_select('node', 'n');
       
   885   $query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $term));
       
   886   $query->leftJoin('history', 'h', 'n.nid = h.nid AND h.uid = :uid', array(':uid' => $uid));
       
   887   $query->addExpression('COUNT(n.nid)', 'count');
       
   888   return $query
       
   889     ->condition('status', 1)
       
   890     ->condition('n.created', NODE_NEW_LIMIT, '>')
       
   891     ->isNull('h.nid')
       
   892     ->addTag('node_access')
       
   893     ->execute()
       
   894     ->fetchField();
       
   895 }
       
   896 
       
   897 /**
       
   898  * Gets all the topics in a forum.
       
   899  *
       
   900  * @param $tid
       
   901  *   The term ID of the forum.
       
   902  * @param $sortby
       
   903  *   One of the following integers indicating the sort criteria:
       
   904  *   - 1: Date - newest first.
       
   905  *   - 2: Date - oldest first.
       
   906  *   - 3: Posts with the most comments first.
       
   907  *   - 4: Posts with the least comments first.
       
   908  * @param $forum_per_page
       
   909  *   The maximum number of topics to display per page.
       
   910  *
       
   911  * @return
       
   912  *   A list of all the topics in a forum.
       
   913  */
       
   914 function forum_get_topics($tid, $sortby, $forum_per_page) {
       
   915   global $user, $forum_topic_list_header;
       
   916 
       
   917   $forum_topic_list_header = array(
       
   918     NULL,
       
   919     array('data' => t('Topic'), 'field' => 'f.title'),
       
   920     array('data' => t('Replies'), 'field' => 'f.comment_count'),
       
   921     array('data' => t('Last reply'), 'field' => 'f.last_comment_timestamp'),
       
   922   );
       
   923 
       
   924   $order = _forum_get_topic_order($sortby);
       
   925   for ($i = 0; $i < count($forum_topic_list_header); $i++) {
       
   926     if ($forum_topic_list_header[$i]['field'] == $order['field']) {
       
   927       $forum_topic_list_header[$i]['sort'] = $order['sort'];
       
   928     }
       
   929   }
       
   930 
       
   931   $query = db_select('forum_index', 'f')->extend('PagerDefault')->extend('TableSort');
       
   932   $query->fields('f');
       
   933   $query
       
   934     ->condition('f.tid', $tid)
       
   935     ->addTag('node_access')
       
   936     ->orderBy('f.sticky', 'DESC')
       
   937     ->orderByHeader($forum_topic_list_header)
       
   938     ->limit($forum_per_page);
       
   939 
       
   940   $count_query = db_select('forum_index', 'f');
       
   941   $count_query->condition('f.tid', $tid);
       
   942   $count_query->addExpression('COUNT(*)');
       
   943   $count_query->addTag('node_access');
       
   944 
       
   945   $query->setCountQuery($count_query);
       
   946   $result = $query->execute();
       
   947   $nids = array();
       
   948   foreach ($result as $record) {
       
   949     $nids[] = $record->nid;
       
   950   }
       
   951   if ($nids) {
       
   952     $query = db_select('node', 'n')->extend('TableSort');
       
   953     $query->fields('n', array('title', 'nid', 'type', 'sticky', 'created', 'uid'));
       
   954     $query->addField('n', 'comment', 'comment_mode');
       
   955 
       
   956     $query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
       
   957     $query->fields('ncs', array('cid', 'last_comment_uid', 'last_comment_timestamp', 'comment_count'));
       
   958 
       
   959     $query->join('forum_index', 'f', 'f.nid = ncs.nid');
       
   960     $query->addField('f', 'tid', 'forum_tid');
       
   961 
       
   962     $query->join('users', 'u', 'n.uid = u.uid');
       
   963     $query->addField('u', 'name');
       
   964 
       
   965     $query->join('users', 'u2', 'ncs.last_comment_uid = u2.uid');
       
   966 
       
   967     $query->addExpression('CASE ncs.last_comment_uid WHEN 0 THEN ncs.last_comment_name ELSE u2.name END', 'last_comment_name');
       
   968 
       
   969     $query
       
   970       ->orderBy('f.sticky', 'DESC')
       
   971       ->orderByHeader($forum_topic_list_header)
       
   972       ->condition('n.nid', $nids);
       
   973 
       
   974     $result = $query->execute();
       
   975   }
       
   976   else {
       
   977     $result = array();
       
   978   }
       
   979 
       
   980   $topics = array();
       
   981   $first_new_found = FALSE;
       
   982   foreach ($result as $topic) {
       
   983     if ($user->uid) {
       
   984       // A forum is new if the topic is new, or if there are new comments since
       
   985       // the user's last visit.
       
   986       if ($topic->forum_tid != $tid) {
       
   987         $topic->new = 0;
       
   988       }
       
   989       else {
       
   990         $history = _forum_user_last_visit($topic->nid);
       
   991         $topic->new_replies = comment_num_new($topic->nid, $history);
       
   992         $topic->new = $topic->new_replies || ($topic->last_comment_timestamp > $history);
       
   993       }
       
   994     }
       
   995     else {
       
   996       // Do not track "new replies" status for topics if the user is anonymous.
       
   997       $topic->new_replies = 0;
       
   998       $topic->new = 0;
       
   999     }
       
  1000 
       
  1001     // Make sure only one topic is indicated as the first new topic.
       
  1002     $topic->first_new = FALSE;
       
  1003     if ($topic->new != 0 && !$first_new_found) {
       
  1004       $topic->first_new = TRUE;
       
  1005       $first_new_found = TRUE;
       
  1006     }
       
  1007 
       
  1008     if ($topic->comment_count > 0) {
       
  1009       $last_reply = new stdClass();
       
  1010       $last_reply->created = $topic->last_comment_timestamp;
       
  1011       $last_reply->name = $topic->last_comment_name;
       
  1012       $last_reply->uid = $topic->last_comment_uid;
       
  1013       $topic->last_reply = $last_reply;
       
  1014     }
       
  1015     $topics[] = $topic;
       
  1016   }
       
  1017 
       
  1018   return $topics;
       
  1019 }
       
  1020 
       
  1021 /**
       
  1022  * Preprocesses variables for forums.tpl.php.
       
  1023  *
       
  1024  * @param $variables
       
  1025  *   An array containing the following elements:
       
  1026  *   - forums: An array of all forum objects to display for the given taxonomy
       
  1027  *     term ID. If tid = 0 then all the top-level forums are displayed.
       
  1028  *   - topics: An array of all the topics in the current forum.
       
  1029  *   - parents: An array of taxonomy term objects that are ancestors of the
       
  1030  *     current term ID.
       
  1031  *   - tid: Taxonomy term ID of the current forum.
       
  1032  *   - sortby: One of the following integers indicating the sort criteria:
       
  1033  *     - 1: Date - newest first.
       
  1034  *     - 2: Date - oldest first.
       
  1035  *     - 3: Posts with the most comments first.
       
  1036  *     - 4: Posts with the least comments first.
       
  1037  *   - forum_per_page: The maximum number of topics to display per page.
       
  1038  *
       
  1039  * @see forums.tpl.php
       
  1040  */
       
  1041 function template_preprocess_forums(&$variables) {
       
  1042   global $user;
       
  1043 
       
  1044   $vid = variable_get('forum_nav_vocabulary', 0);
       
  1045   $vocabulary = taxonomy_vocabulary_load($vid);
       
  1046   $title = !empty($vocabulary->name) ? $vocabulary->name : '';
       
  1047 
       
  1048   // Breadcrumb navigation:
       
  1049   $breadcrumb[] = l(t('Home'), NULL);
       
  1050   if ($variables['tid']) {
       
  1051     $breadcrumb[] = l($vocabulary->name, 'forum');
       
  1052   }
       
  1053   if ($variables['parents']) {
       
  1054     $variables['parents'] = array_reverse($variables['parents']);
       
  1055     foreach ($variables['parents'] as $p) {
       
  1056       if ($p->tid == $variables['tid']) {
       
  1057         $title = $p->name;
       
  1058       }
       
  1059       else {
       
  1060         $breadcrumb[] = l($p->name, 'forum/' . $p->tid);
       
  1061       }
       
  1062     }
       
  1063   }
       
  1064   drupal_set_breadcrumb($breadcrumb);
       
  1065   drupal_set_title($title);
       
  1066 
       
  1067   if ($variables['forums_defined'] = count($variables['forums']) || count($variables['parents'])) {
       
  1068     if (!empty($variables['forums'])) {
       
  1069       $variables['forums'] = theme('forum_list', $variables);
       
  1070     }
       
  1071     else {
       
  1072       $variables['forums'] = '';
       
  1073     }
       
  1074 
       
  1075     if ($variables['tid'] && !in_array($variables['tid'], variable_get('forum_containers', array()))) {
       
  1076       $variables['topics'] = theme('forum_topic_list', $variables);
       
  1077       drupal_add_feed('taxonomy/term/' . $variables['tid'] . '/feed', 'RSS - ' . $title);
       
  1078     }
       
  1079     else {
       
  1080       $variables['topics'] = '';
       
  1081     }
       
  1082 
       
  1083     // Provide separate template suggestions based on what's being output. Topic id is also accounted for.
       
  1084     // Check both variables to be safe then the inverse. Forums with topic ID's take precedence.
       
  1085     if ($variables['forums'] && !$variables['topics']) {
       
  1086       $variables['theme_hook_suggestions'][] = 'forums__containers';
       
  1087       $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid'];
       
  1088       $variables['theme_hook_suggestions'][] = 'forums__containers__' . $variables['tid'];
       
  1089     }
       
  1090     elseif (!$variables['forums'] && $variables['topics']) {
       
  1091       $variables['theme_hook_suggestions'][] = 'forums__topics';
       
  1092       $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid'];
       
  1093       $variables['theme_hook_suggestions'][] = 'forums__topics__' . $variables['tid'];
       
  1094     }
       
  1095     else {
       
  1096       $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid'];
       
  1097     }
       
  1098 
       
  1099   }
       
  1100   else {
       
  1101     drupal_set_title(t('No forums defined'));
       
  1102     $variables['forums'] = '';
       
  1103     $variables['topics'] = '';
       
  1104   }
       
  1105 }
       
  1106 
       
  1107 /**
       
  1108  * Preprocesses variables for forum-list.tpl.php.
       
  1109  *
       
  1110  * @param $variables
       
  1111  *   An array containing the following elements:
       
  1112  *   - forums: An array of all forum objects to display for the given taxonomy
       
  1113  *     term ID. If tid = 0 then all the top-level forums are displayed.
       
  1114  *   - parents: An array of taxonomy term objects that are ancestors of the
       
  1115  *     current term ID.
       
  1116  *   - tid: Taxonomy term ID of the current forum.
       
  1117  *
       
  1118  * @see forum-list.tpl.php
       
  1119  * @see theme_forum_list()
       
  1120  */
       
  1121 function template_preprocess_forum_list(&$variables) {
       
  1122   global $user;
       
  1123   $row = 0;
       
  1124   // Sanitize each forum so that the template can safely print the data.
       
  1125   foreach ($variables['forums'] as $id => $forum) {
       
  1126     $variables['forums'][$id]->description = !empty($forum->description) ? filter_xss_admin($forum->description) : '';
       
  1127     $variables['forums'][$id]->link = url("forum/$forum->tid");
       
  1128     $variables['forums'][$id]->name = check_plain($forum->name);
       
  1129     $variables['forums'][$id]->is_container = !empty($forum->container);
       
  1130     $variables['forums'][$id]->zebra = $row % 2 == 0 ? 'odd' : 'even';
       
  1131     $row++;
       
  1132 
       
  1133     $variables['forums'][$id]->new_text = '';
       
  1134     $variables['forums'][$id]->new_url = '';
       
  1135     $variables['forums'][$id]->new_topics = 0;
       
  1136     $variables['forums'][$id]->old_topics = $forum->num_topics;
       
  1137     $variables['forums'][$id]->icon_class = 'default';
       
  1138     $variables['forums'][$id]->icon_title = t('No new posts');
       
  1139     if ($user->uid) {
       
  1140       $variables['forums'][$id]->new_topics = _forum_topics_unread($forum->tid, $user->uid);
       
  1141       if ($variables['forums'][$id]->new_topics) {
       
  1142         $variables['forums'][$id]->new_text = format_plural($variables['forums'][$id]->new_topics, '1 new', '@count new');
       
  1143         $variables['forums'][$id]->new_url = url("forum/$forum->tid", array('fragment' => 'new'));
       
  1144         $variables['forums'][$id]->icon_class = 'new';
       
  1145         $variables['forums'][$id]->icon_title = t('New posts');
       
  1146       }
       
  1147       $variables['forums'][$id]->old_topics = $forum->num_topics - $variables['forums'][$id]->new_topics;
       
  1148     }
       
  1149     $variables['forums'][$id]->last_reply = theme('forum_submitted', array('topic' => $forum->last_post));
       
  1150   }
       
  1151   // Give meaning to $tid for themers. $tid actually stands for term id.
       
  1152   $variables['forum_id'] = $variables['tid'];
       
  1153   unset($variables['tid']);
       
  1154 }
       
  1155 
       
  1156 /**
       
  1157  * Preprocesses variables for forum-topic-list.tpl.php.
       
  1158  *
       
  1159  * @param $variables
       
  1160  *   An array containing the following elements:
       
  1161  *   - tid: Taxonomy term ID of the current forum.
       
  1162  *   - topics: An array of all the topics in the current forum.
       
  1163  *   - forum_per_page: The maximum number of topics to display per page.
       
  1164  *
       
  1165  * @see forum-topic-list.tpl.php
       
  1166  * @see theme_forum_topic_list()
       
  1167  */
       
  1168 function template_preprocess_forum_topic_list(&$variables) {
       
  1169   global $forum_topic_list_header;
       
  1170 
       
  1171   // Create the tablesorting header.
       
  1172   $ts = tablesort_init($forum_topic_list_header);
       
  1173   $header = '';
       
  1174   foreach ($forum_topic_list_header as $cell) {
       
  1175     $cell = tablesort_header($cell, $forum_topic_list_header, $ts);
       
  1176     $header .= _theme_table_cell($cell, TRUE);
       
  1177   }
       
  1178   $variables['header'] = $header;
       
  1179 
       
  1180   if (!empty($variables['topics'])) {
       
  1181     $row = 0;
       
  1182     foreach ($variables['topics'] as $id => $topic) {
       
  1183       $variables['topics'][$id]->icon = theme('forum_icon', array('new_posts' => $topic->new, 'num_posts' => $topic->comment_count, 'comment_mode' => $topic->comment_mode, 'sticky' => $topic->sticky, 'first_new' => $topic->first_new));
       
  1184       $variables['topics'][$id]->zebra = $row % 2 == 0 ? 'odd' : 'even';
       
  1185       $row++;
       
  1186 
       
  1187       // We keep the actual tid in forum table, if it's different from the
       
  1188       // current tid then it means the topic appears in two forums, one of
       
  1189       // them is a shadow copy.
       
  1190       if ($variables['tid'] != $topic->forum_tid) {
       
  1191         $variables['topics'][$id]->moved = TRUE;
       
  1192         $variables['topics'][$id]->title = check_plain($topic->title);
       
  1193         $variables['topics'][$id]->message = l(t('This topic has been moved'), "forum/$topic->forum_tid");
       
  1194       }
       
  1195       else {
       
  1196         $variables['topics'][$id]->moved = FALSE;
       
  1197         $variables['topics'][$id]->title = l($topic->title, "node/$topic->nid");
       
  1198         $variables['topics'][$id]->message = '';
       
  1199       }
       
  1200       $variables['topics'][$id]->created = theme('forum_submitted', array('topic' => $topic));
       
  1201       $variables['topics'][$id]->last_reply = theme('forum_submitted', array('topic' => isset($topic->last_reply) ? $topic->last_reply : NULL));
       
  1202 
       
  1203       $variables['topics'][$id]->new_text = '';
       
  1204       $variables['topics'][$id]->new_url = '';
       
  1205       if ($topic->new_replies) {
       
  1206         $variables['topics'][$id]->new_text = format_plural($topic->new_replies, '1 new', '@count new');
       
  1207         $variables['topics'][$id]->new_url = url("node/$topic->nid", array('query' => comment_new_page_count($topic->comment_count, $topic->new_replies, $topic), 'fragment' => 'new'));
       
  1208       }
       
  1209 
       
  1210     }
       
  1211   }
       
  1212   else {
       
  1213     // Make this safe for the template.
       
  1214     $variables['topics'] = array();
       
  1215   }
       
  1216   // Give meaning to $tid for themers. $tid actually stands for term id.
       
  1217   $variables['topic_id'] = $variables['tid'];
       
  1218   unset($variables['tid']);
       
  1219 
       
  1220   $variables['pager'] = theme('pager');
       
  1221 }
       
  1222 
       
  1223 /**
       
  1224  * Preprocesses variables for forum-icon.tpl.php.
       
  1225  *
       
  1226  * @param $variables
       
  1227  *   An array containing the following elements:
       
  1228  *   - new_posts: Indicates whether or not the topic contains new posts.
       
  1229  *   - num_posts: The total number of posts in all topics.
       
  1230  *   - comment_mode: An integer indicating whether comments are open, closed,
       
  1231  *     or hidden.
       
  1232  *   - sticky: Indicates whether the topic is sticky.
       
  1233  *   - first_new: Indicates whether this is the first topic with new posts.
       
  1234  *
       
  1235  * @see forum-icon.tpl.php
       
  1236  * @see theme_forum_icon()
       
  1237  */
       
  1238 function template_preprocess_forum_icon(&$variables) {
       
  1239   $variables['hot_threshold'] = variable_get('forum_hot_topic', 15);
       
  1240   if ($variables['num_posts'] > $variables['hot_threshold']) {
       
  1241     $variables['icon_class'] = $variables['new_posts'] ? 'hot-new' : 'hot';
       
  1242     $variables['icon_title'] = $variables['new_posts'] ? t('Hot topic, new comments') : t('Hot topic');
       
  1243   }
       
  1244   else {
       
  1245     $variables['icon_class'] = $variables['new_posts'] ? 'new' : 'default';
       
  1246     $variables['icon_title'] = $variables['new_posts'] ? t('New comments') : t('Normal topic');
       
  1247   }
       
  1248 
       
  1249   if ($variables['comment_mode'] == COMMENT_NODE_CLOSED || $variables['comment_mode'] == COMMENT_NODE_HIDDEN) {
       
  1250     $variables['icon_class'] = 'closed';
       
  1251     $variables['icon_title'] = t('Closed topic');
       
  1252   }
       
  1253 
       
  1254   if ($variables['sticky'] == 1) {
       
  1255     $variables['icon_class'] = 'sticky';
       
  1256     $variables['icon_title'] = t('Sticky topic');
       
  1257   }
       
  1258 }
       
  1259 
       
  1260 /**
       
  1261  * Preprocesses variables for forum-submitted.tpl.php.
       
  1262  *
       
  1263  * The submission information will be displayed in the forum list and topic
       
  1264  * list.
       
  1265  *
       
  1266  * @param $variables
       
  1267  *   An array containing the following elements:
       
  1268  *   - topic: The topic object.
       
  1269  *
       
  1270  * @see forum-submitted.tpl.php
       
  1271  * @see theme_forum_submitted()
       
  1272  */
       
  1273 function template_preprocess_forum_submitted(&$variables) {
       
  1274   $variables['author'] = isset($variables['topic']->uid) ? theme('username', array('account' => $variables['topic'])) : '';
       
  1275   $variables['time'] = isset($variables['topic']->created) ? format_interval(REQUEST_TIME - $variables['topic']->created) : '';
       
  1276 }
       
  1277 
       
  1278 /**
       
  1279  * Gets the last time the user viewed a node.
       
  1280  *
       
  1281  * @param $nid
       
  1282  *   The node ID.
       
  1283  *
       
  1284  * @return
       
  1285  *   The timestamp when the user last viewed this node, if the user has
       
  1286  *   previously viewed the node; otherwise NODE_NEW_LIMIT.
       
  1287  */
       
  1288 function _forum_user_last_visit($nid) {
       
  1289   global $user;
       
  1290   $history = &drupal_static(__FUNCTION__, array());
       
  1291 
       
  1292   if (empty($history)) {
       
  1293     $result = db_query('SELECT nid, timestamp FROM {history} WHERE uid = :uid', array(':uid' => $user->uid));
       
  1294     foreach ($result as $t) {
       
  1295       $history[$t->nid] = $t->timestamp > NODE_NEW_LIMIT ? $t->timestamp : NODE_NEW_LIMIT;
       
  1296     }
       
  1297   }
       
  1298   return isset($history[$nid]) ? $history[$nid] : NODE_NEW_LIMIT;
       
  1299 }
       
  1300 
       
  1301 /**
       
  1302  * Gets topic sorting information based on an integer code.
       
  1303  *
       
  1304  * @param $sortby
       
  1305  *   One of the following integers indicating the sort criteria:
       
  1306  *   - 1: Date - newest first.
       
  1307  *   - 2: Date - oldest first.
       
  1308  *   - 3: Posts with the most comments first.
       
  1309  *   - 4: Posts with the least comments first.
       
  1310  *
       
  1311  * @return
       
  1312  *   An array with the following values:
       
  1313  *   - field: A field for an SQL query.
       
  1314  *   - sort: 'asc' or 'desc'.
       
  1315  */
       
  1316 function _forum_get_topic_order($sortby) {
       
  1317   switch ($sortby) {
       
  1318     case 1:
       
  1319       return array('field' => 'f.last_comment_timestamp', 'sort' => 'desc');
       
  1320       break;
       
  1321     case 2:
       
  1322       return array('field' => 'f.last_comment_timestamp', 'sort' => 'asc');
       
  1323       break;
       
  1324     case 3:
       
  1325       return array('field' => 'f.comment_count', 'sort' => 'desc');
       
  1326       break;
       
  1327     case 4:
       
  1328       return array('field' => 'f.comment_count', 'sort' => 'asc');
       
  1329       break;
       
  1330   }
       
  1331 }
       
  1332 
       
  1333 /**
       
  1334  * Updates the taxonomy index for a given node.
       
  1335  *
       
  1336  * @param $nid
       
  1337  *   The ID of the node to update.
       
  1338  */
       
  1339 function _forum_update_forum_index($nid) {
       
  1340   $count = db_query('SELECT COUNT(cid) FROM {comment} c INNER JOIN {forum_index} i ON c.nid = i.nid WHERE c.nid = :nid AND c.status = :status', array(
       
  1341     ':nid' => $nid,
       
  1342     ':status' => COMMENT_PUBLISHED,
       
  1343   ))->fetchField();
       
  1344 
       
  1345   if ($count > 0) {
       
  1346     // Comments exist.
       
  1347     $last_reply = db_query_range('SELECT cid, name, created, uid FROM {comment} WHERE nid = :nid AND status = :status ORDER BY cid DESC', 0, 1, array(
       
  1348       ':nid' => $nid,
       
  1349       ':status' => COMMENT_PUBLISHED,
       
  1350     ))->fetchObject();
       
  1351     db_update('forum_index')
       
  1352       ->fields( array(
       
  1353         'comment_count' => $count,
       
  1354         'last_comment_timestamp' => $last_reply->created,
       
  1355       ))
       
  1356       ->condition('nid', $nid)
       
  1357       ->execute();
       
  1358   }
       
  1359   else {
       
  1360     // Comments do not exist.
       
  1361     $node = db_query('SELECT uid, created FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchObject();
       
  1362     db_update('forum_index')
       
  1363       ->fields( array(
       
  1364         'comment_count' => 0,
       
  1365         'last_comment_timestamp' => $node->created,
       
  1366       ))
       
  1367       ->condition('nid', $nid)
       
  1368       ->execute();
       
  1369   }
       
  1370 }
       
  1371 
       
  1372 /**
       
  1373  * Implements hook_rdf_mapping().
       
  1374  */
       
  1375 function forum_rdf_mapping() {
       
  1376   return array(
       
  1377     array(
       
  1378       'type' => 'node',
       
  1379       'bundle' => 'forum',
       
  1380       'mapping' => array(
       
  1381         'rdftype' => array('sioc:Post', 'sioct:BoardPost'),
       
  1382         'taxonomy_forums' => array(
       
  1383           'predicates' => array('sioc:has_container'),
       
  1384           'type' => 'rel',
       
  1385         ),
       
  1386       ),
       
  1387     ),
       
  1388     array(
       
  1389       'type' => 'taxonomy_term',
       
  1390       'bundle' => 'forums',
       
  1391       'mapping' => array(
       
  1392         'rdftype' => array('sioc:Container', 'sioc:Forum'),
       
  1393       ),
       
  1394     ),
       
  1395   );
       
  1396 }