From c8872285383390805fcad5c824efc7be631ad963 Mon Sep 17 00:00:00 2001
From: Peter Rotich <peter@enhancesoft.com>
Date: Fri, 13 Jul 2018 18:21:32 +0000
Subject: [PATCH] Collaborators Revisited

UI changes to make it more user friendly
---
 include/ajax.thread.php           |   7 +
 include/class.email.php           |   5 +-
 include/class.ticket.php          |  16 ++
 include/staff/ticket-view.inc.php | 387 +++++++++++++++++-------------
 scp/js/scp.js                     |   3 +-
 5 files changed, 252 insertions(+), 166 deletions(-)

diff --git a/include/ajax.thread.php b/include/ajax.thread.php
index 7e72aef12..288634bb8 100644
--- a/include/ajax.thread.php
+++ b/include/ajax.thread.php
@@ -223,8 +223,15 @@ class ThreadAjaxAPI extends AjaxController {
 
         $errors = $info = array();
         if ($thread->updateCollaborators($_POST, $errors))
+            $users = array();
+            foreach ($thread->getCollaborators() as $c)
+                $users[] = array('id' => $c->getUserId(),
+                        'name' => $c->getName(),
+                        'email' => $c->getEmail());
+
             Http::response(201, $this->json_encode(array(
                             'id' => $thread->getId(),
+                            'users' => $users,
                             'text' => sprintf('(%d)',
                                 $thread->getNumCollaborators())
                             )
diff --git a/include/class.email.php b/include/class.email.php
index b0dce8bbc..34d1f542e 100644
--- a/include/class.email.php
+++ b/include/class.email.php
@@ -443,11 +443,14 @@ class Email extends VerySimpleModel {
         return self::$perms;
     }
 
-    static function getAddresses($options=array()) {
+    static function getAddresses($options=array(), $flat=true) {
         $objects = static::objects();
         if ($options['smtp'])
             $objects = $objects->filter(array('smtp_active'=>true));
 
+        if (!$flat)
+            return $objects;
+
         $addresses = array();
         foreach ($objects->values_flat('email_id', 'email') as $row) {
             list($id, $email) = $row;
diff --git a/include/class.ticket.php b/include/class.ticket.php
index d50180eef..7d1182062 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -843,6 +843,22 @@ implements RestrictedAccess, Threadable, Searchable {
         return $this->recipients;
     }
 
+    function getCollaborators() {
+        return $this->getThread()->getCollaborators();
+    }
+
+    function getNumCollaborators() {
+        return $this->getThread()->getNumCollaborators();
+    }
+
+    function getActiveCollaborators() {
+        return $this->getThread()->getActiveCollaborators();
+    }
+
+    function getNumActiveCollaborators() {
+        return $this->getThread()->getNumActiveCollaborators();
+    }
+
     function getAssignmentForm($source=null, $options=array()) {
 
         $prompt = $assignee = '';
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index 21d5646e9..f63259ac5 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -371,7 +371,7 @@ if($ticket->isOverdue())
                             else
                               $recipients = 0;
 
-                             echo sprintf('<span><a class="collaborators preview"
+                             echo sprintf('<span><a class="manage-collaborators preview"
                                     href="#thread/%d/collaborators"><span id="t%d-recipients"><i class="icon-group"></i> (%s)</span></a></span>',
                                     $ticket->getThreadId(),
                                     $ticket->getThreadId(),
@@ -694,7 +694,10 @@ if ($errors['err'] && isset($_POST['a'])) {
             id="post-note-tab"><?php echo __('Post Internal Note');?></a></li>
     </ul>
     <?php
-    if ($role->hasPerm(Ticket::PERM_REPLY)) { ?>
+    if ($role->hasPerm(Ticket::PERM_REPLY)) {
+        $replyTo = $_POST['reply-to'] ?: 'all';
+        $emailReply = ($replyTo != 'none');
+        ?>
     <form id="reply" class="tab_content spellcheck exclusive save"
         data-lock-object-id="ticket/<?php echo $ticket->getId(); ?>"
         data-lock-id="<?php echo $mylock ? $mylock->getId() : ''; ?>"
@@ -712,127 +715,188 @@ if ($errors['err'] && isset($_POST['a'])) {
             <?php
             }?>
            <tbody id="to_sec">
-           <?php
-           # XXX: Add user-to-name and user-to-email HTML ID#s
-           if ($addresses = Email::getAddresses(array('smtp' => true))){
-           ?>
            <tr>
                <td width="120">
                    <label><strong><?php echo __('From'); ?>:</strong></label>
                </td>
                <td>
-                   <select id="from_name" name="from_name">
+                   <select id="from_email_id" name="from_email_id">
                      <?php
-                     $sql=' SELECT email_id, email, name, smtp_host '
-                         .' FROM '.EMAIL_TABLE.' WHERE smtp_active = 1';
-                     if(($res=db_query($sql)) && db_num_rows($res)) {
-                         while (list($id, $email, $name, $host) = db_fetch_row($res)){
-                             $email=$name?"$name &lt;$email&gt;":$email;
-                             ?>
-                             <option value="<?php echo $id; ?>"<?php echo ($dept->getEmail()->email_id==$id)?'selected="selected"':''; ?>><?php echo $email; ?></option>
-                         <?php
+                     // Department email (default).
+                     echo sprintf('<option value="%s" selected="selected">%s</option>',
+                             $dept->getEmail()->getId(),
+                             Format::htmlchars($dept->getEmail()->getAddress()));
+                     // Optional SMTP addreses user can send email via
+                     if (($emails = Email::getAddresses(array('smtp' =>
+                                 true), false)) && count($emails)) {
+                         echo '<option value=""
+                             disabled="disabled">&nbsp;</option>';
+                         $emailId = $_POST['from_email_id'] ?: 0;
+                         foreach ($emails as $e) {
+                             if ($dept->getEmail()->getId() == $e->getId())
+                                 continue;
+                             echo sprintf('<option value="%s" %s>%s</option>',
+                                     $e->getId(),
+                                      $e->getId() == $emailId ?
+                                      'selected="selected"' : '',
+                                      Format::htmlchars($e->getAddress()));
                          }
-                     } ?>
+                     }
+                     ?>
                    </select>
                </td>
            </tr>
-           <?php } ?>
-            <tr>
+            </tbody>
+            <tbody id="recipients">
+             <tr id="user-row">
+                <td width="120">
+                    <label><strong><?php echo __('Recipients'); ?>:</strong></label>
+                </td>
+                <td><a href="#tickets/<?php echo $ticket->getId(); ?>/user"
+                    onclick="javascript:
+                        $.userLookup('ajax.php/tickets/<?php echo $ticket->getId(); ?>/user',
+                                function (user) {
+                                    window.location = 'tickets.php?id='<?php $ticket->getId(); ?>
+                                });
+                        return false;
+                        "><span ><?php
+                            echo Format::htmlchars($ticket->getOwner()->getEmail()->getAddress());
+                    ?></span></a>
+                </td>
+              </tr>
+               <tr><td>&nbsp;</td>
+                   <td>
+                   <div style="margin-bottom:2px;">
+                    <?php
+                         echo sprintf('<span><a id="show_ccs"
+                                 class="icon-caret-right"></i>&nbsp;%s </a>
+                                 &nbsp;
+                                 <a class="manage-collaborators preview noclick %s"
+                                  href="#thread/%d/collaborators">
+                                 (%s)</a></span>',
+                                 __('Collaborators'),
+                                 $ticket->getNumCollaborators()
+                                  ? '' : 'hidden',
+                                 $ticket->getThreadId(),
+                                 sprintf(__('%s of %s'),
+                                         sprintf('<span
+                                             class="collabselection__count">%d</span>',
+                                             $ticket->getNumActiveCollaborators()),
+                                         sprintf('<span
+                                             class="collabselection__total">%d</span>',
+                                              $ticket->getNumCollaborators())
+                                         )
+                                     );
+                    ?>
+                   </div>
+                   <div id="ccs" class="hidden">
+                     <div>
+                        <span style="margin: 10px 5px 1px 0;" class="faded pull-left"><?php echo __('Select or Add New Collaborators'); ?>&nbsp;</span>
+                        <?php
+                        if ($role->hasPerm(Ticket::PERM_REPLY)) { ?>
+                        <span class="action-button pull-left" style="margin: 2px  0 5px 20px;"
+                            data-dropdown="#action-dropdown-collaborators"
+                            data-placement="bottom"
+                            data-toggle="tooltip"
+                            title="<?php echo __('Manage Collaborators'); ?>"
+                            >
+                            <i class="icon-caret-down pull-right"></i>
+                            <a class="ticket-action" id="collabs-button"
+                                data-redirect="tickets.php?id=<?php echo
+                                $ticket->getId(); ?>"
+                                href="#thread/<?php echo
+                                $ticket->getThreadId(); ?>/collaborators">
+                                <i class="icon-group"></i></a>
+                         </span>
+                         <?php
+                        }  ?>
+                         <span class="error">&nbsp;&nbsp;<?php echo $errors['ccs']; ?></span>
+                        </div>
+                        <?php
+                        if ($role->hasPerm(Ticket::PERM_REPLY)) { ?>
+                        <div id="action-dropdown-collaborators" class="action-dropdown anchor-right">
+                          <ul>
+                             <li><a class="manage-collaborators"
+                                href="#thread/<?php echo
+                                $ticket->getThreadId(); ?>/add-collaborator/addcc"><i
+                                class="icon-plus"></i> <?php echo __('Add New'); ?></a>
+                             <li><a class="manage-collaborators"
+                                href="#thread/<?php echo
+                                $ticket->getThreadId(); ?>/collaborators"><i
+                                class="icon-cog"></i> <?php echo __('Manage Collaborators'); ?></a>
+                          </ul>
+                        </div>
+                        <?php
+                        } ?>
+                     <div class="clear">
+                      <select id="collabselection" name="ccs[]" multiple="multiple"
+                          data-placeholder="<?php
+                            echo __('Select Active Collaborators'); ?>">
+                          <?php
+                          $collabs = $ticket->getCollaborators();
+                          foreach ($collabs as $c) {
+                              echo sprintf('<option value="%s" %s class="%s">%s</option>',
+                                      $c->getUserId(),
+                                      $c->isActive() ?
+                                      'selected="selected"' : '',
+                                      $c->isActive() ?
+                                      'active' : 'disabled',
+                                      $c->getName());
+                          }
+                          ?>
+                      </select>
+                     </div>
+                 </div>
+                 </td>
+             </tr>
+             <tr>
                 <td width="120">
-                    <label><strong><?php echo __('Reply To'); ?>:</strong></label>
+                    <label><?php echo __('Reply To'); ?>:</label>
                 </td>
                 <td>
                     <?php
-                    //see who sent the last message and decide which option to select.
-                    $lastUser = $ticket->getLastUserRespondent();
+                    // Supported Reply Types
+                    $replyTypes = array(
+                            'all'   =>  __('All Active Recipients'),
+                            'user'  =>  sprintf('%s (%s)',
+                                __('Ticket Owner'),
+                                Format::htmlchars($ticket->getOwner()->getEmail())),
+                            'none'  =>  sprintf('&mdash; %s  &mdash;',
+                                __('Do Not Email Reply'))
+                            );
 
-                    if ($ticket->getOwnerId() == $lastUser->getId())
-                      $ticketUser = true;
-                    else {
-                      $collabs = $ticket->getThread()->getCollaborators();
-                      foreach ($collabs as $collab) {
-                        if ($collab->getUserId() == $lastUser->getId() && $collab->isCc())
-                          $ccUser = true;
-                      }
-                    }
-                    if ($ticketUser || $ccUser)
-                      $emailReply = true;
+                    $replyTo = $_POST['reply-to'] ?: 'all';
+                    $emailReply = ($replyTo != 'none');
                     ?>
-                    <select id="emailreply" name="emailreply">
-                        <option value="reply-all"><?php echo __('Reply All'); ?></option>
-                        <option value="reply-user"><?php echo __('Reply to User'); ?></option>
-                        <option value="0" <?php echo !$emailReply ? 'selected="selected"' : ''; ?>
-                        >&mdash; <?php echo __('Do Not Email Reply'); ?> &mdash;</option>
+                    <select id="reply-to" name="reply-to">
+                        <?php
+                        foreach ($replyTypes as $k => $v) {
+                            echo sprintf('<option value="%s" %s>%s</option>',
+                                    $k,
+                                    ($k == $replyTo) ?
+                                    'selected="selected"' : '',
+                                    $v);
+                        }
+                        ?>
                     </select>
                     <i class="help-tip icon-question-sign" href="#reply_types"></i>
                 </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 id="user-row">
-                <td width="120" style="padding-left:20px;">
-                    <label><?php echo __('User'); ?></label>
-                </td>
-                <td>
-                  <label><?php echo User::lookup($ticket->user_id); ?></label>
-                </td>
-             </tr>
-             <?php $collaborators = $ticket->getThread()->getCollaborators();
-             $cc_cids = array();
-             foreach ($collaborators as $c) {
-               if ($c->flags & Collaborator::FLAG_CC && $c->flags & Collaborator::FLAG_ACTIVE)
-                  $cc_cids[] = $c->user_id;
-             }
-            ?>
-             <tr id="cc-row">
-                 <td width="160" style="padding-left:20px;"><?php echo __('Cc'); ?></td>
-                 <td>
-                    <span>
-                      <select class="collabSelections" name="ccs[]" id="cc_users" multiple="multiple"
-                          data-placeholder="<?php echo __('Select Contacts'); ?>">
-                          <?php
-                          foreach ($cc_cids as $u) {
-                            if($u != $ticket->user_id) {
-                              ?>
-                              <option value="<?php echo $u; ?>" <?php
-                              if (in_array($u, $cc_cids))
-                              echo 'selected="selected"'; ?>><?php echo User::lookup($u); ?>
-                            </option>
-                          <?php } } ?>
-                          ?>
-                      </select>
-                    </span>
-
-                         <a class="inline button" style="overflow:inherit" href="#"
-                         onclick="javascript:
-                         $.userLookup('ajax.php/users/lookup/form', function (user) {
-                           var newUser = new Option(user.name, user.id, true, true);
-                           return $(&quot;#cc_users&quot;).append(newUser).trigger('change');
-                         });
-                         "><i class="icon-plus"></i> <?php echo __('Add New'); ?></a>
-
-                     <br/><span class="error"><?php echo $errors['ccs']; ?></span>
-                 </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>
-            <?php
-            }?>
+            <tr><td colspan="2">&nbsp;</td></tr>
             <tr>
                 <td width="120" style="vertical-align:top">
                     <label><strong><?php echo __('Response');?>:</strong></label>
                 </td>
                 <td>
-<?php if ($cfg->isCannedResponseEnabled()) { ?>
+                <?php
+                if ($errors['response'])
+                    echo sprintf('<div class="error">%s</div>',
+                            $errors['response']);
+
+                if ($cfg->isCannedResponseEnabled()) { ?>
+                  <div>
                     <select id="cannedResp" name="cannedResp">
                         <option value="0" selected="selected"><?php echo __('Select a canned response');?></option>
                         <option value='original'><?php echo __('Original Message'); ?></option>
@@ -846,8 +910,8 @@ if ($errors['err'] && isset($_POST['a'])) {
                         }
                         ?>
                     </select>
-                    <br>
-<?php } # endif (canned-resonse-enabled)
+                    </div>
+                <?php } # endif (canned-resonse-enabled)
                     $signature = '';
                     switch ($thisstaff->getDefaultSignatureType()) {
                     case 'dept':
@@ -858,6 +922,7 @@ if ($errors['err'] && isset($_POST['a'])) {
                         $signature = $thisstaff->getSignature();
                         break;
                     } ?>
+                  <div>
                     <input type="hidden" name="draft_id" value=""/>
                     <textarea name="response" id="response" cols="50"
                         data-signature-field="signature" data-dept-id="<?php echo $dept->getId(); ?>"
@@ -872,6 +937,7 @@ if ($errors['err'] && isset($_POST['a'])) {
     list($draft, $attrs) = Draft::getDraftAndDataAttrs('ticket.response', $ticket->getId(), $info['response']);
     echo $attrs; ?>><?php echo $_POST ? $info['response'] : $draft;
                     ?></textarea>
+                </div>
                 <div id="reply_form_attachments" class="attachments">
                 <?php
                     print $response_form->getField('attachments')->render();
@@ -1156,9 +1222,20 @@ $(function() {
         });
     });
 
-    $("#emailreply").ready(function(){
-     checkReply();
-    });
+    $(document).on('click', 'a.manage-collaborators', function(e) {
+        e.preventDefault();
+        var url = 'ajax.php/'+$(this).attr('href').substr(1);
+        $.dialog(url, 201, function (xhr) {
+           var resp = $.parseJSON(xhr.responseText);
+           if (resp.user && !resp.users)
+              resp.users.push(resp.user);
+            // TODO: Process resp.users
+           $('.tip_box').remove();
+        }, {
+            onshow: function() { $('#user-search').focus(); }
+        });
+        return false;
+     });
 
     // Post Reply or Note action buttons.
     $('a.post-response').click(function (e) {
@@ -1184,70 +1261,54 @@ $(function() {
         return false;
     });
 
-    $('#emailreply').on('change', function(){
-     checkReply();
+  $('#show_ccs').click(function() {
+    var show = $(this);
+    var collabs = $('a#managecollabs');
+    $('#ccs').slideToggle('fast', function(){
+        if ($(this).is(":hidden")) {
+            collabs.hide();
+            show.removeClass('icon-caret-down').addClass('icon-caret-right');
+        } else {
+            collabs.show();
+            show.removeClass('icon-caret-right').addClass('icon-caret-down');
+        }
     });
+    return false;
+   });
 
-    function checkReply() {
-      var value = $("#emailreply").val();
-      switch (value) {
-        case "reply-all":
-          $('#user-row').show();
-          $('#cc-row').show();
-          break;
-        case "reply-user":
-          $('#user-row').show();
-          $('#cc-row').hide();
-          break;
-        default:
-          $('#user-row').show();
-          $('#cc-row').show();
-          break;
-      }
-    }
-});
-
-$(function() {
- $('.collabSelections').on("select2:unselecting", function(e) {
-   var el = $(this);
-   var target = '#' + e.currentTarget.id;
-     var confirmation = confirm(__("Are you sure you want to remove the collaborator from receiving this reply?"));
-     if (confirmation == false) {
-       $(target).on("select2:opening", function(e) {
-         return false;
-       });
-       return false;
-     }
-
-});
-
- $('.collabSelections').select2({
-   width: '350px',
-   minimumInputLength: 3,
-   ajax: {
-     url: "ajax.php/users/local",
-     dataType: 'json',
-     data: function (params) {
-       if (!params) {
-         params.term = 'test';
-       }
-       return {
-         q: params.term,
-       };
-     },
-     processResults: function (data) {
-       return {
-         results: $.map(data, function (item) {
-           return {
-             text: item.name,
-             slug: item.slug,
-             id: item.id
-           }
-         })
-       };
-     }
-   }
- });
+  $('.collaborators.noclick').click(function() {
+    $('#show_ccs').trigger('click');
+   });
 
+  $('#collabselection').select2({
+    width: '350px',
+    allowClear: true,
+    sorter: (data) => {
+        return data.filter(function (item) {
+                return !item.selected;
+                });
+    },
+    templateResult: (e) => {
+        var $e = $(
+        '<span><i class="icon-user"></i> ' + e.text + '</span>'
+        );
+        return $e;
+    }
+   }).on("select2:unselecting", function(e) {
+        if (!confirm(__("Are you sure you want to DISABLE the collaborator?")))
+            e.preventDefault();
+   }).on("select2:selecting", function(e) {
+        if (!confirm(__("Are you sure you want to ENABLE the collaborator?")))
+             e.preventDefault();
+   }).on('change', function(e) {
+    var id = e.currentTarget.id;
+    var count = $('li.select2-selection__choice').length;
+    var total = $('#' + id +' option').length;
+    $('.' + id + '__count').html(count);
+    $('.' + id + '__total').html(total);
+    $('.' + id + '__total').parent().toggle((total));
+   }).on('select2:opening select2:closing', function(e) {
+    $(this).parent().find('.select2-search__field').prop('disabled', true);
+   });
 });
 </script>
diff --git a/scp/js/scp.js b/scp/js/scp.js
index 508123366..4f67a74d1 100644
--- a/scp/js/scp.js
+++ b/scp/js/scp.js
@@ -1031,12 +1031,11 @@ $(document).on('submit', 'form', function() {
 });
 
 //Collaborators
-$(document).on('click', 'a.collaborator, a.collaborators', function(e) {
+$(document).on('click', 'a.collaborator, a.collaborators:not(.noclick)', function(e) {
     e.preventDefault();
     var url = 'ajax.php/'+$(this).attr('href').substr(1);
     $.dialog(url, 201, function (xhr) {
        var resp = $.parseJSON(xhr.responseText);
-       $('input#t'+resp.id+'-emailcollab').show();
        $('#t'+resp.id+'-recipients').text(resp.text);
        $('.tip_box').remove();
     }, {
-- 
GitLab