diff --git a/bootstrap.php b/bootstrap.php
index 424f3637c1bc5cc9ecde66edd30fd07555490562..8985717d43a30697bbb4f67033880b6225cab6e6 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -89,12 +89,15 @@ class Bootstrap {
         define('FAQ_CATEGORY_TABLE',$prefix.'faq_category');
 
         define('DRAFT_TABLE',$prefix.'draft');
+
+        define('THREAD_TABLE', $prefix.'thread');
+        define('THREAD_ENTRY_TABLE', $prefix.'thread_entry');
+        define('THREAD_ENTRY_ATTACHMENT_TABLE', $prefix.'thread_entry_attachment');
+        define('THREAD_ENTRY_EMAIL_TABLE', $prefix.'thread_entry_email');
+
         define('TICKET_TABLE',$prefix.'ticket');
-        define('TICKET_THREAD_TABLE',$prefix.'ticket_thread');
-        define('TICKET_ATTACHMENT_TABLE',$prefix.'ticket_attachment');
         define('TICKET_LOCK_TABLE',$prefix.'ticket_lock');
         define('TICKET_EVENT_TABLE',$prefix.'ticket_event');
-        define('TICKET_EMAIL_INFO_TABLE',$prefix.'ticket_email_info');
         define('TICKET_COLLABORATOR_TABLE', $prefix.'ticket_collaborator');
         define('TICKET_STATUS_TABLE', $prefix.'ticket_status');
         define('TICKET_PRIORITY_TABLE',$prefix.'ticket_priority');
diff --git a/include/ajax.reports.php b/include/ajax.reports.php
index bdaa7d9fe3fb828fe7428ed7a2c4cb03101d328d..d432fee88ba7166aff44dba6e0385d89adfedb51 100644
--- a/include/ajax.reports.php
+++ b/include/ajax.reports.php
@@ -111,9 +111,11 @@ class OverviewReportAjaxAPI extends AjaxController {
                 FORMAT(AVG(DATEDIFF(B2.created, B1.created)),1) AS ResponseTime
             FROM '.$info['table'].' T1
                 LEFT JOIN '.TICKET_TABLE.' T2 ON (T2.'.$info['pk'].'=T1.'.$info['pk'].')
-                LEFT JOIN '.TICKET_THREAD_TABLE.' B2 ON (B2.ticket_id = T2.ticket_id
-                    AND B2.thread_type="R")
-                LEFT JOIN '.TICKET_THREAD_TABLE.' B1 ON (B2.pid = B1.id)
+                LEFT JOIN '.THREAD_TABLE.' B0 ON (B0.object_id=T2.ticket_id
+                    AND B0.object_type="T")
+                LEFT JOIN '.THREAD_ENTRY_TABLE.' B2 ON (B2.thread_id = B0.id
+                    AND B2.`type`="R")
+                LEFT JOIN '.THREAD_ENTRY_TABLE.' B1 ON (B2.pid = B1.id)
                 LEFT JOIN '.STAFF_TABLE.' S1 ON (S1.staff_id=B2.staff_id)
             WHERE '.$info['filter'].' AND B1.created BETWEEN '.$start.' AND '.$stop.'
             GROUP BY T1.'.$info['pk'].'
diff --git a/include/class.api.php b/include/class.api.php
index 0fd2c17e3f0ab552513c775e5f2f93ee52bc1e7f..9fe2142044062ef86033447ddf72a009a142bf6f 100644
--- a/include/class.api.php
+++ b/include/class.api.php
@@ -348,9 +348,9 @@ class ApiXmlDataParser extends XmlDataParser {
                     $value['body'] = Format::utf8encode($value['body'], $value['encoding']);
 
                 if (!strcasecmp($value['type'], 'text/html'))
-                    $value = new HtmlThreadBody($value['body']);
+                    $value = new HtmlThreadEntryBody($value['body']);
                 else
-                    $value = new TextThreadBody($value['body']);
+                    $value = new TextThreadEntryBody($value['body']);
 
             } else if ($key == "attachments") {
                 if(!isset($value['file'][':text']))
@@ -393,9 +393,9 @@ class ApiJsonDataParser extends JsonDataParser {
                 $data = Format::parseRfc2397($value, 'utf-8');
 
                 if (isset($data['type']) && $data['type'] == 'text/html')
-                    $value = new HtmlThreadBody($data['data']);
+                    $value = new HtmlThreadEntryBody($data['data']);
                 else
-                    $value = new TextThreadBody($data['data']);
+                    $value = new TextThreadEntryBody($data['data']);
 
             } else if ($key == "attachments") {
                 foreach ($value as &$info) {
diff --git a/include/class.attachment.php b/include/class.attachment.php
index d7f2d7f668cbd920cbc25dee19abf8364b5862ca..8bd1c806332fb06cc04e8ecef3e54cb097bd155f 100644
--- a/include/class.attachment.php
+++ b/include/class.attachment.php
@@ -19,27 +19,24 @@ require_once(INCLUDE_DIR.'class.file.php');
 class Attachment {
     var $id;
     var $file_id;
-    var $ticket_id;
 
     var $info;
 
-    function Attachment($id,$tid=0) {
+    function Attachment($id, $tid=0) {
 
-        $sql='SELECT * FROM '.TICKET_ATTACHMENT_TABLE.' WHERE attach_id='.db_input($id);
+        $sql = ' SELECT * FROM '.THREAD_ENTRY_ATTACHMENT_TABLE.' WHERE id='.db_input($id);
         if($tid)
-            $sql.=' AND ticket_id='.db_input($tid);
+            $sql.=' AND thread_entry_id='.db_input($tid);
 
         if(!($res=db_query($sql)) || !db_num_rows($res))
             return false;
 
         $this->ht=db_fetch_array($res);
 
-        $this->id=$this->ht['attach_id'];
+        $this->id=$this->ht['id'];
         $this->file_id=$this->ht['file_id'];
-        $this->ticket_id=$this->ht['ticket_id'];
 
         $this->file=null;
-        $this->ticket=null;
 
         return true;
     }
@@ -48,17 +45,6 @@ class Attachment {
         return $this->id;
     }
 
-    function getTicketId() {
-        return $this->ticket_id;
-    }
-
-    function getTicket() {
-        if(!$this->ticket && $this->getTicketId())
-            $this->ticket = Ticket::lookup($this->getTicketId());
-
-        return $this->ticket;
-    }
-
     function getFileId() {
         return $this->file_id;
     }
@@ -84,23 +70,25 @@ class Attachment {
 
     /* Static functions */
     function getIdByFileHash($hash, $tid=0) {
-        $sql='SELECT attach_id FROM '.TICKET_ATTACHMENT_TABLE.' a '
+        $sql='SELECT a.id FROM '.THREAD_ENTRY_ATTACHMENT_TABLE.' a '
             .' INNER JOIN '.FILE_TABLE.' f ON(f.id=a.file_id) '
             .' WHERE f.`key`='.db_input($hash);
         if($tid)
-            $sql.=' AND a.ticket_id='.db_input($tid);
+            $sql.=' AND a.thread_entry_id='.db_input($tid);
 
         return db_result(db_query($sql));
     }
 
-    function lookup($var,$tid=0) {
-        $id=is_numeric($var)?$var:self::getIdByFileHash($var,$tid);
+    function lookup($var, $tid=0) {
 
-        return ($id && is_numeric($id)
-            && ($attach = new Attachment($id,$tid))
-            && $attach->getId()==$id)?$attach:null;
-    }
+        $id = is_numeric($var) ? $var : self::getIdByFileHash($var, $tid);
 
+        return ($id
+                && is_numeric($id)
+                && ($attach = new Attachment($id, $tid))
+                && $attach->getId()==$id
+            ) ? $attach : null;
+    }
 }
 
 class GenericAttachments {
diff --git a/include/class.export.php b/include/class.export.php
index 6f8ec713f3437a2cb21a264a80aa4637966ddd7e..51aec30250c6921671b3f76fbdaf0c9a3323b55d 100644
--- a/include/class.export.php
+++ b/include/class.export.php
@@ -316,8 +316,8 @@ class DatabaseExporter {
         GROUP_DEPT_TABLE, TEAM_TABLE, TEAM_MEMBER_TABLE, FAQ_TABLE,
         FAQ_TOPIC_TABLE, FAQ_CATEGORY_TABLE, DRAFT_TABLE,
         CANNED_TABLE, TICKET_TABLE, ATTACHMENT_TABLE,
-        TICKET_THREAD_TABLE, TICKET_ATTACHMENT_TABLE, TICKET_PRIORITY_TABLE,
-        TICKET_LOCK_TABLE, TICKET_EVENT_TABLE, TICKET_EMAIL_INFO_TABLE,
+        THREAD_TABLE, THREAD_ENTRY_TABLE, THREAD_ENTRY_EMAIL_TABLE,
+        TICKET_LOCK_TABLE, TICKET_EVENT_TABLE, TICKET_PRIORITY_TABLE,
         EMAIL_TABLE, EMAIL_TEMPLATE_TABLE, EMAIL_TEMPLATE_GRP_TABLE,
         FILTER_TABLE, FILTER_RULE_TABLE, SLA_TABLE, API_KEY_TABLE,
         TIMEZONE_TABLE, SESSION_TABLE, PAGE_TABLE,
diff --git a/include/class.file.php b/include/class.file.php
index 15f4ea17a0f8dc294672bfa4d695a7a12dc1ff09..7fe6a769942d88d4bd96545496f44d19d2b97a94 100644
--- a/include/class.file.php
+++ b/include/class.file.php
@@ -29,11 +29,14 @@ class AttachmentFile {
         if(!$id && !($id=$this->getId()))
             return false;
 
-        $sql='SELECT id, f.type, size, name, `key`, signature, ft, bk, f.created, '
-            .' count(DISTINCT a.object_id) as canned, count(DISTINCT t.ticket_id) as tickets '
+        $sql='SELECT f.id, f.type, size, name, `key`, signature, ft, bk, f.created, '
+            .' count(DISTINCT a.object_id) as canned, '
+            .' count(DISTINCT t.id) as entries '
             .' FROM '.FILE_TABLE.' f '
-            .' LEFT JOIN '.ATTACHMENT_TABLE.' a ON(a.file_id=f.id) '
-            .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' t ON(t.file_id=f.id) '
+            .' LEFT JOIN '.ATTACHMENT_TABLE.' a
+                ON(a.file_id=f.id) '
+            .' LEFT JOIN '.THREAD_ENTRY_ATTACHMENT_TABLE.' t
+                ON(t.file_id = f.id) '
             .' WHERE f.id='.db_input($id)
             .' GROUP BY f.id';
         if(!($res=db_query($sql)) || !db_num_rows($res))
@@ -57,8 +60,8 @@ class AttachmentFile {
         return $this->getHashtable();
     }
 
-    function getNumTickets() {
-        return $this->ht['tickets'];
+    function getNumEntries() {
+        return $this->ht['entries'];
     }
 
     function isCanned() {
@@ -66,7 +69,7 @@ class AttachmentFile {
     }
 
     function isInUse() {
-        return ($this->getNumTickets() || $this->isCanned());
+        return ($this->getNumEntries() || $this->isCanned());
     }
 
     function getId() {
@@ -577,7 +580,7 @@ class AttachmentFile {
         //      files attached to tickets or other things in the attachment
         //      table and are not logos
         $sql = 'SELECT id FROM '.FILE_TABLE.' WHERE id NOT IN ('
-                .'SELECT file_id FROM '.TICKET_ATTACHMENT_TABLE
+                .'SELECT file_id FROM '.THREAD_ENTRY_ATTACHMENT_TABLE
                 .' UNION '
                 .'SELECT file_id FROM '.ATTACHMENT_TABLE
             .") AND `ft` = 'T' AND TIMESTAMPDIFF(DAY, `created`, CURRENT_TIMESTAMP) > 1";
diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php
index 1210bffa3f3bfd924169e87939d369c229a4a9ce..77725c0729e9100733aacfc3c6d58feebb64d032 100644
--- a/include/class.mailfetch.php
+++ b/include/class.mailfetch.php
@@ -515,7 +515,7 @@ class MailFetcher {
             foreach ($struct->parameters as $p) {
                 if (strtolower($p->attribute) == 'report-type'
                         && $p->value == 'delivery-status') {
-                    return new TextThreadBody( $this->getPart(
+                    return new TextThreadEntryBody( $this->getPart(
                                 $mid, 'text/plain', $this->charset, $struct, false, 1));
                 }
             }
@@ -555,19 +555,19 @@ class MailFetcher {
 
         if ($cfg->isHtmlThreadEnabled()) {
             if ($html=$this->getPart($mid, 'text/html', $this->charset))
-                $body = new HtmlThreadBody($html);
+                $body = new HtmlThreadEntryBody($html);
             elseif ($text=$this->getPart($mid, 'text/plain', $this->charset))
-                $body = new TextThreadBody($text);
+                $body = new TextThreadEntryBody($text);
         }
         elseif ($text=$this->getPart($mid, 'text/plain', $this->charset))
-            $body = new TextThreadBody($text);
+            $body = new TextThreadEntryBody($text);
         elseif ($html=$this->getPart($mid, 'text/html', $this->charset))
-            $body = new TextThreadBody(
+            $body = new TextThreadEntryBody(
                     Format::html2text(Format::safe_html($html),
                         100, false));
 
         if (!isset($body))
-            $body = new TextThreadBody('');
+            $body = new TextThreadEntryBody('');
 
         if ($cfg->stripQuotedReply())
             $body->stripQuotedReply($cfg->getReplySeparator());
diff --git a/include/class.mailparse.php b/include/class.mailparse.php
index 4038f1226fa53b3a038bbea912ada03e3ceaf108..c2b51d7f07b46523bebc22292388d8bc494ecc62 100644
--- a/include/class.mailparse.php
+++ b/include/class.mailparse.php
@@ -304,19 +304,19 @@ class Mail_Parse {
 
         if ($cfg && $cfg->isHtmlThreadEnabled()) {
             if ($html=$this->getPart($this->struct,'text/html'))
-                $body = new HtmlThreadBody($html);
+                $body = new HtmlThreadEntryBody($html);
             elseif ($text=$this->getPart($this->struct,'text/plain'))
-                $body = new TextThreadBody($text);
+                $body = new TextThreadEntryBody($text);
         }
         elseif ($text=$this->getPart($this->struct,'text/plain'))
-            $body = new TextThreadBody($text);
+            $body = new TextThreadEntryBody($text);
         elseif ($html=$this->getPart($this->struct,'text/html'))
-            $body = new TextThreadBody(
+            $body = new TextThreadEntryBody(
                     Format::html2text(Format::safe_html($html),
                         100, false));
 
         if (!isset($body))
-            $body = new TextThreadBody('');
+            $body = new TextThreadEntryBody('');
 
         if ($cfg && $cfg->stripQuotedReply())
             $body->stripQuotedReply($cfg->getReplySeparator());
diff --git a/include/class.search.php b/include/class.search.php
index d4bb2a723c298ab3e267f446b68e277644cc73ba..87cabbbb968c201ac8ac67ccba8a9093f4f0aa20 100644
--- a/include/class.search.php
+++ b/include/class.search.php
@@ -374,8 +374,7 @@ class MysqlSearchBackend extends SearchBackend {
         };
 
         // THREADS ----------------------------------
-
-        $sql = "SELECT A1.`id`, A1.`title`, A1.`body`, A1.`format` FROM `".TICKET_THREAD_TABLE."` A1
+        $sql = "SELECT A1.`id`, A1.`title`, A1.`body`, A1.`format` FROM `".THREAD_ENTRY_TABLE."` A1
             LEFT JOIN `".TABLE_PREFIX."_search` A2 ON (A1.`id` = A2.`object_id` AND A2.`object_type`='H')
             WHERE A2.`object_id` IS NULL AND (A1.poster <> 'SYSTEM')
             AND (LENGTH(A1.`title`) + LENGTH(A1.`body`) > 0)
@@ -384,7 +383,7 @@ class MysqlSearchBackend extends SearchBackend {
             return false;
 
         while ($row = db_fetch_row($res)) {
-            $body = ThreadBody::fromFormattedText($row[2], $row[3]);
+            $body = ThreadEntryBody::fromFormattedText($row[2], $row[3]);
             $body = $body->getSearchable();
             $title = Format::searchable($row[1]);
             if (!$body && !$title)
diff --git a/include/class.staff.php b/include/class.staff.php
index 4fa79eb6a9a62e83065c1c7b80d92cd9b6b1832a..048db0984691bb4837a9bbf9f3480757a1cb91f1 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -564,6 +564,11 @@ implements AuthenticatedUser {
         if (!parent::delete())
             return false;
 
+        //Update the poster and clear staff_id on ticket thread table.
+        db_query('UPDATE '.THREAD_ENTRY_TABLE
+                .' SET staff_id=0, poster= '.db_input($this->getName()->getOriginal())
+                .' WHERE staff_id='.db_input($this->getId()));
+
         // DO SOME HOUSE CLEANING
         //Move remove any ticket assignments...TODO: send alert to Dept. manager?
         db_query('UPDATE '.TICKET_TABLE.' SET staff_id=0 WHERE staff_id='.db_input($this->getId()));
diff --git a/include/class.thread.php b/include/class.thread.php
index 7a95ebd3da025a6cd0e08e66aaa35a0fb2a4a748..81b9f49411a771cd9495a3a508e23ed31c418bd4 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -2,7 +2,7 @@
 /*********************************************************************
     class.thread.php
 
-    Ticket thread
+    Thread of things!
     XXX: Please DO NOT add any ticket related logic! use ticket class.
 
     Peter Rotich <peter@osticket.com>
@@ -20,127 +20,91 @@ include_once(INCLUDE_DIR.'class.draft.php');
 //Ticket thread.
 class Thread {
 
-    var $id; // same as ticket ID.
-    var $ticket;
-
-    function Thread($ticket) {
-
-        $this->ticket = $ticket;
-
-        $this->id = 0;
+    var $ht;
 
-        $this->load();
+    function Thread($id) {
+        $this->load($id);
     }
 
-    function load() {
+    function load($id=0) {
 
-        if(!$this->getTicketId())
+        if (!$id && !($id=$this->getId()))
             return null;
 
-        $sql='SELECT ticket.ticket_id as id '
-            .' ,count(DISTINCT attach.attach_id) as attachments '
-            .' ,count(DISTINCT message.id) as messages '
-            .' ,count(DISTINCT response.id) as responses '
-            .' ,count(DISTINCT note.id) as notes '
-            .' FROM '.TICKET_TABLE.' ticket '
-            .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON ('
-                .'ticket.ticket_id=attach.ticket_id) '
-            .' LEFT JOIN '.TICKET_THREAD_TABLE.' message ON ('
-                ."ticket.ticket_id=message.ticket_id AND message.thread_type = 'M') "
-            .' LEFT JOIN '.TICKET_THREAD_TABLE.' response ON ('
-                ."ticket.ticket_id=response.ticket_id AND response.thread_type = 'R') "
-            .' LEFT JOIN '.TICKET_THREAD_TABLE.' note ON ( '
-                ."ticket.ticket_id=note.ticket_id AND note.thread_type = 'N') "
-            .' WHERE ticket.ticket_id='.db_input($this->getTicketId())
-            .' GROUP BY ticket.ticket_id';
-
-        if(!($res=db_query($sql)) || !db_num_rows($res))
-            return false;
+        $sql='SELECT thread.* '
+            .' ,count(DISTINCT attach.id) as attachments '
+            .' ,count(DISTINCT entry.id) as entries '
+            .' FROM '.THREAD_TABLE.' thread '
+            .' LEFT JOIN '.THREAD_ENTRY_TABLE.' entry
+                ON (entry.thread_id = thread.id) '
+            .' LEFT JOIN '.THREAD_ENTRY_ATTACHMENT_TABLE.' attach
+                ON (attach.thread_entry_id=entry.id) '
+            .' WHERE thread.id='.db_input($id)
+            .' GROUP BY thread.id';
 
-        $this->ht = db_fetch_array($res);
+        $this->ht = array();
+        if (($res=db_query($sql)) && db_num_rows($res))
+            $this->ht = db_fetch_array($res);
 
-        $this->id = $this->ht['id'];
+        return ($this->ht);
+    }
 
-        return true;
+    function reload() {
+        return $this->load();
     }
 
     function getId() {
-        return $this->id;
+        return $this->ht['id'];
     }
 
-    function getTicketId() {
-        return $this->getTicket()?$this->getTicket()->getId():0;
+    function getObjectId() {
+        return $this->ht['object_id'];
     }
 
-    function getTicket() {
-        return $this->ticket;
+    function getObjectType() {
+        return $this->ht['object_type'];
     }
 
     function getNumAttachments() {
         return $this->ht['attachments'];
     }
 
-    function getNumMessages() {
-        return $this->ht['messages'];
-    }
-
-    function getNumResponses() {
-        return $this->ht['responses'];
-    }
-
-    function getNumNotes() {
-        return $this->ht['notes'];
-    }
-
-    function getCount() {
-        return $this->getNumMessages() + $this->getNumResponses();
-    }
-
-    function getMessages() {
-        return $this->getEntries('M');
-    }
-
-    function getResponses() {
-        return $this->getEntries('R');
-    }
-
-    function getNotes() {
-        return $this->getEntries('N');
+    function getNumEntries() {
+        return $this->ht['entries'];
     }
 
     function getEntries($type, $order='ASC') {
 
-        if(!$order || !in_array($order, array('DESC','ASC')))
+        if (!$order || !in_array($order, array('DESC','ASC')))
             $order='ASC';
 
-        $sql='SELECT thread.*
+        $sql='SELECT entry.*
                , COALESCE(user.name,
                     IF(staff.staff_id,
                         CONCAT_WS(" ", staff.firstname, staff.lastname),
                         NULL)) as name '
-            .' ,count(DISTINCT attach.attach_id) as attachments '
-            .' FROM '.TICKET_THREAD_TABLE.' thread '
+            .' ,count(DISTINCT attach.id) as attachments '
+            .' FROM '.THREAD_ENTRY_TABLE.' entry '
             .' LEFT JOIN '.USER_TABLE.' user
-                ON (thread.user_id=user.id) '
+                ON (entry.user_id=user.id) '
             .' LEFT JOIN '.STAFF_TABLE.' staff
-                ON (thread.staff_id=staff.staff_id) '
-            .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach
-                ON (thread.ticket_id=attach.ticket_id
-                        AND thread.id=attach.ref_id) '
-            .' WHERE  thread.ticket_id='.db_input($this->getTicketId());
+                ON (entry.staff_id=staff.staff_id) '
+            .' LEFT JOIN '.THREAD_ENTRY_ATTACHMENT_TABLE.' attach
+                ON (attach.thread_entry_id = entry.id) '
+            .' WHERE  entry.thread_id='.db_input($this->getId());
 
         if($type && is_array($type))
-            $sql.=' AND thread.thread_type IN('.implode(',', db_input($type)).')';
+            $sql.=' AND entry.`type` IN ('.implode(',', db_input($type)).')';
         elseif($type)
-            $sql.=' AND thread.thread_type='.db_input($type);
+            $sql.=' AND entry.`type` = '.db_input($type);
 
-        $sql.=' GROUP BY thread.id '
-             .' ORDER BY thread.created '.$order;
+        $sql.=' GROUP BY entry.id '
+             .' ORDER BY entry.created '.$order;
 
         $entries = array();
         if(($res=db_query($sql)) && db_num_rows($res)) {
             while($rec=db_fetch_array($res)) {
-                $rec['body'] = ThreadBody::fromFormattedText($rec['body'], $rec['format']);
+                $rec['body'] = ThreadEntryBody::fromFormattedText($rec['body'], $rec['format']);
                 $entries[] = $rec;
             }
         }
@@ -149,71 +113,51 @@ class Thread {
     }
 
     function getEntry($id) {
-        return ThreadEntry::lookup($id, $this->getTicketId());
-    }
-
-    function addNote($vars, &$errors) {
-
-        //Add ticket Id.
-        $vars['ticketId'] = $this->getTicketId();
-
-        return Note::create($vars, $errors);
-    }
-
-    function addMessage($vars, &$errors) {
-
-        $vars['ticketId'] = $this->getTicketId();
-        $vars['staffId'] = 0;
-
-        return Message::create($vars, $errors);
+        return ThreadEntry::lookup($id, $this->getId());
     }
 
-    function addResponse($vars, &$errors) {
-
-        $vars['ticketId'] = $this->getTicketId();
-        $vars['userId'] = 0;
-
-        return Response::create($vars, $errors);
-    }
 
     function deleteAttachments() {
 
-        $deleted=0;
         // Clear reference table
-        $res=db_query('DELETE FROM '.TICKET_ATTACHMENT_TABLE.' WHERE ticket_id='.db_input($this->getTicketId()));
-        if ($res && db_affected_rows())
-            $deleted = AttachmentFile::deleteOrphans();
+        $sql = 'DELETE FROM '.THREAD_ENTRY_ATTACHMENT_TABLE. ' a '
+             . 'INNER JOIN '.THREAD_ENTRY_TABLE.' e
+                    ON(e.id = a.thread_entry_id) '
+             . ' WHERE e.thread_id='.db_input($this->getId());
+
+        $deleted=0;
+        if (($res=db_query($sql)) && ($deleted=db_affected_rows()))
+            AttachmentFile::deleteOrphans();
 
         return $deleted;
     }
 
     function delete() {
 
-        $sql = 'UPDATE '.TICKET_EMAIL_INFO_TABLE.' mid
-            INNER JOIN '.TICKET_THREAD_TABLE.' thread ON (thread.id = mid.thread_id)
-            SET mid.headers = null WHERE thread.ticket_id = '
-            .db_input($this->getTicketId());
-        db_query($sql);
+        //Self delete
+        $sql = 'DELETE FROM '.THREAD_TABLE.' WHERE
+            id='.db_input($this->getId());
 
-        $res=db_query('DELETE FROM '.TICKET_THREAD_TABLE.' WHERE ticket_id='.db_input($this->getTicketId()));
-        if(!$res || !db_affected_rows())
+        if (!db_query($sql) || !db_affected_rows())
             return false;
 
+        // Clear email meta data (header..etc)
+        $sql = 'UPDATE '.THREAD_ENTRY_EMAIL_TABLE.' email '
+             . 'INNER JOIN '.THREAD_ENTRY_TABLE.' entry
+                    ON (entry.id = email.thread_entry_id) '
+             . 'SET email.headers = null '
+             . 'WHERE entry.thread_id = '.db_input($this->getId());
+        db_query($sql);
+
+        // Mass delete entries
         $this->deleteAttachments();
+        $sql = 'DELETE FROM '.THREAD_ENTRY_TABLE
+             . ' WHERE thread_id='.db_input($this->getId());
+        db_query($sql);
 
         return true;
     }
 
-    /* static */
-    function lookup($ticket) {
-
-        return ($ticket
-                && is_object($ticket)
-                && ($thread = new Thread($ticket))
-                && $thread->getId()
-                )?$thread:null;
-    }
-
     function getVar($name) {
         switch ($name) {
         case 'original':
@@ -226,6 +170,27 @@ class Thread {
             break;
         }
     }
+
+    static function create($vars) {
+
+        if (!$vars || !$vars['object_id'] || !$vars['object_type'])
+            return false;
+
+        $sql = 'INSERT INTO '.THREAD_TABLE.' SET created=NOW() '
+              .', object_id='.db_input($vars['object_id'])
+              .', object_type='.db_input($vars['object_type']);
+
+        return db_query($sql) ? db_insert_id() : 0;
+    }
+
+    static function lookup($id) {
+
+        return ($id
+                && ($thread = new Thread($id))
+                && $thread->getId()
+                )
+            ? $thread : null;
+    }
 }
 
 
@@ -234,46 +199,41 @@ Class ThreadEntry {
     var $id;
     var $ht;
 
-    var $staff;
-    var $ticket;
-
+    var $thread;
     var $attachments;
 
-
-    function ThreadEntry($id, $type='', $ticketId=0) {
-        $this->load($id, $type, $ticketId);
+    function ThreadEntry($id, $threadId=0, $type='') {
+        $this->load($id, $threadId, $type);
     }
 
-    function load($id=0, $type='', $ticketId=0) {
+    function load($id=0, $threadId=0, $type='') {
 
-        if(!$id && !($id=$this->getId()))
+        if (!$id && !($id=$this->getId()))
             return false;
 
-        $sql='SELECT thread.*, info.email_mid, info.headers '
-            .' ,count(DISTINCT attach.attach_id) as attachments '
-            .' FROM '.TICKET_THREAD_TABLE.' thread '
-            .' LEFT JOIN '.TICKET_EMAIL_INFO_TABLE.' info
-                ON (thread.id=info.thread_id) '
-            .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach
-                ON (thread.ticket_id=attach.ticket_id
-                        AND thread.id=attach.ref_id) '
-            .' WHERE  thread.id='.db_input($id);
+        $sql='SELECT entry.*, email.mid, email.headers '
+            .' ,count(DISTINCT attach.id) as attachments '
+            .' FROM '.THREAD_ENTRY_TABLE.' entry '
+            .' LEFT JOIN '.THREAD_ENTRY_EMAIL_TABLE.' email
+                ON (email.thread_entry_id=entry.id) '
+            .' LEFT JOIN '.THREAD_ENTRY_ATTACHMENT_TABLE.' attach
+                ON (attach.thread_entry_id=entry.id) '
+            .' WHERE  entry.id='.db_input($id);
 
-        if($type)
-            $sql.=' AND thread.thread_type='.db_input($type);
+        if ($type)
+            $sql.=' AND entry.type='.db_input($type);
 
-        if($ticketId)
-            $sql.=' AND thread.ticket_id='.db_input($ticketId);
+        if ($threadId)
+            $sql.=' AND entry.thread_id='.db_input($threadId);
 
-        $sql.=' GROUP BY thread.id ';
+        $sql.=' GROUP BY entry.id ';
 
-        if(!($res=db_query($sql)) || !db_num_rows($res))
+        if (!($res=db_query($sql)) || !db_num_rows($res))
             return false;
 
         $this->ht = db_fetch_array($res);
         $this->id = $this->ht['id'];
 
-        $this->staff = $this->ticket = null;
         $this->attachments = array();
 
         return true;
@@ -292,7 +252,7 @@ Class ThreadEntry {
     }
 
     function getType() {
-        return $this->ht['thread_type'];
+        return $this->ht['type'];
     }
 
     function getSource() {
@@ -308,20 +268,20 @@ Class ThreadEntry {
     }
 
     function getBody() {
-        return ThreadBody::fromFormattedText($this->ht['body'], $this->ht['format']);
+        return ThreadEntryBody::fromFormattedText($this->ht['body'], $this->ht['format']);
     }
 
     function setBody($body) {
         global $cfg;
 
-        if (!$body instanceof ThreadBody) {
+        if (!$body instanceof ThreadEntryBody) {
             if ($cfg->isHtmlThreadEnabled())
-                $body = new HtmlThreadBody($body);
+                $body = new HtmlThreadEntryBody($body);
             else
-                $body = new TextThreadBody($body);
+                $body = new TextThreadEntryBody($body);
         }
 
-        $sql='UPDATE '.TICKET_THREAD_TABLE.' SET updated=NOW()'
+        $sql='UPDATE '.THREAD_ENTRY_TABLE.' SET updated=NOW()'
             .',format='.db_input($body->getType())
             .',body='.db_input((string) $body)
             .' WHERE id='.db_input($this->getId());
@@ -340,12 +300,8 @@ Class ThreadEntry {
         return $this->ht['attachments'];
     }
 
-    function getTicketId() {
-        return $this->ht['ticket_id'];
-    }
-
     function getEmailMessageId() {
-        return $this->ht['email_mid'];
+        return $this->ht['mid'];
     }
 
     function getEmailHeaderArray() {
@@ -398,12 +354,16 @@ Class ThreadEntry {
 
     }
 
-    function getTicket() {
+    function getThreadId() {
+        return $this->ht['thread_id'];
+    }
+
+    function getThread() {
 
-        if(!$this->ticket && $this->getTicketId())
-            $this->ticket = Ticket::lookup($this->getTicketId());
+        if(!$this->thread && $this->getThreadId())
+            $this->thread = Thread::lookup($this->getThreadId());
 
-        return $this->ticket;
+        return $this->thread;
     }
 
     function getStaffId() {
@@ -548,26 +508,26 @@ Class ThreadEntry {
 
         $inline = is_array($file) && @$file['inline'];
 
-        // TODO: Add a unique index to TICKET_ATTACHMENT_TABLE (file_id,
-        // ref_id), and remove this block
-        if ($id = db_result(db_query('SELECT attach_id FROM '.TICKET_ATTACHMENT_TABLE
-                .' WHERE file_id='.db_input($fileId).' AND ref_id='
-                .db_input($this->getId()))))
+        // TODO: Add a unique index to THREAD_ENTRY_ATTACHMENT_TABLE (file_id,
+        // thread_entry_id), and remove this block
+        if ($id = db_result(db_query('SELECT id FROM '.THREAD_ENTRY_ATTACHMENT_TABLE
+                .' WHERE file_id='.db_input($fileId)
+                .' AND thread_entry_id=' .db_input($this->getId()))))
+
             return $id;
 
-        $sql ='INSERT IGNORE INTO '.TICKET_ATTACHMENT_TABLE.' SET created=NOW() '
+        $sql ='INSERT IGNORE INTO '.THREAD_ENTRY_ATTACHMENT_TABLE.' SET created=NOW() '
              .' ,file_id='.db_input($fileId)
-             .' ,ticket_id='.db_input($this->getTicketId())
-             .' ,inline='.db_input($inline ? 1 : 0)
-             .' ,ref_id='.db_input($this->getId());
+             .' ,thread_entry_id='.db_input($this->getId())
+             .' ,inline='.db_input($inline ? 1 : 0);
 
         return (db_query($sql) && ($id=db_insert_id()))?$id:0;
     }
 
     function saveAttachments($files) {
         $ids=array();
-        foreach($files as $file)
-           if(($id=$this->saveAttachment($file)))
+        foreach ($files as $file)
+           if (($id=$this->saveAttachment($file)))
                $ids[] = $id;
 
         return $ids;
@@ -575,19 +535,19 @@ Class ThreadEntry {
 
     function getAttachments() {
 
-        if($this->attachments)
+        if ($this->attachments)
             return $this->attachments;
 
         //XXX: inner join the file table instead?
-        $sql='SELECT a.attach_id, f.id as file_id, f.size, lower(f.`key`) as file_hash, f.name, a.inline '
+        $sql='SELECT a.id, f.id as file_id, f.size, lower(f.`key`) as file_hash, f.name, a.inline '
             .' FROM '.FILE_TABLE.' f '
-            .' INNER JOIN '.TICKET_ATTACHMENT_TABLE.' a ON(f.id=a.file_id) '
-            .' WHERE a.ticket_id='.db_input($this->getTicketId())
-            .' AND a.ref_id='.db_input($this->getId());
+            .' INNER JOIN '.THREAD_ENTRY_ATTACHMENT_TABLE.' a
+                ON(a.file_id=f.id) '
+            .' WHERE a.thread_entry_id='.db_input($this->getId());
 
         $this->attachments = array();
-        if(($res=db_query($sql)) && db_num_rows($res)) {
-            while($rec=db_fetch_array($res))
+        if (($res=db_query($sql)) && db_num_rows($res)) {
+            while ($rec=db_fetch_array($res))
                 $this->attachments[] = $rec;
         }
 
@@ -598,7 +558,7 @@ Class ThreadEntry {
         $json = array();
         foreach ($this->getAttachments() as $att) {
             $json[$att['file_hash']] = array(
-                'download_url' => sprintf('attachment.php?id=%d&h=%s', $att['attach_id'],
+                'download_url' => sprintf('attachment.php?id=%d&h=%s', $att['id'],
                     strtolower(md5($att['file_id'].session_id().$att['file_hash']))),
                 'filename' => $att['name'],
             );
@@ -619,7 +579,7 @@ Class ThreadEntry {
                 $size=sprintf('<em>(%s)</em>', Format::file_size($attachment['size']));
 
             $str.=sprintf('<a class="Icon file no-pjax" href="%s?id=%d&h=%s" target="%s">%s</a>%s&nbsp;%s',
-                    $file, $attachment['attach_id'], $hash, $target, Format::htmlchars($attachment['name']), $size, $separator);
+                    $file, $attachment['id'], $hash, $target, Format::htmlchars($attachment['name']), $size, $separator);
         }
 
         return $str;
@@ -774,12 +734,17 @@ Class ThreadEntry {
 
     /* static */
     function logEmailHeaders($id, $mid, $header=false) {
-        $sql='INSERT INTO '.TICKET_EMAIL_INFO_TABLE
-            .' SET thread_id='.db_input($id)
-            .', email_mid='.db_input($mid); //TODO: change it to message_id.
+
+        if (!$id || !$mid)
+            return false;
+
+        $sql='INSERT INTO '.THREAD_ENTRY_EMAIL_TABLE
+            .' SET thread_entry_id='.db_input($id)
+            .', mid='.db_input($mid);
         if ($header)
             $sql .= ', headers='.db_input($header);
-        return db_query($sql)?db_insert_id():0;
+
+        return db_query($sql) ? db_insert_id() : 0;
     }
 
     /* variables */
@@ -809,12 +774,10 @@ Class ThreadEntry {
         return false;
     }
 
-    /* static calls */
-
-    function lookup($id, $tid=0, $type='') {
+    static function lookup($id, $tid=0, $type='') {
         return ($id
                 && is_numeric($id)
-                && ($e = new ThreadEntry($id, $type, $tid))
+                && ($e = new ThreadEntry($id, $tid, $type))
                 && $e->getId()==$id
                 )?$e:null;
     }
@@ -835,8 +798,9 @@ Class ThreadEntry {
     function lookupByEmailHeaders(&$mailinfo, &$seen=false) {
         // Search for messages using the References header, then the
         // in-reply-to header
-        $search = 'SELECT thread_id, email_mid FROM '.TICKET_EMAIL_INFO_TABLE
-               . ' WHERE email_mid=%s ORDER BY thread_id DESC';
+        $search = 'SELECT thread_entery_id, mid FROM '.THREAD_ENTRY_EMAIL_TABLE
+               . ' WHERE mid=%s '
+               . ' ORDER BY thread_entry_id DESC';
 
         if (list($id, $mid) = db_fetch_row(db_query(
                 sprintf($search, db_input($mailinfo['mid']))))) {
@@ -971,7 +935,7 @@ Class ThreadEntry {
         global $ost;
 
         $domain = md5($ost->getConfig()->getURL());
-        $ticket = $this->getTicket();
+        $ticket = $this->getThread()->getObject();
         return sprintf('$%s$%s@%s',
             base64_encode(pack('V', $this->getId())),
             substr(md5($to . $ticket->getNumber() . $ticket->getId()), -10),
@@ -980,19 +944,19 @@ Class ThreadEntry {
     }
 
     //new entry ... we're trusting the caller to check validity of the data.
-    function create($vars) {
+    static function create($vars) {
         global $cfg;
 
         //Must have...
-        if(!$vars['ticketId'] || !$vars['type'] || !in_array($vars['type'], array('M','R','N')))
+        if (!$vars['threadId'] || !$vars['type'])
             return false;
 
 
-        if (!$vars['body'] instanceof ThreadBody) {
+        if (!$vars['body'] instanceof ThreadEntryBody) {
             if ($cfg->isHtmlThreadEnabled())
-                $vars['body'] = new HtmlThreadBody($vars['body']);
+                $vars['body'] = new HtmlThreadEntryBody($vars['body']);
             else
-                $vars['body'] = new TextThreadBody($vars['body']);
+                $vars['body'] = new TextThreadEntryBody($vars['body']);
         }
 
         // Drop stripped images
@@ -1008,7 +972,7 @@ Class ThreadEntry {
         }
 
         // Handle extracted embedded images (<img src="data:base64,..." />).
-        // The extraction has already been performed in the ThreadBody
+        // The extraction has already been performed in the ThreadEntryBody
         // class. Here they should simply be added to the attachments list
         if ($atts = $vars['body']->getEmbeddedHtmlImages()) {
             if (!is_array($vars['attachments']))
@@ -1025,22 +989,22 @@ Class ThreadEntry {
         if ($poster && is_object($poster))
             $poster = (string) $poster;
 
-        $sql=' INSERT INTO '.TICKET_THREAD_TABLE.' SET created=NOW() '
-            .' ,thread_type='.db_input($vars['type'])
-            .' ,ticket_id='.db_input($vars['ticketId'])
-            .' ,title='.db_input(Format::sanitize($vars['title'], true))
-            .' ,format='.db_input($vars['body']->getType())
-            .' ,staff_id='.db_input($vars['staffId'])
-            .' ,user_id='.db_input($vars['userId'])
-            .' ,poster='.db_input($poster)
-            .' ,source='.db_input($vars['source']);
+        $sql=' INSERT INTO '.THREAD_ENTRY_TABLE.' SET `created` = NOW() '
+            .' ,`type` = '.db_input($vars['type'])
+            .' ,`thread_id` = '.db_input($vars['threadId'])
+            .' ,`title` = '.db_input(Format::sanitize($vars['title'], true))
+            .' ,`format` = '.db_input($vars['body']->getType())
+            .' ,`staff_id` = '.db_input($vars['staffId'])
+            .' ,`user_id` = '.db_input($vars['userId'])
+            .' ,`poster` = '.db_input($poster)
+            .' ,`source` = '.db_input($vars['source']);
 
         if (!isset($vars['attachments']) || !$vars['attachments'])
             // Otherwise, body will be configured in a block below (after
             // inline attachments are saved and updated in the database)
             $sql.=' ,body='.db_input($body);
 
-        if(isset($vars['pid']))
+        if (isset($vars['pid']))
             $sql.=' ,pid='.db_input($vars['pid']);
         // Check if 'reply_to' is in the $vars as the previous ThreadEntry
         // instance. If the body of the previous message is found in the new
@@ -1049,11 +1013,12 @@ Class ThreadEntry {
                 && $vars['reply_to'] instanceof ThreadEntry)
             $sql.=' ,pid='.db_input($vars['reply_to']->getId());
 
-        if($vars['ip_address'])
+        if ($vars['ip_address'])
             $sql.=' ,ip_address='.db_input($vars['ip_address']);
 
         //echo $sql;
-        if(!db_query($sql) || !($entry=self::lookup(db_insert_id(), $vars['ticketId'])))
+        if (!db_query($sql)
+                || !($entry=self::lookup(db_insert_id(), $vars['threadId'])))
             return false;
 
         /************* ATTACHMENTS *****************/
@@ -1086,16 +1051,20 @@ Class ThreadEntry {
                         'src="cid:'.$a['key'].'"', $body);
                 }
             }
-            $sql = 'UPDATE '.TICKET_THREAD_TABLE.' SET body='.db_input($body)
+
+            $sql = 'UPDATE '.THREAD_ENTRY_TABLE
+                .' SET body='.db_input($body)
                 .' WHERE `id`='.db_input($entry->getId());
+
             if (!db_query($sql) || !db_affected_rows())
                 return false;
         }
 
         // Email message id (required for all thread posts)
         if (!isset($vars['mid']))
-            $vars['mid'] = sprintf('<%s@%s>', Misc::randCode(24),
-                substr(md5($cfg->getUrl()), -10));
+            $vars['mid'] = sprintf('<%s@%s>',
+                    Misc::randCode(24), substr(md5($cfg->getUrl()), -10));
+
         $entry->saveEmailInfo($vars);
 
         // Inline images (attached to the draft)
@@ -1106,172 +1075,13 @@ Class ThreadEntry {
         return $entry;
     }
 
-    function add($vars) {
-        return ($entry=self::create($vars))?$entry->getId():0;
+    static function add($vars) {
+        return ($entry=self::create($vars)) ? $entry->getId() : 0;
     }
 }
 
-/* Message - Ticket thread entry of type message */
-class Message extends ThreadEntry {
-
-    function Message($id, $ticketId=0) {
-        parent::ThreadEntry($id, 'M', $ticketId);
-    }
-
-    function getSubject() {
-        return $this->getTitle();
-    }
-
-    function create($vars, &$errors) {
-        return self::lookup(self::add($vars, $errors));
-    }
-
-    function add($vars, &$errors) {
-
-        if(!$vars || !is_array($vars) || !$vars['ticketId'])
-            $errors['err'] = __('Missing or invalid data');
-        elseif(!$vars['message'])
-            $errors['message'] = __('Message content is required');
-
-        if($errors) return false;
-
-        $vars['type'] = 'M';
-        $vars['body'] = $vars['message'];
-
-        if (!$vars['poster']
-                && $vars['userId']
-                && ($user = User::lookup($vars['userId'])))
-            $vars['poster'] = (string) $user->getName();
-
-        return ThreadEntry::add($vars);
-    }
-
-    function lookup($id, $tid=0, $type='M') {
-
-        return ($id
-                && is_numeric($id)
-                && ($m = new Message($id, $tid))
-                && $m->getId()==$id
-                )?$m:null;
-    }
-
-    function lastByTicketId($ticketId) {
-        return self::byTicketId($ticketId);
-    }
-
-    function firstByTicketId($ticketId) {
-        return self::byTicketId($ticketId, false);
-    }
-
-    function byTicketId($ticketId, $last=true) {
-
-        $sql=' SELECT thread.id FROM '.TICKET_THREAD_TABLE.' thread '
-            .' WHERE thread_type=\'M\' AND thread.ticket_id = '.db_input($ticketId)
-            .sprintf(' ORDER BY thread.id %s LIMIT 1', $last ? 'DESC' : 'ASC');
-
-        if (($res = db_query($sql)) && ($id = db_result($res)))
-            return Message::lookup($id);
-
-        return null;
-    }
-}
-
-/* Response - Ticket thread entry of type response */
-class Response extends ThreadEntry {
-
-    function Response($id, $ticketId=0) {
-        parent::ThreadEntry($id, 'R', $ticketId);
-    }
-
-    function getSubject() {
-        return $this->getTitle();
-    }
-
-    function getRespondent() {
-        return $this->getStaff();
-    }
-
-    function create($vars, &$errors) {
-        return self::lookup(self::add($vars, $errors));
-    }
-
-    function add($vars, &$errors) {
-
-        if(!$vars || !is_array($vars) || !$vars['ticketId'])
-            $errors['err'] = __('Missing or invalid data');
-        elseif(!$vars['response'])
-            $errors['response'] = __('Response content is required');
-
-        if($errors) return false;
-
-        $vars['type'] = 'R';
-        $vars['body'] = $vars['response'];
-        if(!$vars['pid'] && $vars['msgId'])
-            $vars['pid'] = $vars['msgId'];
-
-        if (!$vars['poster']
-                && $vars['staffId']
-                && ($staff = Staff::lookup($vars['staffId'])))
-            $vars['poster'] = (string) $staff->getName();
-
-        return ThreadEntry::add($vars);
-    }
-
-
-    function lookup($id, $tid=0, $type='R') {
-
-        return ($id
-                && is_numeric($id)
-                && ($r = new Response($id, $tid))
-                && $r->getId()==$id
-                )?$r:null;
-    }
-}
-
-/* Note - Ticket thread entry of type note (Internal Note) */
-class Note extends ThreadEntry {
-
-    function Note($id, $ticketId=0) {
-        parent::ThreadEntry($id, 'N', $ticketId);
-    }
-
-    function getMessage() {
-        return $this->getBody();
-    }
-
-    /* static */
-    function create($vars, &$errors) {
-        return self::lookup(self::add($vars, $errors));
-    }
-
-    function add($vars, &$errors) {
-
-        //Check required params.
-        if(!$vars || !is_array($vars) || !$vars['ticketId'])
-            $errors['err'] = __('Missing or invalid data');
-        elseif(!$vars['note'])
-            $errors['note'] = __('Note content is required');
-
-        if($errors) return false;
-
-        //TODO: use array_intersect_key  when we move to php 5 to extract just what we need.
-        $vars['type'] = 'N';
-        $vars['body'] = $vars['note'];
-
-        return ThreadEntry::add($vars);
-    }
 
-    function lookup($id, $tid=0, $type='N') {
-
-        return ($id
-                && is_numeric($id)
-                && ($n = new Note($id, $tid))
-                && $n->getId()==$id
-                )?$n:null;
-    }
-}
-
-class ThreadBody /* extends SplString */ {
+class ThreadEntryBody /* extends SplString */ {
 
     static $types = array('text', 'html');
 
@@ -1286,7 +1096,7 @@ class ThreadBody /* extends SplString */ {
     function __construct($body, $type='text', $options=array()) {
         $type = strtolower($type);
         if (!in_array($type, static::$types))
-            throw new Exception("$type: Unsupported ThreadBody type");
+            throw new Exception("$type: Unsupported ThreadEntryBody type");
         $this->body = (string) $body;
         if (strlen($this->body) > 250000) {
             $max_packet = db_get_variable('max_allowed_packet', 'global');
@@ -1309,10 +1119,10 @@ class ThreadBody /* extends SplString */ {
         $conv = $this->type . ':' . strtolower($type);
         switch ($conv) {
         case 'text:html':
-            return new ThreadBody(sprintf('<pre>%s</pre>',
+            return new ThreadEntryBody(sprintf('<pre>%s</pre>',
                 Format::htmlchars($this->body)), $type);
         case 'html:text':
-            return new ThreadBody(Format::html2text((string) $this), $type);
+            return new ThreadEntryBody(Format::html2text((string) $this), $type);
         }
     }
 
@@ -1376,16 +1186,16 @@ class ThreadBody /* extends SplString */ {
     static function fromFormattedText($text, $format=false) {
         switch ($format) {
         case 'text':
-            return new TextThreadBody($text);
+            return new TextThreadEntryBody($text);
         case 'html':
-            return new HtmlThreadBody($text, array('strip-embedded'=>false));
+            return new HtmlThreadEntryBody($text, array('strip-embedded'=>false));
         default:
-            return new ThreadBody($text);
+            return new ThreadEntryBody($text);
         }
     }
 }
 
-class TextThreadBody extends ThreadBody {
+class TextThreadEntryBody extends ThreadEntryBody {
     function __construct($body, $options=array()) {
         parent::__construct($body, 'text', $options);
     }
@@ -1416,7 +1226,7 @@ class TextThreadBody extends ThreadBody {
         return $this->display('email');
     }
 }
-class HtmlThreadBody extends ThreadBody {
+class HtmlThreadEntryBody extends ThreadEntryBody {
     function __construct($body, $options=array()) {
         if (!isset($options['strip-embedded']) || $options['strip-embedded'])
             $body = $this->extractEmbeddedHtmlImages($body);
@@ -1461,4 +1271,269 @@ class HtmlThreadBody extends ThreadBody {
         }
     }
 }
+
+
+/* Message - Ticket thread entry of type message */
+class MessageThreadEntry extends ThreadEntry {
+
+    const ENTRY_TYPE = 'M';
+
+    function MessageThreadEntry($id, $threadId=0) {
+        parent::ThreadEntry($id, $threadId, self::ENTRY_TYPE);
+    }
+
+    function getSubject() {
+        return $this->getTitle();
+    }
+
+    static function create($vars, &$errors) {
+        return self::lookup(self::add($vars, $errors));
+    }
+
+    static function add($vars, &$errors) {
+
+        if (!$vars || !is_array($vars) || !$vars['threadId'])
+            $errors['err'] = __('Missing or invalid data');
+        elseif (!$vars['message'])
+            $errors['message'] = __('Message content is required');
+
+        if ($errors) return false;
+
+        $vars['type'] = self::ENTRY_TYPE;
+        $vars['body'] = $vars['message'];
+
+        if (!$vars['poster']
+                && $vars['userId']
+                && ($user = User::lookup($vars['userId'])))
+            $vars['poster'] = (string) $user->getName();
+
+        return parent::add($vars);
+    }
+
+    static function lookup($id, $tid=0) {
+
+        return ($id
+                && is_numeric($id)
+                && ($m = new MessageThreadEntry($id, $tid))
+                && $m->getId()==$id
+                )?$m:null;
+    }
+
+    //TODO: redo shit below.
+
+    function lastByTicketId($ticketId) {
+        return self::byTicketId($ticketId);
+    }
+
+    function firstByTicketId($ticketId) {
+        return self::byTicketId($ticketId, false);
+    }
+
+    function byTicketId($ticketId, $last=true) {
+
+        $sql=' SELECT thread.id FROM '.TICKET_THREAD_TABLE.' thread '
+            .' WHERE thread_type=\'M\' AND thread.ticket_id = '.db_input($ticketId)
+            .sprintf(' ORDER BY thread.id %s LIMIT 1', $last ? 'DESC' : 'ASC');
+
+        if (($res = db_query($sql)) && ($id = db_result($res)))
+            return Message::lookup($id);
+
+        return null;
+    }
+}
+
+
+/* thread entry of type response */
+class ResponseThreadEntry extends ThreadEntry {
+
+    const ENTRY_TYPE = 'R';
+
+    function ResponseThreadEntry($id, $threadId=0) {
+        parent::ThreadEntry($id, $threadId, self::ENTRY_TYPE);
+    }
+
+    function getSubject() {
+        return $this->getTitle();
+    }
+
+    function getRespondent() {
+        return $this->getStaff();
+    }
+
+    static function create($vars, &$errors) {
+        return self::lookup(self::add($vars, $errors));
+    }
+
+    static function add($vars, &$errors) {
+
+        if (!$vars || !is_array($vars) || !$vars['threadId'])
+            $errors['err'] = __('Missing or invalid data');
+        elseif (!$vars['response'])
+            $errors['response'] = __('Response content is required');
+
+        if ($errors) return false;
+
+        $vars['type'] = self::ENTRY_TYPE;
+        $vars['body'] = $vars['response'];
+        if (!$vars['pid'] && $vars['msgId'])
+            $vars['pid'] = $vars['msgId'];
+
+        if (!$vars['poster']
+                && $vars['staffId']
+                && ($staff = Staff::lookup($vars['staffId'])))
+            $vars['poster'] = (string) $staff->getName();
+
+        return parent::add($vars);
+    }
+
+    static function lookup($id, $tid=0) {
+
+        return ($id
+                && is_numeric($id)
+                && ($r = new ResponseThreadEntry($id, $tid))
+                && $r->getId()==$id
+                )?$r:null;
+    }
+}
+
+/* Thread entry of type note (Internal Note) */
+class NoteThreadEntry extends ThreadEntry {
+    const ENTRY_TYPE = 'N';
+
+    function NoteThreadEntry($id, $threadId=0) {
+        parent::ThreadEntry($id, $threadId, self::ENTRY_TYPE);
+    }
+
+    function getMessage() {
+        return $this->getBody();
+    }
+
+    static function create($vars, &$errors) {
+        return self::lookup(self::add($vars, $errors));
+    }
+
+    static function add($vars, &$errors) {
+
+        //Check required params.
+        if (!$vars || !is_array($vars) || !$vars['threadId'])
+            $errors['err'] = __('Missing or invalid data');
+        elseif (!$vars['note'])
+            $errors['note'] = __('Note content is required');
+
+        if ($errors) return false;
+
+        //TODO: use array_intersect_key  when we move to php 5 to extract just what we need.
+        $vars['type'] = self::ENTRY_TYPE;
+        $vars['body'] = $vars['note'];
+
+        return parent::add($vars);
+    }
+
+    static function lookup($id, $tid=0) {
+
+        return ($id
+                && is_numeric($id)
+                && ($n = new NoteThreadEntry($id, $tid))
+                && $n->getId()==$id
+                )?$n:null;
+    }
+}
+
+// Ticket specific thread utils.
+class TicketThread extends Thread {
+    private $_entries = array();
+
+    function __construct($id) {
+
+        parent::__construct($id);
+
+        if ($this->getId()) {
+            $sql= ' SELECT `type`, count(DISTINCT e.id) as count '
+                 .' FROM '.THREAD_TABLE. ' t '
+                 .' INNER JOIN '.THREAD_ENTRY_TABLE. ' e ON (e.thread_id = t.id) '
+                 .' WHERE t.id='.db_input($this->getId())
+                 .' GROUP BY e.`type`';
+
+            if (($res=db_query($sql)) && db_num_rows($res)) {
+                while ($row=db_fetch_row($res))
+                    $this->_entries[$row[0]] = $row[1];
+            }
+        }
+    }
+
+    function getNumMessages() {
+        return $this->_entries[MessageThreadEntry::ENTRY_TYPE];
+    }
+
+    function getNumResponses() {
+        return $this->_entries[ResponseThreadEntry::ENTRY_TYPE];
+    }
+
+    function getNumNotes() {
+        return $this->_entries[NoteThreadEntry::ENTRY_TYPE];
+    }
+
+    function getMessages() {
+        return $this->getEntries(MessageThreadEntry::ENTRY_TYPE);
+    }
+
+    function getResponses() {
+        return $this->getEntries(ResponseThreadEntry::ENTRY_TYPE);
+    }
+
+    function getNotes() {
+        return $this->getEntries(NoteThreadEntry::ENTRY_TYPE);
+    }
+
+    function addNote($vars, &$errors) {
+
+        //Add ticket Id.
+        $vars['threadId'] = $this->getId();
+        return NoteThreadEntry::create($vars, $errors);
+    }
+
+    function addMessage($vars, &$errors) {
+
+        $vars['threadId'] = $this->getId();
+        $vars['staffId'] = 0;
+
+        return MessageThreadEntry::create($vars, $errors);
+    }
+
+    function addResponse($vars, &$errors) {
+
+        $vars['threadId'] = $this->getId();
+        $vars['userId'] = 0;
+
+        return ResponseThreadEntry::create($vars, $errors);
+    }
+
+    //TODO: revisit
+    function getVar($name) {
+        switch ($name) {
+            case 'original':
+                return MessageThreadEntry::first($this->getId())->getBody();
+            break;
+        case 'last_message':
+        case 'lastmessage':
+            return $this->ticket->getLastMessage()->getBody();
+            break;
+        }
+    }
+
+    static function create($ticket) {
+        $id = is_object($ticket) ? $ticket->getId() : $ticket;
+        return parent::create(array(
+                    'object_id' => $id,
+                    'object_type' => 'T'));
+    }
+
+    static function lookup($id) {
+
+        return ($id
+                && ($t= new TicketThread($id))
+                && $t->getId()
+                ) ? $t : null;
+    }
+}
 ?>
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 8227d1142b1533cde82b53b28b522b67ba892e54..a092fa661e5300e917b3abc657a1365304db8972 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -183,23 +183,27 @@ class Ticket {
 
     function load($id=0) {
 
-        if(!$id && !($id=$this->getId()))
+        if (!$id && !($id=$this->getId()))
             return false;
 
-        $sql='SELECT  ticket.*, lock_id, dept.name as dept_name '
+        $sql='SELECT  ticket.*, thread.id as thread_id, lock_id, dept.name as dept_name '
             .' ,count(distinct attach.attach_id) as attachments'
             .' FROM '.TICKET_TABLE.' ticket '
             .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.id) '
             .' LEFT JOIN '.SLA_TABLE.' sla ON (ticket.sla_id=sla.id AND sla.isactive=1) '
             .' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock
                 ON ( ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW()) '
-            .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach
-                ON ( ticket.ticket_id=attach.ticket_id) '
+            .' LEFT JOIN '.THREAD_TABLE.' thread
+                ON ( thread.object_id = ticket.ticket_id AND thread.object_type="T" ) '
+            .' LEFT JOIN '.THREAD_ENTRY_TABLE.' entry
+                ON ( entry.thread_id = thread.id ) '
+            .' LEFT JOIN '.THREAD_ENTRY_ATTACHMENT_TABLE.' attach
+                ON ( attach.thread_entry_id = entry.id ) '
             .' WHERE ticket.ticket_id='.db_input($id)
             .' GROUP BY ticket.ticket_id';
 
         //echo $sql;
-        if(!($res=db_query($sql)) || !db_num_rows($res))
+        if (!($res=db_query($sql)) || !db_num_rows($res))
             return false;
 
 
@@ -657,19 +661,25 @@ class Ticket {
 
     function getLastRespondent() {
 
-        $sql ='SELECT  resp.staff_id '
-             .' FROM '.TICKET_THREAD_TABLE.' resp '
-             .' LEFT JOIN '.STAFF_TABLE. ' USING(staff_id) '
-             .' WHERE  resp.ticket_id='.db_input($this->getId()).' AND resp.staff_id>0 '
-             .'   AND  resp.thread_type="R"'
-             .' ORDER BY resp.created DESC LIMIT 1';
+        if (!isset($this->lastrespondent)) {
 
-        if(!($res=db_query($sql)) || !db_num_rows($res))
-            return null;
+            $sql ='SELECT resp.staff_id '
+                 .' FROM '.THREAD_ENTRY_TABLE.' resp '
+                 .' LEFT JOIN '.THREAD_TABLE.' t ON( t.id=resp.thread_id) '
+                 .' LEFT JOIN '.STAFF_TABLE. ' s ON(s.staff_id=resp.staff_id) '
+                 .' WHERE  t.object_id='.db_input($this->getId())
+                 .'     AND t.object_type="T" AND resp.staff_id>0 AND  resp.`type`="R" '
+                 .' ORDER BY resp.created DESC LIMIT 1';
+
+            if(!($res=db_query($sql)) || !db_num_rows($res))
+                return null;
+
+            list($id)=db_fetch_row($res);
 
-        list($id)=db_fetch_row($res);
+            $this->lastrespondent = Staff::lookup($id);
+        }
 
-        return Staff::lookup($id);
+        return $this->lastrespondent;
 
     }
 
@@ -706,10 +716,14 @@ class Ticket {
         return $this->last_message;
     }
 
+    function getThreadId() {
+        return $this->ht['thread_id'];
+    }
+
     function getThread() {
 
-        if(!$this->thread)
-            $this->thread = Thread::lookup($this);
+        if (!$this->thread && $this->getThreadId())
+            $this->thread = TicketThread::lookup($this->getThreadId());
 
         return $this->thread;
     }
@@ -1930,10 +1944,10 @@ class Ticket {
             $files[] = $file['id'];
 
         if ($cfg->isHtmlThreadEnabled())
-            $response = new HtmlThreadBody(
+            $response = new HtmlThreadEntryBody(
                     $this->replaceVars($canned->getHtml()));
         else
-            $response = new TextThreadBody(
+            $response = new TextThreadEntryBody(
                     $this->replaceVars($canned->getPlainText()));
 
         $info = array('msgId' => $msgId,
@@ -2002,6 +2016,7 @@ class Ticket {
                 && $cfg->autoClaimTickets())
             $this->setStaffId($thisstaff->getId()); //direct assignment;
 
+        $this->lastrespondent = null;
         $this->onResponse(); //do house cleaning..
 
         /* email the user??  - if disabled - then bail out */
@@ -2084,7 +2099,7 @@ class Ticket {
         $errors = array();
         //Unless specified otherwise, assume HTML
         if ($note && is_string($note))
-            $note = new HtmlThreadBody($note);
+            $note = new HtmlThreadEntryBody($note);
 
         return $this->postNote(
                 array(
@@ -2833,7 +2848,10 @@ class Ticket {
              $sql.=' ,duedate='.db_input(date('Y-m-d G:i',Misc::dbtime($vars['duedate'].' '.$vars['time'])));
 
 
-        if(!db_query($sql) || !($id=db_insert_id()) || !($ticket =Ticket::lookup($id)))
+        if(!db_query($sql)
+                || !($id=db_insert_id())
+                || !($thread=TicketThread::create($id))
+                || !($ticket =Ticket::lookup($id)))
             return null;
 
         /* -------------------- POST CREATE ------------------------ */
diff --git a/include/client/view.inc.php b/include/client/view.inc.php
index 2a98a5c72acd99ddf44cd9f5a0fef896ef7dc23d..8e3aa3fcf49e1828ff7766ec3374c1d76f3a1da3 100644
--- a/include/client/view.inc.php
+++ b/include/client/view.inc.php
@@ -114,12 +114,12 @@ if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) {
     foreach($thread as $entry) {
 
         //Making sure internal notes are not displayed due to backend MISTAKES!
-        if(!$threadType[$entry['thread_type']]) continue;
+        if(!$threadType[$entry['type']]) continue;
         $poster = $entry['poster'];
-        if($entry['thread_type']=='R' && ($cfg->hideStaffName() || !$entry['staff_id']))
+        if($entry['type']=='R' && ($cfg->hideStaffName() || !$entry['staff_id']))
             $poster = ' ';
         ?>
-        <table class="thread-entry <?php echo $threadType[$entry['thread_type']]; ?>" cellspacing="0" cellpadding="1" width="800" border="0">
+        <table class="thread-entry <?php echo $threadType[$entry['type']]; ?>" cellspacing="0" cellpadding="1" width="800" border="0">
             <tr><th><div>
 <?php echo Format::datetime($entry['created']); ?>
                 &nbsp;&nbsp;<span class="textra"></span>
diff --git a/include/staff/templates/tickets.tmpl.php b/include/staff/templates/tickets.tmpl.php
index d85b1e4c57d4807c4be14204f5b93f8a41f89fff..338e4c2b33ae1be5cddcab8ebc62e926609073dc 100644
--- a/include/staff/templates/tickets.tmpl.php
+++ b/include/staff/templates/tickets.tmpl.php
@@ -40,12 +40,15 @@ while ($row = db_fetch_array($res))
 
 if ($results) {
     $counts_sql = 'SELECT ticket.ticket_id,
-        count(DISTINCT attach.attach_id) as attachments,
-        count(DISTINCT thread.id) as thread_count,
+        count(DISTINCT attach.id) as attachments,
+        count(DISTINCT entry.id) as thread_count,
         count(DISTINCT collab.id) as collaborators
-        FROM '.TICKET_TABLE.' ticket
-        LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON (ticket.ticket_id=attach.ticket_id) '
-     .' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON ( ticket.ticket_id=thread.ticket_id) '
+        FROM '.TICKET_TABLE.' ticket '
+     .' LEFT JOIN '.THREAD_TABLE.' thread
+            ON (thread.object_id=ticket.ticket_id AND thread.object_type="T") '
+     .' LEFT JOIN '.THREAD_ENTRY_TABLE.' entry ON (entry.thread_id=thread.id) '
+     .' LEFT JOIN '.ATTACHMENT_TABLE.' attach
+            ON (attach.object_id=entry.id AND attach.`type` = "H") '
      .' LEFT JOIN '.TICKET_COLLABORATOR_TABLE.' collab
             ON ( ticket.ticket_id=collab.ticket_id) '
      .' WHERE ticket.ticket_id IN ('.implode(',', db_input(array_keys($results))).')
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index abdb74ed1a71040920afeb5f46cea4c3fc8da4d3..25ceb77835578ee84e655a005131cdc845abf270 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -387,7 +387,7 @@ $tcount+= $ticket->getNumNotes();
     $types = array('M', 'R', 'N');
     if(($thread=$ticket->getThreadEntries($types))) {
        foreach($thread as $entry) { ?>
-        <table class="thread-entry <?php echo $threadTypes[$entry['thread_type']]; ?>" cellspacing="0" cellpadding="1" width="940" border="0">
+        <table class="thread-entry <?php echo $threadTypes[$entry['type']]; ?>" cellspacing="0" cellpadding="1" width="940" border="0">
             <tr>
                 <th colspan="4" width="100%">
                 <div>
@@ -429,8 +429,8 @@ $tcount+= $ticket->getNumNotes();
             } ?>
         </table>
         <?php
-        if($entry['thread_type']=='M')
-            $msgId=$entry['id'];
+        if ($entry['type'] == 'M')
+            $msgId = $entry['id'];
        }
     } else {
         echo '<p>'.__('Error fetching ticket thread - get technical help.').'</p>';
diff --git a/include/upgrader/streams/core/b26f29a6-186868f5.cleanup.sql b/include/upgrader/streams/core/b26f29a6-186868f5.cleanup.sql
new file mode 100644
index 0000000000000000000000000000000000000000..27dca7f84160f3916b8e4bbb968af9622ae89979
--- /dev/null
+++ b/include/upgrader/streams/core/b26f29a6-186868f5.cleanup.sql
@@ -0,0 +1,9 @@
+ALTER TABLE `%TABLE_PREFIX%thread`
+    DROP COLUMN `tid`;
+
+ALTER TABLE `%TABLE_PREFIX%thread_entry`
+    DROP COLUMN `ticket_id`;
+
+OPTIMIZE TABLE `%TABLE_PREFIX%ticket`;
+OPTIMIZE TABLE `%TABLE_PREFIX%thread`;
+OPTIMIZE TABLE `%TABLE_PREFIX%thread_entry`;
diff --git a/include/upgrader/streams/core/b26f29a6-186868f5.patch.sql b/include/upgrader/streams/core/b26f29a6-186868f5.patch.sql
new file mode 100644
index 0000000000000000000000000000000000000000..7f1898fc6b35b62f35c6af30193337950f81d94f
--- /dev/null
+++ b/include/upgrader/streams/core/b26f29a6-186868f5.patch.sql
@@ -0,0 +1,80 @@
+/**
+ * @version v1.9.5
+ * @signature 2257f6f22ca4b31bea6045b8b7d59d56
+ * @title Threads revisited
+ *
+ * This patch adds ability to thread anything
+ *
+ */
+
+-- Add thread table
+DROP TABLE IF EXISTS `%TABLE_PREFIX%thread`;
+CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%thread` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) unsigned NOT NULL,
+  `object_type` char(1) NOT NULL,
+  `extra` text,
+  `created` datetime NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `object_id` (`object_id`),
+  KEY `object_type` (`object_type`)
+) DEFAULT CHARSET=utf8;
+
+-- create threads
+INSERT INTO `%TABLE_PREFIX%thread`
+    (`object_id`, `object_type`, `created`)
+    SELECT t1.ticket_id, 'T', t1.created
+        FROM `%TABLE_PREFIX%ticket_thread` t1
+        JOIN (
+            SELECT ticket_id, MIN(id) as id
+            FROM `%TABLE_PREFIX%ticket_thread`
+            WHERE `thread_type` = 'M'
+            GROUP BY ticket_id
+    ) t2
+    ON (t1.ticket_id=t2.ticket_id and t1.id=t2.id)
+    ORDER BY t1.created;
+
+ALTER TABLE  `%TABLE_PREFIX%ticket_thread`
+    ADD  `thread_id` INT( 11 ) UNSIGNED NOT NULL DEFAULT  '0' AFTER  `pid` ,
+    ADD INDEX (  `thread_id` );
+
+UPDATE  `%TABLE_PREFIX%ticket_thread` t1
+    LEFT JOIN  `%TABLE_PREFIX%thread` t2 ON ( t2.object_id = t1.ticket_id )
+    SET t1.thread_id = t2.id;
+
+-- convert ticket_thread to thread_entry
+ALTER TABLE  `%TABLE_PREFIX%ticket_thread`
+    CHANGE  `thread_type`  `type` CHAR( 1 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
+    ADD INDEX (  `type` );
+
+RENAME TABLE `%TABLE_PREFIX%ticket_thread` TO  `%TABLE_PREFIX%thread_entry` ;
+
+-- add thread id to ticket table
+ALTER TABLE  `%TABLE_PREFIX%ticket`
+    ADD  `thread_id` INT( 11 ) UNSIGNED NOT NULL DEFAULT  '0' AFTER  `number` ,
+    ADD INDEX (  `thread_id` );
+
+UPDATE  `%TABLE_PREFIX%ticket` t1
+    LEFT JOIN  `%TABLE_PREFIX%thread` t2 ON ( t2.object_id = t1.ticket_id )
+    SET t1.thread_id = t2.id;
+
+-- convert ticket_attachment to thread_entry_attachment
+ALTER TABLE  `%TABLE_PREFIX%ticket_attachment`
+    CHANGE  `attach_id`  `id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT,
+    CHANGE  `ref_id`  `thread_entry_id` INT( 11 ) UNSIGNED NOT NULL DEFAULT  '0';
+    DROP  `ticket_id`;
+
+RENAME TABLE `%TABLE_PREFIX%ticket_attachment` TO  `%TABLE_PREFIX%thread_entry_attachment`;
+
+-- convert ticket_email_info to thread_entry_mid
+ALTER TABLE  `%TABLE_PREFIX%ticket_email_info`
+    CHANGE  `thread_id`  `thread_entry_id` INT( 11 ) UNSIGNED NOT NULL,
+    CHANGE  `email_mid`  `mid` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
+    ADD INDEX (  `thread_entry_id` );
+
+RENAME TABLE `%TABLE_PREFIX%ticket_email_info` TO  `%TABLE_PREFIX%thread_entry_email`;
+
+-- Set new schema signature
+UPDATE `%TABLE_PREFIX%config`
+    SET `value` = '2257f6f22ca4b31bea6045b8b7d59d56'
+    WHERE `key` = 'schema_signature' AND `namespace` = 'core';
diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql
index 3462ad310ad8d379387bd04feee3e89712e364a1..7f968dbd6f542276be62ac09f69150c1d5ff5cdc 100644
--- a/setup/inc/streams/core/install-mysql.sql
+++ b/setup/inc/streams/core/install-mysql.sql
@@ -609,6 +609,65 @@ CREATE TABLE `%TABLE_PREFIX%team_member` (
   PRIMARY KEY  (`team_id`,`staff_id`)
 ) DEFAULT CHARSET=utf8;
 
+DROP TABLE IF EXISTS `%TABLE_PREFIX%thread`;
+CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%thread` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) unsigned NOT NULL,
+  `object_type` char(1) NOT NULL,
+  `extra` text,
+  `created` datetime NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `object_id` (`object_id`),
+  KEY `object_type` (`object_type`)
+) DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `%TABLE_PREFIX%thread_entry`;
+CREATE TABLE `%TABLE_PREFIX%thread_entry` (
+  `id` int(11) unsigned NOT NULL auto_increment,
+  `pid` int(11) unsigned NOT NULL default '0',
+  `thread_id` int(11) unsigned NOT NULL default '0',
+  `staff_id` int(11) unsigned NOT NULL default '0',
+  `user_id` int(11) unsigned not null default 0,
+  `type` char(1) NOT NULL default '',
+  `poster` varchar(128) NOT NULL default '',
+  `source` varchar(32) NOT NULL default '',
+  `title` varchar(255),
+  `body` text NOT NULL,
+  `format` varchar(16) NOT NULL default 'html',
+  `ip_address` varchar(64) NOT NULL default '',
+  `created` datetime NOT NULL,
+  `updated` datetime NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `pid` (`pid`),
+  KEY `thread_id` (`thread_id`),
+  KEY `staff_id` (`staff_id`),
+  KEY `type` (`type`)
+) DEFAULT CHARSET=utf8;
+
+
+DROP TABLE IF EXISTS `%TABLE_PREFIX%thread_entry_attachment`;
+CREATE TABLE `%TABLE_PREFIX%thread_entry_attachment` (
+  `id` int(11) unsigned NOT NULL auto_increment,
+  `file_id` int(10) unsigned NOT NULL default '0',
+  `thread_entry_id` int(11) unsigned NOT NULL default '0',
+  `inline` tinyint(1) NOT NULL default  '0',
+  `created` datetime NOT NULL,
+  PRIMARY KEY  (`id`),
+  KEY `file_id` (`file_id`),
+  KEY `ref_id` (`thread_entry_id`)
+) DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `%TABLE_PREFIX%thread_entry_email`;
+CREATE TABLE `%TABLE_PREFIX%thread_entry_email` (
+  `id` int(11) unsigned NOT NULL auto_increment,
+  `thread_entry_id` int(11) unsigned NOT NULL,
+  `mid` varchar(255) NOT NULL,
+  `headers` text,
+  PRIMARY KEY (`id`),
+  KEY `thread_entry_id` (`thread_entry_id`),
+  KEY `mid` (`mid`)
+) DEFAULT CHARSET=utf8;
+
 DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket`;
 CREATE TABLE `%TABLE_PREFIX%ticket` (
   `ticket_id` int(11) unsigned NOT NULL auto_increment,
@@ -649,20 +708,6 @@ CREATE TABLE `%TABLE_PREFIX%ticket` (
   KEY `sla_id` (`sla_id`)
 ) DEFAULT CHARSET=utf8;
 
-DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_attachment`;
-CREATE TABLE `%TABLE_PREFIX%ticket_attachment` (
-  `attach_id` int(11) unsigned NOT NULL auto_increment,
-  `ticket_id` int(11) unsigned NOT NULL default '0',
-  `file_id` int(10) unsigned NOT NULL default '0',
-  `ref_id` int(11) unsigned NOT NULL default '0',
-  `inline` tinyint(1) NOT NULL default  '0',
-  `created` datetime NOT NULL,
-  PRIMARY KEY  (`attach_id`),
-  KEY `ticket_id` (`ticket_id`),
-  KEY `ref_id` (`ref_id`),
-  KEY `file_id` (`file_id`)
-) DEFAULT CHARSET=utf8;
-
 DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_lock`;
 CREATE TABLE `%TABLE_PREFIX%ticket_lock` (
   `lock_id` int(11) unsigned NOT NULL auto_increment,
@@ -675,16 +720,6 @@ CREATE TABLE `%TABLE_PREFIX%ticket_lock` (
   KEY `staff_id` (`staff_id`)
 ) DEFAULT CHARSET=utf8;
 
-DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_email_info`;
-CREATE TABLE `%TABLE_PREFIX%ticket_email_info` (
-  `id` int(11) unsigned NOT NULL auto_increment,
-  `thread_id` int(11) unsigned NOT NULL,
-  `email_mid` varchar(255) NOT NULL,
-  `headers` text,
-  PRIMARY KEY (`id`),
-  KEY `email_mid` (`email_mid`)
-) DEFAULT CHARSET=utf8;
-
 DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_event`;
 CREATE TABLE `%TABLE_PREFIX%ticket_event` (
   `ticket_id` int(11) unsigned NOT NULL default '0',
@@ -731,28 +766,6 @@ CREATE TABLE `%TABLE_PREFIX%ticket_priority` (
   KEY `ispublic` (`ispublic`)
 ) DEFAULT CHARSET=utf8;
 
-DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_thread`;
-CREATE TABLE `%TABLE_PREFIX%ticket_thread` (
-  `id` int(11) unsigned NOT NULL auto_increment,
-  `pid` int(11) unsigned NOT NULL default '0',
-  `ticket_id` int(11) unsigned NOT NULL default '0',
-  `staff_id` int(11) unsigned NOT NULL default '0',
-  `user_id` int(11) unsigned not null default 0,
-  `thread_type` enum('M','R','N') NOT NULL,
-  `poster` varchar(128) NOT NULL default '',
-  `source` varchar(32) NOT NULL default '',
-  `title` varchar(255),
-  `body` mediumtext NOT NULL,
-  `format` varchar(16) NOT NULL default 'html',
-  `ip_address` varchar(64) NOT NULL default '',
-  `created` datetime NOT NULL,
-  `updated` datetime NOT NULL,
-  PRIMARY KEY  (`id`),
-  KEY `ticket_id` (`ticket_id`),
-  KEY `staff_id` (`staff_id`),
-  KEY `pid` (`pid`)
-) DEFAULT CHARSET=utf8;
-
 CREATE TABLE `%TABLE_PREFIX%ticket_collaborator` (
   `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
   `isactive` tinyint(1) NOT NULL DEFAULT '1',