mirror of
https://github.com/discourse/discourse.git
synced 2025-01-31 03:09:29 +08:00
FIX: Autoclose message was not correct when immediate
FEATURE: Warn when setting auto-close for right now
This commit is contained in:
parent
b09ee9cb82
commit
e52852dd0f
|
@ -5,6 +5,8 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||||
export default Ember.Controller.extend(ModalFunctionality, {
|
export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
auto_close_valid: true,
|
auto_close_valid: true,
|
||||||
auto_close_invalid: Em.computed.not('auto_close_valid'),
|
auto_close_invalid: Em.computed.not('auto_close_valid'),
|
||||||
|
disable_submit: Em.computed.or('auto_close_invalid', 'loading'),
|
||||||
|
loading: false,
|
||||||
|
|
||||||
@observes("model.details.auto_close_at", "model.details.auto_close_hours")
|
@observes("model.details.auto_close_at", "model.details.auto_close_hours")
|
||||||
setAutoCloseTime() {
|
setAutoCloseTime() {
|
||||||
|
@ -29,7 +31,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
|
|
||||||
setAutoClose(time) {
|
setAutoClose(time) {
|
||||||
const self = this;
|
const self = this;
|
||||||
this.send('hideModal');
|
this.set('loading', true);
|
||||||
Discourse.ajax({
|
Discourse.ajax({
|
||||||
url: `/t/${this.get('model.id')}/autoclose`,
|
url: `/t/${this.get('model.id')}/autoclose`,
|
||||||
type: 'PUT',
|
type: 'PUT',
|
||||||
|
@ -40,16 +42,34 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
timezone_offset: (new Date().getTimezoneOffset())
|
timezone_offset: (new Date().getTimezoneOffset())
|
||||||
}
|
}
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
|
self.set('loading', false);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.send('closeModal');
|
this.send('closeModal');
|
||||||
this.set('model.details.auto_close_at', result.auto_close_at);
|
this.set('model.details.auto_close_at', result.auto_close_at);
|
||||||
this.set('model.details.auto_close_hours', result.auto_close_hours);
|
this.set('model.details.auto_close_hours', result.auto_close_hours);
|
||||||
} else {
|
} else {
|
||||||
bootbox.alert(I18n.t('composer.auto_close.error'), function() { self.send('reopenModal'); } );
|
bootbox.alert(I18n.t('composer.auto_close.error'));
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
bootbox.alert(I18n.t('composer.auto_close.error'), function() { self.send('reopenModal'); } );
|
// TODO - incorrectly responds to network errors as bad input
|
||||||
|
bootbox.alert(I18n.t('composer.auto_close.error'));
|
||||||
|
self.set('loading', false);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
|
willCloseImmediately: function() {
|
||||||
|
if (!this.get('model.details.auto_close_based_on_last_post')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let closeDate = new Date(this.get('model.last_posted_at'));
|
||||||
|
closeDate.setHours(closeDate.getHours() + this.get('model.auto_close_time'));
|
||||||
|
return closeDate < new Date();
|
||||||
|
}.property('model.details.auto_close_based_on_last_post', 'model.auto_close_time', 'model.last_posted_at'),
|
||||||
|
|
||||||
|
willCloseI18n: function() {
|
||||||
|
if (this.get('model.details.auto_close_based_on_last_post')) {
|
||||||
|
return I18n.t('topic.auto_close_immediate', {hours: this.get('model.auto_close_time')});
|
||||||
|
}
|
||||||
|
}.property('model.details.auto_close_based_on_last_post', 'model.auto_close_time')
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -340,6 +340,13 @@ const Topic = RestModel.extend({
|
||||||
keys.forEach(key => this.set(key, json[key]));
|
keys.forEach(key => this.set(key, json[key]));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
reload() {
|
||||||
|
const self = this;
|
||||||
|
return Discourse.ajax('/t/' + this.get('id'), { type: 'GET' }).then(function(topic_json) {
|
||||||
|
self.updateFromJson(topic_json);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
isPinnedUncategorized: function() {
|
isPinnedUncategorized: function() {
|
||||||
return this.get('pinned') && this.get('category.isUncategorizedCategory');
|
return this.get('pinned') && this.get('category.isUncategorizedCategory');
|
||||||
}.property('pinned', 'category.isUncategorizedCategory'),
|
}.property('pinned', 'category.isUncategorizedCategory'),
|
||||||
|
|
|
@ -4,10 +4,17 @@
|
||||||
autoCloseValid=auto_close_valid
|
autoCloseValid=auto_close_valid
|
||||||
autoCloseBasedOnLastPost=model.details.auto_close_based_on_last_post
|
autoCloseBasedOnLastPost=model.details.auto_close_based_on_last_post
|
||||||
limited=model.details.auto_close_based_on_last_post }}
|
limited=model.details.auto_close_based_on_last_post }}
|
||||||
|
{{#if willCloseImmediately}}
|
||||||
|
<div class="warning">
|
||||||
|
{{fa-icon "warning"}}
|
||||||
|
{{willCloseI18n}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
{{d-button class="btn-primary" disabled=auto_close_invalid label="topic.auto_close_save" action="saveAutoClose"}}
|
{{d-button class="btn-primary" disabled=disable_submit label="topic.auto_close_save" action="saveAutoClose"}}
|
||||||
<a {{action "closeModal"}}>{{i18n 'cancel'}}</a>
|
<a {{action "closeModal"}}>{{i18n 'cancel'}}</a>
|
||||||
|
{{conditional-loading-spinner size="small" condition=loading}}
|
||||||
{{d-button class="pull-right" action="removeAutoClose" label="topic.auto_close_remove"}}
|
{{d-button class="pull-right" action="removeAutoClose" label="topic.auto_close_remove"}}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -835,10 +835,12 @@ class Topic < ActiveRecord::Base
|
||||||
self.auto_close_at = utc.local(now.year, now.month, now.day, m[1].to_i, m[2].to_i)
|
self.auto_close_at = utc.local(now.year, now.month, now.day, m[1].to_i, m[2].to_i)
|
||||||
self.auto_close_at += offset_minutes * 60 if offset_minutes
|
self.auto_close_at += offset_minutes * 60 if offset_minutes
|
||||||
self.auto_close_at += 1.day if self.auto_close_at < now
|
self.auto_close_at += 1.day if self.auto_close_at < now
|
||||||
|
self.auto_close_hours = -1
|
||||||
elsif arg.is_a?(String) && arg.include?("-") && timestamp = utc.parse(arg)
|
elsif arg.is_a?(String) && arg.include?("-") && timestamp = utc.parse(arg)
|
||||||
# a timestamp in client's time zone, like "2015-5-27 12:00"
|
# a timestamp in client's time zone, like "2015-5-27 12:00"
|
||||||
self.auto_close_at = timestamp
|
self.auto_close_at = timestamp
|
||||||
self.auto_close_at += offset_minutes * 60 if offset_minutes
|
self.auto_close_at += offset_minutes * 60 if offset_minutes
|
||||||
|
self.auto_close_hours = -1
|
||||||
self.errors.add(:auto_close_at, :invalid) if timestamp < Time.zone.now
|
self.errors.add(:auto_close_at, :invalid) if timestamp < Time.zone.now
|
||||||
else
|
else
|
||||||
num_hours = arg.to_f
|
num_hours = arg.to_f
|
||||||
|
@ -864,6 +866,10 @@ class Topic < ActiveRecord::Base
|
||||||
else
|
else
|
||||||
self.auto_close_user ||= (self.user.staff? || self.user.trust_level == TrustLevel[4] ? self.user : Discourse.system_user)
|
self.auto_close_user ||= (self.user.staff? || self.user.trust_level == TrustLevel[4] ? self.user : Discourse.system_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.auto_close_at.try(:<, Time.zone.now)
|
||||||
|
auto_close(auto_close_user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self
|
self
|
||||||
|
|
|
@ -52,7 +52,16 @@ TopicStatusUpdate = Struct.new(:topic, :user) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def message_for_autoclosed(locale_key)
|
def message_for_autoclosed(locale_key)
|
||||||
num_minutes = topic.auto_close_started_at ? ((Time.zone.now - topic.auto_close_started_at) / 1.minute).round : topic.age_in_minutes
|
num_minutes = ((
|
||||||
|
if topic.auto_close_based_on_last_post
|
||||||
|
topic.auto_close_hours.hours
|
||||||
|
elsif topic.auto_close_started_at
|
||||||
|
Time.zone.now - topic.auto_close_started_at
|
||||||
|
else
|
||||||
|
Time.zone.now - topic.created_at
|
||||||
|
end
|
||||||
|
) / 1.minute).round
|
||||||
|
|
||||||
if num_minutes.minutes >= 2.days
|
if num_minutes.minutes >= 2.days
|
||||||
I18n.t("#{locale_key}_days", count: (num_minutes.minutes / 1.day).round)
|
I18n.t("#{locale_key}_days", count: (num_minutes.minutes / 1.day).round)
|
||||||
else
|
else
|
||||||
|
|
|
@ -1142,6 +1142,7 @@ en:
|
||||||
auto_close_title: 'Auto-Close Settings'
|
auto_close_title: 'Auto-Close Settings'
|
||||||
auto_close_save: "Save"
|
auto_close_save: "Save"
|
||||||
auto_close_remove: "Don't Auto-Close This Topic"
|
auto_close_remove: "Don't Auto-Close This Topic"
|
||||||
|
auto_close_immediate: "The last post in the topic is already %{hours} hours old, so the topic will be closed immediately."
|
||||||
|
|
||||||
progress:
|
progress:
|
||||||
title: topic progress
|
title: topic progress
|
||||||
|
|
|
@ -1036,6 +1036,46 @@ describe TopicsController do
|
||||||
Topic.any_instance.expects(:set_auto_close).with(nil, anything)
|
Topic.any_instance.expects(:set_auto_close).with(nil, anything)
|
||||||
xhr :put, :autoclose, topic_id: @topic.id, auto_close_time: nil, auto_close_based_on_last_post: false, timezone_offset: -240
|
xhr :put, :autoclose, topic_id: @topic.id, auto_close_time: nil, auto_close_based_on_last_post: false, timezone_offset: -240
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "will close a topic when the time expires" do
|
||||||
|
topic = Fabricate(:topic)
|
||||||
|
Timecop.freeze(20.hours.ago) do
|
||||||
|
create_post(topic: topic, raw: "This is the body of my cool post in the topic, but it's a bit old now")
|
||||||
|
end
|
||||||
|
topic.save
|
||||||
|
|
||||||
|
Jobs.expects(:enqueue_at).at_least_once
|
||||||
|
xhr :put, :autoclose, topic_id: topic.id, auto_close_time: 24, auto_close_based_on_last_post: true
|
||||||
|
|
||||||
|
topic.reload
|
||||||
|
expect(topic.closed).to eq(false)
|
||||||
|
expect(topic.posts.last.raw).to match(/cool post/)
|
||||||
|
|
||||||
|
Timecop.freeze(5.hours.from_now) do
|
||||||
|
Jobs::CloseTopic.new.execute({topic_id: topic.id, user_id: @admin.id})
|
||||||
|
end
|
||||||
|
|
||||||
|
topic.reload
|
||||||
|
expect(topic.closed).to eq(true)
|
||||||
|
expect(topic.posts.last.raw).to match(/automatically closed/)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "will immediately close if the last post is old enough" do
|
||||||
|
topic = Fabricate(:topic)
|
||||||
|
Timecop.freeze(20.hours.ago) do
|
||||||
|
create_post(topic: topic)
|
||||||
|
end
|
||||||
|
topic.save
|
||||||
|
Topic.reset_highest(topic.id)
|
||||||
|
topic.reload
|
||||||
|
|
||||||
|
xhr :put, :autoclose, topic_id: topic.id, auto_close_time: 10, auto_close_based_on_last_post: true
|
||||||
|
|
||||||
|
topic.reload
|
||||||
|
expect(topic.closed).to eq(true)
|
||||||
|
expect(topic.posts.last.raw).to match(/after the last reply/)
|
||||||
|
expect(topic.posts.last.raw).to match(/10 hours/)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,18 +3,21 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require_dependency 'post_destroyer'
|
require_dependency 'post_destroyer'
|
||||||
|
|
||||||
|
# TODO - test pinning, create_moderator_post
|
||||||
|
|
||||||
describe TopicStatusUpdate do
|
describe TopicStatusUpdate do
|
||||||
|
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
let(:admin) { Fabricate(:admin) }
|
let(:admin) { Fabricate(:admin) }
|
||||||
|
|
||||||
it "avoids notifying on automatically closed topics" do
|
it "avoids notifying on automatically closed topics" do
|
||||||
# TODO: TopicStatusUpdate should supress message bus updates from the users it "pretends to read"
|
# TODO: TopicStatusUpdate should suppress message bus updates from the users it "pretends to read"
|
||||||
post = PostCreator.create(user,
|
post = PostCreator.create(user,
|
||||||
raw: "this is a test post 123 this is a test post",
|
raw: "this is a test post 123 this is a test post",
|
||||||
title: "hello world title",
|
title: "hello world title",
|
||||||
)
|
)
|
||||||
# TODO needed so counts sync up, PostCreator really should not give back out-of-date Topic
|
# TODO needed so counts sync up, PostCreator really should not give back out-of-date Topic
|
||||||
|
post.topic.set_auto_close('10')
|
||||||
post.topic.reload
|
post.topic.reload
|
||||||
|
|
||||||
TopicStatusUpdate.new(post.topic, admin).update!("autoclosed", true)
|
TopicStatusUpdate.new(post.topic, admin).update!("autoclosed", true)
|
||||||
|
@ -27,6 +30,7 @@ describe TopicStatusUpdate do
|
||||||
|
|
||||||
it "adds an autoclosed message" do
|
it "adds an autoclosed message" do
|
||||||
topic = create_topic
|
topic = create_topic
|
||||||
|
topic.set_auto_close('10')
|
||||||
|
|
||||||
TopicStatusUpdate.new(topic, admin).update!("autoclosed", true)
|
TopicStatusUpdate.new(topic, admin).update!("autoclosed", true)
|
||||||
|
|
||||||
|
@ -39,13 +43,14 @@ describe TopicStatusUpdate do
|
||||||
it "adds an autoclosed message based on last post" do
|
it "adds an autoclosed message based on last post" do
|
||||||
topic = create_topic
|
topic = create_topic
|
||||||
topic.auto_close_based_on_last_post = true
|
topic.auto_close_based_on_last_post = true
|
||||||
|
topic.set_auto_close('10')
|
||||||
|
|
||||||
TopicStatusUpdate.new(topic, admin).update!("autoclosed", true)
|
TopicStatusUpdate.new(topic, admin).update!("autoclosed", true)
|
||||||
|
|
||||||
last_post = topic.posts.last
|
last_post = topic.posts.last
|
||||||
expect(last_post.post_type).to eq(Post.types[:small_action])
|
expect(last_post.post_type).to eq(Post.types[:small_action])
|
||||||
expect(last_post.action_code).to eq('autoclosed.enabled')
|
expect(last_post.action_code).to eq('autoclosed.enabled')
|
||||||
expect(last_post.raw).to eq(I18n.t("topic_statuses.autoclosed_enabled_lastpost_minutes", count: 0))
|
expect(last_post.raw).to eq(I18n.t("topic_statuses.autoclosed_enabled_lastpost_hours", count: 10))
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user