diff --git a/include/class.client.php b/include/class.client.php
index 1e82ebe411a214ecbc1e8f94dd33f2e5c120d836..83684cae3e51155a81f6c39a4ae95821429e399f 100644
--- a/include/class.client.php
+++ b/include/class.client.php
@@ -321,6 +321,7 @@ class EndUser extends BaseAuthenticatedUser {
     }
 
     private function getStats() {
+        global $cfg;
         $basic = Ticket::objects()
             ->annotate(array('count' => SqlAggregate::COUNT('ticket_id')))
             ->values('status__state', 'topic_id')
@@ -338,10 +339,11 @@ class EndUser extends BaseAuthenticatedUser {
         // one index. Therefore, to scan two indexes (by user_id and
         // thread.collaborators.user_id), we need two queries. A union will
         // help out with that.
-        $mine->union($collab->filter(array(
-            'thread__collaborators__user_id' => $this->getId(),
-            Q::not(array('user_id' => $this->getId()))
-        )));
+        if ($cfg->collaboratorTicketsVisibility())
+            $mine->union($collab->filter(array(
+                'thread__collaborators__user_id' => $this->getId(),
+                Q::not(array('user_id' => $this->getId()))
+            )));
 
         if ($orgid = $this->getOrgId()) {
             // Also generate a separate query for all the tickets owned by
diff --git a/include/class.config.php b/include/class.config.php
index 8cd3012e74920e916c7a45d518d7fc903cd153cc..41ee9a88779583742ecb9e977143a068b09a6ab6 100644
--- a/include/class.config.php
+++ b/include/class.config.php
@@ -197,6 +197,7 @@ class OsticketConfig extends Config {
         'agent_name_format' =>  'full', # First Last
         'client_name_format' => 'original', # As entered
         'auto_claim_tickets'=>  true,
+        'collaborator_ticket_visibility' =>  true,
         'system_language' =>    'en_US',
         'default_storage_bk' => 'D',
         'message_autoresponder_collabs' => true,
@@ -919,6 +920,10 @@ class OsticketConfig extends Config {
         return $this->get('auto_claim_tickets');
     }
 
+    function collaboratorTicketsVisibility() {
+        return $this->get('collaborator_ticket_visibility');
+    }
+
     function getDefaultTicketQueueId() {
         return $this->get('default_ticket_queue');
     }
@@ -1271,6 +1276,7 @@ class OsticketConfig extends Config {
             'max_open_tickets'=>$vars['max_open_tickets'],
             'enable_captcha'=>isset($vars['enable_captcha'])?1:0,
             'auto_claim_tickets'=>isset($vars['auto_claim_tickets'])?1:0,
+            'collaborator_ticket_visibility'=>isset($vars['collaborator_ticket_visibility'])?1:0,
             'show_related_tickets'=>isset($vars['show_related_tickets'])?1:0,
             'allow_client_updates'=>isset($vars['allow_client_updates'])?1:0,
             'ticket_lock' => $vars['ticket_lock'],
diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php
index 7644cbb7eaee141a20de00e0ae705336ff3770ed..18f42a58bd4061612e2a30598e66718adde047c2 100644
--- a/include/class.mailfetch.php
+++ b/include/class.mailfetch.php
@@ -335,23 +335,31 @@ class MailFetcher {
 
         $header['system_emails'] = array();
         $header['recipients'] = array();
+        $header['thread_entry_recipients'] = array();
         foreach($tolist as $source => $list) {
             foreach($list as $addr) {
                 if (!($emailId=Email::getIdByEmail(strtolower($addr->mailbox).'@'.$addr->host))) {
                     //Skip virtual Delivered-To addresses
                     if ($source == 'delivered-to') continue;
 
+                    $name = $this->mime_decode(@$addr->personal);
+                    $email = strtolower($addr->mailbox).'@'.$addr->host;
                     $header['recipients'][] = array(
                             'source' => sprintf(_S("Email (%s)"),$source),
-                            'name' => $this->mime_decode(@$addr->personal),
-                            'email' => strtolower($addr->mailbox).'@'.$addr->host);
+                            'name' => $name,
+                            'email' => $email);
+
+                    $header['thread_entry_recipients'][$source][] = sprintf('%s <%s>', $name, $email);
                 } elseif ($emailId) {
                     $header['system_emails'][] = $emailId;
+                    $system_email = Email::lookup($emailId);
+                    $header['thread_entry_recipients']['to'][] = (string) $system_email;
                     if (!$header['emailId'])
                         $header['emailId'] = $emailId;
                 }
             }
         }
+        $header['thread_entry_recipients']['to'] = array_unique($header['thread_entry_recipients']['to']);
 
         //See if any of the recipients is a delivered to address
         if ($tolist['delivered-to']) {
diff --git a/include/class.mailparse.php b/include/class.mailparse.php
index 58eb2a984e157f17ba0a0d87b3b295aedfeb420e..7d474c411f3ad9da889408f396e7c2fb9b617f9f 100644
--- a/include/class.mailparse.php
+++ b/include/class.mailparse.php
@@ -648,23 +648,31 @@ class EmailDataParser {
             $tolist['delivered-to'] = $dt;
 
         $data['system_emails'] = array();
+        $data['thread_entry_recipients'] = array();
         foreach ($tolist as $source => $list) {
             foreach($list as $addr) {
                 if (!($emailId=Email::getIdByEmail(strtolower($addr->mailbox).'@'.$addr->host))) {
                     //Skip virtual Delivered-To addresses
                     if ($source == 'delivered-to') continue;
 
+                    $name = $this->mime_decode(@$addr->personal);
+                    $email = strtolower($addr->mailbox).'@'.$addr->host;
                     $data['recipients'][] = array(
                         'source' => sprintf(_S("Email (%s)"), $source),
-                        'name' => trim(@$addr->personal, '"'),
-                        'email' => strtolower($addr->mailbox).'@'.$addr->host);
+                        'name' => $name,
+                        'email' => $email);
+
+                    $data['thread_entry_recipients'][$source][] = sprintf('%s <%s>', $name, $email);
                 } elseif ($emailId) {
                     $data['system_emails'][] = $emailId;
+                    $system_email = Email::lookup($emailId);
+                    $data['thread_entry_recipients']['to'][] = (string) $system_email;
                     if (!$data['emailId'])
                         $data['emailId'] = $emailId;
                 }
             }
         }
+        $data['thread_entry_recipients']['to'] = array_unique($data['thread_entry_recipients']['to']);
 
         /*
          * In the event that the mail was delivered to the system although none of the system
diff --git a/include/class.thread.php b/include/class.thread.php
index f658a25ba762f63b537ebd48e96dd9f569158311..f8455faf51dd6a402f9ab2e1442f13d0f7ec6088 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -428,6 +428,7 @@ implements Searchable {
             'ip' =>     '',
             'reply_to' => $entry,
             'recipients' => $mailinfo['recipients'],
+            'thread_entry_recipients' => $mailinfo['thread_entry_recipients'],
             'to-email-id' => $mailinfo['to-email-id'],
             'autorespond' => !isset($mailinfo['passive']),
         );
@@ -1504,8 +1505,19 @@ implements TemplateVariable {
         ));
 
         //add recipients to thread entry
-        if ($vars['recipients'])
-            $entry->recipients = json_encode($vars['recipients']);
+        if ($vars['thread_entry_recipients']) {
+            $count = 0;
+            foreach ($vars['thread_entry_recipients'] as $key => $value)
+                $count = $count + count($value);
+
+            if ($count > 1)
+                $entry->flags |= ThreadEntry::FLAG_REPLY_ALL;
+            else
+                $entry->flags |= ThreadEntry::FLAG_REPLY_USER;
+
+            $entry->recipients = json_encode($vars['thread_entry_recipients']);
+        }
+
 
         if (Collaborator::getIdByUserId($vars['userId'], $vars['threadId']))
           $entry->flags |= ThreadEntry::FLAG_COLLABORATOR;
@@ -2061,12 +2073,15 @@ class ThreadEvents extends InstrumentedList {
     function log($object, $state, $data=null, $user=null, $annul=null) {
         global $thisstaff, $thisclient;
 
-        if ($object instanceof Ticket)
+        if ($object && ($object instanceof Ticket))
             // TODO: Use $object->createEvent() (nolint)
             $event = ThreadEvent::forTicket($object, $state, $user);
-        elseif ($object instanceof Task)
+        elseif ($object && ($object instanceof Task))
             $event = ThreadEvent::forTask($object, $state, $user);
 
+        if (is_null($event))
+            return;
+
         # Annul previous entries if requested (for instance, reopening a
         # ticket will annul an 'closed' entry). This will be useful to
         # easily prevent repeated statistics.
@@ -2218,7 +2233,7 @@ class CollaboratorEvent extends ThreadEvent {
             }
             $desc = sprintf($base, implode(', ', $collabs));
             break;
-        case isset($data['add']) && $mode!=self::MODE_CLIENT:
+        case isset($data['add']):
             $base = __('<b>{somebody}</b> added <strong>%s</strong> as collaborators {timestamp}');
             $collabs = array();
             if ($data['add']) {
@@ -2861,17 +2876,10 @@ implements TemplateVariable {
     function addResponse($vars, &$errors) {
         $vars['threadId'] = $this->getId();
         $vars['userId'] = 0;
-        $vars['pid'] = $this->getLastMessage()->getId();
+        if ($message = $this->getLastMessage())
+            $vars['pid'] = $message->getId();
 
         $vars['flags'] = 0;
-        switch ($vars['reply-to']) {
-            case 'all':
-                $vars['flags'] |= ThreadEntry::FLAG_REPLY_ALL;
-            break;
-            case 'user':
-                $vars['flags'] |= ThreadEntry::FLAG_REPLY_USER;
-            break;
-        }
 
         if (!($resp = ResponseThreadEntry::add($vars, $errors)))
             return $resp;
diff --git a/include/class.ticket.php b/include/class.ticket.php
index bec6136bfee9b6ce72375cc15cca22f2db1b0400..c9811934a7189c9b979d7ca364c5016b6577fd5f 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -1629,14 +1629,6 @@ implements RestrictedAccess, Threadable, Searchable {
         $poster = User::lookup($entry->user_id);
         $posterEmail = $poster->getEmail()->address;
 
-        $recipients = array();
-        if($vars['ccs']) {
-          foreach ($vars['ccs'] as $cc) {
-            $collab = Collaborator::getIdByUserId($cc, $this->getThread()->getId());
-            $recipients[] = Collaborator::lookup($collab);
-          }
-        }
-
         $vars = array_merge($vars, array(
             'message' => (string) $entry,
             'poster' => $poster ?: _S('A collaborator'),
@@ -1658,35 +1650,22 @@ implements RestrictedAccess, Threadable, Searchable {
             }
         }
 
-        $collaborators = array();
-        $collabsCc = array();
-        foreach ($recipients as $recipient) {
-            if(get_class($recipient) == 'Collaborator') {
-              if ($recipient->isCc())
-                $collabsCc[] = $recipient->getEmail()->address;
-            }
+        foreach ($recipients as $key => $recipient) {
+            $recipient = $recipient->getContact();
 
             if(get_class($recipient) == 'TicketOwner')
-              $owner = $recipient;
-         }
+                $owner = $recipient;
 
-        foreach ($collabsCc as $cc) {
-          if (in_array($cc, $skip))
-            continue;
-          elseif ($cc != $posterEmail)
-            $collaborators[] = $cc;
-        }
+            if ((get_class($recipient) == 'Collaborator' ? $recipient->getUserId() : $recipient->getId()) == $entry->user_id)
+                unset($recipients[$key]);
+         }
 
-        //the ticket user is a recipient
+        //see if the ticket user is a recipient
         if ($owner->getEmail()->address != $poster->getEmail()->address && !in_array($owner->getEmail()->address, $skip))
           $owner_recip = $owner->getEmail()->address;
 
-        $collaborators['cc'] = $collaborators;
-
-        //collaborator email sent out
-        if ($collaborators['cc']  || $owner_recip) {
-          //say dear collaborator if the ticket user is not a recipient
-          if (!$owner_recip) {
+        //say dear collaborator if the ticket user is not a recipient
+        if (!$owner_recip) {
             $nameFormats = array_keys(PersonsName::allFormats());
             $names = array();
             foreach ($nameFormats as $key => $value) {
@@ -1694,16 +1673,13 @@ implements RestrictedAccess, Threadable, Searchable {
             }
             $names = array_merge($names, array('recipient' => $recipient));
             $cnotice = $this->replaceVars($msg, $names);
-          }
-
-          //otherwise address email to ticket user
-          else
+        }
+        //otherwise address email to ticket user
+        else
             $cnotice = $this->replaceVars($msg, array('recipient' => $owner));
 
-          //if the ticket user is a recipient, put them in to address otherwise, cc all recipients
-          $email->send($owner_recip ? $owner_recip : '', $cnotice['subj'], $cnotice['body'], $attachments,
-              $options, $collaborators);
-        }
+        $email->send($recipients, $cnotice['subj'], $cnotice['body'], $attachments,
+            $options);
     }
 
     function onMessage($message, $autorespond=true, $reopen=true) {
@@ -2691,6 +2667,26 @@ implements RestrictedAccess, Threadable, Searchable {
         if ($vars['userId'] == $this->user_id)
           $isMsg = true;
 
+      // Get active recipients of the response
+      // Initial Message from Tickets created by Agent
+      if ($vars['reply-to'])
+          $recipients = $this->getRecipients($vars['reply-to'], $vars['ccs']);
+      // Messages from Web Portal
+      elseif (strcasecmp($origin, 'email')) {
+          $recipients = $this->getRecipients('all');
+          foreach ($recipients as $key => $recipient) {
+              if (!$recipientContact = $recipient->getContact())
+                  continue;
+
+              $userId = $recipientContact->getUserId() ?: $recipientContact->getId();
+              // Do not list the poster as a recipient
+              if ($userId == $vars['userId'])
+                unset($recipients[$key]);
+          }
+      }
+      if ($recipients && $recipients instanceof MailingList)
+          $vars['thread_entry_recipients'] = $recipients->getEmailAddresses();
+
         if (!($message = $this->getThread()->addMessage($vars, $errors)))
             return null;
 
@@ -2747,7 +2743,9 @@ implements RestrictedAccess, Threadable, Searchable {
 
         $this->onMessage($message, ($autorespond && $alerts), $reopen); //must be called b4 sending alerts to staff.
 
-        if ($autorespond && $alerts && $cfg && $cfg->notifyCollabsONNewMessage()) {
+        if ($autorespond && $alerts
+            && $cfg && $cfg->notifyCollabsONNewMessage()
+            && strcasecmp($origin, 'email')) {
           //when user replies, this is where collabs notified
           $this->notifyCollaborators($message, array('signature' => ''));
         }
@@ -2902,14 +2900,14 @@ implements RestrictedAccess, Threadable, Searchable {
         if (!$vars['ip_address'] && $_SERVER['REMOTE_ADDR'])
             $vars['ip_address'] = $_SERVER['REMOTE_ADDR'];
 
-        // Add new collaboratorss (if any).
+        // Add new collaborators (if any).
         if (isset($vars['ccs']) && count($vars['ccs']))
-            $this->addCollaborators($vars['ccs']);
+            $this->addCollaborators($vars['ccs'], array(), $errors);
 
         // Get active recipients of the response
         $recipients = $this->getRecipients($vars['reply-to'], $vars['ccs']);
         if ($recipients instanceof MailingList)
-            $vars['recipients'] = $recipients->getEmailAddresses();
+            $vars['thread_entry_recipients'] = $recipients->getEmailAddresses();
 
         if (!($response = $this->getThread()->addResponse($vars, $errors)))
             return null;
@@ -3845,6 +3843,10 @@ implements RestrictedAccess, Threadable, Searchable {
         // Start tracking ticket lifecycle events (created should come first!)
         $ticket->logEvent('created', null, $thisstaff ?: $user);
 
+        // Add collaborators (if any)
+        if (isset($vars['ccs']) && count($vars['ccs']))
+          $ticket->addCollaborators($vars['ccs'], array(), $errors);
+
         // Add organizational collaborators
         if ($org && $org->autoAddCollabs()) {
             $pris = $org->autoAddPrimaryContactsAsCollabs();
@@ -4068,10 +4070,6 @@ implements RestrictedAccess, Threadable, Searchable {
         // Effective role for the department
         $role = $ticket->getRole($thisstaff);
 
-        // Add collaborators (if any)
-        if (isset($vars['ccs']) && count($vars['ccs']))
-          $ticket->addCollaborators($vars['ccs'], array(), $errors);
-
         $alert = strcasecmp('none', $vars['reply-to']);
         // post response - if any
         $response = null;
diff --git a/include/class.util.php b/include/class.util.php
index 962ef6f7a33efd342c3365b7c723d520ef9bc4fc..a3bc2305d130c8b7bf5885cc4df99f4dcf1cae22 100644
--- a/include/class.util.php
+++ b/include/class.util.php
@@ -20,6 +20,10 @@ implements EmailContact {
         $this->type = $type;
     }
 
+    function getContact() {
+        return $this->contact;
+    }
+
     function getId() {
         return $this->contact->getId();
     }
diff --git a/include/client/tickets.inc.php b/include/client/tickets.inc.php
index 92b131690ce0d877012568fa01e0a7234a79d4ac..9e678e31cbaa4a2ff9fec9d63fcb38ed172540b9 100644
--- a/include/client/tickets.inc.php
+++ b/include/client/tickets.inc.php
@@ -77,7 +77,11 @@ if ($settings['status'])
 // unique values
 $visibility = $basic_filter->copy()
     ->values_flat('ticket_id')
-    ->filter(array('user_id' => $thisclient->getId()))
+    ->filter(array('user_id' => $thisclient->getId()));
+
+// Add visibility of Tickets where the User is a Collaborator if enabled
+if ($cfg->collaboratorTicketsVisibility())
+    $visibility = $visibility
     ->union($basic_filter->copy()
         ->values_flat('ticket_id')
         ->filter(array('thread__collaborators__user_id' => $thisclient->getId()))
diff --git a/include/i18n/en_US/config.yaml b/include/i18n/en_US/config.yaml
index 32783896b5ee0083d2ea481d79a851c787fc0d3a..4d1f67f410f8f2561a68d39affc5d88e93a91559 100644
--- a/include/i18n/en_US/config.yaml
+++ b/include/i18n/en_US/config.yaml
@@ -67,6 +67,7 @@ core:
     assigned_alert_team_lead: 0
     assigned_alert_team_members: 0
     auto_claim_tickets: 1
+    collaborator_ticket_visibility: 1
     show_related_tickets: 1
     show_assigned_tickets: 1
     show_answered_tickets: 0
diff --git a/include/i18n/en_US/help/tips/settings.ticket.yaml b/include/i18n/en_US/help/tips/settings.ticket.yaml
index b13bd17c4f3bc9b2739ddb52f02c69c2d3ca497a..7d4a72fd2981ca81347d052670c93e470adac79e 100644
--- a/include/i18n/en_US/help/tips/settings.ticket.yaml
+++ b/include/i18n/en_US/help/tips/settings.ticket.yaml
@@ -94,6 +94,15 @@ claim_tickets:
         <br><br>
         Reopened tickets are always assigned to the last respondent.
 
+collaborator_ticket_visibility:
+    title: Collaborator Tickets Visibility
+    content: >
+        If Enabled, Users will have visibility to ALL Tickets they participate in
+        when signing into the Web Portal.
+        <br><br>
+        If Disabled, Users will only be able to see their own Tickets
+        when signing into the Web Portal.
+
 assigned_tickets:
     title: Assigned Tickets
     content: >
diff --git a/include/staff/settings-tickets.inc.php b/include/staff/settings-tickets.inc.php
index dd259ea8a1e350c30344ac528e42a3453d423de8..210b6d3f319dd33f9bb6bc20e3b8dca22348b2c5 100644
--- a/include/staff/settings-tickets.inc.php
+++ b/include/staff/settings-tickets.inc.php
@@ -206,6 +206,13 @@ if(!($maxfileuploads=ini_get('max_file_uploads')))
                 <?php echo __('Enable'); ?>&nbsp;<i class="help-tip icon-question-sign" href="#claim_tickets"></i>
             </td>
         </tr>
+        <tr>
+            <td><?php echo __('Collaborator Tickets Visibility'); ?>:</td>
+            <td>
+                <input type="checkbox" name="collaborator_ticket_visibility" <?php echo $config['collaborator_ticket_visibility']?'checked="checked"':''; ?>>
+                <?php echo __('Enable'); ?>&nbsp;<i class="help-tip icon-question-sign" href="#collaborator_ticket_visibility"></i>
+            </td>
+        </tr>
         <tr>
             <th colspan="2">
                 <em><b><?php echo __('Attachments');?></b>:  <?php echo __('Size and maximum uploads setting mainly apply to web tickets.');?></em>
diff --git a/include/staff/templates/thread-email-recipients.tmpl.php b/include/staff/templates/thread-email-recipients.tmpl.php
index 3bae51c02fab5ae3b20fc24c1c7e22f78427a8c2..6935c1fa873ec8cad13273bc580a05f960147b49 100644
--- a/include/staff/templates/thread-email-recipients.tmpl.php
+++ b/include/staff/templates/thread-email-recipients.tmpl.php
@@ -11,8 +11,8 @@ if (!$_REQUEST['mode']) { ?>
 $recipients = Format::htmlchars($recipients);
  foreach ($recipients as $k => $v) {
     echo sprintf('<tr><td nowrap width="5" valign="top"><b>%s</b>:</td><td>%s</td></tr>',
-            ucfirst($k),            
-            implode('<br>', $v)
+            ucfirst($k),
+            is_array($v) ? implode('<br>', $v) : $v
              );
  }
  ?>
diff --git a/include/staff/templates/thread-entry.tmpl.php b/include/staff/templates/thread-entry.tmpl.php
index 3407e534b966dd6d9a588e45367e70a3731c789f..f45f20b028afdd17fd5b6b16fc11e84042c14bbf 100644
--- a/include/staff/templates/thread-entry.tmpl.php
+++ b/include/staff/templates/thread-entry.tmpl.php
@@ -55,10 +55,10 @@ if ($user && $cfg->isAvatarsEnabled())
             <span class="label label-bare"><?php echo __('Resent'); ?></span>
 <?php   }
         if ($entry->flags & ThreadEntry::FLAG_REPLY_ALL) { ?>
-            <span class="label label-bare"><?php echo __('Reply All'); ?></span>
+            <span class="label label-bare"><i class="icon-group"></i></span>
 <?php   }
         if ($entry->flags & ThreadEntry::FLAG_REPLY_USER) { ?>
-            <span class="label label-bare"><?php echo __('Reply to User'); ?></span>
+            <span class="label label-bare"><i class="icon-user"></i></span>
 <?php   }
         if ($entry->flags & ThreadEntry::FLAG_COLLABORATOR && $entry->type == 'M') { ?>
             <span class="label label-bare"><?php echo __('Cc Collaborator'); ?></span>
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index ef7aff8e979fca11da68859188131edfba87c29e..c1747a63f2125fa75e7438ca67d500b83cf76426 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -770,8 +770,8 @@ if ($errors['err'] && isset($_POST['a'])) {
                    <td>
                    <div style="margin-bottom:2px;">
                     <?php
-                         echo sprintf('<span><a id="show_ccs"
-                                 class="icon-caret-right"></i>&nbsp;%s </a>
+                         echo sprintf('<span><a id="show_ccs">
+                                 <i id="arrow-icon" class="icon-caret-right"></i>&nbsp;%s </a>
                                  &nbsp;
                                  <a class="manage-collaborators
                                  collaborators preview noclick %s"
@@ -1265,7 +1265,7 @@ $(function() {
     });
 
   $('#show_ccs').click(function() {
-    var show = $(this);
+    var show = $('#arrow-icon');
     var collabs = $('a#managecollabs');
     $('#ccs').slideToggle('fast', function(){
         if ($(this).is(":hidden")) {
@@ -1286,12 +1286,12 @@ $(function() {
   $('#collabselection').select2({
     width: '350px',
     allowClear: true,
-    sorter: (data) => {
+    sorter: function(data) {
         return data.filter(function (item) {
                 return !item.selected;
                 });
     },
-    templateResult: (e) => {
+    templateResult: function(e) {
         var $e = $(
         '<span><i class="icon-user"></i> ' + e.text + '</span>'
         );