diff --git a/include/ajax.tasks.php b/include/ajax.tasks.php
index 8181b4eaea1646aba8e229a96a6ac8fd9d2755b2..e0330dc98daf11ec850e10a6ac859c4916c67271 100644
--- a/include/ajax.tasks.php
+++ b/include/ajax.tasks.php
@@ -119,7 +119,7 @@ class TasksAjaxAPI extends AjaxController {
             Http::response(404, __('Unknown action'));
 
 
-        $errors = $e = array();
+        $info = $errors = $e = array();
         $inc = null;
         $i = $count = 0;
         if ($_POST) {
diff --git a/include/ajax.thread.php b/include/ajax.thread.php
new file mode 100644
index 0000000000000000000000000000000000000000..33160073030b41c4a2706cc39947b9f7e6b5c932
--- /dev/null
+++ b/include/ajax.thread.php
@@ -0,0 +1,299 @@
+<?php
+/*********************************************************************
+    ajax.thread.php
+
+    AJAX interface for thread
+
+    Peter Rotich <peter@osticket.com>
+    Copyright (c)  2015 osTicket
+    http://www.osticket.com
+
+    Released under the GNU General Public License WITHOUT ANY WARRANTY.
+    See LICENSE.TXT for details.
+
+    vim: expandtab sw=4 ts=4 sts=4:
+**********************************************************************/
+
+if(!defined('INCLUDE_DIR')) die('403');
+
+include_once(INCLUDE_DIR.'class.ticket.php');
+require_once(INCLUDE_DIR.'class.ajax.php');
+require_once(INCLUDE_DIR.'class.note.php');
+include_once INCLUDE_DIR . 'class.thread_actions.php';
+
+class ThreadAjaxAPI extends AjaxController {
+
+    function lookup() {
+        global $thisstaff;
+
+        if(!is_numeric($_REQUEST['q']))
+            return self::lookupByEmail();
+
+
+        $limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25;
+        $tickets=array();
+
+        $visibility = Q::any(array(
+            'staff_id' => $thisstaff->getId(),
+            'team_id__in' => $thisstaff->teams->values_flat('team_id'),
+        ));
+        if (!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts())) {
+            $visibility->add(array('dept_id__in' => $depts));
+        }
+
+
+        $hits = TicketModel::objects()
+            ->filter(Q::any(array(
+                'number__startswith' => $_REQUEST['q'],
+            )))
+            ->filter($visibility)
+            ->values('number', 'user__emails__address')
+            ->annotate(array('tickets' => SqlAggregate::COUNT('ticket_id')))
+            ->order_by('-created')
+            ->limit($limit);
+
+        foreach ($hits as $T) {
+            $tickets[] = array('id'=>$T['number'], 'value'=>$T['number'],
+                'info'=>"{$T['number']} — {$T['user__emails__address']}",
+                'matches'=>$_REQUEST['q']);
+        }
+        if (!$tickets)
+            return self::lookupByEmail();
+
+        return $this->json_encode($tickets);
+    }
+
+
+    function addRemoteCollaborator($tid, $bk, $id) {
+        global $thisstaff;
+
+        if (!($thread=Thread::lookup($tid))
+                || !($object=$thread->getObject())
+                || !$object->checkStaffPerm($thisstaff))
+            Http::response(404, 'No such thread');
+        elseif (!$bk || !$id)
+            Http::response(422, 'Backend and user id required');
+        elseif (!($backend = StaffAuthenticationBackend::getBackend($bk)))
+            Http::response(404, 'User not found');
+
+        $user_info = $backend->lookup($id);
+        $form = UserForm::getUserForm()->getForm($user_info);
+        $info = array();
+        if (!$user_info)
+            $info['error'] = __('Unable to find user in directory');
+
+        return self::_addcollaborator($thread, null, $form, $info);
+    }
+
+    //Collaborators utils
+    function addCollaborator($tid, $uid=0) {
+        global $thisstaff;
+
+        if (!($thread=Thread::lookup($tid))
+                || !($object=$thread->getObject())
+                || !$object->checkStaffPerm($thisstaff))
+            Http::response(404, __('No such thread'));
+
+
+        $user = $uid? User::lookup($uid) : null;
+
+        //If not a post then assume new collaborator form
+        if(!$_POST)
+            return self::_addcollaborator($thread, $user);
+
+        $user = $form = null;
+        if (isset($_POST['id']) && $_POST['id']) { //Existing user/
+            $user =  User::lookup($_POST['id']);
+        } else { //We're creating a new user!
+            $form = UserForm::getUserForm()->getForm($_POST);
+            $user = User::fromForm($form);
+        }
+
+        $errors = $info = array();
+        if ($user) {
+            // FIXME: Refuse to add ticket owner??
+            if (($c=$thread->addCollaborator($user,
+                            array('isactive'=>1), $errors))) {
+                $note = Format::htmlchars(sprintf(__('%s <%s> added as a collaborator'),
+                            Format::htmlchars($c->getName()), $c->getEmail()));
+
+                $thread->getObject()->postThreadEntry('N',
+                        array(
+                            'title' => __('New Collaborator Added'),
+                            'note' => $note));
+                $info = array('msg' => sprintf(__('%s added as a collaborator'),
+                            Format::htmlchars($c->getName())));
+                return self::_collaborators($thread, $info);
+            }
+        }
+
+        if($errors && $errors['err']) {
+            $info +=array('error' => $errors['err']);
+        } else {
+            $info +=array('error' =>__('Unable to add collaborator. Internal error'));
+        }
+
+        return self::_addcollaborator($thread, $user, $form, $info);
+    }
+
+    function updateCollaborator($tid, $cid) {
+        global $thisstaff;
+
+        if (!($thread=Thread::lookup($tid))
+                || !($object=$thread->getObject())
+                || !$object->checkStaffPerm($thisstaff))
+            Http::response(405, 'No such thread');
+
+
+        if (!($c=Collaborator::lookup(array(
+                            'id' => $cid,
+                            'thread_id' => $thread->getId())))
+                || !($user=$c->getUser()))
+            Http::response(406, 'Unknown collaborator');
+
+        $errors = array();
+        if(!$user->updateInfo($_POST, $errors))
+            return self::_collaborator($c ,$user->getForms($_POST), $errors);
+
+        $info = array('msg' => sprintf('%s updated successfully',
+                    Format::htmlchars($c->getName())));
+
+        return self::_collaborators($thread, $info);
+    }
+
+    function viewCollaborator($tid, $cid) {
+        global $thisstaff;
+
+        if (!($thread=Thread::lookup($tid))
+                || !($object=$thread->getObject())
+                || !$object->checkStaffPerm($thisstaff))
+            Http::response(404, 'No such thread');
+
+
+        if (!($collaborator=Collaborator::lookup(array(
+                            'id' => $cid,
+                            'thread_id' => $thread->getId()))))
+            Http::response(404, 'Unknown collaborator');
+
+        return self::_collaborator($collaborator);
+    }
+
+    function showCollaborators($tid) {
+        global $thisstaff;
+
+        if(!($thread=Thread::lookup($tid))
+                || !($object=$thread->getObject())
+                || !$object->checkStaffPerm($thisstaff))
+            Http::response(404, 'No such thread');
+
+        if ($thread->getCollaborators())
+            return self::_collaborators($thread);
+
+        return self::_addcollaborator($thread);
+    }
+
+    function previewCollaborators($tid) {
+        global $thisstaff;
+
+        if (!($thread=Thread::lookup($tid))
+                || !($object=$thread->getObject())
+                || !$object->checkStaffPerm($thisstaff))
+            Http::response(404, 'No such thread');
+
+        ob_start();
+        include STAFFINC_DIR . 'templates/collaborators-preview.tmpl.php';
+        $resp = ob_get_contents();
+        ob_end_clean();
+
+        return $resp;
+    }
+
+    function _addcollaborator($thread, $user=null, $form=null, $info=array()) {
+        global $thisstaff;
+
+        $info += array(
+                    'title' => __('Add a collaborator'),
+                    'action' => sprintf('#thread/%d/add-collaborator',
+                        $thread->getId()),
+                    'onselect' => sprintf('ajax.php/thread/%d/add-collaborator/',
+                        $thread->getId()),
+                    );
+
+        ob_start();
+        include STAFFINC_DIR . 'templates/user-lookup.tmpl.php';
+        $resp = ob_get_contents();
+        ob_end_clean();
+
+        return $resp;
+    }
+
+    function updateCollaborators($tid) {
+        global $thisstaff;
+
+        if (!($thread=Thread::lookup($tid))
+                || !($object=$thread->getObject())
+                || !$object->checkStaffPerm($thisstaff))
+            Http::response(404, 'No such thread');
+
+        $errors = $info = array();
+        if ($thread->updateCollaborators($_POST, $errors))
+            Http::response(201, sprintf('Recipients (%d of %d)',
+                        $thread->getNumActiveCollaborators(),
+                        $thread->getNumCollaborators()));
+
+        if($errors && $errors['err'])
+            $info +=array('error' => $errors['err']);
+
+        return self::_collaborators($thread, $info);
+    }
+
+
+
+    function _collaborator($collaborator, $form=null, $info=array()) {
+        global $thisstaff;
+
+        $info += array('action' => sprintf('#thread/%d/collaborators/%d',
+                    $collaborator->thread_id, $collaborator->getId()));
+
+        $user = $collaborator->getUser();
+
+        ob_start();
+        include(STAFFINC_DIR . 'templates/user.tmpl.php');
+        $resp = ob_get_contents();
+        ob_end_clean();
+
+        return $resp;
+    }
+
+    function _collaborators($thread, $info=array()) {
+
+        ob_start();
+        include(STAFFINC_DIR . 'templates/collaborators.tmpl.php');
+        $resp = ob_get_contents();
+        ob_end_clean();
+
+        return $resp;
+    }
+
+    function triggerThreadAction($ticket_id, $thread_id, $action) {
+        $thread = ThreadEntry::lookup($thread_id);
+        if (!$thread)
+            Http::response(404, 'No such ticket thread entry');
+        if ($thread->getThread()->getObjectId() != $ticket_id)
+            Http::response(404, 'No such ticket thread entry');
+
+        $valid = false;
+        foreach ($thread->getActions() as $group=>$list) {
+            foreach ($list as $name=>$A) {
+                if ($A->getId() == $action) {
+                    $valid = true; break;
+                }
+            }
+        }
+        if (!$valid)
+            Http::response(400, 'Not a valid action for this thread');
+
+        $thread->triggerAction($action);
+    }
+}
+?>
diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index d1ea440c0495351edb894f76acccb13599d670be..1e95dde3ec1c033f51fb70b13456739aafea7cfb 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -192,189 +192,6 @@ class TicketsAjaxAPI extends AjaxController {
         include STAFFINC_DIR . 'templates/ticket-preview.tmpl.php';
     }
 
-    function addRemoteCollaborator($tid, $bk, $id) {
-        global $thisstaff;
-
-        if (!($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffPerm($thisstaff))
-            Http::response(404, 'No such ticket');
-        elseif (!$bk || !$id)
-            Http::response(422, 'Backend and user id required');
-        elseif (!($backend = StaffAuthenticationBackend::getBackend($bk)))
-            Http::response(404, 'User not found');
-
-        $user_info = $backend->lookup($id);
-        $form = UserForm::getUserForm()->getForm($user_info);
-        $info = array();
-        if (!$user_info)
-            $info['error'] = __('Unable to find user in directory');
-
-        return self::_addcollaborator($ticket, null, $form, $info);
-    }
-
-    //Collaborators utils
-    function addCollaborator($tid, $uid=0) {
-        global $thisstaff;
-
-        if (!($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffPerm($thisstaff))
-            Http::response(404, __('No such ticket'));
-
-
-        $user = $uid? User::lookup($uid) : null;
-
-        //If not a post then assume new collaborator form
-        if(!$_POST)
-            return self::_addcollaborator($ticket, $user);
-
-        $user = $form = null;
-        if (isset($_POST['id']) && $_POST['id']) { //Existing user/
-            $user =  User::lookup($_POST['id']);
-        } else { //We're creating a new user!
-            $form = UserForm::getUserForm()->getForm($_POST);
-            $user = User::fromForm($form);
-        }
-
-        $errors = $info = array();
-        if ($user) {
-            if ($user->getId() == $ticket->getOwnerId())
-                $errors['err'] = sprintf(__('Ticket owner, %s, is a collaborator by default!'),
-                        Format::htmlchars($user->getName()));
-            elseif (($c=$ticket->addCollaborator($user,
-                            array('isactive'=>1), $errors))) {
-                $note = Format::htmlchars(sprintf(__('%s <%s> added as a collaborator'),
-                            Format::htmlchars($c->getName()), $c->getEmail()));
-                $ticket->logNote(__('New Collaborator Added'), $note,
-                    $thisstaff, false);
-                $info = array('msg' => sprintf(__('%s added as a collaborator'),
-                            Format::htmlchars($c->getName())));
-                return self::_collaborators($ticket, $info);
-            }
-        }
-
-        if($errors && $errors['err']) {
-            $info +=array('error' => $errors['err']);
-        } else {
-            $info +=array('error' =>__('Unable to add collaborator. Internal error'));
-        }
-
-        return self::_addcollaborator($ticket, $user, $form, $info);
-    }
-
-    function updateCollaborator($cid) {
-        global $thisstaff;
-
-        if(!($c=Collaborator::lookup($cid))
-                || !($user=$c->getUser())
-                || !($ticket=$c->getTicket())
-                || !$ticket->checkStaffPerm($thisstaff)
-                )
-            Http::response(404, 'Unknown collaborator');
-
-        $errors = array();
-        if(!$user->updateInfo($_POST, $errors))
-            return self::_collaborator($c ,$user->getForms($_POST), $errors);
-
-        $info = array('msg' => sprintf('%s updated successfully',
-                    Format::htmlchars($c->getName())));
-
-        return self::_collaborators($ticket, $info);
-    }
-
-    function viewCollaborator($cid) {
-        global $thisstaff;
-
-        if(!($collaborator=Collaborator::lookup($cid))
-                || !($ticket=$collaborator->getTicket())
-                || !$ticket->checkStaffPerm($thisstaff))
-            Http::response(404, 'Unknown collaborator');
-
-        return self::_collaborator($collaborator);
-    }
-
-    function showCollaborators($tid) {
-        global $thisstaff;
-
-        if(!($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffPerm($thisstaff))
-            Http::response(404, 'No such ticket');
-
-        if($ticket->getCollaborators())
-            return self::_collaborators($ticket);
-
-        return self::_addcollaborator($ticket);
-    }
-
-    function previewCollaborators($tid) {
-        global $thisstaff;
-
-        if (!($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffPerm($thisstaff))
-            Http::response(404, 'No such ticket');
-
-        ob_start();
-        include STAFFINC_DIR . 'templates/collaborators-preview.tmpl.php';
-        $resp = ob_get_contents();
-        ob_end_clean();
-
-        return $resp;
-    }
-
-    function _addcollaborator($ticket, $user=null, $form=null, $info=array()) {
-
-        $info += array(
-                    'title' => sprintf(__('Ticket #%s: Add a collaborator'), $ticket->getNumber()),
-                    'action' => sprintf('#tickets/%d/add-collaborator', $ticket->getId()),
-                    'onselect' => sprintf('ajax.php/tickets/%d/add-collaborator/', $ticket->getId()),
-                    );
-        return self::_userlookup($user, $form, $info);
-    }
-
-
-    function updateCollaborators($tid) {
-        global $thisstaff;
-
-        if(!($ticket=Ticket::lookup($tid))
-                || !$ticket->checkStaffPerm($thisstaff))
-            Http::response(404, 'No such ticket');
-
-        $errors = $info = array();
-        if ($ticket->updateCollaborators($_POST, $errors))
-            Http::response(201, sprintf('Recipients (%d of %d)',
-                        $ticket->getNumActiveCollaborators(),
-                        $ticket->getNumCollaborators()));
-
-        if($errors && $errors['err'])
-            $info +=array('error' => $errors['err']);
-
-        return self::_collaborators($ticket, $info);
-    }
-
-
-
-    function _collaborator($collaborator, $form=null, $info=array()) {
-
-        $info += array('action' => '#collaborators/'.$collaborator->getId());
-
-        $user = $collaborator->getUser();
-
-        ob_start();
-        include(STAFFINC_DIR . 'templates/user.tmpl.php');
-        $resp = ob_get_contents();
-        ob_end_clean();
-
-        return $resp;
-    }
-
-    function _collaborators($ticket, $info=array()) {
-
-        ob_start();
-        include(STAFFINC_DIR . 'templates/collaborators.tmpl.php');
-        $resp = ob_get_contents();
-        ob_end_clean();
-
-        return $resp;
-    }
 
     function viewUser($tid) {
         global $thisstaff;
diff --git a/include/class.collaborator.php b/include/class.collaborator.php
index c457f1cd3cf4feac927e6076a428bc6a90ca4766..205b5200b151911c757611483fae9560f041bb27 100644
--- a/include/class.collaborator.php
+++ b/include/class.collaborator.php
@@ -51,6 +51,10 @@ implements EmailContact, ITicketUser {
         return $this->created;
     }
 
+    function getThreadId() {
+        return $this->thread_id;
+    }
+
     function getTicketId() {
         if ($this->thread->object_type == ObjectModel::OBJECT_TYPE_TICKET)
             return $this->thread->object_id;
@@ -140,18 +144,5 @@ implements EmailContact, ITicketUser {
         return false;
     }
 
-    static function forThread($tid, $criteria=array()) {
-
-        $collaborators = static::objects()
-            ->filter(array('thread_id' => $tid));
-
-        if (isset($criteria['isactive']))
-            $collaborators->filter(array('isactive' => $criteria['isactive']));
-
-        // TODO: sort by name of the user
-        $collaborators->order_by('user__name');
-
-        return $collaborators;
-    }
 }
 ?>
diff --git a/include/class.task.php b/include/class.task.php
index c629379f6e88470408ab6eab9e36467576a38958..5f2f761baa25c8339cd503edfcd60712df1df28c 100644
--- a/include/class.task.php
+++ b/include/class.task.php
@@ -181,7 +181,7 @@ class TaskModel extends VerySimpleModel {
 RolePermission::register(/* @trans */ 'Tasks', TaskModel::getPermissions());
 
 
-class Task extends TaskModel {
+class Task extends TaskModel implements Threadable {
     var $form;
     var $entry;
 
@@ -269,6 +269,10 @@ class Task extends TaskModel {
         return $assignees ? implode($glue, $assignees):'';
     }
 
+    function getThreadId() {
+        return $this->thread->getId();
+    }
+
     function getThread() {
         return $this->thread;
     }
@@ -284,6 +288,15 @@ class Task extends TaskModel {
         return $thread;
     }
 
+    function postThreadEntry($type, $vars) {
+        $errors = array();
+        switch ($type) {
+        case 'N':
+        default:
+            return $this->postNote($vars, $errors);
+        }
+    }
+
     function getForm() {
         if (!isset($this->form)) {
             // Look for the entry first
diff --git a/include/class.thread.php b/include/class.thread.php
index bd50ecb20c48a8c7e3a2e935cfdf878042c07748..3268c5cc8c31d8cfa7a95cd0b6701c003c404ef7 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -46,6 +46,7 @@ class Thread extends VerySimpleModel {
     );
 
     var $_object;
+    var $_collaborators; // Cache for collabs
 
     function getId() {
         return $this->id;
@@ -89,6 +90,108 @@ class Thread extends VerySimpleModel {
         return $base;
     }
 
+    // Collaborators
+    function getNumCollaborators() {
+        return $this->collaborators->count();
+    }
+
+    function getNumActiveCollaborators() {
+
+        if (!isset($this->ht['active_collaborators']))
+            $this->ht['active_collaborators'] = count($this->getActiveCollaborators());
+
+        return $this->ht['active_collaborators'];
+    }
+
+    function getActiveCollaborators() {
+        return $this->getCollaborators(array('isactive'=>1));
+    }
+
+    function getCollaborators($criteria=array()) {
+
+        if ($this->_collaborators && !$criteria)
+            return $this->_collaborators;
+
+        $collaborators = $this->collaborators
+            ->filter(array('thread_id' => $this->getId()));
+
+        if (isset($criteria['isactive']))
+            $collaborators->filter(array('isactive' => $criteria['isactive']));
+
+        // TODO: sort by name of the user
+        $collaborators->order_by('user__name');
+
+        if (!$criteria)
+            $this->_collaborators = $collaborators;
+
+        return $collaborators;
+    }
+
+    function addCollaborator($user, $vars, &$errors) {
+
+        if (!$user)
+            return null;
+
+        $vars = array_merge(array(
+                'threadId' => $this->getId(),
+                'userId' => $user->getId()), $vars);
+        if (!($c=Collaborator::add($vars, $errors)))
+            return null;
+
+        $this->_collaborators = null;
+
+        return $c;
+    }
+
+    function updateCollaborators($vars, &$errors) {
+        global $thisstaff;
+
+        if (!$thisstaff) return;
+
+        //Deletes
+        if($vars['del'] && ($ids=array_filter($vars['del']))) {
+            $collabs = array();
+            foreach ($ids as $k => $cid) {
+                if (($c=Collaborator::lookup($cid))
+                        && $c->getThreadId() == $this->getId()
+                        && $c->delete())
+                     $collabs[] = $c;
+            }
+
+            $this->getObject()->postThreadEntry('N',
+                    array(
+                        'title' => _S('Collaborators Removed'),
+                        'note' => implode("<br>", $collabs)));
+        }
+
+        //statuses
+        $cids = null;
+        if($vars['cid'] && ($cids=array_filter($vars['cid']))) {
+            $this->collaborators->filter(array(
+                'thread_id' => $this->getId(),
+                'id__in' => $cids
+            ))->update(array(
+                'updated' => SqlFunction::NOW(),
+                'isactive' => 1,
+            ));
+        }
+
+        if ($cids) {
+            $this->collaborators->filter(array(
+                'thread_id' => $this->getId(),
+                Q::not(array('id__in' => $cids))
+            ))->update(array(
+                'updated' => SqlFunction::NOW(),
+                'isactive' => 0,
+            ));
+        }
+
+        unset($this->ht['active_collaborators']);
+        $this->_collaborators = null;
+
+        return true;
+    }
+    // Render thread
     function render($type=false) {
 
         $entries = $this->getEntries();
@@ -1878,6 +1981,8 @@ abstract class ThreadEntryAction {
 }
 
 interface Threadable {
+    function getThreadId();
+    function getThread();
     function postThreadEntry($type, $vars);
 }
 ?>
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 9198bf4b4cc3fdab010dd3d718316a020554916f..3127c85071c9b6b28d8c652d7abd036642da8af0 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -883,35 +883,6 @@ implements RestrictedAccess, Threadable, TemplateVariable {
         return $entries;
     }
 
-    //Collaborators
-    function getNumCollaborators() {
-        return count($this->getCollaborators());
-    }
-
-    function getNumActiveCollaborators() {
-
-        if (!isset($this->ht['active_collaborators']))
-            $this->ht['active_collaborators'] = count($this->getActiveCollaborators());
-
-        return $this->ht['active_collaborators'];
-    }
-
-    function getActiveCollaborators() {
-        return $this->getCollaborators(array('isactive'=>1));
-    }
-
-
-    function getCollaborators($criteria=array()) {
-
-        if ($criteria)
-            return Collaborator::forThread($this->getThreadId(), $criteria);
-
-        if (!isset($this->collaborators))
-            $this->collaborators = Collaborator::forThread($this->getThreadId());
-
-        return $this->collaborators;
-    }
-
     //UserList of recipients  (owner + collaborators)
     function getRecipients() {
 
diff --git a/include/staff/tasks.inc.php b/include/staff/tasks.inc.php
index cd07dfa21bbd3edb44012f34cba80a06ecc9dd37..e74a543994d0823b9cabd010c021e293c36d1769 100644
--- a/include/staff/tasks.inc.php
+++ b/include/staff/tasks.inc.php
@@ -86,10 +86,19 @@ $tasks->filter(Q::any($visibility));
 
 // Add in annotations
 $tasks->annotate(array(
-    //'collab_count' => SqlAggregate::COUNT('collaborators'),
-    'attachment_count' => SqlAggregate::COUNT('thread__entries__attachments'),
-    'thread_count' => SqlAggregate::COUNT('thread__entries'),
-    // 'isopen' => new SqlExpr(array('flags__hasbit' => TaskModel::ISOPEN)),
+    'collab_count' => SqlAggregate::COUNT('thread__collaborators', true),
+    'attachment_count' => SqlAggregate::COUNT(SqlCase::N()
+       ->when(new SqlField('thread__entries__attachments__inline'), null)
+       ->otherwise(new SqlField('thread__entries__attachments')),
+        true
+    ),
+    'thread_count' => SqlAggregate::COUNT(SqlCase::N()
+        ->when(
+            new Q(array('thread__entries__flags__hasbit'=>ThreadEntry::FLAG_HIDDEN)),
+            null)
+        ->otherwise(new SqlField('thread__entries__id')),
+       true
+    ),
 ));
 
 $tasks->values('id', 'number', 'created', 'staff_id', 'team_id',
diff --git a/include/staff/templates/collaborators-preview.tmpl.php b/include/staff/templates/collaborators-preview.tmpl.php
index 964c225e7db174f8e0a614889104378f14d7e312..e7fb4f5985d6b46472e49e9453081d325c927b44 100644
--- a/include/staff/templates/collaborators-preview.tmpl.php
+++ b/include/staff/templates/collaborators-preview.tmpl.php
@@ -2,7 +2,7 @@
 <table border="0" cellspacing="" cellpadding="1">
 <colgroup><col style="min-width: 250px;"></col></colgroup>
 <?php
-if (($users=$ticket->getCollaborators())) {?>
+if (($users=$thread->getCollaborators())) {?>
 <?php
     foreach($users as $user) {
         echo sprintf('<tr><td %s><i class="icon-%s"></i> %s <em>&lt;%s&gt;</em></td></tr>',
@@ -12,16 +12,16 @@ if (($users=$ticket->getCollaborators())) {?>
                 $user->getEmail());
     }
 }  else {
-    echo "<strong>".__("Ticket doesn't have any collaborators.")."</strong>";
+    echo "<strong>".__("Thread doesn't have any collaborators.")."</strong>";
 }?>
 </table>
 <?php
 $options = array();
 
 $options[] = sprintf(
-        '<a class="collaborators" id="managecollab" href="#tickets/%d/collaborators">%s</a>',
-        $ticket->getId(),
-        $ticket->getNumCollaborators()
+        '<a class="collaborators" id="managecollab" href="#thread/%d/collaborators">%s</a>',
+        $thread->getId(),
+        $thread->getNumCollaborators()
         ? __('Manage Collaborators') : __('Add Collaborator')
         );
 
diff --git a/include/staff/templates/collaborators.tmpl.php b/include/staff/templates/collaborators.tmpl.php
index cd17a3ca12f9051e5c7c1cc91187d059f0127ef2..c49b1b266edaf1118a5f058ab8cb2f67fa5dc8cb 100644
--- a/include/staff/templates/collaborators.tmpl.php
+++ b/include/staff/templates/collaborators.tmpl.php
@@ -1,4 +1,4 @@
-<h3 class="drag-handle"><?php echo __('Ticket Collaborators'); ?></h3>
+<h3 class="drag-handle"><?php echo __('Collaborators'); ?></h3>
 <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
 <?php
 if($info && $info['msg']) {
@@ -6,9 +6,9 @@ if($info && $info['msg']) {
 } ?>
 <hr/>
 <?php
-if(($users=$ticket->getCollaborators())) {?>
+if(($users=$thread->getCollaborators())) {?>
 <div id="manage_collaborators">
-<form method="post" class="collaborators" action="#tickets/<?php echo $ticket->getId(); ?>/collaborators">
+<form method="post" class="collaborators" action="#thread/<?php echo $thread->getId(); ?>/collaborators">
     <table border="0" cellspacing="1" cellpadding="1" width="100%">
     <?php
     foreach($users as $user) {
@@ -16,7 +16,7 @@ if(($users=$ticket->getCollaborators())) {?>
         echo sprintf('<tr>
                         <td>
                             <input type="checkbox" name="cid[]" id="c%d" value="%d" %s>
-                            <a class="collaborator" href="#collaborators/%d/view">%s</a>
+                            <a class="collaborator" href="#thread/%d/collaborators/%d/view">%s</a>
                             <span class="faded"><em>%s</em></span></td>
                         <td width="10">
                             <input type="hidden" name="del[]" id="d%d" value="">
@@ -26,6 +26,7 @@ if(($users=$ticket->getCollaborators())) {?>
                     $user->getId(),
                     $user->getId(),
                     $checked,
+                    $thread->getId(),
                     $user->getId(),
                     Format::htmlchars($user->getName()),
                     $user->getEmail(),
@@ -36,7 +37,7 @@ if(($users=$ticket->getCollaborators())) {?>
     </table>
     <hr style="margin-top:1em"/>
     <div><a class="collaborator"
-        href="#tickets/<?php echo $ticket->getId(); ?>/add-collaborator"
+        href="#thread/<?php echo $thread->getId(); ?>/add-collaborator"
         ><i class="icon-plus-sign"></i> <?php echo __('Add New Collaborator'); ?></a></div>
     <div id="savewarning" style="display:none; padding-top:2px;"><p
     id="msg_warning"><?php echo __('You have made changes that you need to save.'); ?></p></div>
@@ -57,15 +58,22 @@ if(($users=$ticket->getCollaborators())) {?>
     echo __("Bro, not sure how you got here!");
 }
 
-if ($_POST && $ticket && $ticket->getNumCollaborators()) {
+if ($_POST && $thread && $thread->getNumCollaborators()) {
+
+    $collaborators = sprintf('Participants (%d)',
+            $thread->getNumCollaborators());
+
     $recipients = sprintf(__('Recipients (%d of %d)'),
-          $ticket->getNumActiveCollaborators(),
-          $ticket->getNumCollaborators());
+          $thread->getNumActiveCollaborators(),
+          $thread->getNumCollaborators());
     ?>
     <script type="text/javascript">
         $(function() {
             $('#emailcollab').show();
-            $('#recipients').html('<?php echo $recipients; ?>');
+            $('#t<?php echo $thread->getId(); ?>-recipients')
+            .html('<?php echo $recipients; ?>');
+            $('#t<?php echo $thread->getId(); ?>-collaborators')
+            .html('<?php echo $collaborators; ?>');
             });
     </script>
 <?php
diff --git a/include/staff/templates/task-preview.tmpl.php b/include/staff/templates/task-preview.tmpl.php
index d6968f49a42da80daa6ccc8f730d548edbd519b0..5203956880e919bd708714227f1af258bb5206d5 100644
--- a/include/staff/templates/task-preview.tmpl.php
+++ b/include/staff/templates/task-preview.tmpl.php
@@ -23,6 +23,13 @@ echo '<ul class="tabs" id="task-preview">';
 echo '
         <li class="active"><a href="#summary"
             ><i class="icon-list-alt"></i>&nbsp;'.__('Task Summary').'</a></li>';
+if ($task->getThread()->getNumCollaborators()) {
+    echo sprintf('
+        <li><a id="collab_tab" href="#collab"
+            ><i class="icon-fixed-width icon-group
+            faded"></i>&nbsp;'.__('Collaborators (%d)').'</a></li>',
+            $task->getThread()->getNumCollaborators());
+}
 echo '</ul>';
 echo '<div id="task-preview_container">';
 echo '<div class="tab_content" id="summary">';
@@ -72,9 +79,37 @@ echo '
     </table>';
 echo '</div>';
 ?>
-</div>
 <?php
 //TODO: add link to view if the user has permission
-
-echo '</div>';
 ?>
+<div class="hidden tab_content" id="collab">
+    <table border="0" cellspacing="" cellpadding="1">
+        <colgroup><col style="min-width: 250px;"></col></colgroup>
+        <?php
+        if (($collabs=$task->getThread()->getCollaborators())) {?>
+        <?php
+            foreach($collabs as $collab) {
+                echo sprintf('<tr><td %s><i class="icon-%s"></i>
+                        <a href="users.php?id=%d" class="no-pjax">%s</a> <em>&lt;%s&gt;</em></td></tr>',
+                        ($collab->isActive()? '' : 'class="faded"'),
+                        ($collab->isActive()? 'comments' :  'comment-alt'),
+                        $collab->getUserId(),
+                        $collab->getName(),
+                        $collab->getEmail());
+            }
+        }  else {
+            echo __("Task doesn't have any collaborators.");
+        }?>
+    </table>
+    <br>
+    <?php
+    echo sprintf('<span><a class="collaborators"
+                            href="#thread/%d/collaborators">%s</a></span>',
+                            $task->getThreadId(),
+                            $task->getThread()->getNumCollaborators()
+                                ? __('Manage Collaborators') : __('Add Collaborator')
+                                );
+    ?>
+</div>
+</div>
+</div>
diff --git a/include/staff/templates/task-view.tmpl.php b/include/staff/templates/task-view.tmpl.php
index cf9c3897fd76f68591f08fa573cebbd230693906..0e3f509268b1b5dcded6377b5a137af6be80a029 100644
--- a/include/staff/templates/task-view.tmpl.php
+++ b/include/staff/templates/task-view.tmpl.php
@@ -61,7 +61,7 @@ if ($task->isOverdue())
 ?>
 <table width="940" cellpadding="2" cellspacing="0" border="0">
     <tr>
-        <td width="70%" class="has_bottom_border">
+        <td width="<?php echo $ticket ? '70%' : '20%'; ?>" class="has_bottom_border">
             <?php
             if ($ticket) { ?>
                 <strong>
@@ -157,6 +157,34 @@ if (!$ticket) { ?>
                         <th width="100"><?php echo __('Status');?>:</th>
                         <td><?php echo $task->getStatus(); ?></td>
                     </tr>
+
+                    <tr>
+                        <th><?php echo __('Create Date');?>:</th>
+                        <td><?php echo Format::datetime($task->getCreateDate()); ?></td>
+                    </tr>
+                    <?php
+                    if($task->isOpen()){ ?>
+                    <tr>
+                        <th><?php echo __('Due Date');?>:</th>
+                        <td><?php echo $task->duedate ?
+                        Format::datetime($task->duedate) : '<span
+                        class="faded">&mdash; '.__('None').' &mdash;</span>'; ?></td>
+                    </tr>
+                    <?php
+                    }else { ?>
+                    <tr>
+                        <th><?php echo __('Close Date');?>:</th>
+                        <td><?php echo 0 ?
+                        Format::datetime($task->getCloseDate()) : ''; ?></td>
+                    </tr>
+                    <?php
+                    }
+                    ?>
+                </table>
+            </td>
+            <td width="50%" style="vertical-align:top">
+                <table cellspacing="0" cellpadding="4" width="100%" border="0">
+
                     <tr>
                         <th><?php echo __('Department');?>:</th>
                         <td><?php echo Format::htmlchars($task->dept->getName()); ?></td>
@@ -189,36 +217,24 @@ if (!$ticket) { ?>
                     </tr>
                     <?php
                     } ?>
-                </table>
-            </td>
-            <td width="50%" style="vertical-align:top">
-                <table cellspacing="0" cellpadding="4" width="100%" border="0">
-                    <tr>
-                        <th><?php echo __('SLA Plan');?>:</th>
-                        <td><?php echo $sla?Format::htmlchars($sla->getName()):'<span class="faded">&mdash; '.__('None').' &mdash;</span>'; ?></td>
-                    </tr>
                     <tr>
-                        <th><?php echo __('Create Date');?>:</th>
-                        <td><?php echo Format::datetime($task->getCreateDate()); ?></td>
-                    </tr>
-                    <?php
-                    if($task->isOpen()){ ?>
-                    <tr>
-                        <th><?php echo __('Due Date');?>:</th>
-                        <td><?php echo $task->duedate ?
-                        Format::datetime($task->duedate) : '<span
-                        class="faded">&mdash; '.__('None').' &mdash;</span>'; ?></td>
-                    </tr>
-                    <?php
-                    }else { ?>
-                    <tr>
-                        <th><?php echo __('Close Date');?>:</th>
-                        <td><?php echo 0 ?
-                        Format::datetime($task->getCloseDate()) : ''; ?></td>
+                        <th><?php echo __('Collaborators');?>:</th>
+                        <td>
+                            <?php
+                            $collaborators = __('Add Participants');
+                            if ($task->getThread()->getNumCollaborators())
+                                $collaborators = sprintf(__('Participants (%d)'),
+                                        $task->getThread()->getNumCollaborators());
+
+                            echo sprintf('<span><a class="collaborators preview"
+                                    href="#thread/%d/collaborators"><span
+                                    id="t%d-collaborators">%s</span></a></span>',
+                                    $task->getThreadId(),
+                                    $task->getThreadId(),
+                                    $collaborators);
+                           ?>
+                        </td>
                     </tr>
-                    <?php
-                    }
-                    ?>
                 </table>
             </td>
         </tr>
diff --git a/include/staff/templates/ticket-preview.tmpl.php b/include/staff/templates/ticket-preview.tmpl.php
index 875c9d701bdcd641540b6f1c5d37e76b674ddc4c..f4091921f15fc4f32455e2fdd7aed9f94008f93d 100644
--- a/include/staff/templates/ticket-preview.tmpl.php
+++ b/include/staff/templates/ticket-preview.tmpl.php
@@ -34,12 +34,12 @@ echo '<ul class="tabs" id="ticket-preview">';
 echo '
         <li class="active"><a id="preview_tab" href="#preview"
             ><i class="icon-list-alt"></i>&nbsp;'.__('Ticket Summary').'</a></li>';
-if ($ticket->getNumCollaborators()) {
+if ($ticket->getThread()->getNumCollaborators()) {
 echo sprintf('
         <li><a id="collab_tab" href="#collab"
             ><i class="icon-fixed-width icon-group
             faded"></i>&nbsp;'.__('Collaborators (%d)').'</a></li>',
-            $ticket->getNumCollaborators());
+            $ticket->getThread()->getNumCollaborators());
 }
 echo '</ul>';
 echo '<div id="ticket-preview_container">';
@@ -121,7 +121,7 @@ echo '</div>'; // ticket preview content.
     <table border="0" cellspacing="" cellpadding="1">
         <colgroup><col style="min-width: 250px;"></col></colgroup>
         <?php
-        if (($collabs=$ticket->getCollaborators())) {?>
+        if (($collabs=$ticket->getThread()->getCollaborators())) {?>
         <?php
             foreach($collabs as $collab) {
                 echo sprintf('<tr><td %s><i class="icon-%s"></i>
@@ -141,7 +141,7 @@ echo '</div>'; // ticket preview content.
     echo sprintf('<span><a class="collaborators"
                             href="#tickets/%d/collaborators">%s</a></span>',
                             $ticket->getId(),
-                            $ticket->getNumCollaborators()
+                            $ticket->getThread()->getNumCollaborators()
                                 ? __('Manage Collaborators') : __('Add Collaborator')
                                 );
     ?>
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index 83cb718ea1a20acd10e8919f378ffce7aa8d07bc..74407ea1952c951da73f9345baa0dc6fe88ddb5a 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -510,18 +510,19 @@ $tcount = $ticket->getThreadEntries($types)->count();
                 <td>
                     <input type='checkbox' value='1' name="emailcollab" id="emailcollab"
                         <?php echo ((!$info['emailcollab'] && !$errors) || isset($info['emailcollab']))?'checked="checked"':''; ?>
-                        style="display:<?php echo $ticket->getNumCollaborators() ? 'inline-block': 'none'; ?>;"
+                        style="display:<?php echo $ticket->getThread()->getNumCollaborators() ? 'inline-block': 'none'; ?>;"
                         >
                     <?php
                     $recipients = __('Add Recipients');
-                    if ($ticket->getNumCollaborators())
+                    if ($ticket->getThread()->getNumCollaborators())
                         $recipients = sprintf(__('Recipients (%d of %d)'),
-                                $ticket->getNumActiveCollaborators(),
-                                $ticket->getNumCollaborators());
+                                $ticket->getThread()->getNumActiveCollaborators(),
+                                $ticket->getThread()->getNumCollaborators());
 
                     echo sprintf('<span><a class="collaborators preview"
-                            href="#tickets/%d/collaborators"><span id="recipients">%s</span></a></span>',
-                            $ticket->getId(),
+                            href="#thread/%d/collaborators"><span id="t%d-recipients">%s</span></a></span>',
+                            $ticket->getThreadId(),
+                            $ticket->getThreadId(),
                             $recipients);
                    ?>
                 </td>
diff --git a/scp/ajax.php b/scp/ajax.php
index f6e2f36de5eb6a966eca31a4980cae32ea8406d9..54e2770736488081832ac325a9a345e94bb5081d 100644
--- a/scp/ajax.php
+++ b/scp/ajax.php
@@ -143,12 +143,6 @@ $dispatcher = patterns('',
         url_post('^(?P<tid>\d+)/lock$', 'acquireLock'),
         url_post('^(?P<tid>\d+)/lock/(?P<id>\d+)/renew', 'renewLock'),
         url_post('^(?P<tid>\d+)/lock/(?P<id>\d+)/release', 'releaseLock'),
-        url_get('^(?P<tid>\d+)/collaborators/preview$', 'previewCollaborators'),
-        url_get('^(?P<tid>\d+)/collaborators$', 'showCollaborators'),
-        url_post('^(?P<tid>\d+)/collaborators$', 'updateCollaborators'),
-        url_get('^(?P<tid>\d+)/add-collaborator/(?P<uid>\d+)$', 'addCollaborator'),
-        url_get('^(?P<tid>\d+)/add-collaborator/auth:(?P<bk>\w+):(?P<id>.+)$', 'addRemoteCollaborator'),
-        url('^(?P<tid>\d+)/add-collaborator$', 'addCollaborator'),
         url_get('^(?P<tid>\d+)/forms/manage$', 'manageForms'),
         url_post('^(?P<tid>\d+)/forms/manage$', 'updateForms'),
         url_get('^(?P<tid>\d+)/canned-resp/(?P<cid>\w+).(?P<format>json|txt)', 'cannedResponse'),
@@ -189,9 +183,15 @@ $dispatcher = patterns('',
         url_get('^mass/(?P<action>[\w.]+)', 'massProcess'),
         url_post('^mass/(?P<action>[\w.]+)', 'massProcess')
     )),
-    url('^/collaborators/', patterns('ajax.tickets.php:TicketsAjaxAPI',
-        url_get('^(?P<cid>\d+)/view$', 'viewCollaborator'),
-        url_post('^(?P<cid>\d+)$', 'updateCollaborator')
+    url('^/thread/', patterns('ajax.thread.php:ThreadAjaxAPI',
+        url_get('^(?P<tid>\d+)/collaborators/preview$', 'previewCollaborators'),
+        url_get('^(?P<tid>\d+)/collaborators$', 'showCollaborators'),
+        url_post('^(?P<tid>\d+)/collaborators$', 'updateCollaborators'),
+        url_get('^(?P<tid>\d+)/add-collaborator/(?P<uid>\d+)$', 'addCollaborator'),
+        url_get('^(?P<tid>\d+)/add-collaborator/auth:(?P<bk>\w+):(?P<id>.+)$', 'addRemoteCollaborator'),
+        url('^(?P<tid>\d+)/add-collaborator$', 'addCollaborator'),
+        url_get('^(?P<tid>\d+)/collaborators/(?P<cid>\d+)/view$', 'viewCollaborator'),
+        url_post('^(?P<tid>\d+)/collaborators/(?P<cid>\d+)$', 'updateCollaborator')
     )),
     url('^/draft/', patterns('ajax.draft.php:DraftAjaxAPI',
         url_post('^(?P<id>\d+)$', 'updateDraft'),