2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2025-09-06 10:50:21 +08:00

FIX: TopicTimestampChanger should not allow timestamps in the future.

This commit is contained in:
Guo Xiang Tan 2017-05-22 16:03:49 +08:00
parent 4382a0bb07
commit 238a156300
4 changed files with 46 additions and 33 deletions

View file

@ -1,6 +1,7 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality'; import ModalFunctionality from 'discourse/mixins/modal-functionality';
import computed from 'ember-addons/ember-computed-decorators'; import computed from 'ember-addons/ember-computed-decorators';
import DiscourseURL from 'discourse/lib/url'; import DiscourseURL from 'discourse/lib/url';
import Topic from 'discourse/models/topic';
// Modal related to changing the timestamp of posts // Modal related to changing the timestamp of posts
export default Ember.Controller.extend(ModalFunctionality, { export default Ember.Controller.extend(ModalFunctionality, {
@ -43,7 +44,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
const self = this, const self = this,
topic = this.get('topicController.model'); topic = this.get('topicController.model');
Discourse.Topic.changeTimestamp( Topic.changeTimestamp(
topic.get('id'), topic.get('id'),
this.get('createdAt').unix() this.get('createdAt').unix()
).then(function() { ).then(function() {

View file

@ -560,11 +560,11 @@ class TopicsController < ApplicationController
begin begin
TopicTimestampChanger.new( TopicTimestampChanger.new(
topic_id: params[:topic_id].to_i, topic_id: params[:topic_id].to_i,
timestamp: params[:timestamp].to_i timestamp: params[:timestamp].to_f
).change! ).change!
render json: success_json render json: success_json
rescue ActiveRecord::RecordInvalid rescue ActiveRecord::RecordInvalid, TopicTimestampChanger::InvalidTimestampError
render json: failed_json, status: 422 render json: failed_json, status: 422
end end
end end

View file

@ -1,22 +1,27 @@
class TopicTimestampChanger class TopicTimestampChanger
def initialize(params) class InvalidTimestampError < StandardError; end
@topic = params[:topic] || Topic.with_deleted.find(params[:topic_id])
def initialize(timestamp:, topic: nil, topic_id: nil)
@topic = topic || Topic.with_deleted.find(topic_id)
@posts = @topic.posts @posts = @topic.posts
@timestamp = Time.at(params[:timestamp]) @current_timestamp = Time.zone.now
@timestamp = Time.zone.at(timestamp)
raise InvalidTimestampError if @timestamp.to_f > @current_timestamp.to_f
@time_difference = calculate_time_difference @time_difference = calculate_time_difference
end end
def change! def change!
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
last_posted_at = @timestamp last_posted_at = @timestamp
now = Time.zone.now
@posts.each do |post| @posts.each do |post|
if post.is_first_post? if post.is_first_post?
update_post(post, @timestamp) update_post(post, @timestamp)
else else
new_created_at = Time.at(post.created_at.to_f + @time_difference) new_created_at = Time.at(post.created_at.to_f + @time_difference)
new_created_at = now if new_created_at > now new_created_at = @current_timestamp if new_created_at > @current_timestamp
last_posted_at = new_created_at if new_created_at > last_posted_at last_posted_at = new_created_at if new_created_at > last_posted_at
update_post(post, new_created_at) update_post(post, new_created_at)
end end

View file

@ -4,53 +4,60 @@ describe TopicTimestampChanger do
describe "change!" do describe "change!" do
let(:old_timestamp) { Time.zone.now } let(:old_timestamp) { Time.zone.now }
let(:new_timestamp) { old_timestamp + 1.day } let(:new_timestamp) { old_timestamp + 1.day }
let!(:topic) { Fabricate(:topic, created_at: old_timestamp) } let(:topic) { Fabricate(:topic, created_at: old_timestamp) }
let!(:p1) { Fabricate(:post, topic: topic, created_at: old_timestamp) } let!(:p1) { Fabricate(:post, topic: topic, created_at: old_timestamp) }
let!(:p2) { Fabricate(:post, topic: topic, created_at: old_timestamp + 1.day) } let!(:p2) { Fabricate(:post, topic: topic, created_at: old_timestamp + 1.day) }
let(:params) { { topic_id: topic.id, timestamp: new_timestamp.to_f } }
context 'new timestamp is in the future' do context 'new timestamp is in the future' do
let(:new_timestamp) { old_timestamp + 2.day } let(:new_timestamp) { old_timestamp + 2.day }
it 'should raise the right error' do
expect { TopicTimestampChanger.new(topic: topic, timestamp: new_timestamp.to_f).change! }
.to raise_error(TopicTimestampChanger::InvalidTimestampError)
end
end
context 'new timestamp is in the past' do
let(:new_timestamp) { old_timestamp - 2.day }
it 'changes the timestamp of the topic and opening post' do it 'changes the timestamp of the topic and opening post' do
Timecop.freeze do Timecop.freeze do
TopicTimestampChanger.new(params).change! TopicTimestampChanger.new(topic: topic, timestamp: new_timestamp.to_f).change!
topic.reload topic.reload
[:created_at, :updated_at, :bumped_at].each do |column| [:created_at, :updated_at, :bumped_at].each do |column|
expect(topic.public_send(column)).to be_within_one_second_of(new_timestamp) expect(topic.public_send(column)).to be_within(1.second).of(new_timestamp)
end end
p1.reload p1.reload
[:created_at, :updated_at].each do |column| [:created_at, :updated_at].each do |column|
expect(p1.public_send(column)).to be_within_one_second_of(new_timestamp) expect(p1.public_send(column)).to be_within(1.second).of(new_timestamp)
end end
expect(topic.last_posted_at).to be_within_one_second_of(p2.reload.created_at) p2.reload
[:created_at, :updated_at].each do |column|
expect(p2.public_send(column)).to be_within(1.second).of(new_timestamp + 1.day)
end
expect(topic.last_posted_at).to be_within(1.second).of(p2.reload.created_at)
end end
end end
end
describe 'predated timestamp' do describe 'when posts have timestamps in the future' do
it 'updates the timestamp of posts in the topic with the time difference applied' do let(:new_timestamp) { Time.zone.now }
TopicTimestampChanger.new(params).change! let(:p3) { Fabricate(:post, topic: topic, created_at: new_timestamp + 3.day) }
p2.reload it 'should set the new timestamp as the default timestamp' do
[:created_at, :updated_at].each do |column| Timecop.freeze do
expect(p2.public_send(column)).to be_within_one_second_of(old_timestamp + 2.day) p3
end TopicTimestampChanger.new(topic: topic, timestamp: new_timestamp.to_f).change!
end
end
describe 'backdated timestamp' do p3.reload
let(:new_timestamp) { old_timestamp - 1.day }
it 'updates the timestamp of posts in the topic with the time difference applied' do [:created_at, :updated_at].each do |column|
TopicTimestampChanger.new(params).change! expect(p3.public_send(column)).to be_within(1.second).of(new_timestamp)
end
p2.reload end
[:created_at, :updated_at].each do |column|
expect(p2.public_send(column)).to be_within_one_second_of(old_timestamp)
end end
end end
end end
@ -59,7 +66,7 @@ describe TopicTimestampChanger do
$redis.set AdminDashboardData.stats_cache_key, "X" $redis.set AdminDashboardData.stats_cache_key, "X"
$redis.set About.stats_cache_key, "X" $redis.set About.stats_cache_key, "X"
TopicTimestampChanger.new(params).change! TopicTimestampChanger.new(topic: topic, timestamp: Time.zone.now.to_f).change!
expect($redis.get(AdminDashboardData.stats_cache_key)).to eq(nil) expect($redis.get(AdminDashboardData.stats_cache_key)).to eq(nil)
expect($redis.get(About.stats_cache_key)).to eq(nil) expect($redis.get(About.stats_cache_key)).to eq(nil)