diff --git a/include/class.mailer.php b/include/class.mailer.php
index c398df9f5ccf26f8bca4a8d16a21009fbc52cdf6..0abb652469fa8d6491dcd5efa1f37ac5bc139664 100644
--- a/include/class.mailer.php
+++ b/include/class.mailer.php
@@ -361,12 +361,12 @@ class Mailer {
                     'References' => $options['thread']->getEmailReferences()
                 );
             }
-            elseif ($parent = $options['thread']->getParent()) {
+            elseif ($original = $options['thread']->findOriginalEmailMessage()) {
                 // Use the parent item as the email information source. This
                 // will apply for staff replies
                 $headers += array(
-                    'In-Reply-To' => $parent->getEmailMessageId(),
-                    'References' => $parent->getEmailReferences(),
+                    'In-Reply-To' => $original->getEmailMessageId(),
+                    'References' => $original->getEmailReferences(),
                 );
             }
 
diff --git a/include/class.thread.php b/include/class.thread.php
index 759a03b0c5ccc4948ee920b53e34a3c981d306a6..2b1d8bec7963775e21de4a6e4a67c2c0ede44286 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -435,6 +435,7 @@ class ThreadEntry extends VerySimpleModel {
     const FLAG_ORIGINAL_MESSAGE         = 0x0001;
     const FLAG_EDITED                   = 0x0002;
     const FLAG_HIDDEN                   = 0x0004;
+    const FLAG_GUARDED                  = 0x0008;   // No replace on edit
 
     const PERM_EDIT     = 'thread.edit';
 
@@ -471,8 +472,7 @@ class ThreadEntry extends VerySimpleModel {
     }
 
     function getParent() {
-        if ($this->getPid())
-            return ThreadEntry::lookup($this->getPid());
+        return $this->parent;
     }
 
     function getType() {
@@ -579,6 +579,21 @@ class ThreadEntry extends VerySimpleModel {
         return $recipients;
     }
 
+    /**
+     * Recurse through the ancestry of this thread entry to find the first
+     * thread entry which cites a email Message-ID field.
+     *
+     * Returns:
+     * <ThreadEntry> or null if neither this thread entry nor any of its
+     * ancestry contains an email header with an email Message-ID header.
+     */
+    function findOriginalEmailMessage() {
+        $P = $this;
+        while (!$P->getEmailMessageId()
+            && ($P = $P->getParent()));
+        return $P;
+    }
+
     function getUIDFromEmailReference($ref) {
 
         $info = unpack('Vtid/Vuid',
diff --git a/include/class.thread_actions.php b/include/class.thread_actions.php
index 121ecb8b98267fb2ad060bdb0f9c064a12167c9f..a228139fba0d6e9335146788c9bfce896c6abc46 100644
--- a/include/class.thread_actions.php
+++ b/include/class.thread_actions.php
@@ -60,7 +60,8 @@ class TEA_EditThreadEntry extends ThreadEntryAction {
 
     function isVisible() {
         // Can't edit system posts
-        return $this->entry->staff_id || $this->entry->user_id;
+        return ($this->entry->staff_id || $this->entry->user_id)
+            && $this->entry->type != 'R';
     }
 
     function isEnabled() {
@@ -108,15 +109,13 @@ JS
         }
     }
 
-    private function trigger__get() {
-        global $cfg;
+    protected function trigger__get() {
+        global $cfg, $thisstaff;
 
         include STAFFINC_DIR . 'templates/thread-entry-edit.tmpl.php';
     }
 
-    private function trigger__post() {
-        global $thisstaff;
-
+    function updateEntry($guard=false) {
         $old = $this->entry;
         $type = ($old->format == 'html')
             ? 'HtmlThreadEntryBody' : 'TextThreadEntryBody';
@@ -124,7 +123,7 @@ JS
 
         if ($new->getClean() == $old->body)
             // No update was performed
-            Http::response(201);
+            return $old;
 
         $entry = ThreadEntry::create(array(
             // Copy most information from the old entry
@@ -134,6 +133,9 @@ JS
             'type' => $old->type,
             'threadId' => $old->thread_id,
 
+            // Connect the new entry to be a child of the previous
+            'pid' => $old->id,
+
             // Add in new stuff
             'title' => $_POST['title'],
             'body' => $new,
@@ -141,31 +143,49 @@ JS
         ));
 
         if (!$entry)
-            return $this->trigger__get();
+            return false;
 
         // Note, anything that points to the $old entry as PID should remain
         // that way for email header lookups and such to remain consistent
 
-        if ($old->flags & ThreadEntry::FLAG_EDITED) {
-            // Second and further edit ---------------
-            $original = ThreadEntry::lookup(array('pid'=>$old->id));
+        if ($old->flags & ThreadEntry::FLAG_EDITED
+            and !($old->flags & ThreadEntry::FLAG_GUARDED)
+        ) {
+            // Replace previous edit --------------------------
+            $original = $old->getParent();
             // Drop the previous edit, and base this edit off the original
             $old->delete();
             $old = $original;
         }
 
-        // Mark the new entry as editited (but not hidden)
+        // Mark the new entry as edited (but not hidden)
         $entry->flags = ($old->flags & ~ThreadEntry::FLAG_HIDDEN)
             | ThreadEntry::FLAG_EDITED;
+
+        // Guard against deletes on future edit if requested. This is done
+        // if an email was triggered by the last edit. In such a case, it
+        // should not be replace by a subsequent edit.
+        if ($guard)
+            $entry->flags |= ThreadEntry::FLAG_GUARDED;
+
+        // Sort in the same place in the thread — XXX: Add a `sequence` id
         $entry->created = $old->created;
         $entry->updated = SqlFunction::NOW();
         $entry->save();
 
         // Hide the old entry from the object thread
-        $old->pid = $entry->id;
         $old->flags |= ThreadEntry::FLAG_HIDDEN;
         $old->save();
 
+        return $entry;
+    }
+
+    protected function trigger__post() {
+        global $thisstaff;
+
+        if (!($entry = $this->updateEntry()))
+            return $this->trigger__get();
+
         Http::response('201', JsonDataEncoder::encode(array(
             'thread_id' => $this->entry->id,
             'new_id' => $entry->id,
@@ -177,8 +197,8 @@ ThreadEntry::registerAction(/* trans */ 'Manage', 'TEA_EditThreadEntry');
 
 class TEA_OrigThreadEntry extends ThreadEntryAction {
     static $id = 'previous';
-    static $name = /* trans */ 'View Original';
-    static $icon = 'undo';
+    static $name = /* trans */ 'View History';
+    static $icon = 'copy';
 
     function isVisible() {
         // Can't edit system posts
@@ -199,8 +219,76 @@ class TEA_OrigThreadEntry extends ThreadEntryAction {
     }
 
     private function trigger__get() {
-        $entry = ThreadEntry::lookup(array('pid'=>$this->entry->getId()));
+        $entry = $this->entry->getParent();
+        if (!$entry)
+            Http::response(404, 'No history for this entry');
         include STAFFINC_DIR . 'templates/thread-entry-view.tmpl.php';
     }
 }
 ThreadEntry::registerAction(/* trans */ 'Manage', 'TEA_OrigThreadEntry');
+
+class TEA_ResendThreadEntry extends TEA_EditThreadEntry {
+    static $id = 'resend';
+    static $name = /* trans */ 'Edit and Resend';
+    static $icon = 'reply-all';
+
+    function isVisible() {
+        // Can only resend replies
+        return $this->entry->staff_id && $this->entry->type == 'R';
+    }
+
+    protected function trigger__post() {
+        $resend = @$_POST['commit'] == 'resend';
+
+        if (!($entry = $this->updateEntry($resend)))
+            return $this->trigger__get();
+
+        if (@$_POST['commit'] == 'resend')
+            $this->resend($entry);
+
+        Http::response('201', JsonDataEncoder::encode(array(
+            'thread_id' => $this->entry->id,
+            'new_id' => $entry->id,
+            'body' => $entry->getBody()->toHtml(),
+        )));
+    }
+
+    function resend($response) {
+        global $cfg, $thisstaff;
+
+        $vars = $_POST;
+        $ticket = $response->getThread()->getObject();
+
+        $dept = $ticket->getDept();
+
+        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' => $response->getStaff(),
+            'poster' => $response->getStaff());
+        $options = array('thread' => $response);
+
+        if (($email=$dept->getEmail())
+            && ($tpl = $dept->getTemplate())
+            && ($msg=$tpl->getReplyMsgTemplate())
+        ) {
+            $msg = $ticket->replaceVars($msg->asArray(),
+                $variables + array('recipient' => $ticket->getOwner()));
+
+            $attachments = $cfg->emailAttachments()
+                ? $response->getAttachments() : array();
+            $email->send($ticket->getOwner(), $msg['subj'], $msg['body'],
+                $attachments, $options);
+        }
+        // TODO: Add an option to the dialog
+        $ticket->notifyCollaborators($response, array('signature' => $signature));
+    }
+}
+ThreadEntry::registerAction(/* trans */ 'Manage', 'TEA_ResendThreadEntry');
diff --git a/include/staff/templates/thread-entry-edit.tmpl.php b/include/staff/templates/thread-entry-edit.tmpl.php
index 8ebeca0ac9bc24e2789333c23efccbaa5ae189e9..0f1533d70b59f1bd6aa37edebb89ec861b167fba 100644
--- a/include/staff/templates/thread-entry-edit.tmpl.php
+++ b/include/staff/templates/thread-entry-edit.tmpl.php
@@ -2,14 +2,30 @@
 <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
 <hr/>
 
-<form method="post" action="<?php
-    echo str_replace('ajax.php/','#',$this->getAjaxUrl()); ?>">
+<form method="post" action="<?php echo $this->getAjaxUrl(true); ?>">
 
 <input type="text" style="width:100%;font-size:14px" placeholder="<?php
     echo __('Title'); ?>" name="title" value="<?php
     echo Format::htmlchars($this->entry->title); ?>"/>
 <hr style="height:0"/>
 <textarea style="display: block; width: 100%; height: auto; min-height: 150px;"
+<?php if ($this->entry->type == 'R') {
+    $signature = '';
+    if (($T = $this->entry->getThread()->getObject()) instanceof Ticket)
+        $dept = $T->getDept();
+    switch ($thisstaff->getDefaultSignatureType()) {
+    case 'dept':
+        if ($dept && $dept->canAppendSignature())
+           $signature = $dept->getSignature();
+       break;
+    case 'mine':
+        $signature = $thisstaff->getSignature();
+        break;
+    } ?>
+    data-dept-id="<?php echo $dept->getId(); ?>"
+    data-signature-field="signature"
+    data-signature="<?php echo Format::viewableImages($signature); ?>"
+<?php } ?>
     name="body"
     class="large <?php
         if ($cfg->isHtmlThreadEnabled() && $this->entry->format == 'html')
@@ -17,16 +33,39 @@
     ?>"><?php echo Format::viewableImages($this->entry->body);
 ?></textarea>
 
+<?php if ($this->entry->type == 'R') { ?>
+<div style="margin:10px 0;"><?php echo __('Signature'); ?>:
+    <label><input type="radio" name="signature" value="none" checked="checked"> <?php echo __('None');?></label>
+    <?php
+    if ($thisstaff->getSignature()) {?>
+    <label><input type="radio" name="signature" value="mine"
+        <?php echo ($info['signature']=='mine')?'checked="checked"':''; ?>> <?php echo __('My Signature');?></label>
+    <?php
+    } ?>
+    <?php
+    if ($dept && $dept->canAppendSignature()) { ?>
+    <label><input type="radio" name="signature" value="dept"
+        <?php echo ($info['signature']=='dept')?'checked="checked"':''; ?>>
+        <?php echo sprintf(__('Department Signature (%s)'), Format::htmlchars($dept->getName())); ?></label>
+    <?php
+    } ?>
+</div>
+<?php } # end of type == 'R' ?>
+
 <hr>
-<p class="full-width">
+<div class="full-width">
     <span class="buttons pull-left">
         <input type="button" name="cancel" class="close"
             value="<?php echo __('Cancel'); ?>">
     </span>
     <span class="buttons pull-right">
-        <input type="submit" name="save"
-            value="<?php echo __('Save Changes'); ?>">
+        <button type="submit" name="commit" value="save" class="button"
+            ><?php echo __('Save'); ?></button>
+<?php if ($this->entry->type == 'R') { ?>
+        <button type="submit" name="commit" value="resend" class="button"
+            ><?php echo __('Save and Resend'); ?></button>
+<?php } ?>
     </span>
-</p>
+</div>
 
 </form>
diff --git a/include/staff/templates/thread-entry-view.tmpl.php b/include/staff/templates/thread-entry-view.tmpl.php
index 500aefed15df408eb8762d93baf9c1393b445078..35c3489c4a871746f077940ebf92099d562ce645 100644
--- a/include/staff/templates/thread-entry-view.tmpl.php
+++ b/include/staff/templates/thread-entry-view.tmpl.php
@@ -2,9 +2,30 @@
 <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
 <hr/>
 
-<div><strong><?php echo Format::htmlchars($entry->title); ?></strong></div>
-<div class="thread-body" style="background-color:transparent">
-    <?php echo $entry->getBody()->toHtml(); ?>
+<div id="history" class="accordian">
+
+<?php
+$E = $entry;
+do { ?>
+<dt>
+    <a href="#"><i class="icon-copy"></i>
+    <strong><?php echo Format::htmlchars($E->title); ?></strong>
+    <em><?php if (strpos($E->updated, '0000-') === false)
+        echo sprintf(__('Edited on %s'), Format::datetime($E->updated));
+    else
+        echo __('Original'); ?></em>
+    </a>
+</dt>
+<dd class="hidden">
+    <div class="thread-body" style="background-color:transparent">
+        <?php echo $E->getBody()->toHtml(); ?>
+    </div>
+</dd>
+<?php
+}
+while (($E = $E->getParent()) && $E->type == $entry->type);
+?>
+
 </div>
 
 <hr>
@@ -16,3 +37,21 @@
 </p>
 
 </form>
+
+<script type="text/javascript">
+$(function() {
+  var I = setInterval(function() {
+    var A = $('#history.accordian');
+    if (!A.length) return;
+    clearInterval(I);
+
+    var allPanels = $('dd', A).hide().removeClass('hidden');
+    $('dt > a', A).click(function() {
+      $('dt', A).removeClass('active');
+      allPanels.slideUp();
+      $(this).parent().addClass('active').next().slideDown();
+      return false;
+    });
+    allPanels.last().show();
+  }, 100);
+});
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index 8e3c73d86fa69f200c2a293f74b9139942dd7d9b..bdc09745da094d2daef7cb944830c7a4a267e9e7 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -429,7 +429,7 @@ $tcount = $ticket->getThreadEntries($types)->count();
                     ?>" href="#" onclick="javascript:
                         if ($(this).hasClass('disabled')) return false;
                         <?php echo str_replace('"', '\\"', $action->getJsStub()); ?>; return false;">
-                        <i class="<?php echo $action->getIcon(); ?>"></i> <?php
+                        <i class="icon-fixed-width <?php echo $action->getIcon(); ?>"></i> <?php
                             echo $action->getName();
                 ?></a></li>
 <?php                   }
diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js
index 69dab8e76f7b1022317710e96efe13d581e4d493..5184801e648742e6da1d38b225eed0df354c8f3d 100644
--- a/js/redactor-osticket.js
+++ b/js/redactor-osticket.js
@@ -156,10 +156,10 @@ RedactorPlugins.signature = function() {
             else
                 this.$signatureBox.hide();
             $('input[name='+$el.data('signatureField')+']', $el.closest('form'))
-                .on('change', false, false, $.proxy(this.updateSignature, this));
+                .on('change', false, false, $.proxy(this.signature.updateSignature, this));
             if ($el.data('deptField'))
                 $(':input[name='+$el.data('deptField')+']', $el.closest('form'))
-                    .on('change', false, false, $.proxy(this.updateSignature, this));
+                    .on('change', false, false, $.proxy(this.signature.updateSignature, this));
             // Expand on hover
             var outer = this.$signatureBox,
                 inner = $('.inner', this.$signatureBox).get(0),
diff --git a/scp/js/scp.js b/scp/js/scp.js
index 8f99d9545c636d7b4e802521632f33c162ca1b19..aed3618a2be9c6bb4a99301f5e38303cafca4821 100644
--- a/scp/js/scp.js
+++ b/scp/js/scp.js
@@ -562,16 +562,25 @@ $.dialog = function (url, codes, cb, options) {
             queue: false,
             complete: function() { if (options.onshow) options.onshow(); }
         });
+        var submit_button = null;
         $(document).off('.dialog');
+        $(document).on('click.dialog',
+            '#popup input[type=submit], #popup button[type=submit]',
+            function(e) { submit_button = $(this); });
         $(document).on('submit.dialog', '.dialog#popup form', function(e) {
             e.preventDefault();
-            var $form = $(this);
+            var $form = $(this),
+                data = $form.serialize();
+            if (submit_button) {
+                data += '&' + escape(submit_button.attr('name')) + '='
+                    + escape(submit_button.attr('value'));
+            }
             $('div#popup-loading', $popup).show()
                 .find('h1').css({'margin-top':function() { return $popup.height()/3-$(this).height()/3}});
             $.ajax({
                 type:  $form.attr('method'),
                 url: 'ajax.php/'+$form.attr('action').substr(1),
-                data: $form.serialize(),
+                data: data,
                 cache: false,
                 success: function(resp, status, xhr) {
                     if (xhr && xhr.status && codes