mirror of
https://github.com/discourse/discourse.git
synced 2024-11-28 14:15:40 +08:00
8ce836c039
When lazy load categories is enabled, categories should be loaded with user activity items and drafts because the categories may not be preloaded on the client side.
272 lines
6.8 KiB
Ruby
272 lines
6.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class PostRevisionSerializer < ApplicationSerializer
|
|
attributes :created_at,
|
|
:post_id,
|
|
# which revision is hidden
|
|
:previous_hidden,
|
|
:current_hidden,
|
|
# dynamic & based on the current scope
|
|
:first_revision,
|
|
:previous_revision,
|
|
:current_revision,
|
|
:next_revision,
|
|
:last_revision,
|
|
# used for display
|
|
:current_version,
|
|
:version_count,
|
|
# from the user
|
|
:username,
|
|
:display_username,
|
|
:avatar_template,
|
|
# all the changes
|
|
:edit_reason,
|
|
:body_changes,
|
|
:title_changes,
|
|
:user_changes,
|
|
:tags_changes,
|
|
:wiki,
|
|
:can_edit
|
|
|
|
has_many :categories, serializer: CategoryBadgeSerializer, embed: :objects
|
|
|
|
# Creates a field called field_name_changes with previous and
|
|
# current members if a field has changed in this revision
|
|
def self.add_compared_field(field)
|
|
changes_name = "#{field}_changes".to_sym
|
|
|
|
self.attributes changes_name
|
|
define_method(changes_name) { { previous: previous[field], current: current[field] } }
|
|
|
|
define_method("include_#{changes_name}?") { previous[field] != current[field] }
|
|
end
|
|
|
|
add_compared_field :wiki
|
|
|
|
def previous_hidden
|
|
previous["hidden"]
|
|
end
|
|
|
|
def current_hidden
|
|
current["hidden"]
|
|
end
|
|
|
|
def first_revision
|
|
revisions.first["revision"]
|
|
end
|
|
|
|
def previous_revision
|
|
@previous_revision ||=
|
|
revisions
|
|
.select { |r| r["revision"] >= first_revision }
|
|
.select { |r| r["revision"] < current_revision }
|
|
.last
|
|
.try(:[], "revision")
|
|
end
|
|
|
|
def current_revision
|
|
object.number
|
|
end
|
|
|
|
def next_revision
|
|
@next_revision ||=
|
|
revisions
|
|
.select { |r| r["revision"] <= last_revision }
|
|
.select { |r| r["revision"] > current_revision }
|
|
.first
|
|
.try(:[], "revision")
|
|
end
|
|
|
|
def last_revision
|
|
@last_revision ||= revisions.select { |r| r["revision"] <= post.version }.last["revision"]
|
|
end
|
|
|
|
def current_version
|
|
@current_version ||= revisions.select { |r| r["revision"] <= current_revision }.count + 1
|
|
end
|
|
|
|
def version_count
|
|
revisions.count
|
|
end
|
|
|
|
def username
|
|
user.username_lower
|
|
end
|
|
|
|
def display_username
|
|
user.username
|
|
end
|
|
|
|
def avatar_template
|
|
user.avatar_template
|
|
end
|
|
|
|
def wiki
|
|
object.post.wiki
|
|
end
|
|
|
|
def can_edit
|
|
scope.can_edit?(object.post)
|
|
end
|
|
|
|
def edit_reason
|
|
# only show 'edit_reason' when revisions are consecutive
|
|
if scope.can_view_hidden_post_revisions? || current["revision"] == previous["revision"] + 1
|
|
current["edit_reason"]
|
|
end
|
|
end
|
|
|
|
def body_changes
|
|
cooked_diff = DiscourseDiff.new(previous["cooked"], current["cooked"])
|
|
raw_diff = DiscourseDiff.new(previous["raw"], current["raw"])
|
|
|
|
{
|
|
inline: cooked_diff.inline_html,
|
|
side_by_side: cooked_diff.side_by_side_html,
|
|
side_by_side_markdown: raw_diff.side_by_side_markdown,
|
|
}
|
|
end
|
|
|
|
def title_changes
|
|
prev = "<div>#{previous["title"] && CGI.escapeHTML(previous["title"])}</div>"
|
|
cur = "<div>#{current["title"] && CGI.escapeHTML(current["title"])}</div>"
|
|
|
|
# always show the title for post_number == 1
|
|
return if object.post.post_number > 1 && prev == cur
|
|
|
|
diff = DiscourseDiff.new(prev, cur)
|
|
|
|
{ inline: diff.inline_html, side_by_side: diff.side_by_side_html }
|
|
end
|
|
|
|
def user_changes
|
|
prev = previous["user_id"]
|
|
cur = current["user_id"]
|
|
return if prev == cur
|
|
|
|
# if stuff is messed up, default to system
|
|
previous = User.find_by(id: prev) || Discourse.system_user
|
|
current = User.find_by(id: cur) || Discourse.system_user
|
|
|
|
{
|
|
previous: {
|
|
username: previous.username_lower,
|
|
display_username: previous.username,
|
|
avatar_template: previous.avatar_template,
|
|
},
|
|
current: {
|
|
username: current.username_lower,
|
|
display_username: current.username,
|
|
avatar_template: current.avatar_template,
|
|
},
|
|
}
|
|
end
|
|
|
|
def tags_changes
|
|
changes = {
|
|
previous: filter_visible_tags(previous["tags"]),
|
|
current: filter_visible_tags(current["tags"]),
|
|
}
|
|
changes[:previous] == changes[:current] ? nil : changes
|
|
end
|
|
|
|
def include_tags_changes?
|
|
scope.can_see_tags?(topic) && previous["tags"] != current["tags"]
|
|
end
|
|
|
|
protected
|
|
|
|
def post
|
|
@post ||= object.post
|
|
end
|
|
|
|
def topic
|
|
@topic ||= object.post.topic
|
|
end
|
|
|
|
def revisions
|
|
@revisions ||=
|
|
all_revisions.select { |r| scope.can_view_hidden_post_revisions? || !r["hidden"] }
|
|
end
|
|
|
|
def all_revisions
|
|
return @all_revisions if @all_revisions
|
|
|
|
post_revisions =
|
|
PostRevision.where(post_id: object.post_id).order(number: :desc).limit(99).to_a.reverse
|
|
|
|
latest_modifications = {
|
|
"raw" => [post.raw],
|
|
"cooked" => [post.cooked],
|
|
"edit_reason" => [post.edit_reason],
|
|
"wiki" => [post.wiki],
|
|
"post_type" => [post.post_type],
|
|
"user_id" => [post.user_id],
|
|
}
|
|
|
|
# Retrieve any `tracked_topic_fields`
|
|
PostRevisor.tracked_topic_fields.each_key do |field|
|
|
next if field == :tags # Special handling below
|
|
latest_modifications[field.to_s] = [topic.public_send(field)] if topic.respond_to?(field)
|
|
end
|
|
|
|
latest_modifications["featured_link"] = [
|
|
post.topic.featured_link,
|
|
] if SiteSetting.topic_featured_link_enabled
|
|
latest_modifications["tags"] = [topic.tags.pluck(:name)] if scope.can_see_tags?(topic)
|
|
|
|
post_revisions << PostRevision.new(
|
|
number: post_revisions.last.number + 1,
|
|
hidden: post.hidden,
|
|
modifications: latest_modifications,
|
|
)
|
|
|
|
@all_revisions = []
|
|
|
|
# backtrack
|
|
post_revisions.each do |pr|
|
|
revision = HashWithIndifferentAccess.new
|
|
revision[:revision] = pr.number
|
|
revision[:hidden] = pr.hidden
|
|
|
|
pr.modifications.each_key { |field| revision[field] = pr.modifications[field][0] }
|
|
|
|
@all_revisions << revision
|
|
end
|
|
|
|
# waterfall
|
|
(@all_revisions.count - 1)
|
|
.downto(1)
|
|
.each do |r|
|
|
cur = @all_revisions[r]
|
|
prev = @all_revisions[r - 1]
|
|
|
|
cur.each_key { |field| prev[field] = prev.has_key?(field) ? prev[field] : cur[field] }
|
|
end
|
|
|
|
@all_revisions
|
|
end
|
|
|
|
def previous
|
|
@previous ||= revisions.select { |r| r["revision"] <= current_revision }.last
|
|
end
|
|
|
|
def current
|
|
@current ||= revisions.select { |r| r["revision"] > current_revision }.first
|
|
end
|
|
|
|
def user
|
|
# if stuff goes pear shape attribute to system
|
|
object.user || Discourse.system_user
|
|
end
|
|
|
|
def filter_visible_tags(tags)
|
|
if tags.is_a?(Array) && tags.size > 0
|
|
@hidden_tag_names ||= DiscourseTagging.hidden_tag_names(scope)
|
|
tags - @hidden_tag_names
|
|
else
|
|
tags
|
|
end
|
|
end
|
|
end
|