diff --git a/include/class.orm.php b/include/class.orm.php
index 2f553e470282773e881b685c63ca36c6bcf247d5..9714a58c6b5e6b6894db3ce8d059be87c45d5a5b 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -917,6 +917,9 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl
     const LOCK_EXCLUSIVE = 1;
     const LOCK_SHARED = 2;
 
+    const ASC = 'ASC';
+    const DESC = 'DESC';
+
     var $compiler = 'MySqlCompiler';
     var $iterator = 'ModelInstanceManager';
 
@@ -976,10 +979,21 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl
             $this->defer[$f] = true;
         return $this;
     }
+    function order_by($order, $direction=false) {
+        $args = func_get_args();
+        if (in_array($direction, array(self::ASC, self::DESC))) {
+            $args = array($args[0]);
+        }
+        else
+            $direction = false;
 
-    function order_by($order) {
-        $this->ordering = array_merge($this->ordering,
-            is_array($order) ?  $order : func_get_args());
+        $new = is_array($order) ?  $order : $args;
+        if ($direction) {
+            foreach ($new as $i=>$x) {
+                $new[$i] = array($x, $direction);
+            }
+        }
+        $this->ordering = array_merge($this->ordering, $new);
         return $this;
     }
     function getSortFields() {
@@ -2370,6 +2384,9 @@ class MySqlCompiler extends SqlCompiler {
             $orders = array();
             foreach ($columns as $sort) {
                 $dir = 'ASC';
+                if (is_array($sort)) {
+                    list($sort, $dir) = $sort;
+                }
                 if ($sort instanceof SqlFunction) {
                     $field = $sort->toSql($this, $model);
                 }
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 0978acfdd3444a135ab03a5c7cf0613b589b7b80..063feee30cd8e662fe4e1b480c65db634c7bc6c1 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -20,8 +20,8 @@ $sort_options = array(
     'priority,updated' =>   __('Priority + Most Recently Updated'),
     'updated' =>            __('Most Recently Updated'),
     'priority,created' =>   __('Priority + Most Recently Created'),
-    'due' =>                __('Due Soon'),
-    'priority,due' =>       __('Priority + Due Soon'),
+    'due' =>                __('Due Date'),
+    'priority,due' =>       __('Priority + Due Date'),
     'number' =>             __('Ticket Number'),
     'answered' =>           __('Most Recently Answered'),
     'closed' =>             __('Most Recently Closed'),
@@ -196,73 +196,78 @@ $tickets = $pageNav->paginate($tickets);
 $queue_sort_key = sprintf(':Q%s:%s:sort', ObjectModel::OBJECT_TYPE_TICKET, $queue_name);
 
 if (isset($_GET['sort'])) {
-    $_SESSION[$queue_sort_key] = $_GET['sort'];
+    $_SESSION[$queue_sort_key] = array($_GET['sort'], $_GET['dir']);
 }
 elseif (!isset($_SESSION[$queue_sort_key])) {
-    $_SESSION[$queue_sort_key] = $queue_sort_options[0];
+    $_SESSION[$queue_sort_key] = array($queue_sort_options[0], 0);
 }
 
-switch ($_SESSION[$queue_sort_key]) {
+list($sort_cols, $sort_dir) = $_SESSION[$queue_sort_key];
+$orm_dir = $sort_dir ? QuerySet::ASC : QuerySet::DESC;
+$orm_dir_r = $sort_dir ? QuerySet::DESC : QuerySet::ASC;
+switch ($sort_cols) {
 case 'number':
     $tickets->extra(array(
-        'order_by'=>array(SqlExpression::times(new SqlField('number'), 1))
+        'order_by'=>array(
+            array(SqlExpression::times(new SqlField('number'), 1), $orm_dir)
+        )
     ));
     break;
 
 case 'priority,created':
-    $tickets->order_by('cdata__:priority__priority_urgency');
+    $tickets->order_by(($sort_dir ? '-' : '') . 'cdata__:priority__priority_urgency');
     // Fall through to columns for `created`
 case 'created':
     $date_header = __('Date Created');
     $date_col = 'created';
     $tickets->values('created');
-    $tickets->order_by('-created');
+    $tickets->order_by($sort_dir ? 'created' : '-created');
     break;
 
 case 'priority,due':
-    $tickets->order_by('cdata__:priority__priority_urgency');
+    $tickets->order_by('cdata__:priority__priority_urgency', $orm_dir_r);
     // Fall through to add in due date filter
 case 'due':
     $date_header = __('Due Date');
     $date_col = 'est_duedate';
     $tickets->values('est_duedate');
-    $tickets->order_by(SqlFunction::COALESCE(new SqlField('est_duedate'), 'zzz'));
+    $tickets->order_by(SqlFunction::COALESCE(new SqlField('est_duedate'), 'zzz'), $orm_dir_r);
     break;
 
 case 'closed':
     $date_header = __('Date Closed');
     $date_col = 'closed';
     $tickets->values('closed');
-    $tickets->order_by('-closed');
+    $tickets->order_by('closed', $orm_dir);
     break;
 
 case 'answered':
     $date_header = __('Last Response');
     $date_col = 'thread__lastresponse';
     $date_fallback = '<em class="faded">'.__('unanswered').'</em>';
-    $tickets->order_by('-thread__lastresponse');
+    $tickets->order_by('thread__lastresponse', $orm_dir);
     $tickets->values('thread__lastresponse');
     break;
 
 case 'hot':
-    $tickets->order_by('-thread_count');
+    $tickets->order_by('thread_count', $orm_dir);
     $tickets->annotate(array(
         'thread_count' => SqlAggregate::COUNT('thread__entries'),
     ));
     break;
 
 case 'relevance':
-    $tickets->order_by(new SqlCode('relevance'));
+    $tickets->order_by(new SqlCode('relevance'), $orm_dir);
     break;
 
 default:
 case 'priority,updated':
-    $tickets->order_by('cdata__:priority__priority_urgency');
+    $tickets->order_by('cdata__:priority__priority_urgency', $orm_dir_r);
     // Fall through for columns defined for `updated`
 case 'updated':
     $date_header = __('Last Updated');
     $date_col = 'lastupdate';
-    $tickets->order_by('-lastupdate');
+    $tickets->order_by('lastupdate', $orm_dir);
     break;
 }
 
@@ -312,23 +317,33 @@ $_SESSION[':Q:tickets'] = $orig_tickets;
 <div id='basic_search'>
   <div class="pull-right" style="height:25px">
     <span class="valign-helper"></span>
-    <span class="action-button muted" data-dropdown="#sort-dropdown">
+    <span class="action-button muted" data-dropdown="#sort-dropdown" data-toggle="tooltip" title="<?php echo $sort_options[$sort_cols]; ?>">
       <i class="icon-caret-down pull-right"></i>
-      <span><i class="icon-sort-by-attributes-alt"></i> <?php echo __('Sort');?></span>
+      <span><i class="icon-sort-by-attributes-alt <?php if ($sort_dir) echo 'icon-flip-vertical'; ?>"></i> <?php echo __('Sort');?></span>
     </span>
     <div id="sort-dropdown" class="action-dropdown anchor-right"
-    onclick="javascript: $.pjax({
-        url:'?' + addSearchParam('sort', $(event.target).data('mode')),
+    onclick="javascript:
+    var query = addSearchParam({'sort': $(event.target).data('mode'), 'dir': $(event.target).data('dir')});
+    $.pjax({
+        url: '?' + query,
         timeout: 2000,
         container: '#pjax-container'});">
       <ul class="bleed-left">
 <?php foreach ($queue_sort_options as $mode) {
 $desc = $sort_options[$mode];
-$selected = $mode == $_SESSION[$queue_sort_key]; ?>
-      <li <?php if ($selected) echo 'class="active"'; ?>>
-        <a href="#" data-mode="<?php echo $mode; ?>"><i class="icon-fixed-width <?php
-          if ($selected) echo 'icon-hand-right';
-          ?>"></i> <?php echo Format::htmlchars($desc); ?></a>
+$icon = '';
+$dir = '0';
+$selected = $sort_cols == $mode; ?>
+    <li <?php
+if ($selected) {
+    echo 'class="active"';
+    $dir = ($sort_dir == '1') ? '0' : '1'; // Flip the direction
+    $icon = ($sort_dir == '1') ? 'icon-hand-up' : 'icon-hand-down';
+}
+?>>
+        <a href="#" data-mode="<?php echo $mode; ?>" data-dir="<?php echo $dir; ?>">
+          <i class="icon-fixed-width <?php echo $icon; ?>"
+          ></i> <?php echo Format::htmlchars($desc); ?></a>
       </li>
 <?php } ?>
     </div>
@@ -584,6 +599,7 @@ $(function() {
         }
         return false;
     });
+    $('[data-toggle=tooltip]').tooltip();
 });
 </script>
 
diff --git a/scp/js/scp.js b/scp/js/scp.js
index 58ced5d337bb21546eecce6c47824f9a312f217c..07135b2ba6066a465dd23fe58e6e0ea980f01102 100644
--- a/scp/js/scp.js
+++ b/scp/js/scp.js
@@ -1158,23 +1158,16 @@ function __(s) {
 }
 
 // Thanks, http://stackoverflow.com/a/487049
-function addSearchParam(key, value) {
-    key = encodeURI(key); value = encodeURI(value);
-
+function addSearchParam(data) {
     var kvp = document.location.search.substr(1).split('&');
-    var i=kvp.length; var x;
+    var i=kvp.length, x, params = {};
     while (i--) {
         x = kvp[i].split('=');
-        if (x[0]==key) {
-            x[1] = value;
-            kvp[i] = x.join('=');
-            break;
-        }
+        params[decodeURIComponent(x[0])] = decodeURIComponent(x[1]);
     }
-    if(i<0) {kvp[kvp.length] = [key,value].join('=');}
 
     //this will reload the page, it's likely better to store this until finished
-    return kvp.join('&');
+    return $.param($.extend(params, data));
 }
 
 // Periodically adjust relative times