diff --git a/app/assets/javascripts/discourse/components/utilities.coffee b/app/assets/javascripts/discourse/components/utilities.coffee
index 69c445f99..82f4c2304 100644
--- a/app/assets/javascripts/discourse/components/utilities.coffee
+++ b/app/assets/javascripts/discourse/components/utilities.coffee
@@ -155,7 +155,9 @@ Discourse.Utilities =
 
 
   # Takes raw input and cooks it to display nicely (mostly markdown)
-  cook: (raw, opts) ->
+  cook: (raw, opts=null) ->
+
+    opts ||= {}
 
     # Make sure we've got a string
     return "" unless raw
diff --git a/app/assets/javascripts/discourse/controllers/topic_controller.js.coffee b/app/assets/javascripts/discourse/controllers/topic_controller.js.coffee
index 6449ba373..cd8101f27 100644
--- a/app/assets/javascripts/discourse/controllers/topic_controller.js.coffee
+++ b/app/assets/javascripts/discourse/controllers/topic_controller.js.coffee
@@ -299,14 +299,15 @@ Discourse.TopicController = Ember.ObjectController.extend Discourse.Presence,
     @get('controllers.modal')?.show(view)
     false
 
+  recoverPost: (post) ->
+    post.set('deleted_at', null)
+    post.recover()
+
   deletePost: (post) ->
-
-    deleted = !!post.get('deleted_at')
-
-    if deleted
-      post.set('deleted_at', null)
+    if post.get('user_id') is Discourse.get('currentUser.id')
+      post.set('cooked', Discourse.Utilities.cook(Em.String.i18n("post.deleted_by_author")))
+      post.set('can_delete', false)
     else
       post.set('deleted_at', new Date())
 
-    post.delete =>
-      # nada
+    post.delete()
diff --git a/app/assets/javascripts/discourse/models/post.js.coffee.erb b/app/assets/javascripts/discourse/models/post.js.coffee.erb
index 64ab9743d..79177716b 100644
--- a/app/assets/javascripts/discourse/models/post.js.coffee.erb
+++ b/app/assets/javascripts/discourse/models/post.js.coffee.erb
@@ -153,8 +153,11 @@ window.Discourse.Post = Ember.Object.extend Discourse.Presence,
         error: (result) -> error?(result)
 
 
+  recover: ->
+    $.ajax "/posts/#{@get('id')}/recover", type: 'PUT', cache: false
+
   delete: (complete) ->
-    $.ajax "/posts/#{@get('id')}", type: 'DELETE', success: (result) -> complete()
+    $.ajax "/posts/#{@get('id')}", type: 'DELETE', success: (result) -> complete?()
 
   # Update the properties of this post from an obj, ignoring cooked as we should already
   # have that rendered.
diff --git a/app/assets/javascripts/discourse/views/post_menu_view.js.coffee b/app/assets/javascripts/discourse/views/post_menu_view.js.coffee
index 65297ee70..9d67b30fb 100644
--- a/app/assets/javascripts/discourse/views/post_menu_view.js.coffee
+++ b/app/assets/javascripts/discourse/views/post_menu_view.js.coffee
@@ -26,7 +26,7 @@ window.Discourse.PostMenuView = Ember.View.extend Discourse.Presence,
   # Trigger re rendering
   needsToRender: (->
     @rerender()
-  ).observes('post.deleted_at', 'post.flagsAvailable.@each', 'post.url', 'post.bookmarked', 'post.reply_count', 'post.replyBelowUrl')
+  ).observes('post.deleted_at', 'post.flagsAvailable.@each', 'post.url', 'post.bookmarked', 'post.reply_count', 'post.replyBelowUrl', 'post.can_delete')
 
   # Replies Button
   renderReplies: (post, buffer) ->
@@ -49,11 +49,14 @@ window.Discourse.PostMenuView = Ember.View.extend Discourse.Presence,
 
   # Delete button
   renderDelete: (post, buffer) ->
-    return unless post.get('can_delete')
 
-    title = if post.get('deleted_at') then Em.String.i18n("post.controls.undelete") else Em.String.i18n("post.controls.delete")
-    buffer.push("<button title=\"#{title}\" data-action=\"delete\"><i class=\"icon-trash\"></i></button>")
+    if post.get('deleted_at')
+      if post.get('can_recover')
+        buffer.push("<button title=\"#{Em.String.i18n("post.controls.undelete")}\" data-action=\"recover\"><i class=\"icon-undo\"></i></button>")
+    else if post.get('can_delete')
+      buffer.push("<button title=\"#{Em.String.i18n("post.controls.delete")}\" data-action=\"delete\"><i class=\"icon-trash\"></i></button>")
 
+  clickRecover: -> @get('controller').recoverPost(@get('post'))        
   clickDelete: -> @get('controller').deletePost(@get('post'))
 
   # Like button
diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb
index d503b66b0..ce4d2e9ba 100644
--- a/app/controllers/posts_controller.rb
+++ b/app/controllers/posts_controller.rb
@@ -74,16 +74,16 @@ class PostsController < ApplicationController
   end
 
   def destroy
-    Post.transaction do
-      post = Post.with_deleted.where(id: params[:id]).first
-      guardian.ensure_can_delete!(post)
-      if post.deleted_at.nil?
-        post.destroy
-      else
-        post.recover
-      end
-      Topic.reset_highest(post.topic_id)
-    end
+    post = Post.where(id: params[:id]).first
+    guardian.ensure_can_delete!(post)
+    post.delete_by(current_user)
+    render nothing: true
+  end
+
+  def recover
+    post = Post.with_deleted.where(id: params[:post_id]).first
+    guardian.ensure_can_recover_post!(post)
+    post.recover
     render nothing: true
   end
 
diff --git a/app/models/post.rb b/app/models/post.rb
index 6617c57a7..9482b9321 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -159,7 +159,23 @@ class Post < ActiveRecord::Base
     else
       @raw_mentions = []
     end
+  end
 
+  # The rules for deletion change depending on who is doing it.
+  def delete_by(deleted_by)
+    if deleted_by.has_trust_level?(:moderator)
+      # As a moderator, delete the post.
+      Post.transaction do
+        self.destroy
+        Topic.reset_highest(self.topic_id)    
+      end
+    elsif deleted_by.id == self.user_id
+      # As the poster, make a revision that says deleted.
+      Post.transaction do
+        revise(deleted_by, I18n.t('js.post.deleted_by_author'), force_new_version: true)
+        update_column(:user_deleted, true)
+      end
+    end
   end
 
   def archetype
@@ -311,6 +327,8 @@ class Post < ActiveRecord::Base
     # We always create a new version if it's been greater than the ninja edit window
     new_version = true if (revised_at - last_version_at) > SiteSetting.ninja_edit_window.to_i
 
+    new_version = true if opts[:force_new_version]
+
     # Create the new version (or don't)
     if new_version
 
diff --git a/app/serializers/post_serializer.rb b/app/serializers/post_serializer.rb
index f51913206..8e5f74379 100644
--- a/app/serializers/post_serializer.rb
+++ b/app/serializers/post_serializer.rb
@@ -28,6 +28,7 @@ class PostSerializer < ApplicationSerializer
              :version,
              :can_edit,
              :can_delete,
+             :can_recover,
              :link_counts,
              :cooked,
              :read,
@@ -66,6 +67,10 @@ class PostSerializer < ApplicationSerializer
     scope.can_delete?(object)
   end
 
+  def can_recover
+    scope.can_recover_post?(object)
+  end
+
   def link_counts
 
     return @single_post_link_counts if @single_post_link_counts.present?
diff --git a/config/locales/en.yml b/config/locales/en.yml
index db014fc30..88b9d9626 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -831,6 +831,7 @@ en:
       reply_as_new_topic: "Reply as new Topic"
       continue_discussion: "Continuing the discussion from {{postLink}}:"
       follow_quote: "go to the quoted post"
+      deleted_by_author: "Post deleted by author."
 
       has_replies_below:
         one: "Reply Below"
diff --git a/config/routes.rb b/config/routes.rb
index 9bcd44afd..c81a86d17 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -103,6 +103,7 @@ Discourse::Application.routes.draw do
     get 'versions'
     put 'bookmark'
     get 'replies'
+    put 'recover'
     collection do
       delete 'destroy_many'
     end
diff --git a/db/migrate/20130207200019_add_user_deleted_to_posts.rb b/db/migrate/20130207200019_add_user_deleted_to_posts.rb
new file mode 100644
index 000000000..04e45a1a0
--- /dev/null
+++ b/db/migrate/20130207200019_add_user_deleted_to_posts.rb
@@ -0,0 +1,5 @@
+class AddUserDeletedToPosts < ActiveRecord::Migration
+  def change
+    add_column :posts, :user_deleted, :boolean, null: false, default: false
+  end
+end
diff --git a/db/structure.sql b/db/structure.sql
index dc1b58777..b7456f03e 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -1798,7 +1798,8 @@ CREATE TABLE posts (
     spam_count integer DEFAULT 0 NOT NULL,
     illegal_count integer DEFAULT 0 NOT NULL,
     inappropriate_count integer DEFAULT 0 NOT NULL,
-    last_version_at timestamp without time zone NOT NULL
+    last_version_at timestamp without time zone NOT NULL,
+    user_deleted boolean DEFAULT false NOT NULL
 );
 
 
@@ -4569,4 +4570,6 @@ INSERT INTO schema_migrations (version) VALUES ('20130203204338');
 
 INSERT INTO schema_migrations (version) VALUES ('20130204000159');
 
-INSERT INTO schema_migrations (version) VALUES ('20130205021905');
\ No newline at end of file
+INSERT INTO schema_migrations (version) VALUES ('20130205021905');
+
+INSERT INTO schema_migrations (version) VALUES ('20130207200019');
\ No newline at end of file
diff --git a/lib/guardian.rb b/lib/guardian.rb
index 48b4d4128..7654a62fa 100644
--- a/lib/guardian.rb
+++ b/lib/guardian.rb
@@ -232,6 +232,15 @@ class Guardian
     # Can't delete the first post
     return false if post.post_number == 1
     
+    # You can delete your own posts
+    return !post.user_deleted? if post.user == @user
+
+    @user.has_trust_level?(:moderator)
+  end
+
+  # Recovery Method
+  def can_recover_post?(post)
+    return false if @user.blank?    
     @user.has_trust_level?(:moderator)
   end
 
diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb
index 3a1d47732..355c2324c 100644
--- a/spec/components/guardian_spec.rb
+++ b/spec/components/guardian_spec.rb
@@ -361,6 +361,26 @@ describe Guardian do
     end
   end
 
+  describe "can_recover_post?" do
+
+    it "returns false for a nil user" do
+      Guardian.new(nil).can_recover_post?(post).should be_false
+    end
+
+    it "returns false for a nil object" do
+      Guardian.new(user).can_recover_post?(nil).should be_false
+    end
+
+    it "returns false for a regular user" do
+      Guardian.new(user).can_recover_post?(post).should be_false
+    end
+
+    it "returns true for a moderator" do
+      Guardian.new(moderator).can_recover_post?(post).should be_true
+    end
+
+  end
+
   describe 'can_edit?' do
 
     it 'returns false with a nil object' do
@@ -576,10 +596,20 @@ describe Guardian do
         Guardian.new.can_delete?(post).should be_false
       end
 
-      it 'returns false when not a moderator' do
+      it "returns false when trying to delete your own post that has already been deleted" do
+        post.delete_by(user)
+        post.reload
         Guardian.new(user).can_delete?(post).should be_false
       end
 
+      it 'returns true when trying to delete your own post' do
+        Guardian.new(user).can_delete?(post).should be_true
+      end
+
+      it "returns false when trying to delete another user's own post" do
+        Guardian.new(Fabricate(:user)).can_delete?(post).should be_false
+      end
+
       it "returns false when it's the OP, even as a moderator" do
         post.update_attribute :post_number, 1
         Guardian.new(moderator).can_delete?(post).should be_false
diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb
index a96c16cc3..302da485e 100644
--- a/spec/controllers/posts_controller_spec.rb
+++ b/spec/controllers/posts_controller_spec.rb
@@ -51,7 +51,8 @@ describe PostsController do
 
     describe 'when logged in' do
 
-      let(:post) { Fabricate(:post, user: log_in(:moderator), post_number: 2) }
+      let(:user) { log_in(:moderator) }
+      let(:post) { Fabricate(:post, user: user, post_number: 2) }
 
       it "raises an error when the user doesn't have permission to see the post" do
         Guardian.any_instance.expects(:can_delete?).with(post).returns(false)
@@ -59,19 +60,39 @@ describe PostsController do
         response.should be_forbidden
       end
 
-      it "deletes the post" do
-        Post.any_instance.expects(:destroy)
-        xhr :delete, :destroy, id: post.id
-      end
-
-      it "updates the highest read data for the forum" do
-        Topic.expects(:reset_highest).with(post.topic_id)
+      it "calls delete_by" do
+        Post.any_instance.expects(:delete_by).with(user)
         xhr :delete, :destroy, id: post.id
       end
 
     end
   end
 
+  describe 'recover a post' do
+    it 'raises an exception when not logged in' do
+      lambda { xhr :put, :recover, post_id: 123 }.should raise_error(Discourse::NotLoggedIn)
+    end
+
+    describe 'when logged in' do
+
+      let(:user) { log_in(:moderator) }
+      let(:post) { Fabricate(:post, user: user, post_number: 2) }
+
+      it "raises an error when the user doesn't have permission to see the post" do
+        Guardian.any_instance.expects(:can_recover_post?).with(post).returns(false)
+        xhr :put, :recover, post_id: post.id
+        response.should be_forbidden
+      end
+
+      it "calls recover" do
+        Post.any_instance.expects(:recover)
+        xhr :put, :recover, post_id: post.id
+      end
+
+    end
+  end
+
+
   describe 'destroy_many' do
     it 'raises an exception when not logged in' do
       lambda { xhr :delete, :destroy_many, post_ids: [123, 345] }.should raise_error(Discourse::NotLoggedIn)
diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb
index ec698f02e..79fdd7921 100644
--- a/spec/models/post_spec.rb
+++ b/spec/models/post_spec.rb
@@ -505,6 +505,48 @@ describe Post do
 
   end
 
+  describe 'delete_by' do
+
+    let(:moderator) { Fabricate(:moderator) }
+    let(:post) { Fabricate(:post) }
+
+    context "as the creator of the post" do
+
+      before do
+        post.delete_by(post.user)
+        post.reload
+      end
+
+      it "doesn't delete the post" do
+        post.deleted_at.should be_blank
+      end
+
+      it "updates the text of the post" do
+        post.raw.should == I18n.t('js.post.deleted_by_author')
+      end
+
+
+      it "creates a new version" do
+        post.version.should == 2
+      end      
+
+    end
+
+    context "as a moderator" do
+
+      before do
+        post.delete_by(post.user)
+        post.reload       
+      end
+
+      it "deletes the post" do
+        post.deleted_at.should be_blank
+      end
+
+    end
+
+  end
+
   describe 'after delete' do
 
     let!(:coding_horror) { Fabricate(:coding_horror) }
@@ -550,6 +592,10 @@ describe Post do
 
     let(:post) { Fabricate(:post, post_args) }
 
+    it "defaults to not user_deleted" do
+      post.user_deleted?.should be_false
+    end
+
     it 'has a post nubmer' do
       post.post_number.should be_present
     end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index fa9098f8d..636bd1ae5 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -88,6 +88,7 @@ end
 Spork.each_run do
   # This code will be run each time you run your specs.
   $redis.client.reconnect  
+  MessageBus.reliable_pub_sub.pub_redis.client.reconnect
 end
 
 # --- Instructions ---