diff --git a/bootstrap.php b/bootstrap.php
index c37e3f6f04d6080f31e1c4ed15f0e4313e6bb2fd..d91679af855da07e45a6f0b94e3d18b8b7fb859e 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -89,6 +89,7 @@ class Bootstrap {
         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_PRIORITY_TABLE',$prefix.'ticket_priority');
         define('PRIORITY_TABLE',TICKET_PRIORITY_TABLE);
 
diff --git a/include/ajax.forms.php b/include/ajax.forms.php
index cdd643a1b44f77d4e30c51bf3cd67fc5886acd77..2142e8e4faa947b5060539e79a8ef9d2b535025a 100644
--- a/include/ajax.forms.php
+++ b/include/ajax.forms.php
@@ -36,7 +36,5 @@ class DynamicFormsAjaxAPI extends AjaxController {
         else
             $field->save();
     }
-
 }
-
 ?>
diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index aa4d99129a6f7d41e2dfc762384c3bf3bd60fa3c..93e90758c2963eebcefa43518b757e5c473d8eef 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -449,6 +449,144 @@ class TicketsAjaxAPI extends AjaxController {
         return $resp;
     }
 
+    //Collaborators utils
+    function addCollaborator($tid) {
+        global $thisstaff;
+
+        if (!($ticket=Ticket::lookup($tid))
+                || !$ticket->checkStaffAccess($thisstaff))
+            Http::response(404, 'No such ticket');
+
+        //If not a post then assume new collaborator form
+        if(!$_POST)
+            return self::_addcollaborator($ticket);
+
+        $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 && ($c=$ticket->addCollaborator($user, $errors))) {
+            $info =array('msg' => sprintf('%s added as a collaborator',
+                        $c->getName()));
+
+            return self::_collaborators($ticket, $info);
+        }
+
+        if($errors && $errors['err']) {
+            $info +=array('error' => $errors['err']);
+        } else {
+            $info +=array('error' =>'Unable to add collaborator - try again');
+        }
+
+
+        return self::_addcollaborator($ticket, $user, $form, $info);
+    }
+
+    function updateCollaborator($cid) {
+        global $thisstaff;
+
+        if(!($c=Collaborator::lookup($cid))
+                || !($user=$c->getUser())
+                || !($ticket=$c->getTicket())
+                || !$ticket->checkStaffAccess($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',
+                    $c->getName()));
+
+        return self::_collaborators($ticket, $info);
+    }
+
+    function viewCollaborator($cid) {
+        global $thisstaff;
+
+        if(!($collaborator=Collaborator::lookup($cid))
+                || !($ticket=$collaborator->getTicket())
+                || !$ticket->checkStaffAccess($thisstaff))
+            Http::response(404, 'Unknown collaborator');
+
+        return self::_collaborator($collaborator);
+    }
+
+    function showCollaborators($tid) {
+        global $thisstaff;
+
+        if(!($ticket=Ticket::lookup($tid))
+                || !$ticket->checkStaffAccess($thisstaff))
+            Http::response(404, 'No such ticket');
+
+        if($ticket->getCollaborators())
+            return self::_collaborators($ticket);
+
+        return self::_addcollaborator($ticket);
+    }
+
+
+
+    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())
+                    );
+
+        return self::_userlookup($user, $form, $info);
+    }
+
+
+    function updateCollaborators($tid) {
+        global $thisstaff;
+
+        if(!($ticket=Ticket::lookup($tid))
+                || !$ticket->checkStaffAccess($thisstaff))
+            Http::response(404, 'No such ticket');
+
+       $errors = $info = array();
+        if ($ticket->updateCollaborators($_POST, $errors)) {
+            $info +=array('msg' => 'Collaborators updated successfully');
+        } elseif($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;
 
@@ -516,6 +654,11 @@ class TicketsAjaxAPI extends AjaxController {
                 'title' => sprintf('Change user for ticket #%s', $ticket->getNumber())
                 );
 
+        return self::_userlookup($user, $info);
+    }
+
+    function _userlookup($user, $form, $info) {
+
         ob_start();
         include(STAFFINC_DIR . 'templates/user-lookup.tmpl.php');
         $resp = ob_get_contents();
@@ -524,6 +667,5 @@ class TicketsAjaxAPI extends AjaxController {
 
     }
 
-
 }
 ?>
diff --git a/include/ajax.users.php b/include/ajax.users.php
index 04b29bec611ee3b19d167645590950b4d3cd40ab..a1096d6f5604f3f749311de45fb0b8ca5f92b343 100644
--- a/include/ajax.users.php
+++ b/include/ajax.users.php
@@ -66,22 +66,10 @@ class UsersAjaxAPI extends AjaxController {
 
     function addUser() {
 
-        $valid = true;
         $form = UserForm::getUserForm()->getForm($_POST);
-        if (!$form->isValid())
-            $valid  = false;
-
-        if (($field=$form->getField('email'))
-                && $field->getClean()
-                && User::lookup(array('emails__address'=>$field->getClean()))) {
-            $field->addError('Email is assigned to another user');
-            $valid = false;
-        }
-
-        if ($valid && ($user = User::fromForm($form->getClean())))
+        if (($user = User::fromForm($form)))
             Http::response(201, $user->to_json());
 
-
         $info = array('error' =>'Error adding user - try again!');
 
         return self::_lookupform($form, $info);
diff --git a/include/api.tickets.php b/include/api.tickets.php
index f0758d5d2ea75324620ce12acbf30fe76222fb06..45cbc2db97eccb120b0f22f7f47ba97ec8026ced 100644
--- a/include/api.tickets.php
+++ b/include/api.tickets.php
@@ -39,7 +39,10 @@ class TicketApiController extends ApiController {
         if(!strcasecmp($format, 'email')) {
             $supported = array_merge($supported, array('header', 'mid',
                 'emailId', 'ticketId', 'reply-to', 'reply-to-name',
-                'in-reply-to', 'references'));
+                'in-reply-to', 'references',
+                'recipients' => array("*" => array("name", "email"))
+                ));
+
             $supported['attachments']['*'][] = 'cid';
         }
 
diff --git a/include/class.collaborator.php b/include/class.collaborator.php
new file mode 100644
index 0000000000000000000000000000000000000000..e04d8571226f539cf82546a0c641a87eb1a026e3
--- /dev/null
+++ b/include/class.collaborator.php
@@ -0,0 +1,151 @@
+<?php
+/*********************************************************************
+    class.collaborator.php
+
+    Ticket collaborator
+
+    Peter Rotich <peter@osticket.com>
+    Copyright (c)  2006-2013 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:
+**********************************************************************/
+require_once(INCLUDE_DIR . 'class.user.php');
+
+class Collaborator {
+
+    var $ht;
+
+    var $user;
+    var $ticket;
+
+    function __construct($id) {
+
+        $this->load($id);
+    }
+
+    function load($id) {
+
+        if(!$id && !($id=$this->getId()))
+            return;
+
+        $sql='SELECT * FROM '.TICKET_COLLABORATOR_TABLE
+            .' WHERE id='.db_input($id);
+
+        $this->ht = db_fetch_array(db_query($sql));
+        $this->ticket = $this->user = null;
+    }
+
+    function reload() {
+        return $this->load();
+    }
+
+    function __call($name, $args) {
+
+        if(!($user=$this->getUser()) || !method_exists($user, $name))
+            return null;
+
+        if($args)
+            return  call_user_func_array(array($user, $name), $args);
+
+        return call_user_func(array($user, $name));
+    }
+
+    function getId() {
+        return $this->ht['id'];
+    }
+
+    function isActive() {
+        return ($this->ht['isactive']);
+    }
+
+    function getTicketId() {
+        return $this->ht['ticket_id'];
+    }
+
+    function getTicket() {
+        if(!$this->ticket && $this->getTicketId())
+            $this->ticket = Ticket::lookup($this->getTicketId());
+
+        return $this->ticket;
+    }
+
+    function getUserId() {
+        return $this->ht['user_id'];
+    }
+
+    function getUser() {
+
+        if(!$this->user && $this->getUserId())
+            $this->user = User::lookup($this->getUserId());
+
+        return $this->user;
+    }
+
+    static function add($info, &$errors) {
+
+        if(!$info || !$info['ticketId'] || !$info['userId'])
+            $errors['err'] = 'Invalid or missing information';
+        elseif(($c=self::lookup($info)))
+            $errors['err'] = sprintf('%s is already a collaborator',
+                    $c->getName());
+
+        if($errors) return false;
+
+        $sql='INSERT INTO '.TICKET_COLLABORATOR_TABLE
+            .' SET isactive=1, updated=NOW() '
+            .' ,ticket_id='.db_input($info['ticketId'])
+            .' ,user_id='.db_input($info['userId']);
+
+        if(db_query($sql) && ($id=db_insert_id()))
+            return self::lookup($id);
+
+        $errors['err'] = 'Unable to add collaborator. Internal error';
+
+        return false;
+    }
+
+    static function forTicket($tid, $criteria=array()) {
+
+        $collaborators = array();
+
+        $sql='SELECT id FROM '.TICKET_COLLABORATOR_TABLE
+            .' WHERE ticket_id='.db_input($tid);
+
+        if(isset($criteria['isactive']))
+            $sql.=' AND isactive='.db_input($criteria['isactive']);
+
+        //TODO: sort by name of the user
+
+        if(($res=db_query($sql)) && db_num_rows($res))
+            while(list($id)=db_fetch_row($res))
+                $collaborators[] = self::lookup($id);
+
+        return $collaborators;
+    }
+
+    static function getIdByInfo($info) {
+
+        $sql='SELECT id FROM '.TICKET_COLLABORATOR_TABLE
+            .' WHERE ticket_id='.db_input($info['ticketId'])
+            .' AND user_id='.db_input($info['userId']);
+
+        list($id) = db_fetch_row(db_query($sql));
+
+        return $id;
+    }
+
+    static function lookup($criteria) {
+        $id = is_numeric($criteria)
+            ? $criteria : self::getIdByInfo($criteria);
+
+        return ($id
+                && ($c = new Collaborator($id))
+                && $c->getId() == $id)
+            ? $c : null;
+    }
+}
+?>
diff --git a/include/class.config.php b/include/class.config.php
index ca426bd2c4661f8f417f961cbebc884f3d977cfd..ac60110e8f2ad06423f2fd551ac109247b8e425d 100644
--- a/include/class.config.php
+++ b/include/class.config.php
@@ -351,14 +351,13 @@ class OsticketConfig extends Config {
     function getDefaultEmail() {
 
         if(!$this->defaultEmail && $this->getDefaultEmailId())
-            $this->defaultEmail=Email::lookup($this->getDefaultEmailId());
+            $this->defaultEmail = Email::lookup($this->getDefaultEmailId());
 
         return $this->defaultEmail;
     }
 
     function getDefaultEmailAddress() {
-        $email=$this->getDefaultEmail();
-        return $email?$email->getAddress():null;
+        return ($email=$this->getDefaultEmail()) ? $email->getAddress() : null;
     }
 
     function getDefaultSLAId() {
@@ -368,7 +367,7 @@ class OsticketConfig extends Config {
     function getDefaultSLA() {
 
         if(!$this->defaultSLA && $this->getDefaultSLAId())
-            $this->defaultSLA=SLA::lookup($this->getDefaultSLAId());
+            $this->defaultSLA = SLA::lookup($this->getDefaultSLAId());
 
         return $this->defaultSLA;
     }
@@ -379,15 +378,18 @@ class OsticketConfig extends Config {
 
     function getAlertEmail() {
 
-        if(!$this->alertEmail && $this->get('alert_email_id'))
-            $this->alertEmail= new Email($this->get('alert_email_id'));
+        if(!$this->alertEmail)
+            if(!($this->alertEmail = Email::lookup($this->getAlertEmailId())))
+                $this->alertEmail = $this->getDefaultEmail();
+
         return $this->alertEmail;
     }
 
     function getDefaultSMTPEmail() {
 
         if(!$this->defaultSMTPEmail && $this->get('default_smtp_id'))
-            $this->defaultSMTPEmail= new Email($this->get('default_smtp_id'));
+            $this->defaultSMTPEmail = Email::lookup($this->get('default_smtp_id'));
+
         return $this->defaultSMTPEmail;
     }
 
diff --git a/include/class.dept.php b/include/class.dept.php
index 8f1542b737d631a07024a6303dd80516963c5f28..0822e056f1c9890e71694bfc0ee129fab7798b16 100644
--- a/include/class.dept.php
+++ b/include/class.dept.php
@@ -77,9 +77,11 @@ class Dept {
     }
 
     function getEmail() {
+        global $cfg;
 
-        if(!$this->email && $this->getEmailId())
-            $this->email=Email::lookup($this->getEmailId());
+        if(!$this->email)
+            if(!($this->email = Email::lookup($this->getEmailId())))
+                $this->email = $cfg->getDefaultEmail();
 
         return $this->email;
     }
@@ -138,19 +140,21 @@ class Dept {
     }
 
     function getTemplate() {
+        global $cfg;
 
-        if(!$this->template && $this->getTemplateId())
-            $this->template = EmailTemplateGroup::lookup($this->getTemplateId());
+        if (!$this->template) {
+            if (!($this->template = EmailTemplateGroup::lookup($this->getTemplateId())))
+                $this->template = $cfg->getDefaultTemplate();
+        }
 
         return $this->template;
     }
 
     function getAutoRespEmail() {
 
-        if(!$this->autorespEmail && $this->ht['autoresp_email_id'] && ($email=Email::lookup($this->ht['autoresp_email_id'])))
-            $this->autorespEmail=$email;
-        else // Defualt to dept email if autoresp is not specified or deleted.
-            $this->autorespEmail=$this->getEmail();
+        if (!$this->autorespEmail && $this->ht['autoresp_email_id'])
+            if (!($this->autorespEmail = Email::lookup($this->ht['autoresp_email_id'])))
+                $this->autorespEmail = $this->getEmail();
 
         return $this->autorespEmail;
     }
diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php
index 3c4306d1eeffe17cb65a2b2207f18be1312d6f11..2b05c3a593fbaef7620f5259bc1728f065834153 100644
--- a/include/class.mailfetch.php
+++ b/include/class.mailfetch.php
@@ -269,9 +269,9 @@ class MailFetcher {
 
         $sender=$headerinfo->from[0];
         //Just what we need...
-        $header=array('name'  =>@$sender->personal,
+        $header=array('name'  => $this->mime_decode(@$sender->personal),
                       'email'  => trim(strtolower($sender->mailbox).'@'.$sender->host),
-                      'subject'=>@$headerinfo->subject,
+                      'subject'=> $this->mime_decode(@$headerinfo->subject),
                       'mid'    => trim(@$headerinfo->message_id),
                       'header' => $this->getHeader($mid),
                       'in-reply-to' => $headerinfo->in_reply_to,
@@ -283,22 +283,33 @@ class MailFetcher {
             $header['reply-to-name'] = $replyto[0]->personal;
         }
 
-        //Try to determine target email - useful when fetched inbox has
-        // aliases that are independent emails within osTicket.
-        $emailId = 0;
+        // Put together a list of recipients
         $tolist = array();
         if($headerinfo->to)
             $tolist = array_merge($tolist, $headerinfo->to);
         if($headerinfo->cc)
             $tolist = array_merge($tolist, $headerinfo->cc);
-        if($headerinfo->bcc)
-            $tolist = array_merge($tolist, $headerinfo->bcc);
 
-        foreach($tolist as $addr)
-            if(($emailId=Email::getIdByEmail(strtolower($addr->mailbox).'@'.$addr->host)))
-                break;
+        $header['recipients'] = array();
+        foreach($tolist as $addr) {
+            if(!($emailId=Email::getIdByEmail(strtolower($addr->mailbox).'@'.$addr->host))) {
+                $header['recipients'][] = array(
+                        'name' => $this->mime_decode(@$addr->personal),
+                        'email' => strtolower($addr->mailbox).'@'.$addr->host);
+            } elseif(!$header['emailId']) {
+                $header['emailId'] = $emailId;
+            }
+        }
 
-        $header['emailId'] = $emailId;
+        //BCCed?
+        if(!$header['emailId']) {
+            unset($header['recipients']); //Nuke the recipients - we were bcced
+            if ($headerinfo->bcc) {
+                foreach($headerinfo->bcc as $addr)
+                    if (($header['emailId'] = Email::getIdByEmail(strtolower($addr->mailbox).'@'.$addr->host)))
+                        break;
+            }
+        }
 
         // Ensure we have a message-id. If unable to read it out of the
         // email, use the hash of the entire email headers
@@ -485,18 +496,18 @@ class MailFetcher {
         }
 
         $vars = $mailinfo;
-        $vars['name']=$this->mime_decode($mailinfo['name']);
-        $vars['subject']=$mailinfo['subject']?$this->mime_decode($mailinfo['subject']):'[No Subject]';
-        $vars['message']=Format::stripEmptyLines($this->getBody($mid));
-        $vars['emailId']=$mailinfo['emailId']?$mailinfo['emailId']:$this->getEmailId();
+        $vars['name'] = $mailinfo['name'];
+        $vars['subject'] = $mailinfo['subject'] ? $mailinfo['subject'] : '[No Subject]';
+        $vars['message'] = Format::stripEmptyLines($this->getBody($mid));
+        $vars['emailId'] = $mailinfo['emailId'] ? $mailinfo['emailId'] : $this->getEmailId();
 
         //Missing FROM name  - use email address.
         if(!$vars['name'])
-            $vars['name'] = $vars['email'];
+            list($vars['name']) = explode('@', $vars['email']);
 
         //An email with just attachments can have empty body.
         if(!$vars['message'])
-            $vars['message'] = '-';
+            $vars['message'] = '--';
 
         if($ost->getConfig()->useEmailPriority())
             $vars['priorityId']=$this->getPriority($mid);
@@ -565,6 +576,7 @@ class MailFetcher {
             return null;
         }
 
+
         return $ticket;
     }
 
diff --git a/include/class.mailparse.php b/include/class.mailparse.php
index 32b0083a6df2c077defd19ac1560627b20ab877c..dfc4c6b3f2932f2a97ca0b7234215b2ba70cbfca 100644
--- a/include/class.mailparse.php
+++ b/include/class.mailparse.php
@@ -154,6 +154,13 @@ class Mail_Parse {
         return Mail_Parse::parseAddressList($header);
     }
 
+    function getBccAddressList(){
+        if (!($header = $this->struct->headers['bcc']))
+            return null;
+
+        return Mail_Parse::parseAddressList($header);
+    }
+
     function getMessageId(){
         return $this->struct->headers['message-id'];
     }
@@ -378,19 +385,35 @@ class EmailDataParser {
         }
 
         //TO Address:Try to figure out the email address... associated with the incoming email.
-        $emailId = 0;
-        if(($tolist = $parser->getToAddressList())) {
-            foreach ($tolist as $toaddr) {
-                if(($emailId=Email::getIdByEmail($toaddr->mailbox.'@'.$toaddr->host)))
-                    break;
+        $data['emailId'] = 0;
+        $data['recipients'] = array();
+        $tolist = array();
+        if(($to = $parser->getToAddressList()))
+            $tolist = array_merge($tolist, $to);
+
+        if(($cc = $parser->getCcAddressList()))
+            $tolist = array_merge($tolist, $cc);
+
+        foreach ($tolist as $addr) {
+            if(!($emailId=Email::getIdByEmail(strtolower($addr->mailbox).'@'.$addr->host))) {
+                $data['recipients'][] = array(
+                          'name' => trim(@$addr->personal, '"'),
+                          'email' => strtolower($addr->mailbox).'@'.$addr->host);
+            } elseif(!$data['emailId']) {
+                $data['emailId'] = $emailId;
             }
         }
-        //maybe we got CC'ed??
-        if(!$emailId && ($cclist=$parser->getCcAddressList())) {
-            foreach ($cclist as $ccaddr) {
-                if(($emailId=Email::getIdByEmail($ccaddr->mailbox.'@'.$ccaddr->host)))
-                    break;
+
+        //maybe we got BCC'ed??
+        if(!$data['emailId']) {
+            unset($data['recipients']);
+            $emailId =  0;
+            if($bcc = $parser->getBccAddressList())
+                foreach ($bcc as $addr) {
+                    if(($emailId=Email::getIdByEmail($addr->mailbox.'@'.$addr->host)))
+                        break;
             }
+            $data['emailId'] = $emailId;
         }
 
         $data['subject'] = $parser->getSubject();
@@ -398,7 +421,6 @@ class EmailDataParser {
         $data['header'] = $parser->getHeader();
         $data['mid'] = $parser->getMessageId();
         $data['priorityId'] = $parser->getPriority();
-        $data['emailId'] = $emailId;
 
         $data['in-reply-to'] = $parser->struct->headers['in-reply-to'];
         $data['references'] = $parser->struct->headers['references'];
diff --git a/include/class.template.php b/include/class.template.php
index a66a74f9ce3e28dcf9c9f6d48fafa90d7fdf3f45..e135034cd4943d45190d70b710e9868f4df6a43f 100644
--- a/include/class.template.php
+++ b/include/class.template.php
@@ -40,6 +40,9 @@ class EmailTemplateGroup {
         'ticket.reply'=>array(
             'name'=>'Response/Reply Template',
             'desc'=>'Template used on ticket response/reply'),
+        'ticket.activity.notice'=>array(
+            'name'=>'New Activity Notice',
+            'desc'=>'Template used to notify collaborators on ticket activity (e.g CC on reply)'),
         'ticket.alert'=>array(
             'name'=>'New Ticket Alert',
             'desc'=>'Alert sent to staff, if enabled, on new ticket.'),
@@ -206,6 +209,10 @@ class EmailTemplateGroup {
         return $this->getMsgTemplate('ticket.reply');
     }
 
+    function  getActivityNoticeMsgTemplate() {
+        return $this->getMsgTemplate('ticket.activity.notice');
+    }
+
     function getOverlimitMsgTemplate() {
         return $this->getMsgTemplate('ticket.overlimit');
     }
diff --git a/include/class.thread.php b/include/class.thread.php
index 13c2e0dfe5ef1c1f4c463ed83daff8c0c3e53fe4..a68db4ddf94d6e51c573bccd90fa89f2c257a1d6 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -785,13 +785,17 @@ Class ThreadEntry {
         }
         $vars['body'] = Format::sanitize($vars['body']);
 
+        $poster = $vars['poster'];
+        if ($poster && is_object($poster))
+            $poster = $poster->getName();
+
         $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))
             .' ,body='.db_input($vars['body'])
             .' ,staff_id='.db_input($vars['staffId'])
-            .' ,poster='.db_input($vars['poster'])
+            .' ,poster='.db_input($poster)
             .' ,source='.db_input($vars['source']);
 
         if(isset($vars['pid']))
diff --git a/include/class.ticket.php b/include/class.ticket.php
index b8dbe21871cd18db35c9df11f7baa26e6722136b..648424ca61cd8d4f2df4856629745875f64cffbb 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -32,6 +32,8 @@ include_once(INCLUDE_DIR.'class.sla.php');
 include_once(INCLUDE_DIR.'class.canned.php');
 require_once(INCLUDE_DIR.'class.dynamic_forms.php');
 require_once(INCLUDE_DIR.'class.user.php');
+require_once(INCLUDE_DIR.'class.collaborator.php');
+
 
 class Ticket {
 
@@ -64,7 +66,7 @@ class Ticket {
 
         $sql='SELECT  ticket.*, lock_id, dept_name '
             .' ,IF(sla.id IS NULL, NULL, DATE_ADD(ticket.created, INTERVAL sla.grace_period HOUR)) as sla_duedate '
-            .' ,count(attach.attach_id) as attachments '
+            .' ,count(attach.attach_id) as attachments, count(collab.id) as collaborators '
             .' FROM '.TICKET_TABLE.' ticket '
             .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.dept_id) '
             .' LEFT JOIN '.SLA_TABLE.' sla ON (ticket.sla_id=sla.id AND sla.isactive=1) '
@@ -72,6 +74,8 @@ class Ticket {
                 .'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 '.TICKET_COLLABORATOR_TABLE.' collab ON ('
+                .'ticket.ticket_id=collab.ticket_id) '
             .' WHERE ticket.ticket_id='.db_input($id)
             .' GROUP BY ticket.ticket_id';
 
@@ -98,6 +102,7 @@ class Ticket {
         $this->stats = null;
         $this->topic = null;
         $this->thread = null;
+        $this->collaborators = null;
 
         //REQUIRED: Preload thread obj - checked on lookup!
         $this->getThread();
@@ -375,9 +380,11 @@ class Ticket {
     }
 
     function getDept() {
+        global $cfg;
 
-        if(!$this->dept && $this->getDeptId())
-            $this->dept= Dept::lookup($this->getDeptId());
+        if(!$this->dept)
+            if(!($this->dept = Dept::lookup($this->getDeptId())))
+                $this->dept = $cfg->getDefaultDept();
 
         return $this->dept;
     }
@@ -562,7 +569,79 @@ class Ticket {
         return $this->getThread()->getEntries($type, $order);
     }
 
+    //Collaborators
+    function getNumCollaborators() {
+        return $this->ht['collaborators'];
+    }
+
+    function getActiveCollaborators() {
+        return $this->getCollaborators(array('isactive'=>1));
+    }
+
+
+    function getCollaborators($criteria=array()) {
+
+        if($criteria)
+            return Collaborator::forTicket($this->getId(), $criteria);
+
+        if(!$this->collaborators && $this->getNumCollaborators())
+            $this->collaborators = Collaborator::forTicket($this->getId());
+
+        return $this->collaborators;
+    }
+
+    function addCollaborator($user, &$errors) {
+
+        if(!$user) return null;
+
+        $vars = array(
+                'ticketId' => $this->getId(),
+                'userId' => $user->getId());
+        if(!($c=Collaborator::add($vars, $errors)))
+            return null;
+
+        $this->ht['collaborators'] +=1;
+        $this->collaborators = null;
+
+
+        return $c;
+    }
+
+    //XXX: Ugly for now
+    function updateCollaborators($vars, &$errors) {
+
+
+        //Deletes
+        if($vars['del'] && ($ids=array_filter($vars['del']))) {
+            $sql='DELETE FROM '.TICKET_COLLABORATOR_TABLE
+                .' WHERE ticket_id='.db_input($this->getId())
+                .' AND id IN('.implode(',', db_input($ids)).')';
+            if(db_query($sql))
+                $this->ht['collaborators'] -= db_affected_rows();
+        }
+
+        //statuses
+        $cids = null;
+        if($vars['cid'] && ($cids=array_filter($vars['cid']))) {
+            $sql='UPDATE '.TICKET_COLLABORATOR_TABLE
+                .' SET updated=NOW(), isactive=1 '
+                .' WHERE ticket_id='.db_input($this->getId())
+                .' AND id IN('.implode(',', db_input($cids)).')';
+            db_query($sql);
+        }
+
+        $sql='UPDATE '.TICKET_COLLABORATOR_TABLE
+            .' SET updated=NOW(), isactive=0 '
+            .' WHERE ticket_id='.db_input($this->getId());
+        if($cids)
+            $sql.=' AND id NOT IN('.implode(',', db_input($cids)).')';
 
+        db_query($sql);
+
+        $this->collaborators = null;
+
+        return true;
+    }
 
     /* -------------------- Setters --------------------- */
     function setLastMsgId($msgid) {
@@ -750,22 +829,20 @@ class Ticket {
         /* ------ SEND OUT NEW TICKET AUTORESP && ALERTS ----------*/
 
         $this->reload(); //get the new goodies.
-        $dept= $this->getDept();
-
-        if(!$dept || !($tpl = $dept->getTemplate()))
-            $tpl= $cfg->getDefaultTemplate();
-
-        if(!$tpl) return false;  //bail out...missing stuff.
-
-        if(!$dept || !($email=$dept->getAutoRespEmail()))
-            $email =$cfg->getDefaultEmail();
+        if(!$cfg
+                || !($dept=$this->getDept())
+                || !($tpl = $dept->getTemplate())
+                || !($email=$dept->getAutoRespEmail())) {
+                return false;  //bail out...missing stuff.
+        }
 
         $options = array(
             'inreplyto'=>$message->getEmailMessageId(),
             'references'=>$message->getEmailReferences());
 
         //Send auto response - if enabled.
-        if($autorespond && $email && $cfg->autoRespONNewTicket()
+        if($autorespond
+                && $cfg->autoRespONNewTicket()
                 && $dept->autoRespONNewTicket()
                 &&  ($msg=$tpl->getAutoRespMsgTemplate())) {
 
@@ -781,12 +858,10 @@ class Ticket {
                 null, $options);
         }
 
-        if(!($email=$cfg->getAlertEmail()))
-            $email =$cfg->getDefaultEmail();
-
         //Send alert to out sleepy & idle staff.
-        if($alertstaff && $email
+        if ($alertstaff
                 && $cfg->alertONNewTicket()
+                && ($email=$cfg->getAlertEmail())
                 && ($msg=$tpl->getNewTicketAlertMsgTemplate())) {
 
             $msg = $this->replaceVars($msg->asArray(), array('message' => $message));
@@ -794,9 +869,8 @@ class Ticket {
             $recipients=$sentlist=array();
             //Alert admin??
             if($cfg->alertAdminONNewTicket()) {
-                $alert = str_replace('%{recipient}', 'Admin', $msg['body']);
-                $email->sendAlert($cfg->getAdminEmail(), $msg['subj'],
-                    $alert, null, $options);
+                $alert = $this->replaceVars($msg, array('recipient' => 'Admin'));
+                $email->sendAlert($cfg->getAdminEmail(), $alert['subj'], $alert['body'], null, $options);
                 $sentlist[]=$cfg->getAdminEmail();
             }
 
@@ -811,13 +885,10 @@ class Ticket {
 
             foreach( $recipients as $k=>$staff) {
                 if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
-                $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']);
-                $email->sendAlert($staff->getEmail(), $msg['subj'], $alert,
-                    null, $options);
+                $alert = $this->replaceVars($msg, array('recipient' => $staff));
+                $email->sendAlert($staff->getEmail(), $alert['subj'], $alert['body'], null, $options);
                 $sentlist[] = $staff->getEmail();
             }
-
-
         }
 
         return true;
@@ -830,18 +901,14 @@ class Ticket {
         $msg=sprintf('Max open tickets (%d) reached  for %s ', $cfg->getMaxOpenTickets(), $this->getEmail());
         $ost->logWarning('Max. Open Tickets Limit ('.$this->getEmail().')', $msg);
 
-        if(!$sendNotice || !$cfg->sendOverLimitNotice()) return true;
+        if(!$sendNotice || !$cfg->sendOverLimitNotice())
+            return true;
 
         //Send notice to user.
-        $dept = $this->getDept();
-
-        if(!$dept || !($tpl=$dept->getTemplate()))
-            $tpl=$cfg->getDefaultTemplate();
-
-        if(!$dept || !($email=$dept->getAutoRespEmail()))
-            $email=$cfg->getDefaultEmail();
-
-        if($tpl && ($msg=$tpl->getOverlimitMsgTemplate()) && $email) {
+        if(($dept = $this->getDept())
+            && ($tpl=$dept->getTemplate())
+            && ($msg=$tpl->getOverlimitMsgTemplate())
+            && ($email=$dept->getAutoRespEmail())) {
 
             $msg = $this->replaceVars($msg->asArray(),
                         array('signature' => ($dept && $dept->isPublic())?$dept->getSignature():''));
@@ -862,7 +929,42 @@ class Ticket {
     }
 
     function onResponse() {
-        db_query('UPDATE '.TICKET_TABLE.' SET isanswered=1,lastresponse=NOW(), updated=NOW() WHERE ticket_id='.db_input($this->getId()));
+        db_query('UPDATE '.TICKET_TABLE.' SET isanswered=1, lastresponse=NOW(), updated=NOW() WHERE ticket_id='.db_input($this->getId()));
+        $this->reload();
+    }
+
+    function  activityNotice($vars) {
+        global $cfg;
+
+        if (!$vars
+                || !$vars['variables']
+                || !($collaborators=$this->getActiveCollaborators())
+                || !($dept=$this->getDept())
+                || !($tpl=$dept->getTemplate())
+                || !($msg=$tpl->getActivityNoticeMsgTemplate())
+                || !($email=$dept->getEmail()))
+            return;
+
+
+        $msg = $this->replaceVars($msg->asArray(), $vars['variables']);
+
+        if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator()))
+            $msg['body'] = "<p style=\"display:none\">$tag<p>".$msg['body'];
+
+        $attachments = ($cfg->emailAttachments() && $vars['attachments'])?$vars['attachments']:array();
+        $options = array();
+        if($vars['inreplyto'])
+            $options['inreplyto'] = $vars['inreplyto'];
+        if($vars['references'])
+            $options['references'] = $vars['references'];
+
+        foreach($collaborators as $collaborator) {
+            $msg = $this->replaceVars($msg, array('recipient' => $collaborator));
+            $email->send($collaborator->getEmail(), $msg['subj'], $msg['body'], $attachments,
+                $options);
+        }
+
+        return;
     }
 
     function onMessage($autorespond=true, $message=null) {
@@ -889,18 +991,14 @@ class Ticket {
 
 
         if(!$autorespond || !$cfg->autoRespONNewMessage()) return;  //no autoresp or alerts.
-
         $this->reload();
-
-
-        if(!$dept || !($tpl = $dept->getTemplate()))
-            $tpl = $cfg->getDefaultTemplate();
-
-        if(!$dept || !($email = $dept->getAutoRespEmail()))
-            $email = $cfg->getDefaultEmail();
+        $dept = $this->getDept();
+        $email = $dept->getAutoRespEmail();
 
         //If enabled...send confirmation to user. ( New Message AutoResponse)
-        if($email && $tpl && ($msg=$tpl->getNewMessageAutorepMsgTemplate())) {
+        if($email
+                && ($tpl=$dept->getTemplate())
+                && ($msg=$tpl->getNewMessageAutorepMsgTemplate())) {
 
             $msg = $this->replaceVars($msg->asArray(),
                             array('signature' => ($dept && $dept->isPublic())?$dept->getSignature():''));
@@ -940,14 +1038,10 @@ class Ticket {
         if(!$alert || !$cfg->alertONAssignment()) return true; //No alerts!
 
         $dept = $this->getDept();
-
-        //Get template.
-        if(!$dept || !($tpl = $dept->getTemplate()))
-            $tpl = $cfg->getDefaultTemplate();
-
-        //Email to use!
-        if(!($email=$cfg->getAlertEmail()))
-            $email = $cfg->getDefaultEmail();
+        if(!$dept
+                || !($tpl = $dept->getTemplate())
+                || !($email = $cfg->getAlertEmail()))
+            return true;
 
         //recipients
         $recipients=array();
@@ -962,7 +1056,7 @@ class Ticket {
         }
 
         //Get the message template
-        if($email && $recipients && $tpl && ($msg=$tpl->getAssignedAlertMsgTemplate())) {
+        if($recipients && ($msg=$tpl->getAssignedAlertMsgTemplate())) {
 
             $msg = $this->replaceVars($msg->asArray(),
                         array('comments' => $comments,
@@ -977,9 +1071,8 @@ class Ticket {
                 'references'=>$note->getEmailReferences());
             foreach( $recipients as $k=>$staff) {
                 if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
-                $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']);
-                $email->sendAlert($staff->getEmail(), $msg['subj'], $alert,
-                    null, $options);
+                $alert = $this->replaceVars($msg, array('recipient' => $staff));
+                $email->sendAlert($staff->getEmail(), $alert['subj'], $alert['body'], null, $options);
                 $sentlist[] = $staff->getEmail();
             }
         }
@@ -994,20 +1087,15 @@ class Ticket {
             $whine = false;
 
         //check if we need to send alerts.
-        if(!$whine || !$cfg->alertONOverdueTicket())
+        if(!$whine
+                || !$cfg->alertONOverdueTicket()
+                || !($dept = $this->getDept()))
             return true;
 
-        $dept = $this->getDept();
-        //Get department-defined or default template.
-        if(!$dept || !($tpl = $dept->getTemplate()))
-            $tpl= $cfg->getDefaultTemplate();
-
-        //Email to use!
-        if(!($email=$cfg->getAlertEmail()))
-            $email =$cfg->getDefaultEmail();
-
         //Get the message template
-        if($tpl && ($msg=$tpl->getOverdueAlertMsgTemplate()) && $email) {
+        if(($tpl = $dept->getTemplate())
+                && ($msg=$tpl->getOverdueAlertMsgTemplate())
+                && ($email=$cfg->getAlertEmail())) {
 
             $msg = $this->replaceVars($msg->asArray(),
                 array('comments' => $comments));
@@ -1032,9 +1120,8 @@ class Ticket {
             $sentlist=array();
             foreach( $recipients as $k=>$staff) {
                 if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
-                $alert = str_replace("%{recipient}", $staff->getFirstName(), $msg['body']);
-                $email->sendAlert($staff->getEmail(), $msg['subj'], $alert,
-                    null);
+                $alert = $this->replaceVars($msg, array('recipient' => $staff));
+                $email->sendAlert($staff->getEmail(), $alert['subj'], $alert['body'], null);
                 $sentlist[] = $staff->getEmail();
             }
 
@@ -1200,19 +1287,12 @@ class Ticket {
         $this->logEvent('transferred');
 
         //Send out alerts if enabled AND requested
-        if(!$alert || !$cfg->alertONTransfer() || !($dept=$this->getDept())) return true; //no alerts!!
-
-
-         //Get template.
-         if(!($tpl = $dept->getTemplate()))
-             $tpl= $cfg->getDefaultTemplate();
-
-         //Email to use!
-         if(!($email=$cfg->getAlertEmail()))
-             $email =$cfg->getDefaultEmail();
+        if(!$alert || !$cfg->alertONTransfer() || !($dept=$this->getDept()))
+            return true; //no alerts!!
 
-         //Get the message template
-         if($tpl && ($msg=$tpl->getTransferAlertMsgTemplate()) && $email) {
+         if(($email=$cfg->getAlertEmail())
+                     && ($tpl = $dept->getTemplate())
+                     && ($msg=$tpl->getTransferAlertMsgTemplate())) {
 
             $msg = $this->replaceVars($msg->asArray(),
                 array('comments' => $comments, 'staff' => $thisstaff));
@@ -1240,9 +1320,8 @@ class Ticket {
                 'references'=>$note->getEmailReferences());
             foreach( $recipients as $k=>$staff) {
                 if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
-                $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']);
-                $email->sendAlert($staff->getEmail(), $msg['subj'], $alert,
-                    null, $options);
+                $alert = $this->replaceVars($msg, array('recipient' => $staff));
+                $email->sendAlert($staff->getEmail(), $alert['subj'], $alert['body'], null, $options);
                 $sentlist[] = $staff->getEmail();
             }
          }
@@ -1377,6 +1456,13 @@ class Ticket {
 
         $this->setLastMsgId($message->getId());
 
+        //Add email recipients as collaborators
+        if($vars['recipients']) {
+            foreach($vars['recipients'] as $recipient)
+                if(($user=User::fromVars($recipient)))
+                    $this->addCollaborator($user, $errors);
+        }
+
         if(!$alerts) return $message; //Our work is done...
 
         $autorespond = true;
@@ -1387,17 +1473,22 @@ class Ticket {
 
         $dept = $this->getDept();
 
-        if(!$dept || !($tpl = $dept->getTemplate()))
-            $tpl= $cfg->getDefaultTemplate();
-
-        if(!($email=$cfg->getAlertEmail()))
-            $email =$cfg->getDefaultEmail();
 
+        $variables = array(
+                'message' => $message,
+                'poster' => ($vars['poster'] ? $vars['poster'] : $this->getName())
+                );
+        $options = array(
+                'inreplyto' => $message->getEmailMessageId(),
+                'references' => $message->getEmailReferences());
         //If enabled...send alert to staff (New Message Alert)
-        if($cfg->alertONNewMessage() && $tpl && $email && ($msg=$tpl->getNewMessageAlertMsgTemplate())) {
+        if($cfg->alertONNewMessage()
+                && ($email = $cfg->getAlertEmail())
+                && ($tpl = $dept->getTemplate())
+                && ($msg = $tpl->getNewMessageAlertMsgTemplate())) {
 
             $attachments = $message->getAttachments();
-            $msg = $this->replaceVars($msg->asArray(), array('message' => $message));
+            $msg = $this->replaceVars($msg->asArray(), $variables);
 
             //Build list of recipients and fire the alerts.
             $recipients=array();
@@ -1415,18 +1506,21 @@ class Ticket {
                 $recipients[]=$manager;
 
             $sentlist=array(); //I know it sucks...but..it works.
-            $options = array(
-                'inreplyto'=>$message->getEmailMessageId(),
-                'references'=>$message->getEmailReferences());
             foreach( $recipients as $k=>$staff) {
                 if(!$staff || !$staff->getEmail() || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
-                $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']);
-                $email->sendAlert($staff->getEmail(), $msg['subj'], $alert,
-                    $attachments, $options);
+                $alert = $this->replaceVars($msg, array('recipient' => $staff));
+                $email->sendAlert($staff->getEmail(), $alert['subj'], $alert['body'], $attachments, $options);
                 $sentlist[] = $staff->getEmail();
             }
         }
 
+        unset($variables['message']);
+        $variables['message'] = $message->asVar();
+
+        $info = $options + array('variables' => $variables);
+        $this->activityNotice($info);
+
+
         return $message;
     }
 
@@ -1455,13 +1549,9 @@ class Ticket {
 
         $dept = $this->getDept();
 
-        if(!($tpl = $dept->getTemplate()))
-            $tpl= $cfg->getDefaultTemplate();
-
-        if(!$dept || !($email=$dept->getEmail()))
-            $email = $cfg->getDefaultEmail();
-
-        if($tpl && ($msg=$tpl->getAutoReplyMsgTemplate()) && $email) {
+        if(($email=$dept->getEmail())
+                && ($tpl = $dept->getTemplate())
+                && ($msg=$tpl->getAutoReplyMsgTemplate())) {
 
             if($dept && $dept->isPublic())
                 $signature=$dept->getSignature();
@@ -1493,7 +1583,7 @@ class Ticket {
 
 
         if(!$vars['poster'] && $thisstaff)
-            $vars['poster'] = $thisstaff->getName();
+            $vars['poster'] = $thisstaff;
 
         if(!$vars['staffId'] && $thisstaff)
             $vars['staffId'] = $thisstaff->getId();
@@ -1506,45 +1596,48 @@ class Ticket {
             $this->setStatus($vars['reply_ticket_status']);
 
         $this->onResponse(); //do house cleaning..
-        $this->reload();
 
         /* email the user??  - if disabled - the bail out */
         if(!$alert) return $response;
 
         $dept = $this->getDept();
 
-        if(!($tpl = $dept->getTemplate()))
-            $tpl= $cfg->getDefaultTemplate();
-
-        if(!$dept || !($email=$dept->getEmail()))
-            $email = $cfg->getDefaultEmail();
-
-        if($tpl && ($msg=$tpl->getReplyMsgTemplate()) && $email) {
+        if($thisstaff && $vars['signature']=='mine')
+            $signature=$thisstaff->getSignature();
+        elseif($vars['signature']=='dept' && $dept && $dept->isPublic())
+            $signature=$dept->getSignature();
+        else
+            $signature='';
 
-            if($thisstaff && $vars['signature']=='mine')
-                $signature=$thisstaff->getSignature();
-            elseif($vars['signature']=='dept' && $dept && $dept->isPublic())
-                $signature=$dept->getSignature();
-            else
-                $signature='';
+        $variables = array(
+                'response' => $response,
+                'signature' => $signature,
+                'staff' => $thisstaff,
+                'poster' => $thisstaff);
+        $options = array(
+                'inreplyto' => $response->getEmailMessageId(),
+                'references' => $response->getEmailReferences());
 
-            //Set attachments if emailing.
-            $attachments = $cfg->emailAttachments()?$response->getAttachments():array();
+        if(($email=$dept->getEmail())
+                && ($tpl = $dept->getTemplate())
+                && ($msg=$tpl->getReplyMsgTemplate())) {
 
-            $msg = $this->replaceVars($msg->asArray(),
-                    array('response' => $response, 'signature' => $signature, 'staff' => $thisstaff));
+            $msg = $this->replaceVars($msg->asArray(), $variables);
 
             if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator()))
                 $msg['body'] = "<p style=\"display:none\">$tag<p>".$msg['body'];
-
-            $options = array(
-                'inreplyto' => $response->getEmailMessageId(),
-                'references' => $response->getEmailReferences());
-            //TODO: setup  5 param (options... e.g mid trackable on replies)
+            $attachments = $cfg->emailAttachments()?$response->getAttachments():array();
             $email->send($this->getEmail(), $msg['subj'], $msg['body'], $attachments,
                 $options);
         }
 
+        if($vars['emailcollab']) {
+            unset($variables['response']);
+            $variables['message'] = $response->asVar();
+            $info = $options + array('variables' => $variables);
+            $this->activityNotice($info);
+        }
+
         return $response;
     }
 
@@ -1618,14 +1711,9 @@ class Ticket {
         if(!$alert || !$cfg->alertONNewNote() || !($dept=$this->getDept()))
             return $note;
 
-        if(!($tpl = $dept->getTemplate()))
-            $tpl= $cfg->getDefaultTemplate();
-
-        if(!($email=$cfg->getAlertEmail()))
-            $email =$cfg->getDefaultEmail();
-
-
-        if($tpl && ($msg=$tpl->getNoteAlertMsgTemplate()) && $email) {
+        if(($email=$cfg->getAlertEmail())
+                && ($tpl = $dept->getTemplate())
+                && ($msg=$tpl->getNoteAlertMsgTemplate())) {
 
             $attachments = $note->getAttachments();
 
@@ -1658,9 +1746,8 @@ class Ticket {
                         || in_array($staff->getEmail(), $sentlist) //No duplicates.
                         || $note->getStaffId() == $staff->getId())  //No need to alert the poster!
                     continue;
-                $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']);
-                $email->sendAlert($staff->getEmail(), $msg['subj'], $alert, $attachments,
-                    $options);
+                $alert = $this->replaceVars($msg, array('recipient' => $staff));
+                $email->sendAlert($staff->getEmail(), $alert['subj'], $alert['body'], $attachments, $options);
                 $sentlist[] = $staff->getEmail();
             }
         }
@@ -2031,7 +2118,7 @@ class Ticket {
             if (!$user) {
                 $user_form = UserForm::getUserForm()->getForm($vars);
                 if (!$user_form->isValid($field_filter)
-                        || !($user=User::fromForm($user_form->getClean())))
+                        || !($user=User::fromVars($user_form->getClean())))
                     $errors['user'] = 'Incomplete client information';
             }
         }
@@ -2225,19 +2312,15 @@ class Ticket {
 
         $ticket->reload();
 
-        if(!$cfg->notifyONNewStaffTicket() || !isset($vars['alertuser']))
+        if(!$cfg->notifyONNewStaffTicket()
+                || !isset($vars['alertuser'])
+                || !($dept=$ticket->getDept()))
             return $ticket; //No alerts.
 
         //Send Notice to user --- if requested AND enabled!!
-
-        $dept=$ticket->getDept();
-        if(!$dept || !($tpl=$dept->getTemplate()))
-            $tpl=$cfg->getDefaultTemplate();
-
-        if(!$dept || !($email=$dept->getEmail()))
-            $email =$cfg->getDefaultEmail();
-
-        if($tpl && ($msg=$tpl->getNewTicketNoticeMsgTemplate()) && $email) {
+        if(($tpl=$dept->getTemplate())
+                && ($msg=$tpl->getNewTicketNoticeMsgTemplate())
+                && ($email=$dept->getEmail())) {
 
             $message = $vars['message'];
             if($response) {
diff --git a/include/class.user.php b/include/class.user.php
index 2d837dad0cf22c7039631027267cb42e6f485126..85bffe026d3769715c466219d487749de92f6fca 100644
--- a/include/class.user.php
+++ b/include/class.user.php
@@ -76,32 +76,46 @@ class User extends UserModel {
             $this->default_email = UserEmail::lookup($ht['default_email_id']);
     }
 
-    static function fromForm($data=false) {
+    static function fromVars($vars) {
         // Try and lookup by email address
-        $user = User::lookup(array('emails__address'=>$data['email']));
+        $user = User::lookup(array('emails__address'=>$vars['email']));
         if (!$user) {
             $user = User::create(array(
-                'name'=>$data['name'],
+                'name'=>$vars['name'],
                 'created'=>new SqlFunction('NOW'),
                 'updated'=>new SqlFunction('NOW'),
                 'default_email'=>
-                    UserEmail::create(array('address'=>$data['email']))
+                    UserEmail::create(array('address'=>$vars['email']))
             ));
             $user->save(true);
             $user->emails->add($user->default_email);
-
             // Attach initial custom fields
-            $uf = UserForm::getInstance();
-            foreach ($uf->getFields() as $f)
-                if (isset($data[$f->get('name')]))
-                    $uf->setAnswer($f->get('name'), $data[$f->get('name')]);
-            $uf->setClientId($user->id);
-            $uf->save();
+            $user->addDynamicData($vars);
         }
 
         return $user;
     }
 
+    static function fromForm($form) {
+
+        if(!$form) return null;
+
+        //Validate the form
+        $valid = true;
+        if (!$form->isValid())
+            $valid  = false;
+
+        //Make sure the email is not in-use
+        if (($field=$form->getField('email'))
+                && $field->getClean()
+                && User::lookup(array('emails__address'=>$field->getClean()))) {
+            $field->addError('Email is assigned to another user');
+            $valid = false;
+        }
+
+        return $valid ? self::fromVars($form->getClean()) : null;
+    }
+
     function getEmail() {
         return $this->default_email->address;
     }
@@ -148,6 +162,18 @@ class User extends UserModel {
                 return $a;
     }
 
+    function addDynamicData($data) {
+
+        $uf = UserForm::getInstance();
+        $uf->setClientId($this->id);
+        foreach ($uf->getFields() as $f)
+            if (isset($data[$f->get('name')]))
+                $uf->setAnswer($f->get('name'), $data[$f->get('name')]);
+        $uf->save();
+
+        return $uf;
+    }
+
     function getDynamicData() {
         if (!isset($this->_entries)) {
             $this->_entries = DynamicFormEntry::forClient($this->id)->all();
@@ -341,6 +367,10 @@ class PersonsName {
         return $this->name;
     }
 
+    function getName() {
+        return $this;
+    }
+
     function asVar() {
         return $this->__toString();
     }
@@ -421,3 +451,5 @@ class UserEmail extends UserEmailModel {
         return $email;
     }
 }
+
+?>
diff --git a/include/i18n/en_US/templates/email/ticket.activity.notice.yaml b/include/i18n/en_US/templates/email/ticket.activity.notice.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..4248931a815db98cd8cd3a7ce35a2048ee07da2c
--- /dev/null
+++ b/include/i18n/en_US/templates/email/ticket.activity.notice.yaml
@@ -0,0 +1,37 @@
+#
+# Email template: ticket.activity.notice.yaml
+#
+# Notice sent to collaborators on ticket activity e.g reply or message
+#
+---
+notes: |
+    Notice sent to collaborators on ticket activity e.g reply or message.
+
+subject: "Re: %{ticket.subject}"
+body: |
+    <img src="cid:6fe1efdea357534d238b86e7860a7c5a" alt="osTicket Logo (kangaroo)" width="113" height="64"
+    style="float: right; width: 113px; margin: 0px 0px 10px 10px;">
+    <h3><span style="color: rgb(127, 127, 127); font-weight: normal; font-family: Georgia; font-size: 30pt"
+    >Stay in the loop</span></h3> <strong><br>
+    Dear %{recipient.name.first},
+    </strong>
+    <br>
+    <br>
+    <div>
+        <em>%{poster.name}</em> just logged a message to a ticket in which you participate.
+    </div>
+    <br>
+    %{message}
+    <br>
+    <br>
+    <hr>
+    <div style="color: rgb(127, 127, 127); font-size: small; text-align: center;">
+    <em>You're getting this email because you are a collaborator
+    on ticket <a href="%{ticket.client_link}" style="color: rgb(84, 141, 212);"
+    >#%{ticket.number}</a>.  To participate, simply reply to this email
+    or <a href="%{ticket.client_link}" style="color: rgb(84, 141, 212);"
+    >click here</a> for a complete archive of the ticket thread.</em>
+    </div>
+    <div style="text-align: center;"> <a href="http://osticket.com/"><img
+    src="cid:b56944cb4722cc5cda9d1e23a3ea7fbc" alt="Powered by osTicket"
+    width="126" height="19" style="width: 126px;"></a> </div>
diff --git a/include/staff/templates/collaborators.tmpl.php b/include/staff/templates/collaborators.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..a10bf0c105b5d9ea5a116a09600d66ac29ca93eb
--- /dev/null
+++ b/include/staff/templates/collaborators.tmpl.php
@@ -0,0 +1,57 @@
+<h3>Ticket Collaborators</h3>
+<b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
+<?php
+if($info && $info['msg']) {
+    echo sprintf('<p id="msg_notice" style="padding-top:2px;">%s</p>', $info['msg']);
+} ?>
+<hr/>
+<?php
+if(($users=$ticket->getCollaborators())) {?>
+<div id="manage_collaborators">
+<form method="post" class="collaborators" action="#tickets/<?php echo $ticket->getId(); ?>/collaborators">
+    <table border="0" cellspacing="1" cellpadding="1" width="100%">
+    <?php
+    foreach($users as $user) {
+        $checked = $user->isActive() ? 'checked="checked"' : '';
+        echo sprintf('<tr>
+                        <td>
+                            <input type="checkbox" name="cid[]" id="c%d" value="%d" %s>
+                            <a class="collaborator" href="#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="">
+                            <a class="remove" href="#d%d">&times;</a></td>
+                        <td width="30">&nbsp;</td>
+                    </tr>',
+                    $user->getId(),
+                    $user->getId(),
+                    $checked,
+                    $user->getId(),
+                    $user->getName(),
+                    $user->getEmail(),
+                    $user->getId(),
+                    $user->getId());
+    }
+    ?>
+    </table>
+    <hr style="margin-top:1em"/>
+    <div><a class="collaborator"
+        href="#tickets/<?php echo $ticket->getId(); ?>/add-collaborator" >Add New Collaborator</a></div>
+    <div id="savewarning" style="display:none; padding-top:2px;"><p id="msg_warning">You have made changes that you need to save.</p></div>
+    <p class="full-width">
+        <span class="buttons" style="float:left">
+            <input type="reset" value="Reset">
+            <input type="button" value="Done" class="close">
+        </span>
+        <span class="buttons" style="float:right">
+            <input type="submit" value="Save Changes">
+        </span>
+     </p>
+</form>
+<div class="clear"></div>
+</div>
+<?php
+} else {
+    echo "Bro, not sure how you got here!";
+}
+?>
diff --git a/include/staff/templates/user-lookup.tmpl.php b/include/staff/templates/user-lookup.tmpl.php
index de8dedb4e79b8d513dedf64c5134d6f1a579f783..8a6edd8f8771ca1cd583f3d1801eb1f72925969d 100644
--- a/include/staff/templates/user-lookup.tmpl.php
+++ b/include/staff/templates/user-lookup.tmpl.php
@@ -10,7 +10,7 @@ if ($info['error']) {
     echo sprintf('<p id="msg_notice">%s</p>', $info['msg']);
 } ?>
 <div id="selected-user-info" style="display:<?php echo $user ? 'block' :'none'; ?>;margin:5px;">
-<form method="get" class="user" action="#users/lookup">
+<form method="post" class="user" action="<?php echo $info['action'] ?  $info['action'] : '#users/lookup'; ?>">
     <input type="hidden" id="user-id" name="id" value="<?php echo $user ? $user->getId() : 0; ?>"/>
     <i class="icon-user icon-4x pull-left icon-border"></i>
     <a class="action-button pull-right" style="overflow:inherit"
@@ -30,7 +30,7 @@ if ($info['error']) {
 </form>
 </div>
 <div id="new-user-form" style="display:<?php echo $user ? 'none' :'block'; ?>;">
-<form method="post" class="user" action="#users/lookup/form">
+<form method="post" class="user" action="<?php echo $info['action'] ?  $info['action'] : '#users/lookup/form'; ?>">
     <table width="100%">
     <?php
         if(!$form) $form = UserForm::getInstance();
diff --git a/include/staff/templates/user.tmpl.php b/include/staff/templates/user.tmpl.php
index 92bbe3754effd5161a51e2f3a1ebca4d9e65c910..37528cc52d13deff96f20c7062231503c43ec45a 100644
--- a/include/staff/templates/user.tmpl.php
+++ b/include/staff/templates/user.tmpl.php
@@ -30,7 +30,7 @@ if ($info['error']) {
 <div id="user-form" style="display:<?php echo $forms ? 'block' : 'none'; ?>;">
 <div><p id="msg_info"><i class="icon-info-sign"></i>&nbsp; Please note that updates will be reflected system-wide.</p></div>
 <?php
-$action = '#users/'.$user->getId();
+$action = $info['action'] ? $info['action'] : ('#users/'.$user->getId());
 if ($ticket && $ticket->getOwnerId() == $user->getId())
     $action = '#tickets/'.$ticket->getId().'/user';
 ?>
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index de1fb3a0f217ac7d788e1ff92315118f3f0828d9..3866de15be2f54303d12e983e5d6dab78dce8042 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -405,20 +405,55 @@ $tcount+= $ticket->getNumNotes();
         <input type="hidden" name="a" value="reply">
         <span class="error"></span>
         <table style="width:100%" border="0" cellspacing="0" cellpadding="3">
+           <tbody id="to_sec">
             <tr>
                 <td width="120">
                     <label><strong>TO:</strong></label>
                 </td>
                 <td>
                     <?php
-                    echo sprintf('<span id="user-to-name">%s</span> <em>&lt;<span id="user-to-email">%s</span>&gt;</em>',
-                                $ticket->getName(), $ticket->getReplyToEmail());
+                    $to =sprintf('%s &lt;%s&gt;', $ticket->getName(), $ticket->getReplyToEmail());
+                    $emailReply = (!isset($info['emailreply']) || $info['emailreply']);
                     ?>
-                    &nbsp;&nbsp;&nbsp;
-                    <label><input type='checkbox' value='1' name="emailreply" id="remailreply"
-                        <?php echo ((!$info['emailreply'] && !$errors) || isset($info['emailreply']))?'checked="checked"':''; ?>> Email Reply</label>
+                    <select id="emailreply" name="emailreply">
+                        <option value="1" <?php echo $emailReply ?  'selected="selected"' : ''; ?>><?php echo $to; ?></option>
+                        <option value="0" <?php echo !$emailReply ? 'selected="selected"' : ''; ?>
+                            >&mdash;Do Not Email Reply&mdash;</option>
+                    </select>
+                </td>
+            </tr>
+            </tbody>
+            <?php
+            if(1) { //Make CC optional feature? NO, for now.
+                ?>
+            <tbody id="cc_sec" style="display:<?php echo $emailReply?
+            'table-row-group':'none'; ?>;">
+            <tr>
+                <td width="120">
+                    <label><strong>CC:</strong></label>
+                </td>
+                <td>
+                    <?php
+                    if($ticket->getNumCollaborators()) { ?>
+                        <input type='checkbox' value='1' name="emailcollab" id="emailcollab"
+                            <?php echo ((!$info['emailcollab'] && !$errors) || isset($info['emailcollab']))?'checked="checked"':''; ?>>
+                       <?php
+                        echo sprintf('<a class="collaborators"
+                                href="#tickets/%d/collaborators/manage">Collaborators (%d)</a>',
+                                $ticket->getId(),
+                                $ticket->getNumCollaborators());
+                    } else {
+                        echo sprintf('<div><a class="collaborators"
+                                href="#tickets/%d/collaborators/manage" >Add Collaborators</a></div>',
+                                $ticket->getId());
+                    }
+                   ?>
                 </td>
             </tr>
+            </tbody>
+            <?php
+            } ?>
+            <tbody id="resp_sec">
             <?php
             if($errors['response']) {?>
             <tr><td width="120">&nbsp;</td><td class="error"><?php echo $errors['response']; ?>&nbsp;</td></tr>
@@ -518,7 +553,7 @@ $tcount+= $ticket->getNumNotes();
             </tr>
             <?php
             } ?>
-            </div>
+         </tbody>
         </table>
         <p  style="padding-left:165px;">
             <input class="btn_sm" type="submit" value="Post Reply">
diff --git a/include/upgrader/streams/core/collaboration.patch.sql b/include/upgrader/streams/core/collaboration.patch.sql
new file mode 100644
index 0000000000000000000000000000000000000000..4f6adf91d8e1ca1ae2ce502e1ac317a1fda219f2
--- /dev/null
+++ b/include/upgrader/streams/core/collaboration.patch.sql
@@ -0,0 +1,25 @@
+/**
+ * @version v1.8.1 Collaboration (CC/BCC support)
+ * @signature f353145f8f4f48ea7f0d8e87083bb57c
+ *
+ * Adds the database structure for collaboration table
+ *
+ */
+
+DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_collaborator`;
+CREATE TABLE `%TABLE_PREFIX%ticket_collaborator` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `isactive` tinyint(1) unsigned NOT NULL DEFAULT '1',
+  `ticket_id` int(11) unsigned NOT NULL DEFAULT '0',
+  `user_id` int(11) unsigned NOT NULL DEFAULT '0',
+  `role` char(1) NOT NULL DEFAULT 'E',
+  `updated` datetime NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `collab` (`ticket_id`,`user_id`)
+) DEFAULT CHARSET=utf8;
+
+
+--  Finish
+UPDATE `%TABLE_PREFIX%config`
+    SET `value` = 'f353145f8f4f48ea7f0d8e87083bb57c'
+        WHERE `key` = 'schema_signature' AND `namespace` = 'core';
diff --git a/scp/ajax.php b/scp/ajax.php
index 106f3366a723a6e25a815796209c1dc5fed07f71..0ea64116e77289b22e9c5aba5a7efac971f8b6ee 100644
--- a/scp/ajax.php
+++ b/scp/ajax.php
@@ -75,9 +75,16 @@ $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/manage$', 'showCollaborators'),
+        url_post('^(?P<tid>\d+)/collaborators$', 'updateCollaborators'),
+        url('^(?P<tid>\d+)/add-collaborator$', 'addCollaborator'),
         url_get('^lookup', 'lookup'),
         url_get('^search', 'search')
     )),
+    url('^/collaborators/', patterns('ajax.tickets.php:TicketsAjaxAPI',
+        url_get('^(?P<cid>\d+)/view$', 'viewCollaborator'),
+        url_post('^(?P<cid>\d+)$', 'updateCollaborator')
+    )),
     url('^/draft/', patterns('ajax.draft.php:DraftAjaxAPI',
         url_post('^(?P<id>\d+)$', 'updateDraft'),
         url_delete('^(?P<id>\d+)$', 'deleteDraft'),
diff --git a/scp/js/scp.js b/scp/js/scp.js
index b61e62057084e164bf1aac9e146f13f292ba9b3b..b80cb1a3b83f41f7825ff3e0b6aad8019c7af002 100644
--- a/scp/js/scp.js
+++ b/scp/js/scp.js
@@ -399,13 +399,13 @@ $(document).ready(function(){
            });
      });
 
-    $.userLookup = function (url, callback) {
+    $.dialog = function (url, code, cb) {
 
         $('.dialog#popup .body').load(url, function () {
             $('#overlay').show();
             $('.dialog#popup').show();
-            $(document).off('.user');
-            $(document).on('submit.user', '.dialog#popup form.user',function(e) {
+            $(document).off('.dialog');
+            $(document).on('submit.dialog', '.dialog#popup form', function(e) {
                 e.preventDefault();
                 var $form = $(this);
                 var $dialog = $form.closest('.dialog');
@@ -415,12 +415,11 @@ $(document).ready(function(){
                     data: $form.serialize(),
                     cache: false,
                     success: function(resp, status, xhr) {
-                        if (xhr && xhr.status == 201) {
-                            var user = $.parseJSON(xhr.responseText);
+                        if (xhr && xhr.status == code) {
                             $('div.body', $dialog).empty();
                             $dialog.hide();
                             $('#overlay').hide();
-                            if(callback) callback(user);
+                            if(cb) cb(xhr.responseText);
                         } else {
                             $('div.body', $dialog).html(resp);
                             $('#msg_notice, #msg_error', $dialog).delay(5000).slideUp();
@@ -434,6 +433,13 @@ $(document).ready(function(){
          });
      };
 
+    $.userLookup = function (url, cb) {
+        $.dialog(url, 201, function (resp) {
+            var user = $.parseJSON(resp);
+            if(cb) cb(user);
+        });
+    };
+
     $('#advanced-search').delegate('#status', 'change', function() {
         switch($(this).val()) {
             case 'closed':
diff --git a/scp/js/ticket.js b/scp/js/ticket.js
index c2110121a8f2e202a6749b6f56fb741cd09d1a7b..c91cacaa9eff51205a25265bdfa210d5e7c3864f 100644
--- a/scp/js/ticket.js
+++ b/scp/js/ticket.js
@@ -363,6 +363,72 @@ jQuery(function($) {
         return false;
     });
 
+    //Collaborators
+    $(document).on('click', 'a.collaborator, a.collaborators', function(e) {
+        e.preventDefault();
+        var url = 'ajax.php/'+$(this).attr('href').substr(1);
+        $.dialog(url, 201, function (resp) {
+            });
+        return false;
+     });
+
+    $(document).on('click', 'form.collaborators a#addcollaborator', function (e) {
+        e.preventDefault();
+        $('div#manage_collaborators').hide();
+        $('div#add_collaborator').fadeIn();
+        return false;
+     });
+
+    $(document).on('click', 'form.collaborators a.remove', function (e) {
+        e.preventDefault();
+        var fObj = $(this).closest('form');
+        $('input'+$(this).attr('href'))
+            .val($(this).attr('href').substr(2))
+            .trigger('change');
+        $(this).closest('tr').addClass('strike');
+
+        return false;
+     });
+
+    $(document).on('change', 'form.collaborators input:checkbox, input[name="del[]"]', function (e) {
+       var fObj = $(this).closest('form');
+       $('div#savewarning', fObj).fadeIn();
+       $('input:submit', fObj).css('color', 'red');
+     });
+
+    $(document).on('click', 'form.collaborators input:reset', function(e) {
+        var fObj = $(this).closest('form');
+        fObj.find('input[name="del[]"]').val('');
+        fObj.find('tr').removeClass('strike');
+        $('div#savewarning', fObj).hide();
+        $('input:submit', fObj).removeAttr('style');
+    });
+
+
+    $(document).on('click', 'form.collaborators input.cancel', function (e) {
+        e.preventDefault();
+        var $elem = $(this);
+
+        if($elem.attr('data-href')) {
+            var href = $elem.data('href').substr(1);
+            $('.dialog.collaborators .body').load('ajax.php/'+href, function () {
+                });
+        } else {
+
+            $('div#manage_collaborators').show();
+            $('div#add_collaborator').hide();
+        }
+        return false;
+    });
+
+    $(document).on('change', 'form#reply select#emailreply', function(e) {
+         var $cc = $('form#reply tbody#cc_sec');
+        if($(this).val() == 0)
+            $cc.hide();
+        else
+            $cc.show();
+     });
+
     var showNonLocalImage = function(div) {
         var $div = $(div),
             $img = $div.append($('<img>')
diff --git a/scp/js/tips.js b/scp/js/tips.js
index 44c924711c1b73bc012674acefce634f007b2eca..205eb6fc9d2df8edbbd17d3ad43dbfab76145d36 100644
--- a/scp/js/tips.js
+++ b/scp/js/tips.js
@@ -93,7 +93,6 @@ jQuery(function() {
             tip_timer = setTimeout(function() {
                 $('.tip_box').remove();
                 $('body').append(the_tip.hide().fadeIn());
-                console.log($(window).width(), tip_content.width(), the_tip.position())
                 if ($(window).width() < tip_content.outerWidth() + the_tip.position().left) {
                     the_tip.css({'left':x_pos-tip_content.outerWidth()-40+'px'});
                     tip_box.addClass('right');
diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql
index 8ab5676efd090558868eef5065c14b19882af0ed..cd260b93e439881d1c2bef6040eccc4f9d00b3b2 100644
--- a/setup/inc/streams/core/install-mysql.sql
+++ b/setup/inc/streams/core/install-mysql.sql
@@ -621,6 +621,19 @@ CREATE TABLE `%TABLE_PREFIX%ticket_thread` (
   KEY `pid` (`pid`)
 ) DEFAULT CHARSET=utf8;
 
+CREATE TABLE `%TABLE_PREFIX%ticket_collaborator` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `isactive` tinyint(1) unsigned NOT NULL DEFAULT '1',
+  `ticket_id` int(11) unsigned NOT NULL DEFAULT '0',
+  `user_id` int(11) unsigned NOT NULL DEFAULT '0',
+  `role` char(1) NOT NULL DEFAULT 'E',
+  `updated` datetime NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `collab` (`ticket_id`,`user_id`)
+) DEFAULT CHARSET=utf8;
+
+
+
 DROP TABLE IF EXISTS `%TABLE_PREFIX%timezone`;
 CREATE TABLE `%TABLE_PREFIX%timezone` (
   `id` int(11) unsigned NOT NULL auto_increment,