web/drupal/modules/forum/forum.module
branchdrupal
changeset 74 0ff3ba646492
equal deleted inserted replaced
73:fcf75e232c5b 74:0ff3ba646492
       
     1 <?php
       
     2 // $Id: forum.module,v 1.448.2.7 2009/06/03 18:27:48 goba Exp $
       
     3 
       
     4 /**
       
     5  * @file
       
     6  * Enable threaded discussions about general topics.
       
     7  */
       
     8 
       
     9 /**
       
    10  * Implementation of hook_help().
       
    11  */
       
    12 function forum_help($path, $arg) {
       
    13   switch ($path) {
       
    14     case 'admin/help#forum':
       
    15       $output = '<p>'. t('The forum module lets you create threaded discussion forums with functionality similar to other message board systems. Forums are useful because they allow community members to discuss topics with one another while ensuring those conversations are archived for later reference. The <a href="@create-topic">forum topic</a> menu item (under <em>Create content</em> on the Navigation menu) creates the initial post of a new threaded discussion, or thread.', array('@create-topic' => url('node/add/forum'))) .'</p>';
       
    16       $output .= '<p>'. t('A threaded discussion occurs as people leave comments on a forum topic (or on other comments within that topic). A forum topic is contained within a forum, which may hold many similar or related forum topics. Forums are (optionally) nested within a container, which may hold many similar or related forums. Both containers and forums may be nested within other containers and forums, and provide structure for your message board. By carefully planning this structure, you make it easier for users to find and comment on a specific forum topic.') .'</p>';
       
    17       $output .= '<p>'. t('When administering a forum, note that:') .'</p>';
       
    18       $output .= '<ul><li>'. t('a forum topic (and all of its comments) may be moved between forums by selecting a different forum while editing a forum topic.') .'</li>';
       
    19       $output .= '<li>'. t('when moving a forum topic between forums, the <em>Leave shadow copy</em> option creates a link in the original forum pointing to the new location.') .'</li>';
       
    20       $output .= '<li>'. t('selecting <em>Read only</em> under <em>Comment settings</em> while editing a forum topic will lock (prevent new comments) on the thread.') .'</li>';
       
    21       $output .= '<li>'. t('selecting <em>Disabled</em> under <em>Comment settings</em> while editing a forum topic will hide all existing comments on the thread, and prevent new ones.') .'</li></ul>';
       
    22       $output .= '<p>'. t('For more information, see the online handbook entry for <a href="@forum">Forum module</a>.', array('@forum' => 'http://drupal.org/handbook/modules/forum/')) .'</p>';
       
    23       return $output;
       
    24     case 'admin/content/forum':
       
    25       return '<p>'. t('This page displays a list of existing forums and containers. Containers (optionally) hold forums, and forums hold forum topics (a forum topic is the initial post to a threaded discussion). To provide structure, both containers and forums may be placed inside other containers and forums. To rearrange forums and containers, grab a drag-and-drop handle under the <em>Name</em> column and drag the forum or container to a new location in the list. (Grab a handle by clicking and holding the mouse while hovering over a handle icon.) Remember that your changes will not be saved until you click the <em>Save</em> button at the bottom of the page.') .'</p>';
       
    26     case 'admin/content/forum/add/container':
       
    27       return '<p>'. t('By grouping related or similar forums, containers help organize forums. For example, a container named "Food" may hold two forums named "Fruit" and "Vegetables", respectively.') .'</p>';
       
    28     case 'admin/content/forum/add/forum':
       
    29       return '<p>'. t('A forum holds related or similar forum topics (a forum topic is the initial post to a threaded discussion). For example, a forum named "Fruit" may contain forum topics titled "Apples" and "Bananas", respectively.') .'</p>';
       
    30     case 'admin/content/forum/settings':
       
    31       return '<p>'. t('These settings allow you to adjust the display of your forum topics. The content types available for use within a forum may be selected by editing the <em>Content types</em> on the <a href="@forum-vocabulary">forum vocabulary page</a>.', array('@forum-vocabulary' => url('admin/content/taxonomy/edit/vocabulary/'. variable_get('forum_nav_vocabulary', '')))) .'</p>';
       
    32   }
       
    33 }
       
    34 
       
    35 /**
       
    36  * Implementation of hook_theme()
       
    37  */
       
    38 function forum_theme() {
       
    39   return array(
       
    40     'forums' => array(
       
    41       'template' => 'forums',
       
    42       'arguments' => array('forums' => NULL, 'topics' => NULL, 'parents' => NULL, 'tid' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
       
    43     ),
       
    44     'forum_list' => array(
       
    45       'template' => 'forum-list',
       
    46       'arguments' => array('forums' => NULL, 'parents' => NULL, 'tid' => NULL),
       
    47     ),
       
    48     'forum_topic_list' => array(
       
    49       'template' => 'forum-topic-list',
       
    50       'arguments' => array('tid' => NULL, 'topics' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
       
    51     ),
       
    52     'forum_icon' => array(
       
    53       'template' => 'forum-icon',
       
    54       'arguments' => array('new_posts' => NULL, 'num_posts' => 0, 'comment_mode' => 0, 'sticky' => 0),
       
    55     ),
       
    56     'forum_topic_navigation' => array(
       
    57       'template' => 'forum-topic-navigation',
       
    58       'arguments' => array('node' => NULL),
       
    59     ),
       
    60     'forum_submitted' => array(
       
    61       'template' => 'forum-submitted',
       
    62       'arguments' => array('topic' => NULL),
       
    63     ),
       
    64   );
       
    65 }
       
    66 
       
    67 /**
       
    68  * Fetch a forum term.
       
    69  *
       
    70  * @param $tid
       
    71  *   The ID of the term which should be loaded.
       
    72  *
       
    73  * @return
       
    74  *   An associative array containing the term data or FALSE if the term cannot be loaded, or is not part of the forum vocabulary.
       
    75  */
       
    76 function forum_term_load($tid) {
       
    77   $result = db_query(db_rewrite_sql('SELECT t.tid, t.vid, t.name, t.description, t.weight FROM {term_data} t WHERE t.tid = %d AND t.vid = %d', 't', 'tid'), $tid, variable_get('forum_nav_vocabulary', ''));
       
    78   return db_fetch_array($result);
       
    79 }
       
    80 
       
    81 /**
       
    82  * Implementation of hook_menu().
       
    83  */
       
    84 function forum_menu() {
       
    85   $items['forum'] = array(
       
    86     'title' => 'Forums',
       
    87     'page callback' => 'forum_page',
       
    88     'access arguments' => array('access content'),
       
    89     'type' => MENU_SUGGESTED_ITEM,
       
    90     'file' => 'forum.pages.inc',
       
    91   );
       
    92   $items['admin/content/forum'] = array(
       
    93     'title' => 'Forums',
       
    94     'description' => 'Control forums and their hierarchy and change forum settings.',
       
    95     'page callback' => 'drupal_get_form',
       
    96     'page arguments' => array('forum_overview'),
       
    97     'access arguments' => array('administer forums'),
       
    98     'file' => 'forum.admin.inc',
       
    99   );
       
   100   $items['admin/content/forum/list'] = array(
       
   101     'title' => 'List',
       
   102     'type' => MENU_DEFAULT_LOCAL_TASK,
       
   103     'weight' => -10,
       
   104   );
       
   105   $items['admin/content/forum/add/container'] = array(
       
   106     'title' => 'Add container',
       
   107     'page callback' => 'forum_form_main',
       
   108     'page arguments' => array('container'),
       
   109     'access arguments' => array('administer forums'),
       
   110     'type' => MENU_LOCAL_TASK,
       
   111     'parent' => 'admin/content/forum',
       
   112     'file' => 'forum.admin.inc',
       
   113   );
       
   114   $items['admin/content/forum/add/forum'] = array(
       
   115     'title' => 'Add forum',
       
   116     'page callback' => 'forum_form_main',
       
   117     'page arguments' => array('forum'),
       
   118     'access arguments' => array('administer forums'),
       
   119     'type' => MENU_LOCAL_TASK,
       
   120     'parent' => 'admin/content/forum',
       
   121     'file' => 'forum.admin.inc',
       
   122   );
       
   123   $items['admin/content/forum/settings'] = array(
       
   124     'title' => 'Settings',
       
   125     'page callback' => 'drupal_get_form',
       
   126     'page arguments' => array('forum_admin_settings'),
       
   127     'access arguments' => array('administer forums'),
       
   128     'weight' => 5,
       
   129     'type' => MENU_LOCAL_TASK,
       
   130     'parent' => 'admin/content/forum',
       
   131     'file' => 'forum.admin.inc',
       
   132   );
       
   133   $items['admin/content/forum/edit/%forum_term'] = array(
       
   134     'page callback' => 'forum_form_main',
       
   135     'access arguments' => array('administer forums'),
       
   136     'type' => MENU_CALLBACK,
       
   137     'file' => 'forum.admin.inc',
       
   138   );
       
   139   $items['admin/content/forum/edit/container/%forum_term'] = array(
       
   140     'title' => 'Edit container',
       
   141     'page callback' => 'forum_form_main',
       
   142     'page arguments' => array('container', 5),
       
   143     'access arguments' => array('administer forums'),
       
   144     'type' => MENU_CALLBACK,
       
   145     'file' => 'forum.admin.inc',
       
   146   );
       
   147   $items['admin/content/forum/edit/forum/%forum_term'] = array(
       
   148     'title' => 'Edit forum',
       
   149     'page callback' => 'forum_form_main',
       
   150     'page arguments' => array('forum', 5),
       
   151     'access arguments' => array('administer forums'),
       
   152     'type' => MENU_CALLBACK,
       
   153     'file' => 'forum.admin.inc',
       
   154   );
       
   155   return $items;
       
   156 }
       
   157 
       
   158 
       
   159 /**
       
   160  * Implementation of hook_init().
       
   161  */
       
   162 function forum_init() {
       
   163   drupal_add_css(drupal_get_path('module', 'forum') .'/forum.css');
       
   164 }
       
   165 
       
   166 /**
       
   167  * Implementation of hook_nodeapi().
       
   168  */
       
   169 function forum_nodeapi(&$node, $op, $teaser, $page) {
       
   170   // We are going to return if $node->type is not one of the node
       
   171   // types assigned to the forum vocabulary.  If forum_nav_vocabulary
       
   172   // is undefined or the vocabulary does not exist, it clearly cannot
       
   173   // be assigned to $node->type, so return to avoid E_ALL warnings.
       
   174   $vid = variable_get('forum_nav_vocabulary', '');
       
   175   $vocabulary = taxonomy_vocabulary_load($vid);
       
   176   if (empty($vocabulary)) {
       
   177     return;
       
   178   }
       
   179 
       
   180   // Operate only on node types assigned for the forum vocabulary.
       
   181   if (!in_array($node->type, $vocabulary->nodes)) {
       
   182     return;
       
   183   }
       
   184 
       
   185   switch ($op) {
       
   186     case 'view':
       
   187       if ($page && taxonomy_node_get_terms_by_vocabulary($node, $vid) && $tree = taxonomy_get_tree($vid)) {
       
   188         // Get the forum terms from the (cached) tree
       
   189         foreach ($tree as $term) {
       
   190           $forum_terms[] = $term->tid;
       
   191         }
       
   192         foreach ($node->taxonomy as $term_id => $term) {
       
   193           if (in_array($term_id, $forum_terms)) {
       
   194             $node->tid = $term_id;
       
   195           }
       
   196         }
       
   197         // Breadcrumb navigation
       
   198         $breadcrumb[] = l(t('Home'), NULL);
       
   199         $breadcrumb[] = l($vocabulary->name, 'forum');
       
   200         if ($parents = taxonomy_get_parents_all($node->tid)) {
       
   201           $parents = array_reverse($parents);
       
   202           foreach ($parents as $p) {
       
   203             $breadcrumb[] = l($p->name, 'forum/'. $p->tid);
       
   204           }
       
   205         }
       
   206         drupal_set_breadcrumb($breadcrumb);
       
   207 
       
   208         if (!$teaser) {
       
   209           $node->content['forum_navigation'] = array(
       
   210             '#value' => theme('forum_topic_navigation', $node),
       
   211             '#weight' => 100,
       
   212           );
       
   213         }
       
   214       }
       
   215       break;
       
   216 
       
   217     case 'prepare':
       
   218       if (empty($node->nid)) {
       
   219         // New topic
       
   220         $node->taxonomy[arg(3)]->vid = $vid;
       
   221         $node->taxonomy[arg(3)]->tid = arg(3);
       
   222       }
       
   223       break;
       
   224 
       
   225     // Check in particular that only a "leaf" term in the associated taxonomy
       
   226     // vocabulary is selected, not a "container" term.
       
   227     case 'validate':
       
   228       if ($node->taxonomy) {
       
   229         // Extract the node's proper topic ID.
       
   230         $vocabulary = $vid;
       
   231         $containers = variable_get('forum_containers', array());
       
   232         foreach ($node->taxonomy as $term) {
       
   233           if (db_result(db_query('SELECT COUNT(*) FROM {term_data} WHERE tid = %d AND vid = %d', $term, $vocabulary))) {
       
   234             if (in_array($term, $containers)) {
       
   235               $term = taxonomy_get_term($term);
       
   236               form_set_error('taxonomy', t('The item %forum is only a container for forums. Please select one of the forums below it.', array('%forum' => $term->name)));
       
   237             }
       
   238           }
       
   239         }
       
   240       }
       
   241       break;
       
   242 
       
   243     // Assign forum taxonomy when adding a topic from within a forum.
       
   244     case 'presave':
       
   245       // Make sure all fields are set properly:
       
   246       $node->icon = !empty($node->icon) ? $node->icon : '';
       
   247 
       
   248       if ($node->taxonomy && $tree = taxonomy_get_tree($vid)) {
       
   249         // Get the forum terms from the (cached) tree if we have a taxonomy.
       
   250         foreach ($tree as $term) {
       
   251           $forum_terms[] = $term->tid;
       
   252         }
       
   253         foreach ($node->taxonomy as $term_id) {
       
   254           if (in_array($term_id, $forum_terms)) {
       
   255             $node->tid = $term_id;
       
   256           }
       
   257         }
       
   258         $old_tid = db_result(db_query_range("SELECT t.tid FROM {term_node} t INNER JOIN {node} n ON t.vid = n.vid WHERE n.nid = %d ORDER BY t.vid DESC", $node->nid, 0, 1));
       
   259         if ($old_tid && isset($node->tid) && ($node->tid != $old_tid) && !empty($node->shadow)) {
       
   260           // A shadow copy needs to be created. Retain new term and add old term.
       
   261           $node->taxonomy[] = $old_tid;
       
   262         }
       
   263       }
       
   264       break;
       
   265 
       
   266     case 'update':
       
   267       if (empty($node->revision) && db_result(db_query('SELECT tid FROM {forum} WHERE nid=%d', $node->nid))) {
       
   268         if (!empty($node->tid)) {
       
   269           db_query('UPDATE {forum} SET tid = %d WHERE vid = %d', $node->tid, $node->vid);
       
   270         }
       
   271         // The node is removed from the forum.
       
   272         else {
       
   273           db_query('DELETE FROM {forum} WHERE nid = %d', $node->nid);
       
   274         }
       
   275         break;
       
   276       }
       
   277       // Deliberate no break -- for new revisions and for previously unassigned terms we need an insert.
       
   278 
       
   279     case 'insert':
       
   280       if (!empty($node->tid)) {
       
   281         db_query('INSERT INTO {forum} (tid, vid, nid) VALUES (%d, %d, %d)', $node->tid, $node->vid, $node->nid);
       
   282       }
       
   283       break;
       
   284 
       
   285     case 'delete':
       
   286       db_query('DELETE FROM {forum} WHERE nid = %d', $node->nid);
       
   287       break;
       
   288 
       
   289     case 'load':
       
   290       return db_fetch_array(db_query('SELECT tid AS forum_tid FROM {forum} WHERE vid = %d', $node->vid));
       
   291   }
       
   292 
       
   293   return;
       
   294 }
       
   295 
       
   296 /**
       
   297  * Implementation of hook_node_info().
       
   298  */
       
   299 function forum_node_info() {
       
   300   return array(
       
   301     'forum' => array(
       
   302       'name' => t('Forum topic'),
       
   303       'module' => 'forum',
       
   304       'description' => t('A <em>forum topic</em> is the initial post to a new discussion thread within a forum.'),
       
   305       'title_label' => t('Subject'),
       
   306     )
       
   307   );
       
   308 }
       
   309 
       
   310 /**
       
   311  * Implementation of hook_access().
       
   312  */
       
   313 function forum_access($op, $node, $account) {
       
   314   switch ($op) {
       
   315     case 'create':
       
   316       return user_access('create forum topics', $account) ? TRUE : NULL;
       
   317     case 'update':
       
   318       return user_access('edit any forum topic', $account) || (user_access('edit own forum topics', $account) && ($account->uid == $node->uid)) ? TRUE : NULL;
       
   319     case 'delete':
       
   320       return user_access('delete any forum topic', $account) || (user_access('delete own forum topics', $account) && ($account->uid == $node->uid)) ? TRUE : NULL;
       
   321   }
       
   322 }
       
   323 
       
   324 /**
       
   325  * Implementation of hook_perm().
       
   326  */
       
   327 function forum_perm() {
       
   328   return array('create forum topics', 'delete own forum topics', 'delete any forum topic', 'edit own forum topics', 'edit any forum topic', 'administer forums');
       
   329 }
       
   330 
       
   331 /**
       
   332  * Implementation of hook_taxonomy().
       
   333  */
       
   334 function forum_taxonomy($op, $type, $term = NULL) {
       
   335   if ($op == 'delete' && $term['vid'] == variable_get('forum_nav_vocabulary', '')) {
       
   336     switch ($type) {
       
   337       case 'term':
       
   338         $results = db_query('SELECT tn.nid FROM {term_node} tn WHERE tn.tid = %d', $term['tid']);
       
   339         while ($node = db_fetch_object($results)) {
       
   340           // node_delete will also remove any association with non-forum vocabularies.
       
   341           node_delete($node->nid);
       
   342         }
       
   343 
       
   344         // For containers, remove the tid from the forum_containers variable.
       
   345         $containers = variable_get('forum_containers', array());
       
   346         $key = array_search($term['tid'], $containers);
       
   347         if ($key !== FALSE) {
       
   348           unset($containers[$key]);
       
   349         }
       
   350         variable_set('forum_containers', $containers);
       
   351         break;
       
   352       case 'vocabulary':
       
   353         variable_del('forum_nav_vocabulary');
       
   354     }
       
   355   }
       
   356 }
       
   357 
       
   358 /**
       
   359  * Implementation of hook_form_alter().
       
   360  */
       
   361 function forum_form_alter(&$form, $form_state, $form_id) {
       
   362   $vid = variable_get('forum_nav_vocabulary', '');
       
   363   if (isset($form['vid']) && $form['vid']['#value'] == $vid) {
       
   364     // Hide critical options from forum vocabulary.
       
   365     if ($form_id == 'taxonomy_form_vocabulary') {
       
   366       $form['help_forum_vocab'] = array(
       
   367         '#value' => t('This is the designated forum vocabulary. Some of the normal vocabulary options have been removed.'),
       
   368         '#weight' => -1,
       
   369       );
       
   370       $form['content_types']['nodes']['#required'] = TRUE;
       
   371       $form['hierarchy'] = array('#type' => 'value', '#value' => 1);
       
   372       $form['settings']['required'] = array('#type' => 'value', '#value' => FALSE);
       
   373       $form['settings']['relations'] = array('#type' => 'value', '#value' => FALSE);
       
   374       $form['settings']['tags'] = array('#type' => 'value', '#value' => FALSE);
       
   375       $form['settings']['multiple'] = array('#type' => 'value', '#value' => FALSE);
       
   376       unset($form['delete']);
       
   377     }
       
   378     // Hide multiple parents select from forum terms.
       
   379     elseif ($form_id == 'taxonomy_form_term') {
       
   380       $form['advanced']['parent']['#access'] = FALSE;
       
   381     }
       
   382   }
       
   383   if ($form_id == 'forum_node_form') {
       
   384     // Make the vocabulary required for 'real' forum-nodes.
       
   385     $vid = variable_get('forum_nav_vocabulary', '');
       
   386     $form['taxonomy'][$vid]['#required'] = TRUE;
       
   387     $form['taxonomy'][$vid]['#options'][''] = t('- Please choose -');
       
   388   }
       
   389 }
       
   390 
       
   391 /**
       
   392  * Implementation of hook_load().
       
   393  */
       
   394 function forum_load($node) {
       
   395   $forum = db_fetch_object(db_query('SELECT * FROM {term_node} WHERE vid = %d', $node->vid));
       
   396 
       
   397   return $forum;
       
   398 }
       
   399 
       
   400 /**
       
   401  * Implementation of hook_block().
       
   402  *
       
   403  * Generates a block containing the currently active forum topics and the
       
   404  * most recently added forum topics.
       
   405  */
       
   406 function forum_block($op = 'list', $delta = 0, $edit = array()) {
       
   407   switch ($op) {
       
   408     case 'list':
       
   409       $blocks[0]['info'] = t('Active forum topics');
       
   410       $blocks[1]['info'] = t('New forum topics');
       
   411       return $blocks;
       
   412 
       
   413     case 'configure':
       
   414       $form['forum_block_num_'. $delta] = array('#type' => 'select', '#title' => t('Number of topics'), '#default_value' => variable_get('forum_block_num_'. $delta, '5'), '#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)));
       
   415       return $form;
       
   416 
       
   417     case 'save':
       
   418       variable_set('forum_block_num_'. $delta, $edit['forum_block_num_'. $delta]);
       
   419       break;
       
   420 
       
   421     case 'view':
       
   422       if (user_access('access content')) {
       
   423         switch ($delta) {
       
   424           case 0:
       
   425             $title = t('Active forum topics');
       
   426             $sql = db_rewrite_sql("SELECT n.nid, n.title, l.comment_count, l.last_comment_timestamp FROM {node} n INNER JOIN {term_node} tn ON tn.vid = n.vid INNER JOIN {term_data} td ON td.tid = tn.tid INNER JOIN {node_comment_statistics} l ON n.nid = l.nid WHERE n.status = 1 AND td.vid = %d ORDER BY l.last_comment_timestamp DESC");
       
   427             $result = db_query_range($sql, variable_get('forum_nav_vocabulary', ''), 0, variable_get('forum_block_num_0', '5'));
       
   428             $content = node_title_list($result);
       
   429             break;
       
   430 
       
   431           case 1:
       
   432             $title = t('New forum topics');
       
   433             $sql = db_rewrite_sql("SELECT n.nid, n.title, l.comment_count FROM {node} n INNER JOIN {term_node} tn ON tn.vid = n.vid INNER JOIN {term_data} td ON td.tid = tn.tid INNER JOIN {node_comment_statistics} l ON n.nid = l.nid WHERE n.status = 1 AND td.vid = %d ORDER BY n.nid DESC");
       
   434             $result = db_query_range($sql, variable_get('forum_nav_vocabulary', ''), 0, variable_get('forum_block_num_1', '5'));
       
   435             $content = node_title_list($result);
       
   436             break;
       
   437         }
       
   438 
       
   439         if (!empty($content)) {
       
   440           $block['subject'] = $title;
       
   441           $block['content'] = $content . theme('more_link', url('forum'), t('Read the latest forum topics.'));
       
   442           return $block;
       
   443         }
       
   444       }
       
   445   }
       
   446 }
       
   447 
       
   448 /**
       
   449  * Implementation of hook_form().
       
   450  */
       
   451 function forum_form(&$node, $form_state) {
       
   452   $type = node_get_types('type', $node);
       
   453   $form['title'] = array('#type' => 'textfield', '#title' => check_plain($type->title_label), '#default_value' => !empty($node->title) ? $node->title : '', '#required' => TRUE, '#weight' => -5);
       
   454 
       
   455   if (!empty($node->nid)) {
       
   456     $vid = variable_get('forum_nav_vocabulary', '');
       
   457     $forum_terms = taxonomy_node_get_terms_by_vocabulary($node, $vid);
       
   458     // if editing, give option to leave shadows
       
   459     $shadow = (count($forum_terms) > 1);
       
   460     $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.'));
       
   461   }
       
   462 
       
   463   $form['body_field'] = node_body_field($node, $type->body_label, 1);
       
   464 
       
   465   $form['#submit'][] = 'forum_submit';
       
   466   // Assign the forum topic submit handler.
       
   467 
       
   468   return $form;
       
   469 }
       
   470 
       
   471 /**
       
   472  * Implementation of hook_term_path().
       
   473  */
       
   474 function forum_term_path($term) {
       
   475   return 'forum/'. $term->tid;
       
   476 }
       
   477 
       
   478 /**
       
   479  * Returns a list of all forums for a given taxonomy id
       
   480  *
       
   481  * Forum objects contain the following fields
       
   482  * -num_topics Number of topics in the forum
       
   483  * -num_posts Total number of posts in all topics
       
   484  * -last_post Most recent post for the forum
       
   485  *
       
   486  * @param $tid
       
   487  *   Taxonomy ID of the vocabulary that holds the forum list.
       
   488  * @return
       
   489  *   Array of object containing the forum information.
       
   490  */
       
   491 function forum_get_forums($tid = 0) {
       
   492 
       
   493   $forums = array();
       
   494   $vid = variable_get('forum_nav_vocabulary', '');
       
   495   $_forums = taxonomy_get_tree($vid, $tid);
       
   496 
       
   497   if (count($_forums)) {
       
   498 
       
   499     $counts = array();
       
   500 
       
   501     $sql = "SELECT r.tid, COUNT(n.nid) AS topic_count, SUM(l.comment_count) AS comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {term_node} r ON n.vid = r.vid WHERE n.status = 1 GROUP BY r.tid";
       
   502     $sql = db_rewrite_sql($sql);
       
   503     $_counts = db_query($sql);
       
   504     while ($count = db_fetch_object($_counts)) {
       
   505       $counts[$count->tid] = $count;
       
   506     }
       
   507   }
       
   508 
       
   509   foreach ($_forums as $forum) {
       
   510     if (in_array($forum->tid, variable_get('forum_containers', array()))) {
       
   511       $forum->container = 1;
       
   512     }
       
   513 
       
   514     if (!empty($counts[$forum->tid])) {
       
   515       $forum->num_topics = $counts[$forum->tid]->topic_count;
       
   516       $forum->num_posts = $counts[$forum->tid]->topic_count + $counts[$forum->tid]->comment_count;
       
   517     }
       
   518     else {
       
   519       $forum->num_topics = 0;
       
   520       $forum->num_posts = 0;
       
   521     }
       
   522 
       
   523     // This query does not use full ANSI syntax since MySQL 3.x does not support
       
   524     // table1 INNER JOIN table2 INNER JOIN table3 ON table2_criteria ON table3_criteria
       
   525     // used to join node_comment_statistics to users.
       
   526     $sql = "SELECT ncs.last_comment_timestamp, IF (ncs.last_comment_uid != 0, u2.name, ncs.last_comment_name) AS last_comment_name, ncs.last_comment_uid FROM {node} n INNER JOIN {users} u1 ON n.uid = u1.uid INNER JOIN {term_node} tn ON n.vid = tn.vid INNER JOIN {node_comment_statistics} ncs ON n.nid = ncs.nid INNER JOIN {users} u2 ON ncs.last_comment_uid=u2.uid WHERE n.status = 1 AND tn.tid = %d ORDER BY ncs.last_comment_timestamp DESC";
       
   527     $sql = db_rewrite_sql($sql);
       
   528     $topic = db_fetch_object(db_query_range($sql, $forum->tid, 0, 1));
       
   529 
       
   530     $last_post = new stdClass();
       
   531     if (!empty($topic->last_comment_timestamp)) {
       
   532       $last_post->timestamp = $topic->last_comment_timestamp;
       
   533       $last_post->name = $topic->last_comment_name;
       
   534       $last_post->uid = $topic->last_comment_uid;
       
   535     }
       
   536     $forum->last_post = $last_post;
       
   537 
       
   538     $forums[$forum->tid] = $forum;
       
   539   }
       
   540 
       
   541   return $forums;
       
   542 }
       
   543 
       
   544 /**
       
   545  * Calculate the number of nodes the user has not yet read and are newer
       
   546  * than NODE_NEW_LIMIT.
       
   547  */
       
   548 function _forum_topics_unread($term, $uid) {
       
   549   $sql = "SELECT COUNT(n.nid) FROM {node} n INNER JOIN {term_node} tn ON n.vid = tn.vid AND tn.tid = %d LEFT JOIN {history} h ON n.nid = h.nid AND h.uid = %d WHERE n.status = 1 AND n.created > %d AND h.nid IS NULL";
       
   550   $sql = db_rewrite_sql($sql);
       
   551   return db_result(db_query($sql, $term, $uid, NODE_NEW_LIMIT));
       
   552 }
       
   553 
       
   554 function forum_get_topics($tid, $sortby, $forum_per_page) {
       
   555   global $user, $forum_topic_list_header;
       
   556 
       
   557   $forum_topic_list_header = array(
       
   558     NULL,
       
   559     array('data' => t('Topic'), 'field' => 'n.title'),
       
   560     array('data' => t('Replies'), 'field' => 'l.comment_count'),
       
   561     array('data' => t('Created'), 'field' => 'n.created'),
       
   562     array('data' => t('Last reply'), 'field' => 'l.last_comment_timestamp'),
       
   563   );
       
   564 
       
   565   $order = _forum_get_topic_order($sortby);
       
   566   for ($i = 0; $i < count($forum_topic_list_header); $i++) {
       
   567     if ($forum_topic_list_header[$i]['field'] == $order['field']) {
       
   568       $forum_topic_list_header[$i]['sort'] = $order['sort'];
       
   569     }
       
   570   }
       
   571 
       
   572   $term = taxonomy_get_term($tid);
       
   573 
       
   574   $sql = db_rewrite_sql("SELECT n.nid, r.tid, n.title, n.type, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, IF(l.last_comment_uid != 0, cu.name, l.last_comment_name) AS last_comment_name, l.last_comment_uid, l.comment_count AS num_comments, f.tid AS forum_tid FROM {node_comment_statistics} l INNER JOIN {node} n ON n.nid = l.nid INNER JOIN {users} cu ON l.last_comment_uid = cu.uid INNER JOIN {term_node} r ON n.vid = r.vid INNER JOIN {users} u ON n.uid = u.uid INNER JOIN {forum} f ON n.vid = f.vid WHERE n.status = 1 AND r.tid = %d");
       
   575   $sql .= tablesort_sql($forum_topic_list_header, 'n.sticky DESC,');
       
   576   $sql .= ', n.created DESC';  // Always add a secondary sort order so that the news forum topics are on top.
       
   577 
       
   578   $sql_count = db_rewrite_sql("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {term_node} r ON n.vid = r.vid AND r.tid = %d WHERE n.status = 1");
       
   579 
       
   580   $result = pager_query($sql, $forum_per_page, 0, $sql_count, $tid);
       
   581   $topics = array();
       
   582   while ($topic = db_fetch_object($result)) {
       
   583     if ($user->uid) {
       
   584       // folder is new if topic is new or there are new comments since last visit
       
   585       if ($topic->tid != $tid) {
       
   586         $topic->new = 0;
       
   587       }
       
   588       else {
       
   589         $history = _forum_user_last_visit($topic->nid);
       
   590         $topic->new_replies = comment_num_new($topic->nid, $history);
       
   591         $topic->new = $topic->new_replies || ($topic->timestamp > $history);
       
   592       }
       
   593     }
       
   594     else {
       
   595       // Do not track "new replies" status for topics if the user is anonymous.
       
   596       $topic->new_replies = 0;
       
   597       $topic->new = 0;
       
   598     }
       
   599 
       
   600     if ($topic->num_comments > 0) {
       
   601       $last_reply = new stdClass();
       
   602       $last_reply->timestamp = $topic->last_comment_timestamp;
       
   603       $last_reply->name = $topic->last_comment_name;
       
   604       $last_reply->uid = $topic->last_comment_uid;
       
   605       $topic->last_reply = $last_reply;
       
   606     }
       
   607     $topics[] = $topic;
       
   608   }
       
   609 
       
   610   return $topics;
       
   611 }
       
   612 
       
   613 /**
       
   614  * Finds the first unread node for a given forum.
       
   615  */
       
   616 function _forum_new($tid) {
       
   617   global $user;
       
   618 
       
   619   $sql = "SELECT n.nid FROM {node} n LEFT JOIN {history} h ON n.nid = h.nid AND h.uid = %d INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND h.nid IS NULL AND n.created > %d ORDER BY created";
       
   620   $sql = db_rewrite_sql($sql);
       
   621   $nid = db_result(db_query_range($sql, $user->uid, $tid, NODE_NEW_LIMIT, 0, 1));
       
   622 
       
   623   return $nid ? $nid : 0;
       
   624 }
       
   625 
       
   626 /**
       
   627  * Process variables for forums.tpl.php
       
   628  *
       
   629  * The $variables array contains the following arguments:
       
   630  * - $forums
       
   631  * - $topics
       
   632  * - $parents
       
   633  * - $tid
       
   634  * - $sortby
       
   635  * - $forum_per_page
       
   636  *
       
   637  * @see forums.tpl.php
       
   638  */
       
   639 function template_preprocess_forums(&$variables) {
       
   640   global $user;
       
   641 
       
   642   $vid = variable_get('forum_nav_vocabulary', '');
       
   643   $vocabulary = taxonomy_vocabulary_load($vid);
       
   644   $title = !empty($vocabulary->name) ? $vocabulary->name : '';
       
   645 
       
   646   // Breadcrumb navigation:
       
   647   $breadcrumb[] = l(t('Home'), NULL);
       
   648   if ($variables['tid']) {
       
   649     $breadcrumb[] = l($vocabulary->name, 'forum');
       
   650   }
       
   651   if ($variables['parents']) {
       
   652     $variables['parents'] = array_reverse($variables['parents']);
       
   653     foreach ($variables['parents'] as $p) {
       
   654       if ($p->tid == $variables['tid']) {
       
   655         $title = $p->name;
       
   656       }
       
   657       else {
       
   658         $breadcrumb[] = l($p->name, 'forum/'. $p->tid);
       
   659       }
       
   660     }
       
   661   }
       
   662   drupal_set_breadcrumb($breadcrumb);
       
   663   drupal_set_title(check_plain($title));
       
   664 
       
   665   if ($variables['forums_defined'] = count($variables['forums']) || count($variables['parents'])) {
       
   666     // Format the "post new content" links listing.
       
   667     $forum_types = array();
       
   668 
       
   669     // Loop through all node types for forum vocabulary.
       
   670     foreach ($vocabulary->nodes as $type) {
       
   671       // Check if the current user has the 'create' permission for this node type.
       
   672       if (node_access('create', $type)) {
       
   673         // Fetch the "General" name of the content type;
       
   674         // Push the link with title and url to the array.
       
   675         $forum_types[$type] = array('title' => t('Post new @node_type', array('@node_type' => node_get_types('name', $type))), 'href' => 'node/add/'. str_replace('_', '-', $type) .'/'. $variables['tid']);
       
   676       }
       
   677     }
       
   678 
       
   679     if (empty($forum_types)) {
       
   680       // The user is logged-in; but denied access to create any new forum content type.
       
   681       if ($user->uid) {
       
   682         $forum_types['disallowed'] = array('title' => t('You are not allowed to post new content in the forum.'));
       
   683       }
       
   684       // The user is not logged-in; and denied access to create any new forum content type.
       
   685       else {
       
   686         $forum_types['login'] = array('title' => t('<a href="@login">Login</a> to post new content in the forum.', array('@login' => url('user/login', array('query' => drupal_get_destination())))), 'html' => TRUE);
       
   687       }
       
   688     }
       
   689     $variables['links'] = $forum_types;
       
   690 
       
   691     if (!empty($variables['forums'])) {
       
   692       $variables['forums'] = theme('forum_list', $variables['forums'], $variables['parents'], $variables['tid']);
       
   693     }
       
   694     else {
       
   695       $variables['forums'] = '';
       
   696     }
       
   697 
       
   698     if ($variables['tid'] && !in_array($variables['tid'], variable_get('forum_containers', array()))) {
       
   699       $variables['topics'] = theme('forum_topic_list', $variables['tid'], $variables['topics'], $variables['sortby'], $variables['forum_per_page']);
       
   700       drupal_add_feed(url('taxonomy/term/'. $variables['tid'] .'/0/feed'), 'RSS - '. $title);
       
   701     }
       
   702     else {
       
   703       $variables['topics'] = '';
       
   704     }
       
   705 
       
   706     // Provide separate template suggestions based on what's being output. Topic id is also accounted for.
       
   707     // Check both variables to be safe then the inverse. Forums with topic ID's take precedence.
       
   708     if ($variables['forums'] && !$variables['topics']) {
       
   709       $variables['template_files'][] = 'forums-containers';
       
   710       $variables['template_files'][] = 'forums-'. $variables['tid'];
       
   711       $variables['template_files'][] = 'forums-containers-'. $variables['tid'];
       
   712     }
       
   713     elseif (!$variables['forums'] && $variables['topics']) {
       
   714       $variables['template_files'][] = 'forums-topics';
       
   715       $variables['template_files'][] = 'forums-'. $variables['tid'];
       
   716       $variables['template_files'][] = 'forums-topics-'. $variables['tid'];
       
   717     }
       
   718     else {
       
   719       $variables['template_files'][] = 'forums-'. $variables['tid'];
       
   720     }
       
   721 
       
   722   }
       
   723   else {
       
   724     drupal_set_title(t('No forums defined'));
       
   725     $variables['links'] = array();
       
   726     $variables['forums'] = '';
       
   727     $variables['topics'] = '';
       
   728   }
       
   729 }
       
   730 
       
   731 /**
       
   732  * Process variables to format a forum listing.
       
   733  *
       
   734  * $variables contains the following information:
       
   735  * - $forums
       
   736  * - $parents
       
   737  * - $tid
       
   738  *
       
   739  * @see forum-list.tpl.php
       
   740  * @see theme_forum_list()
       
   741  */
       
   742 function template_preprocess_forum_list(&$variables) {
       
   743   global $user;
       
   744   $row = 0;
       
   745   // Sanitize each forum so that the template can safely print the data.
       
   746   foreach ($variables['forums'] as $id => $forum) {
       
   747     $variables['forums'][$id]->description = !empty($forum->description) ? filter_xss_admin($forum->description) : '';
       
   748     $variables['forums'][$id]->link = url("forum/$forum->tid");
       
   749     $variables['forums'][$id]->name = check_plain($forum->name);
       
   750     $variables['forums'][$id]->is_container = !empty($forum->container);
       
   751     $variables['forums'][$id]->zebra = $row % 2 == 0 ? 'odd' : 'even';
       
   752     $row++;
       
   753 
       
   754     $variables['forums'][$id]->new_text = '';
       
   755     $variables['forums'][$id]->new_url = '';
       
   756     $variables['forums'][$id]->new_topics = 0;
       
   757     $variables['forums'][$id]->old_topics = $forum->num_topics;
       
   758     if ($user->uid) {
       
   759       $variables['forums'][$id]->new_topics = _forum_topics_unread($forum->tid, $user->uid);
       
   760       if ($variables['forums'][$id]->new_topics) {
       
   761         $variables['forums'][$id]->new_text = format_plural($variables['forums'][$id]->new_topics, '1 new', '@count new');
       
   762         $variables['forums'][$id]->new_url = url("forum/$forum->tid", array('fragment' => 'new'));
       
   763       }
       
   764       $variables['forums'][$id]->old_topics = $forum->num_topics - $variables['forums'][$id]->new_topics;
       
   765     }
       
   766     $variables['forums'][$id]->last_reply = theme('forum_submitted', $forum->last_post);
       
   767   }
       
   768   // Give meaning to $tid for themers. $tid actually stands for term id.
       
   769   $variables['forum_id'] = $variables['tid'];
       
   770   unset($variables['tid']);
       
   771 }
       
   772 
       
   773 /**
       
   774  * Preprocess variables to format the topic listing.
       
   775  *
       
   776  * $variables contains the following data:
       
   777  * - $tid
       
   778  * - $topics
       
   779  * - $sortby
       
   780  * - $forum_per_page
       
   781  *
       
   782  * @see forum-topic-list.tpl.php
       
   783  * @see theme_forum_topic_list()
       
   784  */
       
   785 function template_preprocess_forum_topic_list(&$variables) {
       
   786   global $forum_topic_list_header;
       
   787 
       
   788   // Create the tablesorting header.
       
   789   $ts = tablesort_init($forum_topic_list_header);
       
   790   $header = '';
       
   791   foreach ($forum_topic_list_header as $cell) {
       
   792     $cell = tablesort_header($cell, $forum_topic_list_header, $ts);
       
   793     $header .= _theme_table_cell($cell, TRUE);
       
   794   }
       
   795   $variables['header'] = $header;
       
   796 
       
   797   if (!empty($variables['topics'])) {
       
   798     $row = 0;
       
   799     foreach ($variables['topics'] as $id => $topic) {
       
   800       $variables['topics'][$id]->icon = theme('forum_icon', $topic->new, $topic->num_comments, $topic->comment_mode, $topic->sticky);
       
   801       $variables['topics'][$id]->zebra = $row % 2 == 0 ? 'odd' : 'even';
       
   802       $row++;
       
   803 
       
   804       // We keep the actual tid in forum table, if it's different from the
       
   805       // current tid then it means the topic appears in two forums, one of
       
   806       // them is a shadow copy.
       
   807       if ($topic->forum_tid != $variables['tid']) {
       
   808         $variables['topics'][$id]->moved = TRUE;
       
   809         $variables['topics'][$id]->title = check_plain($topic->title);
       
   810         $variables['topics'][$id]->message = l(t('This topic has been moved'), "forum/$topic->forum_tid");
       
   811       }
       
   812       else {
       
   813         $variables['topics'][$id]->moved = FALSE;
       
   814         $variables['topics'][$id]->title = l($topic->title, "node/$topic->nid");
       
   815         $variables['topics'][$id]->message = '';
       
   816       }
       
   817       $variables['topics'][$id]->created = theme('forum_submitted', $topic);
       
   818       $variables['topics'][$id]->last_reply = theme('forum_submitted', isset($topic->last_reply) ? $topic->last_reply : NULL);
       
   819 
       
   820       $variables['topics'][$id]->new_text = '';
       
   821       $variables['topics'][$id]->new_url = '';
       
   822       if ($topic->new_replies) {
       
   823         $variables['topics'][$id]->new_text = format_plural($topic->new_replies, '1 new', '@count new');
       
   824         $variables['topics'][$id]->new_url = url("node/$topic->nid", array('query' => comment_new_page_count($topic->num_comments, $topic->new_replies, $topic), 'fragment' => 'new'));
       
   825       }
       
   826 
       
   827     }
       
   828   }
       
   829   else {
       
   830     // Make this safe for the template
       
   831     $variables['topics'] = array();
       
   832   }
       
   833   // Give meaning to $tid for themers. $tid actually stands for term id.
       
   834   $variables['topic_id'] = $variables['tid'];
       
   835   unset($variables['tid']);
       
   836 
       
   837   $variables['pager'] = theme('pager', NULL, $variables['forum_per_page'], 0);
       
   838 }
       
   839 
       
   840 /**
       
   841  * Process variables to format the icon for each individual topic.
       
   842  *
       
   843  * $variables contains the following data:
       
   844  * - $new_posts
       
   845  * - $num_posts = 0
       
   846  * - $comment_mode = 0
       
   847  * - $sticky = 0
       
   848  *
       
   849  * @see forum-icon.tpl.php
       
   850  * @see theme_forum_icon()
       
   851  */
       
   852 function template_preprocess_forum_icon(&$variables) {
       
   853   $variables['hot_threshold'] = variable_get('forum_hot_topic', 15);
       
   854   if ($variables['num_posts'] > $variables['hot_threshold']) {
       
   855     $variables['icon'] = $variables['new_posts'] ? 'hot-new' : 'hot';
       
   856   }
       
   857   else {
       
   858     $variables['icon'] = $variables['new_posts'] ? 'new' : 'default';
       
   859   }
       
   860 
       
   861   if ($variables['comment_mode'] == COMMENT_NODE_READ_ONLY || $variables['comment_mode'] == COMMENT_NODE_DISABLED) {
       
   862     $variables['icon'] = 'closed';
       
   863   }
       
   864 
       
   865   if ($variables['sticky'] == 1) {
       
   866     $variables['icon'] = 'sticky';
       
   867   }
       
   868 }
       
   869 
       
   870 /**
       
   871  * Preprocess variables to format the next/previous forum topic navigation links.
       
   872  *
       
   873  * $variables contains $node.
       
   874  *
       
   875  * @see forum-topic-navigation.tpl.php
       
   876  * @see theme_forum_topic_navigation()
       
   877  */
       
   878 function template_preprocess_forum_topic_navigation(&$variables) {
       
   879   $output = '';
       
   880 
       
   881   // get previous and next topic
       
   882   $sql = "SELECT n.nid, n.title, n.sticky, l.comment_count, l.last_comment_timestamp FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 ORDER BY n.sticky DESC, ". _forum_get_topic_order_sql(variable_get('forum_order', 1));
       
   883   $result = db_query(db_rewrite_sql($sql), isset($variables['node']->tid) ? $variables['node']->tid : 0);
       
   884 
       
   885   $stop = $variables['prev'] = $variables['next'] = 0;
       
   886 
       
   887   while ($topic = db_fetch_object($result)) {
       
   888     if ($stop == 1) {
       
   889       $variables['next'] = $topic->nid;
       
   890       $variables['next_title'] = check_plain($topic->title);
       
   891       $variables['next_url'] = url("node/$topic->nid");
       
   892       break;
       
   893     }
       
   894     if ($topic->nid == $variables['node']->nid) {
       
   895       $stop = 1;
       
   896     }
       
   897     else {
       
   898       $variables['prev'] = $topic->nid;
       
   899       $variables['prev_title'] = check_plain($topic->title);
       
   900       $variables['prev_url'] = url("node/$topic->nid");
       
   901     }
       
   902   }
       
   903 }
       
   904 
       
   905 /**
       
   906  * Process variables to format submission info for display in the forum list and topic list.
       
   907  *
       
   908  * $variables will contain: $topic
       
   909  *
       
   910  * @see forum-submitted.tpl.php
       
   911  * @see theme_forum_submitted()
       
   912  */
       
   913 function template_preprocess_forum_submitted(&$variables) {
       
   914   $variables['author'] = isset($variables['topic']->uid) ? theme('username', $variables['topic']) : '';
       
   915   $variables['time'] = isset($variables['topic']->timestamp) ? format_interval(time() - $variables['topic']->timestamp) : '';
       
   916 }
       
   917 
       
   918 function _forum_user_last_visit($nid) {
       
   919   global $user;
       
   920   static $history = array();
       
   921 
       
   922   if (empty($history)) {
       
   923     $result = db_query('SELECT nid, timestamp FROM {history} WHERE uid = %d', $user->uid);
       
   924     while ($t = db_fetch_object($result)) {
       
   925       $history[$t->nid] = $t->timestamp > NODE_NEW_LIMIT ? $t->timestamp : NODE_NEW_LIMIT;
       
   926     }
       
   927   }
       
   928   return isset($history[$nid]) ? $history[$nid] : NODE_NEW_LIMIT;
       
   929 }
       
   930 
       
   931 function _forum_get_topic_order($sortby) {
       
   932   switch ($sortby) {
       
   933     case 1:
       
   934       return array('field' => 'l.last_comment_timestamp', 'sort' => 'desc');
       
   935       break;
       
   936     case 2:
       
   937       return array('field' => 'l.last_comment_timestamp', 'sort' => 'asc');
       
   938       break;
       
   939     case 3:
       
   940       return array('field' => 'l.comment_count', 'sort' => 'desc');
       
   941       break;
       
   942     case 4:
       
   943       return array('field' => 'l.comment_count', 'sort' => 'asc');
       
   944       break;
       
   945   }
       
   946 }
       
   947 
       
   948 function _forum_get_topic_order_sql($sortby) {
       
   949   $order = _forum_get_topic_order($sortby);
       
   950   return $order['field'] .' '. strtoupper($order['sort']);
       
   951 }