Files
danbooru/app/models/note.rb
evazion 3114ef3daf searchable: standardize the <field>_matches operator for text fields.
Standardize it so that all fields of type `text` are searchable with
`search[<field>_matches]`.

Before, the `<field>_matches` param was handled manually and some fields
were left out or handled inconsistently. Now it applies to all columns
of type `text`.

This does a full-text search on the field, so for example, searching
`/artist_commentaries?search[translated_description_matches]=smiling`
will match translated commentaries containing either the word "smiling",
"smiles", "smiled", or "smile".

Note that this only applies to columns defined as type `text`, not to
columns defined as `character varying`. The difference is that `text` is
used for fields containing free-form natural language, such as comments,
notes, forum posts, wiki pages, pool descriptions, etc, while `character
varying` is used for short strings not containing free-form language,
such as tag names, wiki page titles, urls, status fields, etc.

API changes:

* Add the `search[original_title_matches]`, `search[original_description_matches]`,
  `search[translated_title_matches]`, `search[translated_description_matches]` params
  to /artist_commentaries and /artist_commentary_versions.
* Remove the `search[name_matches]` and `search[group_name_matches]` params from /artist_versions.
* Remove the `search[title_matches]` param from /wiki_page_versions.
* Change the `search[name_matches]` param on /pools, /favorite_groups, and /pool_versions
  to do a full-text search instead of a substring match.
2022-09-22 01:52:13 -05:00

134 lines
3.4 KiB
Ruby

# frozen_string_literal: true
class Note < ApplicationRecord
class RevertError < StandardError; end
attr_accessor :html_id
belongs_to :post
has_many :versions, -> {order("note_versions.id ASC")}, :class_name => "NoteVersion", :dependent => :destroy
validates :x, presence: true
validates :y, presence: true
validates :width, presence: true
validates :height, presence: true
validates :body, presence: true
validate :note_within_image
after_save :update_post
after_save :create_version
scope :active, -> { where(is_active: true) }
module SearchMethods
def search(params)
q = search_attributes(params, :id, :created_at, :updated_at, :is_active, :x, :y, :width, :height, :body, :version, :post)
q.apply_default_order(params)
end
end
extend SearchMethods
def note_within_image
return false unless post.present?
if x < 0 || y < 0 || (x > post.image_width) || (y > post.image_height) || width < 0 || height < 0 || (x + width > post.image_width) || (y + height > post.image_height)
errors.add(:note, "must be inside the image")
end
end
def rescale!(x_scale, y_scale)
self.x *= x_scale
self.y *= y_scale
self.width *= x_scale
self.height *= y_scale
save!
end
def update_post
if saved_changes?
if post.notes.active.exists?
post.update_columns(last_noted_at: updated_at)
else
post.update_columns(last_noted_at: nil)
end
end
end
def create_version(updater: CurrentUser.user)
return unless saved_change_to_versioned_attributes?
if merge_version?(updater.id)
merge_version
else
Note.where(:id => id).update_all("version = coalesce(version, 0) + 1")
reload
create_new_version(updater.id)
end
end
def saved_change_to_versioned_attributes?
new_record? || saved_change_to_x? || saved_change_to_y? || saved_change_to_width? || saved_change_to_height? || saved_change_to_is_active? || saved_change_to_body?
end
def create_new_version(updater_id)
versions.create(
:updater_id => updater_id,
:post_id => post_id,
:x => x,
:y => y,
:width => width,
:height => height,
:is_active => is_active,
:body => body,
:version => version
)
end
def merge_version
prev = versions.last
prev.update(x: x, y: y, width: width, height: height, is_active: is_active, body: body)
end
def merge_version?(updater_id)
prev = versions.last
prev && prev.updater_id == updater_id && prev.updated_at > 1.hour.ago
end
def revert_to(version)
if id != version.note_id
raise RevertError, "You cannot revert to a previous version of another note."
end
self.x = version.x
self.y = version.y
self.post_id = version.post_id
self.body = version.body
self.width = version.width
self.height = version.height
self.is_active = version.is_active
end
def revert_to!(version)
revert_to(version)
save!
end
def copy_to(new_post)
new_note = dup
new_note.post_id = new_post.id
new_note.version = 0
width_ratio = new_post.image_width.to_f / post.image_width
height_ratio = new_post.image_height.to_f / post.image_height
new_note.x = x * width_ratio
new_note.y = y * height_ratio
new_note.width = width * width_ratio
new_note.height = height * height_ratio
new_note.save
end
def self.available_includes
[:post]
end
end