diff --git a/assets/default/css/theme.css b/assets/default/css/theme.css
index ba4fc8e02c8e1db4b67936aaa174a6522b6ba3ca..e2120f71909cc711027c791bd0573437bdb04adb 100644
--- a/assets/default/css/theme.css
+++ b/assets/default/css/theme.css
@@ -118,6 +118,12 @@ fieldset {
 a {
   color: #0072bc;
   text-decoration: none;
+  display: inline-block;
+  margin-bottom: 1px;
+}
+a:hover {
+    border-bottom: 1px dotted #0072bc;
+    margin-bottom: 0;
 }
 h1 {
   color: #00AEEF;
@@ -789,9 +795,6 @@ label.required, span.required {
   font-size: 1em;
   background-image: url('../images/icons/thread.gif?1319556657');
 }
-.Icon:hover {
-  text-decoration: underline;
-}
 #ticketTable {
   border: 1px solid #aaa;
   border-left: none;
@@ -828,25 +831,22 @@ label.required, span.required {
 #ticketTable tr.alt td {
   background: #f9f9f9;
 }
-#ticketSearchForm {
-  display: inline-block;
-  float: left;
-  padding: 0 0 5px 0;
+i.refresh {
+  color: #0a0;
+  font-size: 80%;
+  vertical-align: middle;
 }
-a.refresh {
-  display: block;
-  width: auto;
-  float: right;
-  height: 20px;
-  line-height: 20px;
-  text-align: center;
-  padding: 0 10px 0 28px;
-  border: 1px solid #aaa;
-  margin-left: 10px;
-  color: #333;
-  background-position: 5px 50%;
-  background-repeat: no-repeat;
-  background-image: url('../images/icons/refresh.png');
+.states small {
+    font-size: 70%;
+}
+.active.state {
+    font-weight: bold;
+}
+.search.well {
+    padding: 10px;
+    background-color: rgba(0,0,0,0.05);
+    margin-bottom: 10px;
+    margin-top: -15px;
 }
 .infoTable {
   background: #F4FAFF;
@@ -1153,6 +1153,10 @@ img.avatar {
 .thread-body .attachments .filesize {
   margin-left: 0.5em;
 }
+.thread-body .attachments a,
+.thread-body .attachments a:hover {
+  text-decoration: none;
+}
 .thread-body .attachment-info {
     margin-right: 10px;
     display: inline-block;
diff --git a/include/class.client.php b/include/class.client.php
index eb4ef00a870c62a6355d3269ec5b6c0476ed27c5..bbd6270c5f7ee781f58f2335ea29e20c895bb687 100644
--- a/include/class.client.php
+++ b/include/class.client.php
@@ -245,6 +245,10 @@ class  EndUser extends BaseAuthenticatedUser {
         return ($stats=$this->getTicketStats())?$stats['closed']:0;
     }
 
+    function getNumTopicTickets($topic_id) {
+        return ($stats=$this->getTicketStats())?$stats['topics'][$topic_id]:0;
+    }
+
     function getNumOrganizationTickets() {
         if (!($stats=$this->getTicketStats()))
             return 0;
@@ -272,58 +276,20 @@ class  EndUser extends BaseAuthenticatedUser {
     }
 
     private function getStats() {
-
-        $where = ' WHERE ticket.user_id = '.db_input($this->getId())
-                .' OR collab.user_id = '.db_input($this->getId()).' ';
-
-        $where2 = ' WHERE user.org_id > 0 AND user.org_id = '.db_input($this->getOrgId()).' ';
-
-        $join  =  'LEFT JOIN '.THREAD_TABLE.' thread
-                    ON (ticket.ticket_id = thread.object_id and thread.object_type = \'T\')
-                   LEFT JOIN '.THREAD_COLLABORATOR_TABLE.' collab
-                    ON (collab.thread_id=thread.id
-                            AND collab.user_id = '.db_input($this->getId()).' ) ';
-
-        $sql =  'SELECT \'open\', count( ticket.ticket_id ) AS tickets '
-                .'FROM ' . TICKET_TABLE . ' ticket '
-                .'INNER JOIN '.TICKET_STATUS_TABLE. ' status
-                    ON (ticket.status_id=status.id
-                            AND status.state=\'open\') '
-                . $join
-                . $where
-
-                .'UNION SELECT \'closed\', count( ticket.ticket_id ) AS tickets '
-                .'FROM ' . TICKET_TABLE . ' ticket '
-                .'INNER JOIN '.TICKET_STATUS_TABLE. ' status
-                    ON (ticket.status_id=status.id
-                            AND status.state=\'closed\' ) '
-                . $join
-                . $where
-
-                .'UNION SELECT \'org-open\', count( ticket.ticket_id ) AS tickets '
-                .'FROM ' . TICKET_TABLE . ' ticket '
-                .'INNER JOIN '.USER_TABLE.' user ON (ticket.user_id = user.id) '
-                .'INNER JOIN '.TICKET_STATUS_TABLE. ' status
-                    ON (ticket.status_id=status.id
-                            AND status.state=\'open\' ) '
-                . $join
-                . $where2
-
-                .'UNION SELECT \'org-closed\', count( ticket.ticket_id ) AS tickets '
-                .'FROM ' . TICKET_TABLE . ' ticket '
-                .'INNER JOIN '.USER_TABLE.' user ON (ticket.user_id = user.id) '
-                .'INNER JOIN '.TICKET_STATUS_TABLE. ' status
-                    ON (ticket.status_id=status.id
-                            AND status.state=\'closed\' ) '
-                . $join
-                . $where2;
-
-        $res = db_query($sql);
-        $stats = array();
-        while($row = db_fetch_row($res)) {
-            $stats[$row[0]] = $row[1];
+        $basic = Ticket::objects()
+            ->annotate(array('count' => SqlAggregate::COUNT('ticket_id')))
+            ->values('status__state', 'topic_id')
+            ->filter(Q::any(array(
+                'user_id' => $this->getId(),
+                'thread__collaborators__user_id' => $this->getId(),
+            )));
+
+        $stats = array('open' => 0, 'closed' => 0, 'topics' => array());
+        foreach ($basic as $row) {
+            $stats[$row['status__state']] += $row['count'];
+            if ($row['topic_id'])
+                $stats['topics'][$row['topic_id']] += $row['count'];
         }
-
         return $stats;
     }
 
diff --git a/include/class.orm.php b/include/class.orm.php
index cd8992ae881e1917e18ad89cd6014282f5bdc639..3157c0a8497d93351ba911ddae3935b88f44e122 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -1096,7 +1096,7 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl
         // Aggregate works like annotate, except that it sets up values
         // fetching which will disable model creation
         $this->annotate($annotations);
-        $this->values_flat();
+        $this->values();
         // Disable other fields from being fetched
         $this->aggregated = true;
         $this->related = false;
@@ -2267,7 +2267,7 @@ class MySqlCompiler extends SqlCompiler {
                 }
             }
             // If no group by has been set yet, use the root model pk
-            if (!$group_by) {
+            if (!$group_by && !$queryset->aggregated) {
                 foreach ($model::getMeta('pk') as $pk)
                     $group_by[] = $rootAlias .'.'. $pk;
             }
diff --git a/include/client/tickets.inc.php b/include/client/tickets.inc.php
index 58d056a1786b624bb3ea0e63e5c10ae62cc24d80..12908ece25e65bc1f0240ca6ca88522973a01744 100644
--- a/include/client/tickets.inc.php
+++ b/include/client/tickets.inc.php
@@ -1,29 +1,33 @@
 <?php
 if(!defined('OSTCLIENTINC') || !is_object($thisclient) || !$thisclient->isValid()) die('Access Denied');
 
+$settings = &$_SESSION['client:Q'];
+
+// Unpack search, filter, and sort requests
+if (isset($_REQUEST['clear']))
+    $settings = array();
+if (isset($_REQUEST['keywords']))
+    $settings['keywords'] = $_REQUEST['keywords'];
+if (isset($_REQUEST['topic_id']))
+    $settings['topic_id'] = $_REQUEST['topic_id'];
+if (isset($_REQUEST['status']))
+    $settings['status'] = $_REQUEST['status'];
+
 $tickets = TicketModel::objects();
 
 $qs = array();
 $status=null;
-if(isset($_REQUEST['status'])) { //Query string status has nothing to do with the real status used below.
-    $qs += array('status' => $_REQUEST['status']);
-    //Status we are actually going to use on the query...making sure it is clean!
-    $status=strtolower($_REQUEST['status']);
-    switch(strtolower($_REQUEST['status'])) {
-     case 'open':
-		$results_type=__('Open Tickets');
-        $tickets->filter(array('status__state'=>'open'));
-        break;
-     case 'closed':
-		$results_type=__('Closed Tickets');
-        $tickets->filter(array('status__state'=>'closed'));
+
+if ($settings['status'])
+    $status = strtolower($settings['status']);
+    switch ($status) {
+    default:
+        $status = 'open';
+    case 'open':
+    case 'closed':
+		$results_type = ($status == 'closed') ? __('Closed Tickets') : __('Open Tickets');
+        $tickets->filter(array('status__state' => $status));
         break;
-     default:
-        $status=''; //ignore
-    }
-} elseif($thisclient->getNumOpenTickets()) {
-    $status='open'; //Defaulting to open
-	$results_type=__('Open Tickets');
 }
 
 $sortOptions=array('id'=>'number', 'subject'=>'cdata__subject',
@@ -49,17 +53,20 @@ $tickets->filter(Q::any(array(
 )));
 
 // Perform basic search
-$search=($_REQUEST['a']=='search' && $_REQUEST['q']);
-if($search) {
-    $qs += array('a' => $_REQUEST['a'], 'q' => $_REQUEST['q']);
-    if (is_numeric($_REQUEST['q'])) {
-        $tickets->filter(array('number__startswith'=>$_REQUEST['q']));
+if ($settings['keywords']) {
+    $q = $settings['keywords'];
+    if (is_numeric($q)) {
+        $tickets->filter(array('number__startswith'=>$q));
     } else { //Deep search!
         // Use the search engine to perform the search
-        $tickets = $ost->searcher->find($_REQUEST['q'], $tickets);
+        $tickets = $ost->searcher->find($q, $tickets);
     }
 }
 
+if ($settings['topic_id']) {
+    $tickets = $tickets->filter(array('topic_id' => $settings['topic_id']));
+}
+
 TicketForm::ensureDynamicDataView();
 
 $total=$tickets->count();
@@ -89,28 +96,62 @@ $tickets->values(
 );
 
 ?>
-<h1><?php echo __('Tickets');?></h1>
-<br>
+<div class="search well">
+<div class="flush-left">
 <form action="tickets.php" method="get" id="ticketSearchForm">
     <input type="hidden" name="a"  value="search">
-    <input type="text" name="q" size="20" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>">
-    <select name="status">
-        <option value="">&mdash; <?php echo __('Any Status');?> &mdash;</option>
-        <option value="open"
-            <?php echo ($status=='open') ? 'selected="selected"' : '';?>>
-            <?php echo _P('ticket-status', 'Open');?> (<?php echo $thisclient->getNumOpenTickets(); ?>)</option>
-        <?php
-        if($thisclient->getNumClosedTickets()) {
-            ?>
-        <option value="closed"
-            <?php echo ($status=='closed') ? 'selected="selected"' : '';?>>
-            <?php echo __('Closed');?> (<?php echo $thisclient->getNumClosedTickets(); ?>)</option>
-        <?php
-        } ?>
+    <input type="search" name="keywords" size="30" value="<?php echo Format::htmlchars($settings['keywords']); ?>">
+    <input type="submit" value="<?php echo __('Search');?>">
+<div class="pull-right">
+    <?php echo __('Help Topic'); ?>:
+    <select name="topic_id" class="nowarn" onchange="javascript: this.form.submit(); ">
+        <option value="">&mdash; <?php echo __('All Help Topics');?> &mdash;</option>
+<?php foreach (Topic::getHelpTopics(true) as $id=>$name) {
+        $count = $thisclient->getNumTopicTickets($id);
+        if ($count == 0)
+            continue;
+?>
+        <option value="<?php echo $id; ?>"i
+            <?php if ($settings['topic_id'] == $id) echo 'selected="selected"'; ?>
+            ><?php echo sprintf('%s (%d)', Format::htmlchars($name),
+                $thisclient->getNumTopicTickets($id)); ?></option>
+<?php } ?>
     </select>
-    <input type="submit" value="<?php echo __('Go');?>">
+</div>
 </form>
-<a class="refresh" href="<?php echo Format::htmlchars($_SERVER['REQUEST_URI']); ?>"><?php echo __('Refresh'); ?></a>
+</div>
+
+<?php if ($settings['keywords'] || $settings['topic_id'] || $_REQUEST['sort']) { ?>
+<div style="margin-top:10px"><strong><a href="?clear" style="color:#777"><i class="icon-remove-circle"></i> <?php echo __('Clear all filters and sort'); ?></a></strong></div>
+<?php } ?>
+
+</div>
+
+
+<h1 style="margin:10px 0">
+    <a href="<?php echo Format::htmlchars($_SERVER['REQUEST_URI']); ?>"
+        ><i class="refresh icon-refresh"></i>
+    <?php echo __('Tickets'); ?>
+    </a>
+
+<div class="pull-right states">
+    <small>
+    <i class="icon-file-alt"></i>
+    <a class="state <?php if ($status == 'open') echo 'active'; ?>"
+        href="?<?php echo Http::build_query(array('a' => 'search', 'status' => 'open')); ?>">
+    <?php echo sprintf('%s (%d)', _P('ticket-status', 'Open'), $thisclient->getNumOpenTickets()); ?>
+    </a>
+    &nbsp;
+    <span style="color:lightgray">|</span>
+    &nbsp;
+    <i class="icon-file-text"></i>
+    <a class="state <?php if ($status == 'closed') echo 'active'; ?>"
+        href="?<?php echo Http::build_query(array('a' => 'search', 'status' => 'closed')); ?>">
+    <?php echo sprintf('%s (%d)', __('Closed'), $thisclient->getNumClosedTickets()); ?>
+    </a>
+    </small>
+</div>
+</h1>
 <table id="ticketTable" width="800" border="0" cellspacing="0" cellpadding="0">
     <caption><?php echo $showing; ?></caption>
     <thead>
@@ -170,7 +211,7 @@ $tickets->values(
         }
 
      } else {
-         echo '<tr><td colspan="6">'.__('Your query did not match any records').'</td></tr>';
+         echo '<tr><td colspan="5">'.__('Your query did not match any records').'</td></tr>';
      }
     ?>
     </tbody>
diff --git a/include/client/view.inc.php b/include/client/view.inc.php
index 498e6d6aecd75a1aaca5e61a432ee731e1e0f12f..2be53bc477943706361921063d776169e1398dce 100644
--- a/include/client/view.inc.php
+++ b/include/client/view.inc.php
@@ -31,9 +31,9 @@ if ($thisclient && $thisclient->isGuest()
     <tr>
         <td colspan="2" width="100%">
             <h1>
+                <a href="tickets.php?id=<?php echo $ticket->getId(); ?>" title="<?php echo __('Reload'); ?>"><i class="refresh icon-refresh"></i></a>
                 <b><?php echo $ticket->getSubject(); ?></b>
                 <small>#<?php echo $ticket->getNumber(); ?></small>
-                <a href="tickets.php?id=<?php echo $ticket->getId(); ?>" title="<?php echo __('Reload'); ?>"><span class="Icon refresh">&nbsp;</span></a>
 <div class="pull-right">
     <a class="action-button" href="tickets.php?a=print&id=<?php
         echo $ticket->getId(); ?>"><i class="icon-print"></i> <?php echo __('Print'); ?></a>
diff --git a/js/osticket.js b/js/osticket.js
index 0da43e7d94a9836687caf0d45dcdcbe27fa26df8..d885b9216e66a2111639312e7d54ce8f0102618d 100644
--- a/js/osticket.js
+++ b/js/osticket.js
@@ -21,7 +21,7 @@ $(document).ready(function(){
         left : ($(window).width() / 2 - 160)
      });
 
-    $("form :input").change(function() {
+    $(document).on('change', "form :input:not(.nowarn)", function() {
         var fObj = $(this).closest('form');
         if(!fObj.data('changed')){
             fObj.data('changed', true);
@@ -30,7 +30,7 @@ $(document).ready(function(){
                 return __("Are you sure you want to leave? Any changes or info you've entered will be discarded!");
              });
         }
-       });
+    });
 
     $("form :input[type=reset]").click(function() {
         var fObj = $(this).closest('form');