mirror of
https://github.com/discourse/discourse.git
synced 2025-06-06 03:06:53 +08:00
FEATURE: silently close topic (#11392)
New TopicTimer to silently close topic. It will be used by discourse-solved plugin Meta: https://meta.discourse.org/t/allow-auto-close-for-solved-to-do-so-silently/169300
This commit is contained in:

committed by
GitHub

parent
1c87038255
commit
9c5ee4923b
@ -126,7 +126,10 @@ export default Component.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_noticeKey() {
|
_noticeKey() {
|
||||||
const statusType = this.statusType;
|
let statusType = this.statusType;
|
||||||
|
if (statusType === "silent_close") {
|
||||||
|
statusType = "close";
|
||||||
|
}
|
||||||
|
|
||||||
if (this.basedOnLastPost) {
|
if (this.basedOnLastPost) {
|
||||||
return `topic.status_update_notice.auto_${statusType}_based_on_last_post`;
|
return `topic.status_update_notice.auto_${statusType}_based_on_last_post`;
|
||||||
|
@ -5,6 +5,7 @@ module Jobs
|
|||||||
def execute(args)
|
def execute(args)
|
||||||
topic_timer = TopicTimer.find_by(id: args[:topic_timer_id] || args[:topic_status_update_id])
|
topic_timer = TopicTimer.find_by(id: args[:topic_timer_id] || args[:topic_status_update_id])
|
||||||
state = !!args[:state]
|
state = !!args[:state]
|
||||||
|
timer_type = args[:silent] ? :silent_close : :close
|
||||||
|
|
||||||
if topic_timer.blank? || topic_timer.execute_at > Time.zone.now
|
if topic_timer.blank? || topic_timer.execute_at > Time.zone.now
|
||||||
return
|
return
|
||||||
@ -25,16 +26,16 @@ module Jobs
|
|||||||
by_user: Discourse.system_user
|
by_user: Discourse.system_user
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
topic.update_status('autoclosed', state, user)
|
topic.update_status('autoclosed', state, user, { silent: args[:silent] })
|
||||||
end
|
end
|
||||||
|
|
||||||
topic.inherit_auto_close_from_category if state == false
|
topic.inherit_auto_close_from_category(timer_type: timer_type) if state == false
|
||||||
else
|
else
|
||||||
topic_timer.destroy!
|
topic_timer.destroy!
|
||||||
topic.reload
|
topic.reload
|
||||||
|
|
||||||
if topic_timer.based_on_last_post
|
if topic_timer.based_on_last_post
|
||||||
topic.inherit_auto_close_from_category
|
topic.inherit_auto_close_from_category(timer_type: timer_type)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -368,7 +368,7 @@ class Topic < ActiveRecord::Base
|
|||||||
self.last_post_user_id ||= user_id
|
self.last_post_user_id ||= user_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def inherit_auto_close_from_category
|
def inherit_auto_close_from_category(timer_type: :close)
|
||||||
if !self.closed &&
|
if !self.closed &&
|
||||||
!@ignore_category_auto_close &&
|
!@ignore_category_auto_close &&
|
||||||
self.category &&
|
self.category &&
|
||||||
@ -379,7 +379,7 @@ class Topic < ActiveRecord::Base
|
|||||||
duration = based_on_last_post ? self.category.auto_close_hours : nil
|
duration = based_on_last_post ? self.category.auto_close_hours : nil
|
||||||
|
|
||||||
self.set_or_create_timer(
|
self.set_or_create_timer(
|
||||||
TopicTimer.types[:close],
|
TopicTimer.types[timer_type],
|
||||||
self.category.auto_close_hours,
|
self.category.auto_close_hours,
|
||||||
by_user: Discourse.system_user,
|
by_user: Discourse.system_user,
|
||||||
based_on_last_post: based_on_last_post,
|
based_on_last_post: based_on_last_post,
|
||||||
@ -902,6 +902,7 @@ class Topic < ActiveRecord::Base
|
|||||||
action_code: opts[:action_code],
|
action_code: opts[:action_code],
|
||||||
no_bump: opts[:bump].blank?,
|
no_bump: opts[:bump].blank?,
|
||||||
topic_id: self.id,
|
topic_id: self.id,
|
||||||
|
silent: opts[:silent],
|
||||||
skip_validations: true,
|
skip_validations: true,
|
||||||
custom_fields: opts[:custom_fields],
|
custom_fields: opts[:custom_fields],
|
||||||
import_mode: opts[:import_mode])
|
import_mode: opts[:import_mode])
|
||||||
@ -1299,12 +1300,13 @@ class Topic < ActiveRecord::Base
|
|||||||
# * by_user: User who is setting the topic's status update.
|
# * by_user: User who is setting the topic's status update.
|
||||||
# * based_on_last_post: True if time should be based on timestamp of the last post.
|
# * based_on_last_post: True if time should be based on timestamp of the last post.
|
||||||
# * category_id: Category that the update will apply to.
|
# * category_id: Category that the update will apply to.
|
||||||
def set_or_create_timer(status_type, time, by_user: nil, based_on_last_post: false, category_id: SiteSetting.uncategorized_category_id, duration: nil)
|
def set_or_create_timer(status_type, time, by_user: nil, based_on_last_post: false, category_id: SiteSetting.uncategorized_category_id, duration: nil, silent: nil)
|
||||||
return delete_topic_timer(status_type, by_user: by_user) if time.blank? && duration.blank?
|
return delete_topic_timer(status_type, by_user: by_user) if time.blank? && duration.blank?
|
||||||
|
|
||||||
public_topic_timer = !!TopicTimer.public_types[status_type]
|
public_topic_timer = !!TopicTimer.public_types[status_type]
|
||||||
topic_timer_options = { topic: self, public_type: public_topic_timer }
|
topic_timer_options = { topic: self, public_type: public_topic_timer }
|
||||||
topic_timer_options.merge!(user: by_user) unless public_topic_timer
|
topic_timer_options.merge!(user: by_user) unless public_topic_timer
|
||||||
|
topic_timer_options.merge!(silent: silent) if silent
|
||||||
topic_timer = TopicTimer.find_or_initialize_by(topic_timer_options)
|
topic_timer = TopicTimer.find_or_initialize_by(topic_timer_options)
|
||||||
topic_timer.status_type = status_type
|
topic_timer.status_type = status_type
|
||||||
|
|
||||||
|
@ -50,7 +50,8 @@ class TopicTimer < ActiveRecord::Base
|
|||||||
delete: 4,
|
delete: 4,
|
||||||
reminder: 5,
|
reminder: 5,
|
||||||
bump: 6,
|
bump: 6,
|
||||||
delete_replies: 7
|
delete_replies: 7,
|
||||||
|
silent_close: 8
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -97,6 +98,10 @@ class TopicTimer < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
alias_method :cancel_auto_open_job, :cancel_auto_close_job
|
alias_method :cancel_auto_open_job, :cancel_auto_close_job
|
||||||
|
|
||||||
|
def cancel_auto_silent_close_job
|
||||||
|
Jobs.cancel_scheduled_job(:toggle_topic_closed, topic_timer_id: id)
|
||||||
|
end
|
||||||
|
|
||||||
def cancel_auto_publish_to_category_job
|
def cancel_auto_publish_to_category_job
|
||||||
Jobs.cancel_scheduled_job(:publish_topic_to_category, topic_timer_id: id)
|
Jobs.cancel_scheduled_job(:publish_topic_to_category, topic_timer_id: id)
|
||||||
end
|
end
|
||||||
@ -143,6 +148,16 @@ class TopicTimer < ActiveRecord::Base
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def schedule_auto_silent_close_job(time)
|
||||||
|
topic.update_status('closed', false, user) if topic&.closed
|
||||||
|
|
||||||
|
Jobs.enqueue_at(time, :toggle_topic_closed,
|
||||||
|
topic_timer_id: id,
|
||||||
|
silent: true,
|
||||||
|
state: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def schedule_auto_publish_to_category_job(time)
|
def schedule_auto_publish_to_category_job(time)
|
||||||
Jobs.enqueue_at(time, :publish_topic_to_category, topic_timer_id: id)
|
Jobs.enqueue_at(time, :publish_topic_to_category, topic_timer_id: id)
|
||||||
end
|
end
|
||||||
|
@ -11,7 +11,7 @@ TopicStatusUpdater = Struct.new(:topic, :user) do
|
|||||||
updated = change(status, opts)
|
updated = change(status, opts)
|
||||||
if updated
|
if updated
|
||||||
highest_post_number = topic.highest_post_number
|
highest_post_number = topic.highest_post_number
|
||||||
create_moderator_post_for(status, opts[:message])
|
create_moderator_post_for(status, opts)
|
||||||
update_read_state_for(status, highest_post_number)
|
update_read_state_for(status, highest_post_number)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -49,6 +49,7 @@ TopicStatusUpdater = Struct.new(:topic, :user) do
|
|||||||
if @topic_status_update
|
if @topic_status_update
|
||||||
if status.manually_closing_topic? || status.closing_topic?
|
if status.manually_closing_topic? || status.closing_topic?
|
||||||
topic.delete_topic_timer(TopicTimer.types[:close])
|
topic.delete_topic_timer(TopicTimer.types[:close])
|
||||||
|
topic.delete_topic_timer(TopicTimer.types[:silent_close])
|
||||||
elsif status.manually_opening_topic? || status.opening_topic?
|
elsif status.manually_opening_topic? || status.opening_topic?
|
||||||
topic.delete_topic_timer(TopicTimer.types[:open])
|
topic.delete_topic_timer(TopicTimer.types[:open])
|
||||||
end
|
end
|
||||||
@ -65,8 +66,9 @@ TopicStatusUpdater = Struct.new(:topic, :user) do
|
|||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_moderator_post_for(status, message = nil)
|
def create_moderator_post_for(status, opts)
|
||||||
topic.add_moderator_post(user, message || message_for(status), options_for(status))
|
message = opts[:message]
|
||||||
|
topic.add_moderator_post(user, message || message_for(status), options_for(status, opts))
|
||||||
topic.reload
|
topic.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -110,9 +112,10 @@ TopicStatusUpdater = Struct.new(:topic, :user) do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def options_for(status)
|
def options_for(status, opts = {})
|
||||||
{ bump: status.opening_topic?,
|
{ bump: status.opening_topic?,
|
||||||
post_type: Post.types[:small_action],
|
post_type: Post.types[:small_action],
|
||||||
|
silent: opts[:silent],
|
||||||
action_code: status.action_code }
|
action_code: status.action_code }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ class PostCreator
|
|||||||
# hidden_reason_id - Reason for hiding the post (optional)
|
# hidden_reason_id - Reason for hiding the post (optional)
|
||||||
# skip_validations - Do not validate any of the content in the post
|
# skip_validations - Do not validate any of the content in the post
|
||||||
# draft_key - the key of the draft we are creating (will be deleted on success)
|
# draft_key - the key of the draft we are creating (will be deleted on success)
|
||||||
|
# silent - Do not update topic stats and fields like last_post_user_id
|
||||||
#
|
#
|
||||||
# When replying to a topic:
|
# When replying to a topic:
|
||||||
# topic_id - topic we're replying to
|
# topic_id - topic we're replying to
|
||||||
@ -506,13 +507,12 @@ class PostCreator
|
|||||||
def update_topic_stats
|
def update_topic_stats
|
||||||
attrs = { updated_at: Time.now }
|
attrs = { updated_at: Time.now }
|
||||||
|
|
||||||
if @post.post_type != Post.types[:whisper]
|
if @post.post_type != Post.types[:whisper] && !@opts[:silent]
|
||||||
attrs[:last_posted_at] = @post.created_at
|
attrs[:last_posted_at] = @post.created_at
|
||||||
attrs[:last_post_user_id] = @post.user_id
|
attrs[:last_post_user_id] = @post.user_id
|
||||||
attrs[:word_count] = (@topic.word_count || 0) + @post.word_count
|
attrs[:word_count] = (@topic.word_count || 0) + @post.word_count
|
||||||
attrs[:excerpt] = @post.excerpt_for_topic if new_topic?
|
attrs[:excerpt] = @post.excerpt_for_topic if new_topic?
|
||||||
attrs[:bumped_at] = @post.created_at unless @post.no_bump
|
attrs[:bumped_at] = @post.created_at unless @post.no_bump
|
||||||
@topic.update_columns(attrs)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@topic.update_columns(attrs)
|
@topic.update_columns(attrs)
|
||||||
|
@ -581,6 +581,36 @@ describe PostCreator do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'silent' do
|
||||||
|
fab!(:topic) { Fabricate(:topic, user: user) }
|
||||||
|
|
||||||
|
it 'silent do not mess up the public view' do
|
||||||
|
freeze_time DateTime.parse('2010-01-01 12:00')
|
||||||
|
|
||||||
|
first = PostCreator.new(
|
||||||
|
user,
|
||||||
|
topic_id: topic.id,
|
||||||
|
raw: 'this is the first post'
|
||||||
|
).create
|
||||||
|
|
||||||
|
freeze_time 1.year.from_now
|
||||||
|
|
||||||
|
PostCreator.new(user,
|
||||||
|
topic_id: topic.id,
|
||||||
|
reply_to_post_number: 1,
|
||||||
|
silent: true,
|
||||||
|
post_type: Post.types[:regular],
|
||||||
|
raw: 'this is a whispered reply').create
|
||||||
|
|
||||||
|
topic.reload
|
||||||
|
|
||||||
|
# silent post should not muck up that number
|
||||||
|
expect(topic.last_posted_at).to eq_time(first.created_at)
|
||||||
|
expect(topic.last_post_user_id).to eq(first.user_id)
|
||||||
|
expect(topic.word_count).to eq(5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'uniqueness' do
|
context 'uniqueness' do
|
||||||
|
|
||||||
fab!(:topic) { Fabricate(:topic, user: user) }
|
fab!(:topic) { Fabricate(:topic, user: user) }
|
||||||
|
Reference in New Issue
Block a user