FEATURE: the ability to permanently destroy the private message (#11115)

PostDestroyer should accept the option to permanently destroy post from the database. In addition, when the first post is destroyed it destroys the whole topic.

Currently, that feature is limited to private messages and creator of the post. It will be used by discourse-encrypt to explode encrypted private messages.
This commit is contained in:
Krzysztof Kotlarek 2020-11-10 15:40:48 +11:00 committed by GitHub
parent 27e94f2f98
commit 586c8efbd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 71 additions and 5 deletions

View File

@ -1424,6 +1424,12 @@ export default Controller.extend(bufferedProperty("model"), {
.then(() => refresh({ id: data.id })); .then(() => refresh({ id: data.id }));
break; break;
} }
case "destroyed": {
postStream
.triggerDestroyedPost(data.id)
.then(() => refresh({ id: data.id }));
break;
}
case "recovered": { case "recovered": {
postStream postStream
.triggerRecoveredPost(data.id) .triggerRecoveredPost(data.id)

View File

@ -737,6 +737,12 @@ export default RestModel.extend({
return Promise.resolve(); return Promise.resolve();
}, },
triggerDestroyedPost(postId) {
const existing = this._identityMap[postId];
this.removePosts([existing]);
return Promise.resolve();
},
triggerChangedPost(postId, updatedAt, opts) { triggerChangedPost(postId, updatedAt, opts) {
opts = opts || {}; opts = opts || {};

View File

@ -6,6 +6,7 @@ import PreloadStore from "discourse/lib/preload-store";
import Category from "discourse/models/category"; import Category from "discourse/models/category";
import User from "discourse/models/user"; import User from "discourse/models/user";
import { deepEqual } from "discourse-common/lib/object"; import { deepEqual } from "discourse-common/lib/object";
import DiscourseURL from "discourse/lib/url";
function isNew(topic) { function isNew(topic) {
return ( return (
@ -148,6 +149,17 @@ const TopicTrackingState = EmberObject.extend({
} }
tracker.incrementMessageCount(); tracker.incrementMessageCount();
}); });
this.messageBus.subscribe("/destroy", (msg) => {
tracker.incrementMessageCount();
const currentRoute = DiscourseURL.router.currentRoute.parent;
if (
currentRoute.name === "topic" &&
parseInt(currentRoute.params.id, 10) === msg.topic_id
) {
DiscourseURL.redirectTo("/");
}
});
}, },
mutedTopics() { mutedTopics() {

View File

@ -171,6 +171,17 @@ class TopicTrackingState
MessageBus.publish("/delete", message.as_json, group_ids: group_ids) MessageBus.publish("/delete", message.as_json, group_ids: group_ids)
end end
def self.publish_destroy(topic)
group_ids = topic.category && topic.category.secure_group_ids
message = {
topic_id: topic.id,
message_type: "destroy"
}
MessageBus.publish("/destroy", message.as_json, group_ids: group_ids)
end
def self.publish_read(topic_id, last_read_post_number, user_id, notification_level = nil) def self.publish_read(topic_id, last_read_post_number, user_id, notification_level = nil)
highest_post_number = DB.query_single("SELECT highest_post_number FROM topics WHERE id = ?", topic_id).first highest_post_number = DB.query_single("SELECT highest_post_number FROM topics WHERE id = ?", topic_id).first

View File

@ -66,7 +66,7 @@ class PostDestroyer
delete_removed_posts_after = @opts[:delete_removed_posts_after] || SiteSetting.delete_removed_posts_after delete_removed_posts_after = @opts[:delete_removed_posts_after] || SiteSetting.delete_removed_posts_after
if delete_removed_posts_after < 1 || post_is_reviewable? || Guardian.new(@user).can_moderate_topic?(topic) if delete_removed_posts_after < 1 || post_is_reviewable? || Guardian.new(@user).can_moderate_topic?(topic) || permanent?
perform_delete perform_delete
elsif @user.id == @post.user_id elsif @user.id == @post.user_id
mark_for_deletion(delete_removed_posts_after) mark_for_deletion(delete_removed_posts_after)
@ -140,9 +140,10 @@ class PostDestroyer
# When a post is properly deleted. Well, it's still soft deleted, but it will no longer # When a post is properly deleted. Well, it's still soft deleted, but it will no longer
# show up in the topic # show up in the topic
# Permanent option allows to hard delete.
def perform_delete def perform_delete
Post.transaction do Post.transaction do
@post.trash!(@user) permanent? ? @post.destroy! : @post.trash!(@user)
if @post.topic if @post.topic
make_previous_post_the_last_one make_previous_post_the_last_one
mark_topic_changed mark_topic_changed
@ -162,7 +163,9 @@ class PostDestroyer
end end
end end
@post.topic.trash!(@user) if @post.topic && @post.is_first_post? if @post.topic && @post.is_first_post?
permanent? ? @post.topic.destroy! : @post.topic.trash!(@user)
end
update_associated_category_latest_topic update_associated_category_latest_topic
update_user_counts update_user_counts
TopicUser.update_post_action_cache(post_id: @post.id) TopicUser.update_post_action_cache(post_id: @post.id)
@ -178,8 +181,12 @@ class PostDestroyer
update_imap_sync(@post, true) if @post.topic&.deleted_at update_imap_sync(@post, true) if @post.topic&.deleted_at
feature_users_in_the_topic if @post.topic feature_users_in_the_topic if @post.topic
@post.publish_change_to_clients! :deleted if @post.topic @post.publish_change_to_clients!(permanent? ? :destroyed : :deleted) if @post.topic
TopicTrackingState.publish_delete(@post.topic) if @post.topic && @post.post_number == 1 TopicTrackingState.send(permanent? ? :publish_destroy : :publish_delete, @post.topic) if @post.topic && @post.post_number == 1
end
def permanent?
@opts[:permanent] && @user == @post.user && @post.topic.private_message?
end end
# When a user 'deletes' their own post. We just change the text. # When a user 'deletes' their own post. We just change the text.

View File

@ -174,6 +174,7 @@ module SvgSprite
"star", "star",
"step-backward", "step-backward",
"step-forward", "step-forward",
"stopwatch",
"stream", "stream",
"sync-alt", "sync-alt",
"sync", "sync",

View File

@ -961,4 +961,27 @@ describe PostDestroyer do
expect(user.user_profile.reload.featured_topic).to eq(nil) expect(user.user_profile.reload.featured_topic).to eq(nil)
end end
end end
describe "permanent destroy" do
fab!(:private_message_topic) { Fabricate(:private_message_topic) }
fab!(:private_post) { Fabricate(:private_message_post, topic: private_message_topic) }
fab!(:reply) { Fabricate(:private_message_post, topic: private_message_topic) }
it "destroys the post and topic if deleting first post" do
PostDestroyer.new(reply.user, reply, permanent: true).destroy
expect { reply.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect(private_message_topic.reload.persisted?).to be true
PostDestroyer.new(private_post.user, private_post, permanent: true).destroy
expect { private_post.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect { private_message_topic.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'soft delete if not creator of post or not private message' do
PostDestroyer.new(moderator, reply, permanent: true).destroy
expect(reply.deleted_at).not_to eq(nil)
PostDestroyer.new(post.user, post, permanent: true).destroy
expect(post.user_deleted).to be true
end
end
end end