2019-05-02 18:17:27 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-03-21 23:12:02 -04:00
|
|
|
TopicStatusUpdater = Struct.new(:topic, :user) do
|
|
|
|
def update!(status, enabled, opts = {})
|
|
|
|
status = Status.new(status, enabled)
|
|
|
|
|
2021-01-12 17:49:29 -05:00
|
|
|
@topic_timer = topic.public_topic_timer
|
2017-03-21 23:12:02 -04:00
|
|
|
|
2017-04-07 17:10:01 -04:00
|
|
|
updated = nil
|
2017-03-21 23:12:02 -04:00
|
|
|
Topic.transaction do
|
2017-04-07 17:10:01 -04:00
|
|
|
updated = change(status, opts)
|
|
|
|
if updated
|
|
|
|
highest_post_number = topic.highest_post_number
|
2020-12-02 18:43:19 -05:00
|
|
|
create_moderator_post_for(status, opts)
|
2017-04-07 17:10:01 -04:00
|
|
|
update_read_state_for(status, highest_post_number)
|
|
|
|
end
|
2017-03-21 23:12:02 -04:00
|
|
|
end
|
2017-04-07 17:10:01 -04:00
|
|
|
|
|
|
|
updated
|
2017-03-21 23:12:02 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def change(status, opts = {})
|
2017-04-07 17:10:01 -04:00
|
|
|
result = true
|
|
|
|
|
2017-03-21 23:12:02 -04:00
|
|
|
if status.pinned? || status.pinned_globally?
|
|
|
|
topic.update_pinned(status.enabled?, status.pinned_globally?, opts[:until])
|
|
|
|
elsif status.autoclosed?
|
2017-04-07 17:10:01 -04:00
|
|
|
rc = Topic.where(id: topic.id, closed: !status.enabled?).update_all(closed: status.enabled?)
|
|
|
|
topic.closed = status.enabled?
|
|
|
|
result = false if rc == 0
|
2017-03-21 23:12:02 -04:00
|
|
|
else
|
2017-04-07 17:10:01 -04:00
|
|
|
rc = Topic.where(:id => topic.id, status.name => !status.enabled)
|
|
|
|
.update_all(status.name => status.enabled?)
|
|
|
|
|
2019-05-06 21:27:05 -04:00
|
|
|
topic.public_send("#{status.name}=", status.enabled?)
|
2017-04-07 17:10:01 -04:00
|
|
|
result = false if rc == 0
|
2017-03-21 23:12:02 -04:00
|
|
|
end
|
|
|
|
|
2017-09-27 14:00:53 -04:00
|
|
|
if status.manually_closing_topic?
|
|
|
|
DiscourseEvent.trigger(:topic_closed, topic)
|
|
|
|
end
|
|
|
|
|
2020-01-30 11:00:49 -05:00
|
|
|
if status.visible? && status.disabled?
|
|
|
|
UserProfile.remove_featured_topic_from_all_profiles(topic)
|
|
|
|
end
|
|
|
|
|
2022-02-10 20:00:58 -05:00
|
|
|
if status.visible? && result
|
2021-02-16 10:45:12 -05:00
|
|
|
topic.update_category_topic_count_by(status.enabled? ? 1 : -1)
|
2022-02-10 20:00:58 -05:00
|
|
|
UserStatCountUpdater.public_send(status.enabled? ? :increment! : :decrement!, topic.first_post)
|
2021-02-16 10:45:12 -05:00
|
|
|
end
|
|
|
|
|
2021-01-12 17:49:29 -05:00
|
|
|
if @topic_timer
|
2017-03-21 23:12:02 -04:00
|
|
|
if status.manually_closing_topic? || status.closing_topic?
|
2017-08-22 02:22:48 -04:00
|
|
|
topic.delete_topic_timer(TopicTimer.types[:close])
|
2020-12-02 18:43:19 -05:00
|
|
|
topic.delete_topic_timer(TopicTimer.types[:silent_close])
|
2017-03-21 23:12:02 -04:00
|
|
|
elsif status.manually_opening_topic? || status.opening_topic?
|
2017-08-22 02:22:48 -04:00
|
|
|
topic.delete_topic_timer(TopicTimer.types[:open])
|
2021-02-16 16:51:39 -05:00
|
|
|
topic.inherit_auto_close_from_category
|
2017-03-21 23:12:02 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# remove featured topics if we close/archive/make them invisible. Previously we used
|
|
|
|
# to run the whole featuring logic but that could be very slow and have concurrency
|
|
|
|
# errors on large sites with many autocloses and topics being created.
|
|
|
|
if ((status.enabled? && (status.autoclosed? || status.closed? || status.archived?)) ||
|
|
|
|
(status.disabled? && status.visible?))
|
|
|
|
CategoryFeaturedTopic.where(topic_id: topic.id).delete_all
|
|
|
|
end
|
2017-04-07 17:10:01 -04:00
|
|
|
|
|
|
|
result
|
2017-03-21 23:12:02 -04:00
|
|
|
end
|
|
|
|
|
2020-12-02 18:43:19 -05:00
|
|
|
def create_moderator_post_for(status, opts)
|
|
|
|
message = opts[:message]
|
|
|
|
topic.add_moderator_post(user, message || message_for(status), options_for(status, opts))
|
2017-03-21 23:12:02 -04:00
|
|
|
topic.reload
|
|
|
|
end
|
|
|
|
|
|
|
|
def update_read_state_for(status, old_highest_read)
|
|
|
|
if status.autoclosed? && status.enabled?
|
|
|
|
# let's pretend all the people that read up to the autoclose message
|
|
|
|
# actually read the topic
|
|
|
|
PostTiming.pretend_read(topic.id, old_highest_read, topic.highest_post_number)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def message_for(status)
|
|
|
|
if status.autoclosed?
|
2019-05-02 18:17:27 -04:00
|
|
|
locale_key = status.locale_key.dup
|
2021-01-12 17:49:29 -05:00
|
|
|
locale_key << "_lastpost" if @topic_timer&.based_on_last_post
|
2017-03-21 23:12:02 -04:00
|
|
|
message_for_autoclosed(locale_key)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def message_for_autoclosed(locale_key)
|
|
|
|
num_minutes =
|
2021-01-12 17:49:29 -05:00
|
|
|
if @topic_timer&.based_on_last_post
|
2021-02-04 19:12:56 -05:00
|
|
|
(@topic_timer.duration_minutes || 0).minutes.to_i
|
2021-01-12 17:49:29 -05:00
|
|
|
elsif @topic_timer&.created_at
|
|
|
|
Time.zone.now - @topic_timer.created_at
|
2017-03-21 23:12:02 -04:00
|
|
|
else
|
|
|
|
Time.zone.now - topic.created_at
|
|
|
|
end
|
|
|
|
|
2021-02-04 19:12:56 -05:00
|
|
|
# all of the results above are in seconds, this brings them
|
|
|
|
# back to the actual minutes integer
|
2017-03-21 23:12:02 -04:00
|
|
|
num_minutes = (num_minutes / 1.minute).round
|
|
|
|
|
|
|
|
if num_minutes.minutes >= 2.days
|
|
|
|
I18n.t("#{locale_key}_days", count: (num_minutes.minutes / 1.day).round)
|
|
|
|
else
|
|
|
|
num_hours = (num_minutes.minutes / 1.hour).round
|
|
|
|
if num_hours >= 2
|
|
|
|
I18n.t("#{locale_key}_hours", count: num_hours)
|
|
|
|
else
|
|
|
|
I18n.t("#{locale_key}_minutes", count: num_minutes)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-12-02 18:43:19 -05:00
|
|
|
def options_for(status, opts = {})
|
2017-03-21 23:12:02 -04:00
|
|
|
{ bump: status.opening_topic?,
|
|
|
|
post_type: Post.types[:small_action],
|
2020-12-02 18:43:19 -05:00
|
|
|
silent: opts[:silent],
|
2017-03-21 23:12:02 -04:00
|
|
|
action_code: status.action_code }
|
|
|
|
end
|
|
|
|
|
|
|
|
Status = Struct.new(:name, :enabled) do
|
|
|
|
%w(pinned_globally pinned autoclosed closed visible archived).each do |status|
|
|
|
|
define_method("#{status}?") { name == status }
|
|
|
|
end
|
|
|
|
|
|
|
|
def enabled?
|
|
|
|
enabled
|
|
|
|
end
|
|
|
|
|
|
|
|
def disabled?
|
|
|
|
!enabled?
|
|
|
|
end
|
|
|
|
|
|
|
|
def action_code
|
|
|
|
"#{name}.#{enabled? ? 'enabled' : 'disabled'}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def locale_key
|
|
|
|
"topic_statuses.#{action_code.tr('.', '_')}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def opening_topic?
|
|
|
|
(closed? || autoclosed?) && disabled?
|
|
|
|
end
|
|
|
|
|
|
|
|
def closing_topic?
|
|
|
|
(closed? || autoclosed?) && enabled?
|
|
|
|
end
|
|
|
|
|
|
|
|
def manually_closing_topic?
|
|
|
|
closed? && enabled?
|
|
|
|
end
|
|
|
|
|
|
|
|
def manually_opening_topic?
|
|
|
|
closed? && disabled?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|