diff --git a/include/class.thread.php b/include/class.thread.php
index 3cf9308025ffed40b4ca3744dd538c16d9e4715f..e715b6f465273901d62e2d63aeebf57651708e41 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -82,6 +82,7 @@ class Thread extends VerySimpleModel {
             'has_attachments' => SqlAggregate::COUNT('attachments', false,
                 new Q(array('attachments__inline'=>0)))
         ));
+        $base->exclude(array('flags__hasbit'=>ThreadEntry::FLAG_HIDDEN));
         if ($criteria)
             $base->filter($criteria);
         return $base;
@@ -398,7 +399,8 @@ class ThreadEntry extends VerySimpleModel {
     static $meta = array(
         'table' => THREAD_ENTRY_TABLE,
         'pk' => array('id'),
-        'select_related' => array('staff', 'user'),
+        'select_related' => array('staff', 'user', 'email_info'),
+        'ordering' => array('created'),
         'joins' => array(
             'thread' => array(
                 'constraint' => array('thread_id' => 'Thread.id'),
@@ -430,6 +432,8 @@ class ThreadEntry extends VerySimpleModel {
     );
 
     const FLAG_ORIGINAL_MESSAGE         = 0x0001;
+    const FLAG_EDITED                   = 0x0002;
+    const FLAG_HIDDEN                   = 0x0004;
 
     var $_headers;
     var $_thread;
@@ -1734,7 +1738,7 @@ abstract class ThreadEntryAction {
     static $id;                 // Unique identifier used for plumbing
     static $icon = 'cog';
 
-    var $thread;
+    var $entry;
 
     function getName() {
         $class = get_class($this);
@@ -1751,13 +1755,13 @@ abstract class ThreadEntryAction {
     }
 
     function __construct(ThreadEntry $thread) {
-        $this->thread = $thread;
+        $this->entry = $thread;
     }
 
     abstract function trigger();
 
     function getTicket() {
-        return $this->thread->getTicket();
+        return $this->entry->getObject();
     }
 
     function isEnabled() {
@@ -1791,8 +1795,8 @@ abstract class ThreadEntryAction {
     function getAjaxUrl($dialog=false) {
         return sprintf('%stickets/%d/thread/%d/%s',
             $dialog ? '#' : 'ajax.php/',
-            $this->thread->getThread()->getObjectId(),
-            $this->thread->getId(),
+            $this->entry->getThread()->getObjectId(),
+            $this->entry->getId(),
             static::getId()
         );
     }
diff --git a/include/class.thread_actions.php b/include/class.thread_actions.php
index ce6ac82469df253fe5aecb942759de7f4a814252..503578ec46ba4f4e9cceced3e9e52583f1047ff6 100644
--- a/include/class.thread_actions.php
+++ b/include/class.thread_actions.php
@@ -23,14 +23,13 @@ class TEA_ShowEmailHeaders extends ThreadEntryAction {
     static $name = /* trans */ 'View Email Headers';
     static $icon = 'envelope';
 
-    function isEnabled() {
+    function isVisible() {
         global $thisstaff;
 
-        return $thisstaff && $thisstaff->isAdmin();
-    }
+        if (!$this->entry->getEmailHeader())
+            return false;
 
-    function isVisible() {
-        return (bool) $this->thread->getEmailHeader();
+        return $thisstaff && $thisstaff->isAdmin();
     }
 
     function getJsStub() {
@@ -47,9 +46,159 @@ class TEA_ShowEmailHeaders extends ThreadEntryAction {
     }
 
     private function trigger__get() {
-        $headers = $this->thread->getEmailHeader();
+        $headers = $this->entry->getEmailHeader();
 
         include STAFFINC_DIR . 'templates/thread-email-headers.tmpl.php';
     }
 }
 ThreadEntry::registerAction(/* trans */ 'E-Mail', 'TEA_ShowEmailHeaders');
+
+class TEA_EditThreadEntry extends ThreadEntryAction {
+    static $id = 'edit';
+    static $name = /* trans */ 'Edit';
+    static $icon = 'pencil';
+
+    function isVisible() {
+        // Can't edit system posts
+        return $this->entry->staff_id || $this->entry->user_id;
+    }
+
+    function isEnabled() {
+        global $thisstaff;
+
+        // You can edit your own posts or posts by your department members
+        // if your a manager, or everyone's if your an admin
+        return $thisstaff && (
+            $thisstaff->isAdmin()
+            || (($T = $this->entry->getThread()->getObject())
+                && $T instanceof Ticket
+                && $T->getDept()->getManagerId() == $thisstaff->getId()
+            )
+            || ($this->entry->getStaffId() == $thisstaff->getId())
+        );
+    }
+
+    function getJsStub() {
+        return sprintf(<<<JS
+var url = '%s';
+$.dialog(url, [201], function(xhr, resp) {
+  var json = JSON.parse(resp);
+  if (!json || !json.thread_id)
+    return;
+  $('#thread-id-'+json.thread_id)
+    .attr('id', 'thread-id-' + json.new_id)
+    .find('div')
+    .html(json.body)
+    .closest('td')
+    .effect('highlight')
+}, {size:'large'});
+JS
+        , $this->getAjaxUrl());
+    }
+
+
+    function trigger() {
+        switch ($_SERVER['REQUEST_METHOD']) {
+        case 'GET':
+            return $this->trigger__get();
+        case 'POST':
+            return $this->trigger__post();
+        }
+    }
+
+    private function trigger__get() {
+        global $cfg;
+
+        include STAFFINC_DIR . 'templates/thread-entry-edit.tmpl.php';
+    }
+
+    private function trigger__post() {
+        global $thisstaff;
+
+        $old = $this->entry;
+        $type = ($old->format == 'html')
+            ? 'HtmlThreadEntryBody' : 'TextThreadEntryBody';
+        $new = new $type($_POST['body']);
+
+        if ($new->getClean() == $old->body)
+            // No update was performed
+            Http::response(201);
+
+        $entry = ThreadEntry::create(array(
+            // Copy most information from the old entry
+            'poster' => $old->poster,
+            'userId' => $old->user_id,
+            'staffId' => $old->staff_id,
+            'type' => $old->type,
+            'threadId' => $old->thread_id,
+
+            // Add in new stuff
+            'title' => $_POST['title'],
+            'body' => $new,
+            'ip_address' => $_SERVER['REMOTE_ADDR'],
+        ));
+
+        if (!$entry)
+            return $this->trigger__get();
+
+        // 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));
+            // Drop the previous edit, and base this edit off the original
+            $old->delete();
+            $old = $original;
+        }
+
+        // Mark the new entry as editited (but not hidden)
+        $entry->flags = ($old->flags & ~ThreadEntry::FLAG_HIDDEN)
+            | ThreadEntry::FLAG_EDITED;
+        $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();
+
+        Http::response('201', JsonDataEncoder::encode(array(
+            'thread_id' => $this->entry->id,
+            'new_id' => $entry->id,
+            'body' => $entry->getBody()->toHtml(),
+        )));
+    }
+}
+ThreadEntry::registerAction(/* trans */ 'Manage', 'TEA_EditThreadEntry');
+
+class TEA_OrigThreadEntry extends ThreadEntryAction {
+    static $id = 'previous';
+    static $name = /* trans */ 'View Original';
+    static $icon = 'undo';
+
+    function isVisible() {
+        // Can't edit system posts
+        return $this->entry->flags & ThreadEntry::FLAG_EDITED;
+    }
+
+    function getJsStub() {
+        return sprintf("$.dialog('%s');",
+            $this->getAjaxUrl()
+        );
+    }
+
+    function trigger() {
+        switch ($_SERVER['REQUEST_METHOD']) {
+        case 'GET':
+            return $this->trigger__get();
+        }
+    }
+
+    private function trigger__get() {
+        $entry = ThreadEntry::lookup(array('pid'=>$this->entry->getId()));
+        include STAFFINC_DIR . 'templates/thread-entry-view.tmpl.php';
+    }
+}
+ThreadEntry::registerAction(/* trans */ 'Manage', 'TEA_OrigThreadEntry');
diff --git a/include/staff/templates/thread-entry-edit.tmpl.php b/include/staff/templates/thread-entry-edit.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..8ebeca0ac9bc24e2789333c23efccbaa5ae189e9
--- /dev/null
+++ b/include/staff/templates/thread-entry-edit.tmpl.php
@@ -0,0 +1,32 @@
+<h3><?php echo __('Edit Thread Entry'); ?></h3>
+<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()); ?>">
+
+<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;"
+    name="body"
+    class="large <?php
+        if ($cfg->isHtmlThreadEnabled() && $this->entry->format == 'html')
+            echo 'richtext';
+    ?>"><?php echo Format::viewableImages($this->entry->body);
+?></textarea>
+
+<hr>
+<p 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'); ?>">
+    </span>
+</p>
+
+</form>
diff --git a/include/staff/templates/thread-entry-view.tmpl.php b/include/staff/templates/thread-entry-view.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..500aefed15df408eb8762d93baf9c1393b445078
--- /dev/null
+++ b/include/staff/templates/thread-entry-view.tmpl.php
@@ -0,0 +1,18 @@
+<h3><?php echo __('Original Thread Entry'); ?></h3>
+<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>
+
+<hr>
+<p class="full-width">
+    <span class="buttons pull-right">
+        <input type="button" name="cancel" class="close"
+            value="<?php echo __('Close'); ?>">
+    </span>
+</p>
+
+</form>
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index e2cc27c5f3441b18d42b9c6b227b6ae0583158dc..8823977dae020f16d792e772cedb25947c2676d7 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -434,7 +434,13 @@ $tcount = $ticket->getThreadEntries($types)->count();
                     </div>
 <?php           } ?>
                     <span style="vertical-align:middle">
-                        <span style="vertical-align:middle;" class="textra"></span>
+                        <span style="vertical-align:middle;" class="textra">
+        <?php if ($entry->flags & ThreadEntry::FLAG_EDITED) { ?>
+                <span class="label label-bare" title="<?php
+        echo sprintf(__('Edited on %s by %s'), Format::datetime($entry->updated), 'You');
+                ?>"><?php echo __('Edited'); ?></span>
+        <?php } ?>
+                        </span>
                         <span style="vertical-align:middle;"
                             class="tmeta faded title"><?php
                             echo Format::htmlchars($entry->getName()); ?></span>
diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js
index bd32edda61afbc875b2aa08d3fef296f6912db39..69dab8e76f7b1022317710e96efe13d581e4d493 100644
--- a/js/redactor-osticket.js
+++ b/js/redactor-osticket.js
@@ -251,7 +251,8 @@ $(function() {
                     'file', 'table', 'link', '|', 'alignment', '|',
                     'horizontalrule'],
                 'buttonSource': !el.hasClass('no-bar'),
-                'autoresize': !el.hasClass('no-bar'),
+                'autoresize': !el.hasClass('no-bar') && !el.closest('.dialog').length,
+                'maxHeight': el.closest('.dialog').length ? selectedSize : false,
                 'minHeight': selectedSize,
                 'focus': false,
                 'plugins': el.hasClass('no-bar')
diff --git a/scp/css/scp.css b/scp/css/scp.css
index 64735d3de27b7959e24fe40d1179aa2aff0764e1..7817e76560c9610788831f445bd6caa6f4c743e5 100644
--- a/scp/css/scp.css
+++ b/scp/css/scp.css
@@ -1947,7 +1947,7 @@ tr.disabled th {
 
 .label {
   font-size: 11px;
-  padding: 1px 4px 2px;
+  padding: 1px 4px;
   -webkit-border-radius: 3px;
   -moz-border-radius: 3px;
   border-radius: 3px;
@@ -1959,6 +1959,13 @@ tr.disabled th {
   text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
   background-color: #999999;
 }
+.label-bare {
+  background-color: transparent;
+  background-color: rgba(0,0,0,0);
+  border: 1px solid #999999;
+  color: #999999;
+  text-shadow: none;
+}
 .label-info {
   background-color: #3a87ad;
 }