diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index a142fc1056deab841b3cbf5f590a19295313ac80..c9c98b04001d05cf428c14c6600de8df2952ddae 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -448,5 +448,113 @@ 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');
+
+        $errors = $info = array();
+        $user = null;
+        $form = UserForm::getInstance();
+        if ($form->isValid())
+            $user = User::fromForm($form->getClean());
+
+        if ($user && ($c=$ticket->addCollaborator($user, $errors))) {
+            $info +=array('msg' => sprintf('%s added as a collaborator',
+                        $c->getName()));
+            $form = null;
+        } elseif($errors && $errors['err']) {
+            $info +=array('add_error' => $errors['err']);
+        } else {
+            $info +=array('add_error' =>'Errors occurred - try again');
+        }
+
+        return self::_collaborators($ticket, $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, null, $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');
+
+        return self::_collaborators($ticket);
+    }
+
+
+    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, null, $info);
+    }
+
+
+
+    function _collaborator($collaborator, $form=null, $errors=array()) {
+
+        ob_start();
+        include(STAFFINC_DIR . 'templates/collaborator.tmpl.php');
+        $resp = ob_get_contents();
+        ob_end_clean();
+
+        return $resp;
+    }
+
+    function _collaborators($ticket, $form=null, $info=array()) {
+
+        ob_start();
+        include(STAFFINC_DIR . 'templates/collaborators.tmpl.php');
+        $resp = ob_get_contents();
+        ob_end_clean();
+
+        return $resp;
+    }
 }
 ?>
diff --git a/include/staff/templates/collaborator.tmpl.php b/include/staff/templates/collaborator.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..307a7795c2805f1c21f4edba476e5c76898f2ed2
--- /dev/null
+++ b/include/staff/templates/collaborator.tmpl.php
@@ -0,0 +1,30 @@
+<h3>Update Collaborator: <?php echo $collaborator->getName(); ?></h3>
+<b><a class="close" href="#">&times;</a></b>
+<?php
+if($errors && $errors['err']) {
+    echo sprintf('<div><p id="msg_error">%s</p></div>', $errors['err']);
+} ?>
+<hr/>
+<div>
+<div>Please note that updates will be reflected system-wide.</div>
+<form method="post" class="collaborators" action="#collaborators/<?php echo $collaborator->getId(); ?>">
+    <table width="100%">
+    <?php
+        if(!$forms) $forms =  $collaborator->getForms();
+        foreach($forms as$form)
+            $form->render(); ?>
+    </table>
+    <hr style="margin-top:1em"/>
+    <p class="full-width">
+        <span class="buttons" style="float:left">
+            <input type="reset" value="Reset">
+            <input type="button" name="cancel" class="cancel"
+                data-href="#tickets/<?php echo $collaborator->getTicketId(); ?>/collaborators/manage" value="Cancel">
+        </span>
+        <span class="buttons" style="float:right">
+            <input type="submit" value="Update">
+        </span>
+     </p>
+</form>
+<div class="clear"></div>
+</div>
diff --git a/include/staff/templates/collaborators.tmpl.php b/include/staff/templates/collaborators.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..a3a6f99ade775b38940c3a9b8833245ec9810bdb
--- /dev/null
+++ b/include/staff/templates/collaborators.tmpl.php
@@ -0,0 +1,80 @@
+<h3>Ticket Collaborators</h3>
+<b><a class="close" href="#">&times;</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" <?php echo $form?  'style="display:none;"' : ''; ?>>
+<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="editcollaborator" 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 id="addcollaborator" href="#" >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
+}
+?>
+<div id="add_collaborator" <?php echo ($users && !$form)? 'style="display:none;"' : ''; ?>>
+<?php
+if($info && $info['add_error']) { ?>
+<p id="msg_error"><?php echo $info['add_error']; ?></p>
+<?php
+} ?>
+<div>Please complete the form below to add a new collaborator.</div>
+<form method="post" class="collaborators" action="#tickets/<?php echo $ticket->getId(); ?>/collaborators/add">
+    <table width="100%">
+    <?php
+        if(!$form) $form = UserForm::getInstance();
+        $form->render(); ?>
+    </table>
+    <hr style="margin-top:1em"/>
+    <p class="full-width">
+        <span class="buttons" style="float:left">
+            <input type="reset" value="Reset">
+            <input type="button" name="cancel" class="<?php echo !$users ?  'close': 'cancel'; ?>"  value="Cancel">
+        </span>
+        <span class="buttons" style="float:right">
+            <input type="submit" value="Add">
+        </span>
+     </p>
+</form>
+<div class="clear"></div>
+</div>
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index 77003277411ca85485fdb137e283a4b1a6a69bfd..e980c672a73bd48c59b12d37ba4bedbfff193c63 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -818,6 +818,9 @@ $tcount+= $ticket->getNumNotes();
     <?php
     } ?>
 </div>
+<div style="display:none;width:650px;" class="dialog draggable collaborators">
+    <div class="body"></div>
+</div>
 <div style="display:none;" class="dialog draggable" id="user-info">
     <div class="body"></div>
 </div>
diff --git a/scp/ajax.php b/scp/ajax.php
index 26d4e21a1cc58cd8da129dcd683cfc01916e19aa..464becac1d5d3bcd79b8bf644b3fcb9dffbf13c8 100644
--- a/scp/ajax.php
+++ b/scp/ajax.php
@@ -68,9 +68,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_post('^(?P<tid>\d+)/collaborators/add$', '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/ticket.js b/scp/js/ticket.js
index 619becddfc5f5029ebeeb3d64f2a489336432814..78f7ff75d058b4bf430fb057d548ea4240978280 100644
--- a/scp/js/ticket.js
+++ b/scp/js/ticket.js
@@ -363,6 +363,93 @@ jQuery(function($) {
         return false;
     });
 
+    //Collaborators
+    $(document).on('click', 'a#managecollaborators, a#addcollaborators, a.editcollaborator', function(e) {
+        e.preventDefault();
+        var target = $(this).attr('href').substr(1);
+        $('.dialog.collaborators .body').load('ajax.php/'+target, function () {
+            $('#overlay').show();
+            $('.dialog.collaborators').show();
+         });
+
+        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();
+     });
+
+    $(document).on('submit', 'form.collaborators', function(e) {
+        e.preventDefault();
+        var fObj = $(this);
+        $.ajax({
+                type: "POST",
+                url: 'ajax.php/'+fObj.attr('action').substr(1),
+                data: fObj.serialize(),
+                cache: false,
+                success: function(resp){
+                    $('.dialog.collaborators .body').html(resp);
+                    $('#msg_notice, #msg_error').delay(5000).fadeOut();
+                }
+            })
+            .done(function() { })
+            .fail(function() { });
+        return false;
+    });
+
     var showNonLocalImage = function(div) {
         var $div = $(div),
             $img = $div.append($('<img>')