Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1227
diff -u -p -r1.1227 common.inc
--- includes/common.inc	25 Sep 2010 02:00:06 -0000	1.1227
+++ includes/common.inc	25 Sep 2010 14:22:34 -0000
@@ -4573,7 +4573,6 @@ function _drupal_bootstrap_full() {
   require_once DRUPAL_ROOT . '/includes/theme.inc';
   require_once DRUPAL_ROOT . '/includes/pager.inc';
   require_once DRUPAL_ROOT . '/' . variable_get('menu_inc', 'includes/menu.inc');
-  require_once DRUPAL_ROOT . '/includes/tablesort.inc';
   require_once DRUPAL_ROOT . '/includes/file.inc';
   require_once DRUPAL_ROOT . '/includes/unicode.inc';
   require_once DRUPAL_ROOT . '/includes/image.inc';
Index: includes/tablesort.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/tablesort.inc,v
retrieving revision 1.58
diff -u -p -r1.58 tablesort.inc
--- includes/tablesort.inc	5 Dec 2009 20:17:02 -0000	1.58
+++ includes/tablesort.inc	25 Sep 2010 14:24:14 -0000
@@ -16,7 +16,7 @@
 class TableSort extends SelectQueryExtender {
 
   /**
-   * The array of fields that can be sorted by.
+   * An array of table header columns that can be sorted by, as described in theme_table().
    *
    * @var array
    */
@@ -34,23 +34,22 @@ class TableSort extends SelectQueryExten
   /**
    * Order the query based on a header array.
    *
-   * @see theme_table()
    * @param $header
-   *   Table header array.
+   *   An array of table header columns as described in theme_table().
+   *
    * @return SelectQueryInterface
    *   The called object.
+   *
+   * @see theme_table()
    */
-  public function orderByHeader(Array $header) {
+  public function orderByHeader(array $header) {
     $this->header = $header;
     $ts = $this->init();
-    if (!empty($ts['sql'])) {
-      // Based on code from db_escape_table(), but this can also contain a dot.
-      $field = preg_replace('/[^A-Za-z0-9_.]+/', '', $ts['sql']);
-
+    if (!empty($ts['field'])) {
       // Sort order can only be ASC or DESC.
       $sort = drupal_strtoupper($ts['sort']);
       $sort = in_array($sort, array('ASC', 'DESC')) ? $sort : '';
-      $this->orderBy($field, $sort);
+      $this->orderBy($ts['field'], $sort);
     }
     return $this;
   }
@@ -69,23 +68,12 @@ class TableSort extends SelectQueryExten
    * Determine the current sort direction.
    *
    * @param $headers
-   *   An array of column headers in the format described in theme_table().
+   *   An array of table header columns as described in theme_table().
    * @return
    *   The current sort direction ("asc" or "desc").
    */
   protected function getSort() {
-    if (isset($_GET['sort'])) {
-      return ($_GET['sort'] == 'desc') ? 'desc' : 'asc';
-    }
-    // User has not specified a sort. Use default if specified; otherwise use "asc".
-    else {
-      foreach ($this->header as $header) {
-        if (is_array($header) && array_key_exists('sort', $header)) {
-          return $header['sort'];
-        }
-      }
-    }
-    return 'asc';
+    return tablesort_get_sort($this->header);
   }
 
   /**
@@ -104,38 +92,13 @@ class TableSort extends SelectQueryExten
   /**
    * Determine the current sort criterion.
    *
-   * @param $headers
-   *   An array of column headers in the format described in theme_table().
    * @return
    *   An associative array describing the criterion, containing the keys:
-   *   - "name": The localized title of the table column.
-   *   - "sql": The name of the database field to sort on.
+   *   - name: The localized title of the table column.
+   *   - field: The name of the database field to sort on.
    */
   protected function order() {
-    $order = isset($_GET['order']) ? $_GET['order'] : '';
-    foreach ($this->header as $header) {
-      if (isset($header['data']) && $order == $header['data']) {
-        return array('name' => $header['data'], 'sql' => isset($header['field']) ? $header['field'] : '');
-      }
-
-      if (isset($header['sort']) && ($header['sort'] == 'asc' || $header['sort'] == 'desc')) {
-        $default = array('name' => $header['data'], 'sql' => isset($header['field']) ? $header['field'] : '');
-      }
-    }
-
-    if (isset($default)) {
-      return $default;
-    }
-    else {
-      // The first column specified is initial 'order by' field unless otherwise specified
-      if (is_array($this->header[0])) {
-        $this->header[0] += array('data' => NULL, 'field' => NULL);
-        return array('name' => $this->header[0]['data'], 'sql' => $this->header[0]['field']);
-      }
-      else {
-        return array('name' => $this->header[0]);
-      }
-    }
+    return tablesort_get_order($this->header);
   }
 }
 
@@ -158,7 +121,7 @@ function tablesort_init($header) {
  * @param $cell
  *   The cell to format.
  * @param $header
- *   An array of column headers in the format described in theme_table().
+ *   An array of table header columns as described in theme_table().
  * @param $ts
  *   The current table sort context as returned from tablesort_init().
  * @return
@@ -168,7 +131,7 @@ function tablesort_header($cell, $header
   // Special formatting for the currently sorted column header.
   if (is_array($cell) && isset($cell['field'])) {
     $title = t('sort by @s', array('@s' => $cell['data']));
-    if ($cell['data'] == $ts['name']) {
+    if ($cell['field'] == $ts['field']) {
       $ts['sort'] = (($ts['sort'] == 'asc') ? 'desc' : 'asc');
       $cell['class'][] = 'active';
       $image = theme('tablesort_indicator', array('style' => $ts['sort']));
@@ -178,7 +141,12 @@ function tablesort_header($cell, $header
       $ts['sort'] = 'asc';
       $image = '';
     }
-    $cell['data'] = l($cell['data'] . $image, $_GET['q'], array('attributes' => array('title' => $title), 'query' => array_merge($ts['query'], array('sort' => $ts['sort'], 'order' => $cell['data'])), 'html' => TRUE));
+    $options = array(
+      'attributes' => array('title' => $title),
+      'query' => array_merge($ts['query'], array('sort' => $ts['sort'], 'order' => $cell['field'])),
+      'html' => TRUE,
+    );
+    $cell['data'] = l($cell['data'] . $image, $_GET['q'], $options);
 
     unset($cell['field'], $cell['sort']);
   }
@@ -193,7 +161,7 @@ function tablesort_header($cell, $header
  * @param $cell
  *   The cell to format.
  * @param $header
- *   An array of column headers in the format described in theme_table().
+ *   An array of table header columns as described in theme_table().
  * @param $ts
  *   The current table sort context as returned from tablesort_init().
  * @param $i
@@ -202,7 +170,7 @@ function tablesort_header($cell, $header
  *   A properly formatted cell, ready for _theme_table_cell().
  */
 function tablesort_cell($cell, $header, $ts, $i) {
-  if (isset($header[$i]['data']) && $header[$i]['data'] == $ts['name'] && !empty($header[$i]['field'])) {
+  if (isset($header[$i]['field']) && $header[$i]['field'] == $ts['field']) {
     if (is_array($cell)) {
       $cell['class'][] = 'active';
     }
@@ -227,58 +195,65 @@ function tablesort_get_query_parameters(
 /**
  * Determine the current sort criterion.
  *
- * @param $headers
- *   An array of column headers in the format described in theme_table().
+ * @param $header
+ *   An array of table header columns as described in theme_table().
  * @return
  *   An associative array describing the criterion, containing the keys:
- *   - "name": The localized title of the table column.
- *   - "sql": The name of the database field to sort on.
+ *   - name: The localized title of the table column.
+ *   - field: The name of the database field to sort on.
  */
-function tablesort_get_order($headers) {
+function tablesort_get_order($header) {
   $order = isset($_GET['order']) ? $_GET['order'] : '';
-  foreach ($headers as $header) {
-    if (isset($header['data']) && $order == $header['data']) {
-      return array('name' => $header['data'], 'sql' => isset($header['field']) ? $header['field'] : '');
+  foreach ($header as $column) {
+    if (!is_array($column) || !isset($column['field'])) {
+      continue;
     }
-
-    if (isset($header['sort']) && ($header['sort'] == 'asc' || $header['sort'] == 'desc')) {
-      $default = array('name' => $header['data'], 'sql' => isset($header['field']) ? $header['field'] : '');
+    // Use the header column matching the URL parameter.
+    if ($order == $column['field']) {
+      return array('name' => $column['data'], 'field' => $column['field']);
+    }
+    // In case no header column will match the URL parameter, and this column
+    // defines 'sort', it is supposed to be the default sorting column.
+    if (isset($column['sort']) && ($column['sort'] == 'asc' || $column['sort'] == 'desc')) {
+      $default_column = array('name' => $column['data'], 'field' => $column['field']);
+    }
+    // In case there is no default sorting header column, store the first that
+    // defines a field.
+    elseif (!isset($first_column)) {
+      $first_column = array('name' => $column['data'], 'field' => $column['field']);
     }
   }
 
-  if (isset($default)) {
-    return $default;
+  // If there was a default sorting column, return that.
+  if (isset($default_column)) {
+    return $default_column;
   }
-  else {
-    // The first column specified is the initial 'order by' field unless otherwise specified.
-    $first = current($headers);
-    if (is_array($first)) {
-      $first += array('data' => NULL, 'field' => NULL);
-      return array('name' => $first['data'], 'sql' => $first['field']);
-    }
-    else {
-      return array('name' => $first, 'sql' => '');
-    }
+  // Otherwise, use the first header column that defines a field.
+  if (isset($first_column)) {
+    return $first_column;
   }
+  // If we end up here, then a table header did not define any valid tablesort
+  // data.
+  throw new Exception(t('Invalid TableSort data; header needs to define at least one sorting field.'));
 }
 
 /**
  * Determine the current sort direction.
  *
- * @param $headers
- *   An array of column headers in the format described in theme_table().
+ * @param $header
+ *   An array of table header columns as described in theme_table().
  * @return
  *   The current sort direction ("asc" or "desc").
  */
-function tablesort_get_sort($headers) {
+function tablesort_get_sort($header) {
   if (isset($_GET['sort'])) {
     return ($_GET['sort'] == 'desc') ? 'desc' : 'asc';
   }
   // User has not specified a sort. Use default if specified; otherwise use "asc".
   else {
-    foreach ($headers as $header) {
-      if (is_array($header) && array_key_exists('sort', $header)) {
-        return $header['sort'];
+    foreach ($header as $column) {
+      if (is_array($column) && isset($column['sort'])) {
+        return $column['sort'];
       }
     }
   }
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.614
diff -u -p -r1.614 theme.inc
--- includes/theme.inc	24 Sep 2010 21:20:08 -0000	1.614
+++ includes/theme.inc	25 Sep 2010 14:22:12 -0000
@@ -1677,22 +1677,28 @@ function theme_table($variables) {
     $rows[] = array(array('data' => $empty, 'colspan' => $header_count, 'class' => array('empty', 'message')));
   }
 
-  // Format the table header:
+  // Format the table header.
   if (count($header)) {
-    $ts = tablesort_init($header);
+    // Determine whether we need to load tablesort.
+    foreach ($header as $column) {
+      if (is_array($column) && isset($column['field'])) {
+        require_once DRUPAL_ROOT . '/includes/tablesort.inc';
+        $ts = tablesort_init($header);
+        break;
+      }
+    }
     // HTML requires that the thead tag has tr tags in it followed by tbody
     // tags. Using ternary operator to check and see if we have any rows.
     $output .= (count($rows) ? ' <thead><tr>' : ' <tr>');
     foreach ($header as $cell) {
-      $cell = tablesort_header($cell, $header, $ts);
+      if (isset($ts)) {
+        $cell = tablesort_header($cell, $header, $ts);
+      }
       $output .= _theme_table_cell($cell, TRUE);
     }
     // Using ternary operator to close the tags based on whether or not there are rows
     $output .= (count($rows) ? " </tr></thead>\n" : "</tr>\n");
   }
-  else {
-    $ts = array();
-  }
 
   // Format the table rows:
   if (count($rows)) {
@@ -1727,7 +1733,9 @@ function theme_table($variables) {
         $output .= ' <tr' . drupal_attributes($attributes) . '>';
         $i = 0;
         foreach ($cells as $cell) {
-          $cell = tablesort_cell($cell, $header, $ts, $i++);
+          if (isset($ts)) {
+            $cell = tablesort_cell($cell, $header, $ts, $i++);
+          }
           $output .= _theme_table_cell($cell);
         }
         $output .= " </tr>\n";
Index: modules/forum/forum.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v
retrieving revision 1.576
diff -u -p -r1.576 forum.module
--- modules/forum/forum.module	24 Sep 2010 00:37:42 -0000	1.576
+++ modules/forum/forum.module	25 Sep 2010 14:33:52 -0000
@@ -59,7 +59,7 @@ function forum_theme() {
   return array(
     'forums' => array(
       'template' => 'forums',
-      'variables' => array('forums' => NULL, 'topics' => NULL, 'parents' => NULL, 'tid' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
+      'variables' => array('forums' => NULL, 'topics' => array(), 'parents' => NULL, 'tid' => NULL, 'header' => array(), 'forum_per_page' => NULL),
     ),
     'forum_list' => array(
       'template' => 'forum-list',
@@ -67,7 +67,7 @@ function forum_theme() {
     ),
     'forum_topic_list' => array(
       'template' => 'forum-topic-list',
-      'variables' => array('tid' => NULL, 'topics' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
+      'variables' => array('tid' => NULL, 'topics' => array(), 'header' => array(), 'forum_per_page' => NULL),
     ),
     'forum_icon' => array(
       'template' => 'forum-icon',
@@ -854,22 +854,8 @@ function _forum_topics_unread($term, $ui
     ->fetchField();
 }
 
-function forum_get_topics($tid, $sortby, $forum_per_page) {
-  global $user, $forum_topic_list_header;
-
-  $forum_topic_list_header = array(
-    NULL,
-    array('data' => t('Topic'), 'field' => 'f.title'),
-    array('data' => t('Replies'), 'field' => 'f.comment_count'),
-    array('data' => t('Last reply'), 'field' => 'f.last_comment_timestamp'),
-  );
-
-  $order = _forum_get_topic_order($sortby);
-  for ($i = 0; $i < count($forum_topic_list_header); $i++) {
-    if ($forum_topic_list_header[$i]['field'] == $order['field']) {
-      $forum_topic_list_header[$i]['sort'] = $order['sort'];
-    }
-  }
+function forum_get_topics($tid, $header, $forum_per_page) {
+  global $user;
 
   $query = db_select('forum_index', 'f')->extend('PagerDefault')->extend('TableSort');
   $query->fields('f');
@@ -877,7 +863,7 @@ function forum_get_topics($tid, $sortby,
     ->condition('f.tid', $tid)
     ->addTag('node_access')
     ->orderBy('f.sticky', 'DESC')
-    ->orderByHeader($forum_topic_list_header)
+    ->orderByHeader($header)
     ->orderBy('f.last_comment_timestamp', 'DESC')
     ->limit($forum_per_page);
 
@@ -939,7 +925,7 @@ function forum_get_topics($tid, $sortby,
  * - $topics
  * - $parents
  * - $tid
- * - $sortby
+ * - $header
  * - $forum_per_page
  *
  * @see forums.tpl.php
@@ -1058,20 +1044,20 @@ function template_preprocess_forum_list(
  * $variables contains the following data:
  * - $tid
  * - $topics
- * - $sortby
+ * - $header
  * - $forum_per_page
  *
  * @see forum-topic-list.tpl.php
  * @see theme_forum_topic_list()
  */
 function template_preprocess_forum_topic_list(&$variables) {
-  global $forum_topic_list_header;
-
-  // Create the tablesorting header.
-  $ts = tablesort_init($forum_topic_list_header);
+  // Render the table header; this is required, because the forum topic list
+  // table does not use theme_table().
+  require_once DRUPAL_ROOT . '/includes/tablesort.inc';
+  $ts = tablesort_init($variables['header']);
   $header = '';
-  foreach ($forum_topic_list_header as $cell) {
-    $cell = tablesort_header($cell, $forum_topic_list_header, $ts);
+  foreach ($variables['header'] as $cell) {
+    $cell = tablesort_header($cell, $variables['header'], $ts);
     $header .= _theme_table_cell($cell, TRUE);
   }
   $variables['header'] = $header;
@@ -1109,10 +1095,6 @@ function template_preprocess_forum_topic
 
     }
   }
-  else {
-    // Make this safe for the template
-    $variables['topics'] = array();
-  }
   // Give meaning to $tid for themers. $tid actually stands for term id.
   $variables['topic_id'] = $variables['tid'];
   unset($variables['tid']);
@@ -1180,16 +1162,15 @@ function _forum_get_topic_order($sortby)
   switch ($sortby) {
     case 1:
       return array('field' => 'ncs.last_comment_timestamp', 'sort' => 'desc');
-      break;
+
     case 2:
       return array('field' => 'ncs.last_comment_timestamp', 'sort' => 'asc');
-      break;
+
     case 3:
       return array('field' => 'ncs.comment_count', 'sort' => 'desc');
-      break;
+
     case 4:
       return array('field' => 'ncs.comment_count', 'sort' => 'asc');
-      break;
   }
 }
 
Index: modules/forum/forum.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum/forum.pages.inc,v
retrieving revision 1.4
diff -u -p -r1.4 forum.pages.inc
--- modules/forum/forum.pages.inc	28 Apr 2010 05:54:55 -0000	1.4
+++ modules/forum/forum.pages.inc	25 Sep 2010 14:32:31 -0000
@@ -16,14 +16,26 @@ function forum_page($forum_term = NULL) 
   }
 
   $forum_per_page = variable_get('forum_per_page', 25);
-  $sortby = variable_get('forum_order', 1);
+
+  $header = array(
+    NULL,
+    array('data' => t('Topic'), 'field' => 'f.title'),
+    array('data' => t('Replies'), 'field' => 'f.comment_count'),
+    array('data' => t('Last reply'), 'field' => 'f.last_comment_timestamp'),
+  );
+  $order = _forum_get_topic_order(variable_get('forum_order', 1));
+  for ($i = 0; $i < count($header); $i++) {
+    if ($header[$i]['field'] == $order['field']) {
+      $header[$i]['sort'] = $order['sort'];
+    }
+  }
 
   if (empty($forum_term->container)) {
-    $topics = forum_get_topics($forum_term->tid, $sortby, $forum_per_page);
+    $topics = forum_get_topics($forum_term->tid, $header, $forum_per_page);
   }
   else {
-    $topics = '';
+    $topics = array();
   }
 
-  return theme('forums', array('forums' => $forum_term->forums, 'topics' => $topics, 'parents' => $forum_term->parents, 'tid' => $forum_term->tid, 'sortby' => $sortby, 'forums_per_page' => $forum_per_page));
+  return theme('forums', array('forums' => $forum_term->forums, 'topics' => $topics, 'parents' => $forum_term->parents, 'tid' => $forum_term->tid, 'header' => $header, 'forums_per_page' => $forum_per_page));
 }
Index: modules/simpletest/tests/database_test.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/database_test.test,v
retrieving revision 1.103
diff -u -p -r1.103 database_test.test
--- modules/simpletest/tests/database_test.test	24 Sep 2010 02:05:55 -0000	1.103
+++ modules/simpletest/tests/database_test.test	25 Sep 2010 14:22:12 -0000
@@ -2269,10 +2269,10 @@ class DatabaseSelectTableSortDefaultTest
    */
   function testTableSortQuery() {
     $sorts = array(
-      array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
-      array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
-      array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
-      array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
+      array('data' => t('Task ID'), 'field' => 'tid', 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
+      array('data' => t('Task ID'), 'field' => 'tid', 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
+      array('data' => t('Task'), 'field' => 'task', 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
+      array('data' => t('Task'), 'field' => 'task', 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
       // more elements here
 
     );
@@ -2295,10 +2295,10 @@ class DatabaseSelectTableSortDefaultTest
    */
   function testTableSortQueryFirst() {
     $sorts = array(
-      array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
-      array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
-      array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
-      array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
+      array('data' => t('Task ID'), 'field' => 'tid', 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
+      array('data' => t('Task ID'), 'field' => 'tid', 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
+      array('data' => t('Task'), 'field' => 'task', 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
+      array('data' => t('Task'), 'field' => 'task', 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
       // more elements here
 
     );
