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 }));
break;
}
case "destroyed": {
postStream
.triggerDestroyedPost(data.id)
.then(() => refresh({ id: data.id }));
break;
}
case "recovered": {
postStream
.triggerRecoveredPost(data.id)

View File

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

View File

@ -6,6 +6,7 @@ import PreloadStore from "discourse/lib/preload-store";
import Category from "discourse/models/category";
import User from "discourse/models/user";
import { deepEqual } from "discourse-common/lib/object";
import DiscourseURL from "discourse/lib/url";
function isNew(topic) {
return (
@ -148,6 +149,17 @@ const TopicTrackingState = EmberObject.extend({
}
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() {

View File

@ -171,6 +171,17 @@ class TopicTrackingState
MessageBus.publish("/delete", message.as_json, group_ids: group_ids)
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)
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
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
elsif @user.id == @post.user_id
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
# show up in the topic
# Permanent option allows to hard delete.
def perform_delete
Post.transaction do
@post.trash!(@user)
permanent? ? @post.destroy! : @post.trash!(@user)
if @post.topic
make_previous_post_the_last_one
mark_topic_changed
@ -162,7 +163,9 @@ class PostDestroyer
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_user_counts
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
feature_users_in_the_topic if @post.topic
@post.publish_change_to_clients! :deleted if @post.topic
TopicTrackingState.publish_delete(@post.topic) if @post.topic && @post.post_number == 1
@post.publish_change_to_clients!(permanent? ? :destroyed : :deleted) if @post.topic
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
# When a user 'deletes' their own post. We just change the text.

View File

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

View File

@ -961,4 +961,27 @@ describe PostDestroyer do
expect(user.user_profile.reload.featured_topic).to eq(nil)
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