mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
Extensibility for tracking changes to a topic
This commit is contained in:
@@ -2,8 +2,43 @@ require "edit_rate_limiter"
|
||||
|
||||
class PostRevisor
|
||||
|
||||
# Helps us track changes to a topic.
|
||||
#
|
||||
# It's passed to `Topic.track_field` callbacks so they can record if they
|
||||
# changed a value or not. This is needed for things like custom fields.
|
||||
class TopicChanges
|
||||
attr_reader :topic, :user
|
||||
|
||||
def initialize(topic, user)
|
||||
@topic = topic
|
||||
@user = user
|
||||
@changed = {}
|
||||
@errored = false
|
||||
end
|
||||
|
||||
def errored?
|
||||
@errored
|
||||
end
|
||||
|
||||
def guardian
|
||||
@guardian ||= Guardian.new(@user)
|
||||
end
|
||||
|
||||
def record_change(field_name, previous_val, new_val)
|
||||
return if previous_val == new_val
|
||||
diff[field_name] = [previous_val, new_val]
|
||||
end
|
||||
|
||||
def check_result(res)
|
||||
@errored = true if !res
|
||||
end
|
||||
|
||||
def diff
|
||||
@diff ||= {}
|
||||
end
|
||||
end
|
||||
|
||||
POST_TRACKED_FIELDS = %w{raw cooked edit_reason user_id wiki post_type}
|
||||
TOPIC_TRACKED_FIELDS = %w{title category_id}
|
||||
|
||||
attr_reader :category_changed
|
||||
|
||||
@@ -12,6 +47,15 @@ class PostRevisor
|
||||
@topic = topic || post.topic
|
||||
end
|
||||
|
||||
def self.tracked_fields
|
||||
@@tracked_fields ||= {}
|
||||
@@tracked_fields
|
||||
end
|
||||
|
||||
def self.track_field(field, &block)
|
||||
tracked_fields[field] = block
|
||||
end
|
||||
|
||||
# AVAILABLE OPTIONS:
|
||||
# - revised_at: changes the date of the revision
|
||||
# - force_new_version: bypass ninja-edit window
|
||||
@@ -23,6 +67,8 @@ class PostRevisor
|
||||
@fields = fields.with_indifferent_access
|
||||
@opts = opts
|
||||
|
||||
@topic_changes = TopicChanges.new(@topic, editor)
|
||||
|
||||
# some normalization
|
||||
@fields[:raw] = cleanup_whitespaces(@fields[:raw]) if @fields.has_key?(:raw)
|
||||
@fields[:user_id] = @fields[:user_id].to_i if @fields.has_key?(:user_id)
|
||||
@@ -40,7 +86,6 @@ class PostRevisor
|
||||
|
||||
@version_changed = false
|
||||
@post_successfully_saved = true
|
||||
@topic_successfully_saved = true
|
||||
|
||||
@validate_post = true
|
||||
@validate_post = @opts[:validate_post] if @opts.has_key?(:validate_post)
|
||||
@@ -94,10 +139,7 @@ class PostRevisor
|
||||
end
|
||||
|
||||
def topic_changed?
|
||||
TOPIC_TRACKED_FIELDS.each do |field|
|
||||
return true if @fields.has_key?(field) && @fields[field] != @topic.send(field)
|
||||
end
|
||||
false
|
||||
PostRevisor.tracked_fields.keys.any? {|f| @fields.has_key?(f)}
|
||||
end
|
||||
|
||||
def revise_post
|
||||
@@ -174,10 +216,16 @@ class PostRevisor
|
||||
end
|
||||
|
||||
def update_topic
|
||||
@topic.title = @fields[:title] if @fields.has_key?(:title)
|
||||
Topic.transaction do
|
||||
@topic_successfully_saved = @topic.change_category_to_id(@fields[:category_id]) if @fields.has_key?(:category_id)
|
||||
@topic_successfully_saved &&= @topic.save(validate: @validate_topic)
|
||||
PostRevisor.tracked_fields.each do |f, cb|
|
||||
if !@topic_changes.errored? && @fields.has_key?(f)
|
||||
cb.call(@topic_changes, @fields[f])
|
||||
end
|
||||
end
|
||||
|
||||
unless @topic_changes.errored?
|
||||
@topic_changes.check_result(@topic.save(validate: @validate_topic))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -188,7 +236,7 @@ class PostRevisor
|
||||
end
|
||||
|
||||
def create_revision
|
||||
modifications = post_changes.merge(topic_changes)
|
||||
modifications = post_changes.merge(@topic_changes.diff)
|
||||
PostRevision.create!(
|
||||
user_id: @post.last_editor_id,
|
||||
post_id: @post.id,
|
||||
@@ -200,7 +248,7 @@ class PostRevisor
|
||||
def update_revision
|
||||
return unless revision = PostRevision.find_by(post_id: @post.id, number: @post.version)
|
||||
revision.user_id = @post.last_editor_id
|
||||
modifications = post_changes.merge(topic_changes)
|
||||
modifications = post_changes.merge(@topic_changes.diff)
|
||||
modifications.keys.each do |field|
|
||||
if revision.modifications.has_key?(field)
|
||||
old_value = revision.modifications[field][0]
|
||||
@@ -210,17 +258,13 @@ class PostRevisor
|
||||
revision.modifications[field] = modifications[field]
|
||||
end
|
||||
end
|
||||
revision.save
|
||||
revision.save if modifications.length
|
||||
end
|
||||
|
||||
def post_changes
|
||||
@post.previous_changes.slice(*POST_TRACKED_FIELDS)
|
||||
end
|
||||
|
||||
def topic_changes
|
||||
@topic.previous_changes.slice(*TOPIC_TRACKED_FIELDS)
|
||||
end
|
||||
|
||||
def perform_edit
|
||||
return if bypass_rate_limiter?
|
||||
EditRateLimiter.new(@editor).performed!
|
||||
@@ -311,7 +355,18 @@ class PostRevisor
|
||||
end
|
||||
|
||||
def successfully_saved_post_and_topic
|
||||
@post_successfully_saved && @topic_successfully_saved
|
||||
@post_successfully_saved && !@topic_changes.errored?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Fields we want to record revisions for by default
|
||||
PostRevisor.track_field(:title) do |tc, title|
|
||||
tc.record_change('title', tc.topic.title, title)
|
||||
tc.topic.title = title
|
||||
end
|
||||
|
||||
PostRevisor.track_field(:category_id) do |tc, category_id|
|
||||
tc.record_change('category_id', tc.topic.category_id, category_id)
|
||||
tc.check_result(tc.topic.change_category_to_id(category_id))
|
||||
end
|
||||
|
Reference in New Issue
Block a user