FEATURE: Allow durations < 1 hour and < 1 day for topic timers where duration is specified (auto delete replies, close based on last post) (#11961)

This PR allows entering a float value for topic timers e.g. 0.5 for 30 minutes when entering hours, 0.5 for 12 hours when entering days. This is achieved by adding a new column to store the duration of a topic timer in minutes instead of the ambiguous both hours and days that it could be before.

This PR has ommitted the post migration to delete the duration column in topic timers; it will be done in a subsequent PR to ensure that no data is lost if the UPDATE query to set duration_mintues fails.

I have to keep the old keyword of duration in set_or_create_topic_timer for backwards compat, will remove at a later date after plugins are updated.
This commit is contained in:
Martin Brennan
2021-02-05 10:12:56 +10:00
committed by GitHub
parent 6f263653c6
commit 4af77f1e38
22 changed files with 125 additions and 72 deletions
@@ -7,7 +7,7 @@ import {
PUBLISH_TO_CATEGORY_STATUS_TYPE, PUBLISH_TO_CATEGORY_STATUS_TYPE,
} from "discourse/controllers/edit-topic-timer"; } from "discourse/controllers/edit-topic-timer";
import { FORMAT } from "select-kit/components/future-date-input-selector"; import { FORMAT } from "select-kit/components/future-date-input-selector";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed, { on } from "discourse-common/utils/decorators";
import { equal, or, readOnly } from "@ember/object/computed"; import { equal, or, readOnly } from "@ember/object/computed";
import I18n from "I18n"; import I18n from "I18n";
import { action } from "@ember/object"; import { action } from "@ember/object";
@@ -26,7 +26,19 @@ export default Component.extend({
showTimeOnly: or("autoOpen", "autoDelete", "autoBump"), showTimeOnly: or("autoOpen", "autoDelete", "autoBump"),
showFutureDateInput: or("showTimeOnly", "publishToCategory", "autoClose"), showFutureDateInput: or("showTimeOnly", "publishToCategory", "autoClose"),
useDuration: or("isBasedOnLastPost", "autoDeleteReplies"), useDuration: or("isBasedOnLastPost", "autoDeleteReplies"),
originalTopicTimerTime: null, duration: null,
@on("init")
preloadDuration() {
if (!this.useDuration || !this.topicTimer.duration_minutes) {
return;
}
if (this.durationType === "days") {
this.set("duration", this.topicTimer.duration_minutes / 60 / 24);
} else {
this.set("duration", this.topicTimer.duration_minutes / 60);
}
},
@discourseComputed("autoDeleteReplies") @discourseComputed("autoDeleteReplies")
durationType(autoDeleteReplies) { durationType(autoDeleteReplies) {
@@ -93,13 +105,12 @@ export default Component.extend({
@discourseComputed( @discourseComputed(
"topicTimer.updateTime", "topicTimer.updateTime",
"topicTimer.duration", "topicTimer.duration_minutes",
"useDuration", "useDuration"
"durationType"
) )
executeAt(updateTime, duration, useDuration, durationType) { executeAt(updateTime, duration, useDuration) {
if (useDuration) { if (useDuration) {
return moment().add(parseFloat(duration), durationType).format(FORMAT); return moment().add(parseFloat(duration), "minutes").format(FORMAT);
} else { } else {
return updateTime; return updateTime;
} }
@@ -107,13 +118,13 @@ export default Component.extend({
@discourseComputed( @discourseComputed(
"isBasedOnLastPost", "isBasedOnLastPost",
"topicTimer.duration", "topicTimer.duration_minutes",
"topic.last_posted_at" "topic.last_posted_at"
) )
willCloseImmediately(isBasedOnLastPost, duration, lastPostedAt) { willCloseImmediately(isBasedOnLastPost, duration, lastPostedAt) {
if (isBasedOnLastPost && duration) { if (isBasedOnLastPost && duration) {
let closeDate = moment(lastPostedAt); let closeDate = moment(lastPostedAt);
closeDate = closeDate.add(duration, "hours"); closeDate = closeDate.add(duration, "minutes");
return closeDate < moment(); return closeDate < moment();
} }
}, },
@@ -140,7 +151,7 @@ export default Component.extend({
"willCloseImmediately", "willCloseImmediately",
"topicTimer.category_id", "topicTimer.category_id",
"useDuration", "useDuration",
"topicTimer.duration" "topicTimer.duration_minutes"
) )
showTopicStatusInfo( showTopicStatusInfo(
statusType, statusType,
@@ -178,4 +189,13 @@ export default Component.extend({
}); });
this.onChangeInput(type, time); this.onChangeInput(type, time);
}, },
@action
durationChanged(newDuration) {
if (this.durationType === "days") {
this.set("topicTimer.duration_minutes", newDuration * 60 * 24);
} else {
this.set("topicTimer.duration_minutes", newDuration * 60);
}
},
}); });
@@ -63,15 +63,11 @@ export default Component.extend({
} else if (minutesLeft > 2) { } else if (minutesLeft > 2) {
rerenderDelay = 60000; rerenderDelay = 60000;
} }
let durationHours = parseInt(this.duration, 0) || 0; let durationMinutes = parseInt(this.durationMinutes, 0) || 0;
if (isDeleteRepliesType) {
durationHours *= 24;
}
let options = { let options = {
timeLeft: duration.humanize(true), timeLeft: duration.humanize(true),
duration: moment.duration(durationHours, "hours").humanize(), duration: moment.duration(durationMinutes, "minutes").humanize(),
}; };
const categoryId = this.categoryId; const categoryId = this.categoryId;
@@ -60,24 +60,24 @@ export default Controller.extend(ModalFunctionality, {
topicTimer: alias("model.topic_timer"), topicTimer: alias("model.topic_timer"),
_setTimer(time, duration, statusType, basedOnLastPost, categoryId) { _setTimer(time, durationMinutes, statusType, basedOnLastPost, categoryId) {
this.set("loading", true); this.set("loading", true);
TopicTimer.updateStatus( TopicTimer.update(
this.get("model.id"), this.get("model.id"),
time, time,
basedOnLastPost, basedOnLastPost,
statusType, statusType,
categoryId, categoryId,
duration durationMinutes
) )
.then((result) => { .then((result) => {
if (time || duration) { if (time || durationMinutes) {
this.send("closeModal"); this.send("closeModal");
setProperties(this.topicTimer, { setProperties(this.topicTimer, {
execute_at: result.execute_at, execute_at: result.execute_at,
duration: result.duration, duration_minutes: result.duration_minutes,
category_id: result.category_id, category_id: result.category_id,
}); });
@@ -131,14 +131,10 @@ export default Controller.extend(ModalFunctionality, {
this.set("topicTimer.updateTime", time); this.set("topicTimer.updateTime", time);
}, },
onChangeDuration(value) {
this.set("topicTimer.duration", value);
},
saveTimer() { saveTimer() {
if ( if (
!this.get("topicTimer.updateTime") && !this.get("topicTimer.updateTime") &&
!this.get("topicTimer.duration") !this.get("topicTimer.duration_minutes")
) { ) {
this.flash( this.flash(
I18n.t("topic.topic_status_update.time_frame_required"), I18n.t("topic.topic_status_update.time_frame_required"),
@@ -148,9 +144,9 @@ export default Controller.extend(ModalFunctionality, {
} }
if ( if (
this.get("topicTimer.duration") && this.get("topicTimer.duration_minutes") &&
!this.get("topicTimer.updateTime") && !this.get("topicTimer.updateTime") &&
this.get("topicTimer.duration") < 1 this.get("topicTimer.duration_minutes") <= 0
) { ) {
this.flash( this.flash(
I18n.t("topic.topic_status_update.min_duration"), I18n.t("topic.topic_status_update.min_duration"),
@@ -161,7 +157,7 @@ export default Controller.extend(ModalFunctionality, {
this._setTimer( this._setTimer(
this.get("topicTimer.updateTime"), this.get("topicTimer.updateTime"),
this.get("topicTimer.duration"), this.get("topicTimer.duration_minutes"),
this.get("topicTimer.status_type"), this.get("topicTimer.status_type"),
this.get("topicTimer.based_on_last_post"), this.get("topicTimer.based_on_last_post"),
this.get("topicTimer.category_id") this.get("topicTimer.category_id")
@@ -1099,13 +1099,7 @@ export default Controller.extend(bufferedProperty("model"), {
}, },
removeTopicTimer(statusType, topicTimer) { removeTopicTimer(statusType, topicTimer) {
TopicTimer.updateStatus( TopicTimer.update(this.get("model.id"), null, null, statusType, null)
this.get("model.id"),
null,
null,
statusType,
null
)
.then(() => this.set(`model.${topicTimer}`, EmberObject.create({}))) .then(() => this.set(`model.${topicTimer}`, EmberObject.create({})))
.catch((error) => popupAjaxError(error)); .catch((error) => popupAjaxError(error));
}, },
@@ -4,13 +4,13 @@ import { ajax } from "discourse/lib/ajax";
const TopicTimer = RestModel.extend({}); const TopicTimer = RestModel.extend({});
TopicTimer.reopenClass({ TopicTimer.reopenClass({
updateStatus( update(
topicId, topicId,
time, time,
basedOnLastPost, basedOnLastPost,
statusType, statusType,
categoryId, categoryId,
duration durationMinutes
) { ) {
let data = { let data = {
time, time,
@@ -23,8 +23,8 @@ TopicTimer.reopenClass({
if (categoryId) { if (categoryId) {
data.category_id = categoryId; data.category_id = categoryId;
} }
if (duration) { if (durationMinutes) {
data.duration = duration; data.duration_minutes = durationMinutes;
} }
return ajax({ return ajax({
@@ -24,7 +24,7 @@
{{#if useDuration}} {{#if useDuration}}
<div class="controls"> <div class="controls">
<label class="control-label">{{durationLabel}}</label> <label class="control-label">{{durationLabel}}</label>
{{text-field id="topic_timer_duration" class="topic-timer-duration" type="number" value=topicTimer.duration min="1"}} {{text-field id="topic_timer_duration" class="topic-timer-duration" type="number" value=duration min="0.1" step="0.1" onChange=durationChanged}}
</div> </div>
{{/if}} {{/if}}
{{#if willCloseImmediately}} {{#if willCloseImmediately}}
@@ -39,7 +39,7 @@
statusType=statusType statusType=statusType
executeAt=executeAt executeAt=executeAt
basedOnLastPost=topicTimer.based_on_last_post basedOnLastPost=topicTimer.based_on_last_post
duration=topicTimer.duration durationMinutes=topicTimer.duration_minutes
categoryId=topicTimer.category_id categoryId=topicTimer.category_id
}} }}
</div> </div>
@@ -5,7 +5,6 @@
timerTypes=publicTimerTypes timerTypes=publicTimerTypes
onChangeStatusType=(action "onChangeStatusType") onChangeStatusType=(action "onChangeStatusType")
onChangeInput=(action "onChangeInput") onChangeInput=(action "onChangeInput")
onChangeDuration=(action "onChangeDuration")
}} }}
<div class="modal-footer control-group edit-topic-timer-buttons"> <div class="modal-footer control-group edit-topic-timer-buttons">
@@ -298,7 +298,7 @@
statusType=model.topic_timer.status_type statusType=model.topic_timer.status_type
executeAt=model.topic_timer.execute_at executeAt=model.topic_timer.execute_at
basedOnLastPost=model.topic_timer.based_on_last_post basedOnLastPost=model.topic_timer.based_on_last_post
duration=model.topic_timer.duration durationMinutes=model.topic_timer.duration_minutes
categoryId=model.topic_timer.category_id categoryId=model.topic_timer.category_id
removeTopicTimer=(action "removeTopicTimer" model.topic_timer.status_type "topic_timer")}} removeTopicTimer=(action "removeTopicTimer" model.topic_timer.status_type "topic_timer")}}
@@ -16,7 +16,7 @@ acceptance("Topic - Edit timer", function (needs) {
execute_at: new Date( execute_at: new Date(
new Date().getTime() + 1 * 60 * 60 * 1000 new Date().getTime() + 1 * 60 * 60 * 1000
).toISOString(), ).toISOString(),
duration: 1, duration_minutes: 1440,
based_on_last_post: false, based_on_last_post: false,
closed: false, closed: false,
category_id: null, category_id: null,
+3 -2
View File
@@ -461,7 +461,7 @@ class TopicsController < ApplicationController
invalid_param(:status_type) invalid_param(:status_type)
end end
based_on_last_post = params[:based_on_last_post] based_on_last_post = params[:based_on_last_post]
params.require(:duration) if based_on_last_post params.require(:duration_minutes) if based_on_last_post
topic = Topic.find_by(id: params[:topic_id]) topic = Topic.find_by(id: params[:topic_id])
guardian.ensure_can_moderate!(topic) guardian.ensure_can_moderate!(topic)
@@ -472,6 +472,7 @@ class TopicsController < ApplicationController
} }
options.merge!(category_id: params[:category_id]) if !params[:category_id].blank? options.merge!(category_id: params[:category_id]) if !params[:category_id].blank?
options.merge!(duration_minutes: params[:duration_minutes].to_i) if params[:duration_minutes].present?
options.merge!(duration: params[:duration].to_i) if params[:duration].present? options.merge!(duration: params[:duration].to_i) if params[:duration].present?
topic_status_update = topic.set_or_create_timer( topic_status_update = topic.set_or_create_timer(
@@ -483,7 +484,7 @@ class TopicsController < ApplicationController
if topic.save if topic.save
render json: success_json.merge!( render json: success_json.merge!(
execute_at: topic_status_update&.execute_at, execute_at: topic_status_update&.execute_at,
duration: topic_status_update&.duration, duration_minutes: topic_status_update&.duration_minutes,
based_on_last_post: topic_status_update&.based_on_last_post, based_on_last_post: topic_status_update&.based_on_last_post,
closed: topic.closed, closed: topic.closed,
category_id: topic_status_update&.category_id category_id: topic_status_update&.category_id
+2 -2
View File
@@ -11,11 +11,11 @@ module Jobs
replies = topic.posts.where("posts.post_number > 1") replies = topic.posts.where("posts.post_number > 1")
replies = replies.where("like_count < ?", SiteSetting.skip_auto_delete_reply_likes) if SiteSetting.skip_auto_delete_reply_likes > 0 replies = replies.where("like_count < ?", SiteSetting.skip_auto_delete_reply_likes) if SiteSetting.skip_auto_delete_reply_likes > 0
replies.where('posts.created_at < ?', topic_timer.duration.days.ago).each do |post| replies.where('posts.created_at < ?', topic_timer.duration_minutes.minutes.ago).each do |post|
PostDestroyer.new(topic_timer.user, post, context: I18n.t("topic_statuses.auto_deleted_by_timer")).destroy PostDestroyer.new(topic_timer.user, post, context: I18n.t("topic_statuses.auto_deleted_by_timer")).destroy
end end
topic_timer.execute_at = (replies.minimum(:created_at) || Time.zone.now) + topic_timer.duration.days topic_timer.execute_at = (replies.minimum(:created_at) || Time.zone.now) + topic_timer.duration_minutes.minutes
topic_timer.save topic_timer.save
end end
+34 -9
View File
@@ -378,14 +378,14 @@ class Topic < ActiveRecord::Base
!public_topic_timer&.execute_at !public_topic_timer&.execute_at
based_on_last_post = self.category.auto_close_based_on_last_post based_on_last_post = self.category.auto_close_based_on_last_post
duration = based_on_last_post ? self.category.auto_close_hours : nil duration_minutes = based_on_last_post ? self.category.auto_close_hours * 60 : nil
self.set_or_create_timer( self.set_or_create_timer(
TopicTimer.types[timer_type], 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,
duration: duration duration_minutes: duration_minutes
) )
end end
end end
@@ -1307,8 +1307,18 @@ 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, silent: nil) # * duration: TODO(2021-06-01): DEPRECATED - do not use
return delete_topic_timer(status_type, by_user: by_user) if time.blank? && duration.blank? # * duration_minutes: The duration of the timer in minutes, which is used if the timer is based
# on the last post or if the timer type is delete_replies.
# * silent: Affects whether the close topic timer status change will be silent or not.
def set_or_create_timer(status_type, time, by_user: nil, based_on_last_post: false, category_id: SiteSetting.uncategorized_category_id, duration: nil, duration_minutes: nil, silent: nil)
return delete_topic_timer(status_type, by_user: by_user) if time.blank? && duration_minutes.blank? && duration.blank?
duration_minutes = duration_minutes ? duration_minutes.to_i : 0
# TODO(2021-06-01): deprecated - remove this when plugins calling set_or_create_timer
# have been fixed to use duration_minutes
duration = duration ? duration.to_i : 0
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 }
@@ -1319,22 +1329,37 @@ class Topic < ActiveRecord::Base
time_now = Time.zone.now time_now = Time.zone.now
topic_timer.based_on_last_post = !based_on_last_post.blank? topic_timer.based_on_last_post = !based_on_last_post.blank?
topic_timer.duration = duration
if status_type == TopicTimer.types[:publish_to_category] if status_type == TopicTimer.types[:publish_to_category]
topic_timer.category = Category.find_by(id: category_id) topic_timer.category = Category.find_by(id: category_id)
end end
if topic_timer.based_on_last_post if topic_timer.based_on_last_post
if duration > 0 if duration > 0 || duration_minutes > 0
last_post_created_at = self.ordered_posts.last.present? ? self.ordered_posts.last.created_at : time_now last_post_created_at = self.ordered_posts.last.present? ? self.ordered_posts.last.created_at : time_now
topic_timer.execute_at = last_post_created_at + duration.hours
# TODO(2021-06-01): deprecated - remove this when plugins calling set_or_create_timer
# have been fixed to use duration_minutes
if duration > 0
duration_minutes = duration * 60
end
topic_timer.duration_minutes = duration_minutes
topic_timer.execute_at = last_post_created_at + duration_minutes.minutes
topic_timer.created_at = last_post_created_at topic_timer.created_at = last_post_created_at
end end
elsif topic_timer.status_type == TopicTimer.types[:delete_replies] elsif topic_timer.status_type == TopicTimer.types[:delete_replies]
if duration > 0 if duration > 0 || duration_minutes > 0
first_reply_created_at = (self.ordered_posts.where("post_number > 1").minimum(:created_at) || time_now) first_reply_created_at = (self.ordered_posts.where("post_number > 1").minimum(:created_at) || time_now)
topic_timer.execute_at = first_reply_created_at + duration.days
# TODO(2021-06-01): deprecated - remove this when plugins calling set_or_create_timer
# have been fixed to use duration_minutes
if duration > 0
duration_minutes = duration * 60 * 24
end
topic_timer.duration_minutes = duration_minutes
topic_timer.execute_at = first_reply_created_at + duration_minutes.minutes
topic_timer.created_at = first_reply_created_at topic_timer.created_at = first_reply_created_at
end end
else else
+5 -1
View File
@@ -1,6 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
class TopicTimer < ActiveRecord::Base class TopicTimer < ActiveRecord::Base
self.ignored_columns = [
"duration" # TODO(2021-06-01): remove
]
include Trashable include Trashable
belongs_to :user belongs_to :user
@@ -237,7 +241,7 @@ end
# updated_at :datetime not null # updated_at :datetime not null
# category_id :integer # category_id :integer
# public_type :boolean default(TRUE) # public_type :boolean default(TRUE)
# duration :integer # duration_minutes :integer
# #
# Indexes # Indexes
# #
+1 -1
View File
@@ -3,7 +3,7 @@
class TopicTimerSerializer < ApplicationSerializer class TopicTimerSerializer < ApplicationSerializer
attributes :id, attributes :id,
:execute_at, :execute_at,
:duration, :duration_minutes,
:based_on_last_post, :based_on_last_post,
:status_type, :status_type,
:category_id :category_id
+3 -1
View File
@@ -91,13 +91,15 @@ TopicStatusUpdater = Struct.new(:topic, :user) do
def message_for_autoclosed(locale_key) def message_for_autoclosed(locale_key)
num_minutes = num_minutes =
if @topic_timer&.based_on_last_post if @topic_timer&.based_on_last_post
(@topic_timer.duration || 0).hours (@topic_timer.duration_minutes || 0).minutes.to_i
elsif @topic_timer&.created_at elsif @topic_timer&.created_at
Time.zone.now - @topic_timer.created_at Time.zone.now - @topic_timer.created_at
else else
Time.zone.now - topic.created_at Time.zone.now - topic.created_at
end end
# all of the results above are in seconds, this brings them
# back to the actual minutes integer
num_minutes = (num_minutes / 1.minute).round num_minutes = (num_minutes / 1.minute).round
if num_minutes.minutes >= 2.days if num_minutes.minutes >= 2.days
@@ -0,0 +1,16 @@
# frozen_string_literal: true
class AddDurationMinutesToTopicTimer < ActiveRecord::Migration[6.0]
def up
add_column :topic_timers, :duration_minutes, :integer
# 7 is delete_replies type, this duration is measured in days, the other
# duration is measured in hours
DB.exec("UPDATE topic_timers SET duration_minutes = (duration * 60 * 24) WHERE duration_minutes != duration AND status_type = 7 AND duration IS NOT NULL")
DB.exec("UPDATE topic_timers SET duration_minutes = (duration * 60) WHERE duration_minutes != duration AND status_type != 7 AND duration IS NOT NULL")
end
def down
remove_column :topic_timers, :duration_minutes
end
end
+2 -2
View File
@@ -527,12 +527,12 @@ class PostCreator
if topic_timer && if topic_timer &&
topic_timer.based_on_last_post && topic_timer.based_on_last_post &&
topic_timer.duration.to_i > 0 topic_timer.duration_minutes.to_i > 0
@topic.set_or_create_timer(TopicTimer.types[:close], @topic.set_or_create_timer(TopicTimer.types[:close],
nil, nil,
based_on_last_post: topic_timer.based_on_last_post, based_on_last_post: topic_timer.based_on_last_post,
duration: topic_timer.duration duration_minutes: topic_timer.duration_minutes
) )
end end
end end
+1 -1
View File
@@ -345,7 +345,7 @@ describe PostCreator do
based_on_last_post: true, based_on_last_post: true,
execute_at: Time.zone.now - 12.hours, execute_at: Time.zone.now - 12.hours,
created_at: Time.zone.now - 24.hours, created_at: Time.zone.now - 24.hours,
duration: 12 duration_minutes: 12 * 60
) )
end end
+1 -1
View File
@@ -7,7 +7,7 @@ describe Jobs::DeleteReplies do
fab!(:topic) { Fabricate(:topic) } fab!(:topic) { Fabricate(:topic) }
fab!(:topic_timer) do fab!(:topic_timer) do
Fabricate(:topic_timer, status_type: TopicTimer.types[:delete_replies], duration: 2, user: admin, topic: topic, execute_at: 2.days.from_now) Fabricate(:topic_timer, status_type: TopicTimer.types[:delete_replies], duration_minutes: 2880, user: admin, topic: topic, execute_at: 2.days.from_now)
end end
before do before do
+1 -1
View File
@@ -1752,7 +1752,7 @@ describe Topic do
it 'can take a number of hours as a string and can handle based on last post' do it 'can take a number of hours as a string and can handle based on last post' do
freeze_time now freeze_time now
topic.set_or_create_timer(TopicTimer.types[:close], nil, by_user: admin, based_on_last_post: true, duration: 18) topic.set_or_create_timer(TopicTimer.types[:close], nil, by_user: admin, based_on_last_post: true, duration_minutes: '1080')
expect(topic.topic_timers.first.execute_at).to eq_time(18.hours.from_now) expect(topic.topic_timers.first.execute_at).to eq_time(18.hours.from_now)
end end
+5 -5
View File
@@ -3081,7 +3081,7 @@ RSpec.describe TopicsController do
expect(DateTime.parse(json['execute_at'])) expect(DateTime.parse(json['execute_at']))
.to eq_time(DateTime.parse(topic_status_update.execute_at.to_s)) .to eq_time(DateTime.parse(topic_status_update.execute_at.to_s))
expect(json['duration']).to eq(topic_status_update.duration) expect(json['duration_minutes']).to eq(topic_status_update.duration_minutes)
expect(json['closed']).to eq(topic.reload.closed) expect(json['closed']).to eq(topic.reload.closed)
end end
@@ -3099,13 +3099,13 @@ RSpec.describe TopicsController do
json = response.parsed_body json = response.parsed_body
expect(json['execute_at']).to eq(nil) expect(json['execute_at']).to eq(nil)
expect(json['duration']).to eq(nil) expect(json['duration_minutes']).to eq(nil)
expect(json['closed']).to eq(topic.closed) expect(json['closed']).to eq(topic.closed)
end end
it 'should be able to create a topic status update with duration' do it 'should be able to create a topic status update with duration' do
post "/t/#{topic.id}/timer.json", params: { post "/t/#{topic.id}/timer.json", params: {
duration: 5, duration_minutes: 7200,
status_type: TopicTimer.types[7] status_type: TopicTimer.types[7]
} }
@@ -3115,14 +3115,14 @@ RSpec.describe TopicsController do
expect(topic_status_update.topic).to eq(topic) expect(topic_status_update.topic).to eq(topic)
expect(topic_status_update.execute_at).to eq_time(5.days.from_now) expect(topic_status_update.execute_at).to eq_time(5.days.from_now)
expect(topic_status_update.duration).to eq(5) expect(topic_status_update.duration_minutes).to eq(7200)
json = response.parsed_body json = response.parsed_body
expect(DateTime.parse(json['execute_at'])) expect(DateTime.parse(json['execute_at']))
.to eq_time(DateTime.parse(topic_status_update.execute_at.to_s)) .to eq_time(DateTime.parse(topic_status_update.execute_at.to_s))
expect(json['duration']).to eq(topic_status_update.duration) expect(json['duration_minutes']).to eq(topic_status_update.duration_minutes)
end end
it 'should be able to delete a topic status update for delete_replies type' do it 'should be able to delete a topic status update for delete_replies type' do
+1 -1
View File
@@ -59,7 +59,7 @@ describe TopicStatusUpdater do
Fabricate(:post, topic: topic) Fabricate(:post, topic: topic)
topic.set_or_create_timer( topic.set_or_create_timer(
TopicTimer.types[:close], nil, based_on_last_post: true, duration: 10 TopicTimer.types[:close], nil, based_on_last_post: true, duration_minutes: 600
) )
TopicStatusUpdater.new(topic, admin).update!("autoclosed", true) TopicStatusUpdater.new(topic, admin).update!("autoclosed", true)