mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 23:58:31 -05:00
allow end user to recover a post they delete
automatically delete stubs after 1 day
This commit is contained in:
parent
d68f30c09d
commit
1f3c5cb656
13 changed files with 131 additions and 23 deletions
|
@ -27,12 +27,12 @@ Discourse.Post = Discourse.Model.extend({
|
|||
deleted: Em.computed.or('deleted_at', 'deletedViaTopic'),
|
||||
|
||||
postDeletedBy: function() {
|
||||
if (this.get('firstPost')) { return this.get('topic.deleted_by') }
|
||||
if (this.get('firstPost')) { return this.get('topic.deleted_by'); }
|
||||
return this.get('deleted_by');
|
||||
}.property('firstPost', 'deleted_by', 'topic.deleted_by'),
|
||||
|
||||
postDeletedAt: function() {
|
||||
if (this.get('firstPost')) { return this.get('topic.deleted_at') }
|
||||
if (this.get('firstPost')) { return this.get('topic.deleted_at'); }
|
||||
return this.get('deleted_at');
|
||||
}.property('firstPost', 'deleted_at', 'topic.deleted_at'),
|
||||
|
||||
|
@ -199,13 +199,23 @@ Discourse.Post = Discourse.Model.extend({
|
|||
@method recover
|
||||
**/
|
||||
recover: function() {
|
||||
this.setProperties({
|
||||
var post = this;
|
||||
post.setProperties({
|
||||
deleted_at: null,
|
||||
deleted_by: null,
|
||||
can_delete: true
|
||||
user_deleted: false,
|
||||
can_delete: false
|
||||
});
|
||||
|
||||
return Discourse.ajax("/posts/" + (this.get('id')) + "/recover", { type: 'PUT', cache: false });
|
||||
return Discourse.ajax("/posts/" + (this.get('id')) + "/recover", { type: 'PUT', cache: false }).then(function(data){
|
||||
post.setProperties({
|
||||
cooked: data.cooked,
|
||||
raw: data.raw,
|
||||
user_deleted: false,
|
||||
can_delete: true,
|
||||
version: data.version
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -226,7 +236,10 @@ Discourse.Post = Discourse.Model.extend({
|
|||
this.setProperties({
|
||||
cooked: Discourse.Markdown.cook(I18n.t("post.deleted_by_author")),
|
||||
can_delete: false,
|
||||
version: this.get('version') + 1
|
||||
version: this.get('version') + 1,
|
||||
can_recover: true,
|
||||
can_edit: false,
|
||||
user_deleted: true
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ Discourse.PostMenuView = Discourse.View.extend({
|
|||
} else {
|
||||
|
||||
// The delete actions target the post iteself
|
||||
if (post.get('deleted_at')) {
|
||||
if (post.get('deleted_at') || post.get('user_deleted')) {
|
||||
if (!post.get('can_recover')) { return; }
|
||||
label = "post.controls.undelete";
|
||||
action = "recover";
|
||||
|
|
|
@ -12,10 +12,15 @@ Discourse.PostView = Discourse.View.extend({
|
|||
classNameBindings: ['postTypeClass',
|
||||
'selected',
|
||||
'post.hidden:hidden',
|
||||
'post.deleted',
|
||||
'addDeletedClass:deleted',
|
||||
'parentPost:replies-above'],
|
||||
postBinding: 'content',
|
||||
|
||||
addDeletedClass: function() {
|
||||
var post = this.get('post');
|
||||
return post.get('deleted') || post.get('user_deleted');
|
||||
}.property('post.deleted','post.user_deleted'),
|
||||
|
||||
postTypeClass: function() {
|
||||
return this.get('post.post_type') === Discourse.Site.instance().get('post_types.moderator_action') ? 'moderator' : 'regular';
|
||||
}.property('post.post_type'),
|
||||
|
|
|
@ -94,17 +94,13 @@ class PostsController < ApplicationController
|
|||
@post = Post.where(topic_id: params[:topic_id], post_number: params[:post_number]).first
|
||||
guardian.ensure_can_see!(@post)
|
||||
@post.revert_to(params[:version].to_i) if params[:version].present?
|
||||
post_serializer = PostSerializer.new(@post, scope: guardian, root: false)
|
||||
post_serializer.add_raw = true
|
||||
render_json_dump(post_serializer)
|
||||
render_post_json(@post)
|
||||
end
|
||||
|
||||
def show
|
||||
@post = find_post_from_params
|
||||
@post.revert_to(params[:version].to_i) if params[:version].present?
|
||||
post_serializer = PostSerializer.new(@post, scope: guardian, root: false)
|
||||
post_serializer.add_raw = true
|
||||
render_json_dump(post_serializer)
|
||||
render_post_json(@post)
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
@ -120,10 +116,11 @@ class PostsController < ApplicationController
|
|||
def recover
|
||||
post = find_post_from_params
|
||||
guardian.ensure_can_recover_post!(post)
|
||||
post.recover!
|
||||
post.topic.update_statistics
|
||||
destroyer = PostDestroyer.new(current_user, post)
|
||||
destroyer.recover
|
||||
post.reload
|
||||
|
||||
render nothing: true
|
||||
render_post_json(post)
|
||||
end
|
||||
|
||||
def destroy_many
|
||||
|
@ -188,6 +185,12 @@ class PostsController < ApplicationController
|
|||
post
|
||||
end
|
||||
|
||||
def render_post_json(post)
|
||||
post_serializer = PostSerializer.new(post, scope: guardian, root: false)
|
||||
post_serializer.add_raw = true
|
||||
render_json_dump(post_serializer)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
|
|
|
@ -208,6 +208,7 @@ ORDER BY p.created_at desc
|
|||
end
|
||||
|
||||
def self.synchronize_target_topic_ids(post_ids = nil)
|
||||
|
||||
builder = SqlBuilder.new("UPDATE user_actions
|
||||
SET target_topic_id = (select topic_id from posts where posts.id = target_post_id)
|
||||
/*where*/")
|
||||
|
|
|
@ -41,7 +41,8 @@ class PostSerializer < BasicPostSerializer
|
|||
:hidden_reason_id,
|
||||
:trust_level,
|
||||
:deleted_at,
|
||||
:deleted_by
|
||||
:deleted_by,
|
||||
:user_deleted
|
||||
|
||||
|
||||
def moderator?
|
||||
|
|
|
@ -34,5 +34,6 @@ module Clockwork
|
|||
every(1.day, 'version_check')
|
||||
every(1.minute, 'clockwork_heartbeat')
|
||||
every(1.minute, 'poll_mailbox')
|
||||
every(2.hours, 'destroy_old_deletion_stubs')
|
||||
|
||||
end
|
||||
|
|
|
@ -267,7 +267,7 @@ class Guardian
|
|||
end
|
||||
|
||||
def can_edit_post?(post)
|
||||
is_staff? || (not(post.topic.archived?) && is_my_own?(post))
|
||||
is_staff? || (!post.topic.archived? && is_my_own?(post) && !post.user_deleted &&!post.deleted_at)
|
||||
end
|
||||
|
||||
def can_edit_user?(user)
|
||||
|
@ -291,7 +291,7 @@ class Guardian
|
|||
|
||||
# Recovery Method
|
||||
def can_recover_post?(post)
|
||||
is_staff?
|
||||
is_staff? || (is_my_own?(post) && post.user_deleted && !post.deleted_at)
|
||||
end
|
||||
|
||||
def can_recover_topic?(topic)
|
||||
|
|
8
lib/jobs/destroy_old_deletion_stubs.rb
Normal file
8
lib/jobs/destroy_old_deletion_stubs.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
module Jobs
|
||||
# various consistency checks
|
||||
class DestroyOldDeletionStubs < Jobs::Base
|
||||
def execute(args)
|
||||
PostDestroyer.destroy_stubs
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,6 +4,13 @@
|
|||
#
|
||||
class PostDestroyer
|
||||
|
||||
def self.destroy_stubs
|
||||
Post.where(deleted_at: nil, user_deleted: true)
|
||||
.where('updated_at < ? AND post_number > 1', 1.day.ago).each do |post|
|
||||
PostDestroyer.new(Discourse.system_user, post).destroy
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(user, post)
|
||||
@user, @post = user, post
|
||||
end
|
||||
|
@ -16,6 +23,19 @@ class PostDestroyer
|
|||
end
|
||||
end
|
||||
|
||||
def recover
|
||||
if @user.staff? && @post.deleted_at
|
||||
staff_recovered
|
||||
elsif @user.staff? || @user.id == @post.user_id
|
||||
user_recovered
|
||||
end
|
||||
@post.topic.update_statistics
|
||||
end
|
||||
|
||||
def staff_recovered
|
||||
@post.recover!
|
||||
end
|
||||
|
||||
# When a post is properly deleted. Well, it's still soft deleted, but it will no longer
|
||||
# show up in the topic
|
||||
def staff_destroyed
|
||||
|
@ -75,4 +95,12 @@ class PostDestroyer
|
|||
end
|
||||
end
|
||||
|
||||
def user_recovered
|
||||
Post.transaction do
|
||||
@post.update_column(:user_deleted, false)
|
||||
@post.revise(@user, @post.versions.last.modifications["raw"][0], force_new_version: true)
|
||||
@post.update_flagged_posts_count
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -485,6 +485,16 @@ describe Guardian do
|
|||
Guardian.new(post.user).can_edit?(post).should be_true
|
||||
end
|
||||
|
||||
it 'returns false if you are trying to edit a post you soft deleted' do
|
||||
post.user_deleted = true
|
||||
Guardian.new(post.user).can_edit?(post).should be_false
|
||||
end
|
||||
|
||||
it 'returns false if you are trying to edit a deleted post' do
|
||||
post.deleted_at = 1.day.ago
|
||||
Guardian.new(post.user).can_edit?(post).should be_false
|
||||
end
|
||||
|
||||
it 'returns false if another regular user tries to edit your post' do
|
||||
Guardian.new(coding_horror).can_edit?(post).should be_false
|
||||
end
|
||||
|
|
|
@ -10,6 +10,31 @@ describe PostDestroyer do
|
|||
let(:moderator) { Fabricate(:moderator) }
|
||||
let(:post) { create_post }
|
||||
|
||||
describe 'destroy_old_stubs' do
|
||||
it 'destroys stubs for deleted by user posts' do
|
||||
Fabricate(:admin)
|
||||
reply1 = create_post(topic: post.topic)
|
||||
reply2 = create_post(topic: post.topic)
|
||||
reply3 = create_post(topic: post.topic)
|
||||
|
||||
PostDestroyer.new(reply1.user, reply1).destroy
|
||||
PostDestroyer.new(reply2.user, reply2).destroy
|
||||
|
||||
reply2.update_column(:updated_at, 2.days.ago)
|
||||
|
||||
PostDestroyer.destroy_stubs
|
||||
|
||||
reply1.reload
|
||||
reply2.reload
|
||||
reply3.reload
|
||||
|
||||
reply1.deleted_at.should == nil
|
||||
reply2.deleted_at.should_not == nil
|
||||
reply3.deleted_at.should == nil
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
describe 'basic destroying' do
|
||||
|
||||
let(:moderator) { Fabricate(:moderator) }
|
||||
|
@ -17,6 +42,7 @@ describe PostDestroyer do
|
|||
|
||||
context "as the creator of the post" do
|
||||
before do
|
||||
@orig = post.cooked
|
||||
PostDestroyer.new(post.user, post).destroy
|
||||
post.reload
|
||||
end
|
||||
|
@ -24,8 +50,16 @@ describe PostDestroyer do
|
|||
it "doesn't delete the post" do
|
||||
post.deleted_at.should be_blank
|
||||
post.deleted_by.should be_blank
|
||||
post.user_deleted.should be_true
|
||||
post.raw.should == I18n.t('js.post.deleted_by_author')
|
||||
post.version.should == 2
|
||||
|
||||
# lets try to recover
|
||||
PostDestroyer.new(post.user, post).recover
|
||||
post.reload
|
||||
post.version.should == 3
|
||||
post.user_deleted.should be_false
|
||||
post.cooked.should == @orig
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -124,10 +124,14 @@ describe PostsController do
|
|||
response.should be_forbidden
|
||||
end
|
||||
|
||||
it "calls recover and updates the topic's statistics" do
|
||||
Post.any_instance.expects(:recover!)
|
||||
Topic.any_instance.expects(:update_statistics)
|
||||
it "recovers a post correctly" do
|
||||
topic_id = create_post.topic_id
|
||||
post = create_post(topic_id: topic_id)
|
||||
|
||||
PostDestroyer.new(user, post).destroy
|
||||
xhr :put, :recover, post_id: post.id
|
||||
post.reload
|
||||
post.deleted_at.should == nil
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue