eBooru/app/models/post_version.rb
edshot99 4928747b83 add transcript support
it could be possible to load closed captions and subtitles from the transcript div via javascript, but I hate javascript so perhaps some other time.
2024-11-10 13:58:15 -06:00

291 lines
7.5 KiB
Ruby

# frozen_string_literal: true
class PostVersion < ApplicationRecord
class UndoError < StandardError; end
belongs_to :post
belongs_to_updater
user_status_counter :post_update_count, foreign_key: :updater_id
before_validation :fill_version, on: :create
before_validation :fill_changes, on: :create
module SearchMethods
def for_user(user_id)
if user_id
where("updater_id = ?", user_id)
else
none
end
end
def search(params)
ElasticPostVersionQueryBuilder.new(params).search
end
end
extend SearchMethods
include DocumentStore::Model
include PostVersionIndex
def self.queue(post)
self.create({
post_id: post.id,
rating: post.rating,
parent_id: post.parent_id,
source: post.source,
updater_id: CurrentUser.id,
updater_ip_addr: CurrentUser.ip_addr,
tags: post.tag_string,
locked_tags: post.locked_tags,
title: post.title,
description: post.description,
transcript: post.transcript,
reason: post.edit_reason
})
end
def self.calculate_version(post_id)
1 + where("post_id = ?", post_id).maximum(:version).to_i
end
def fill_version
self.version = PostVersion.calculate_version (self.post_id)
end
def fill_changes(prev = nil)
prev ||= previous
if prev
self.added_tags = tag_array - prev.tag_array
self.removed_tags = prev.tag_array - tag_array
self.added_locked_tags = locked_tag_array - prev.locked_tag_array
self.removed_locked_tags = prev.locked_tag_array - locked_tag_array
else
self.added_tags = tag_array
self.removed_tags = []
self.added_locked_tags = locked_tag_array
self.removed_locked_tags = []
end
self.rating_changed = prev.nil? || rating != prev.try(:rating)
self.parent_changed = prev.nil? || parent_id != prev.try(:parent_id)
self.source_changed = prev.nil? || source != prev.try(:source)
self.title_changed = prev.nil? || title != prev.try(:title)
self.description_changed = prev.nil? || description != prev.try(:description)
self.transcript_changed = prev.nil? || transcript != prev.try(:transcript)
end
def tag_array
@tag_array ||= tags.split
end
def locked_tag_array
(locked_tags || "").split
end
def presenter
PostVersionPresenter.new(self)
end
def previous
# HACK: If this if the first version we can avoid a lookup because we know there are no previous versions.
if version <= 1
return nil
end
return @previous if defined?(@previous)
# HACK: if all the post versions for this post have already been preloaded,
# we can use that to avoid a SQL query.
if association(:post).loaded? && post && post.association(:versions).loaded?
@previous = post.versions.sort_by(&:version).reverse.find { |v| v.version < version }
else
@previous = PostVersion.where("post_id = ? and version < ?", post_id, version).order("version desc").first
end
end
def visible?
post && post.visible?
end
def diff_sources(version = nil)
new_sources = source&.split("\n") || []
old_sources = version&.source&.split("\n") || []
added_sources = new_sources - old_sources
removed_sources = old_sources - new_sources
return {
:added_sources => added_sources,
:unchanged_sources => new_sources & old_sources,
:removed_sources => removed_sources
}
end
def diff(version = nil)
latest_tags = post.tag_array + parent_rating_tags(post)
new_tags = tag_array + parent_rating_tags(self)
old_tags = version.present? ? version.tag_array + parent_rating_tags(version) : []
added_tags = new_tags - old_tags
removed_tags = old_tags - new_tags
new_locked = locked_tag_array
old_locked = version.present? ? version.locked_tag_array : []
added_locked = new_locked - old_locked
removed_locked = old_locked - new_locked
return {
added_tags: added_tags,
removed_tags: removed_tags,
obsolete_added_tags: added_tags - latest_tags,
obsolete_removed_tags: removed_tags & latest_tags,
unchanged_tags: new_tags & old_tags,
added_locked_tags: added_locked,
removed_locked_tags: removed_locked,
unchanged_locked_tags: new_locked & old_locked
}
end
def parent_rating_tags(post)
result = ["rating:#{post.rating}"]
result << "parent:#{post.parent_id}" unless post.parent_id.nil?
result
end
def changes
return @changes if defined?(@changes)
delta = {
added_tags: added_tags,
removed_tags: removed_tags,
obsolete_removed_tags: [],
obsolete_added_tags: [],
unchanged_tags: [],
}
latest_tags = post.tag_array
latest_tags << "rating:#{post.rating}" if post.rating.present?
latest_tags << "parent:#{post.parent_id}" if post.parent_id.present?
latest_tags << "source:#{post.source}" if post.source.present?
if parent_changed
if parent_id.present?
delta[:added_tags] << "parent:#{parent_id}"
end
if previous
delta[:removed_tags] << "parent:#{previous.parent_id}"
end
end
if rating_changed
delta[:added_tags] << "rating:#{rating}"
if previous
delta[:removed_tags] << "rating:#{previous.rating}"
end
end
if source_changed
if source.present?
delta[:added_tags] << "source:#{source}"
end
if previous
delta[:removed_tags] << "source:#{previous.source}"
end
end
delta[:obsolete_added_tags] = delta[:added_tags] - latest_tags
delta[:obsolete_removed_tags] = delta[:removed_tags] & latest_tags
if previous
delta[:unchanged_tags] = tag_array & previous.tag_array
else
delta[:unchanged_tags] = []
end
@changes = delta
end
def undo
raise UndoError, "Version 1 is not undoable" unless undoable?
if title_changed
post.title = previous.title
end
if description_changed
post.description = previous.description
end
if transcript_changed
post.transcript = previous.transcript
end
if rating_changed && !post.is_rating_locked?
post.rating = previous.rating
end
if parent_changed
post.parent_id = previous.parent_id
end
if source_changed
post.source = previous.source
end
added = changes[:added_tags] - changes[:obsolete_added_tags]
removed = changes[:removed_tags] - changes[:obsolete_removed_tags]
added.each do |tag|
if tag =~ /^source:/
elsif tag =~ /^parent:/
else
escaped_tag = Regexp.escape(tag)
post.tag_string = post.tag_string.sub(/(?:\A| )#{escaped_tag}(?:\Z| )/, " ").strip
end
end
removed.each do |tag|
if tag =~ /^source:(.+)$/
elsif tag =~ /^parent:/
else
post.tag_string = "#{post.tag_string} #{tag}".strip
end
end
post.edit_reason = "Undo of version #{version}"
end
def undo!
undo
post.save!
end
def undoable?
version > 1
end
concerning :ApiMethods do
def method_attributes
super + %i[obsolete_added_tags obsolete_removed_tags unchanged_tags updater_name]
end
def obsolete_added_tags
changes[:obsolete_added_tags].join(" ")
end
def obsolete_removed_tags
changes[:obsolete_removed_tags].join(" ")
end
def unchanged_tags
changes[:unchanged_tags].join(" ")
end
end
end