posts: add is: and has: metatags.
Add the following metatags: * is:parent * is:child * is:safe * is:questionable * is:explicit * is:sfw (same as -rating:q,e) * is:nsfw (same as rating:q,e) * is:active * is:deleted * is:pending * is:flagged * is:appealed * is:banned * is:modqueue * is:unmoderated * is:jpg * is:png * is:gif * is:mp4 * is:webm * is:swf * is:zip * has:parent * has:children * has:source * has:appeals * has:flags * has:replacements * has:comments * has:commentary * has:notes * has:pools All of these searches were already possible with other metatags, but these might be more convenient.
This commit is contained in:
@@ -13,12 +13,14 @@ class AutocompleteService
|
||||
POST_STATUSES = %w[active deleted pending flagged appealed banned modqueue unmoderated]
|
||||
|
||||
STATIC_METATAGS = {
|
||||
is: %w[parent child sfw nsfw] + POST_STATUSES + MediaAsset::FILE_TYPES + Post::RATINGS.values.map(&:downcase),
|
||||
has: %w[parent children source appeals flags replacements comments commentary notes pools],
|
||||
status: %w[any] + POST_STATUSES,
|
||||
child: %w[any none] + POST_STATUSES,
|
||||
parent: %w[any none] + POST_STATUSES,
|
||||
rating: Post::RATINGS.values.map(&:downcase),
|
||||
embedded: %w[true false],
|
||||
filetype: %w[jpg png gif swf zip webm mp4],
|
||||
filetype: MediaAsset::FILE_TYPES,
|
||||
commentary: %w[true false translated untranslated],
|
||||
disapproved: PostDisapproval::REASONS,
|
||||
order: PostQueryBuilder::ORDER_METATAGS
|
||||
|
||||
@@ -38,7 +38,7 @@ class PostQueryBuilder
|
||||
ordpool note comment commentary id rating source status filetype
|
||||
disapproved parent child search embedded md5 width height mpixels ratio
|
||||
score upvotes downvotes favcount filesize date age order limit tagcount pixiv_id pixiv
|
||||
unaliased exif duration random
|
||||
unaliased exif duration random is has
|
||||
] + COUNT_METATAGS + COUNT_METATAG_SYNONYMS + CATEGORY_COUNT_METATAGS
|
||||
|
||||
ORDER_METATAGS = %w[
|
||||
@@ -121,6 +121,10 @@ class PostQueryBuilder
|
||||
relation.attribute_matches(value, :tag_count)
|
||||
when "duration"
|
||||
relation.attribute_matches(value, "media_assets.duration", :float).joins(:media_asset)
|
||||
when "is"
|
||||
relation.is_matches(value, current_user)
|
||||
when "has"
|
||||
relation.has_matches(value)
|
||||
when "status"
|
||||
relation.status_matches(value, current_user)
|
||||
when "parent"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
class MediaAsset < ApplicationRecord
|
||||
class Error < StandardError; end
|
||||
|
||||
FILE_TYPES = %w[jpg png gif mp4 webm swf zip]
|
||||
FILE_KEY_LENGTH = 9
|
||||
VARIANTS = %i[preview 180x180 360x360 720x720 sample original]
|
||||
MAX_VIDEO_DURATION = Danbooru.config.max_video_duration.to_i
|
||||
@@ -41,7 +42,7 @@ class MediaAsset < ApplicationRecord
|
||||
}
|
||||
|
||||
validates :md5, uniqueness: { conditions: -> { where(status: [:processing, :active]) } }
|
||||
validates :file_ext, inclusion: { in: %w[jpg png gif mp4 webm swf zip], message: "File is not an image or video" }
|
||||
validates :file_ext, inclusion: { in: FILE_TYPES, message: "File is not an image or video" }
|
||||
validates :file_size, numericality: { less_than_or_equal_to: Danbooru.config.max_file_size, message: ->(asset, _) { "too large (size: #{asset.file_size.to_formatted_s(:human_size)}; max size: #{Danbooru.config.max_file_size.to_formatted_s(:human_size)})" } }
|
||||
validates :file_key, length: { is: FILE_KEY_LENGTH }, uniqueness: true, if: :file_key_changed?
|
||||
validates :duration, numericality: { less_than_or_equal_to: MAX_VIDEO_DURATION, message: "must be less than #{MAX_VIDEO_DURATION} seconds", allow_nil: true }, on: :create # XXX should allow admins to bypass
|
||||
|
||||
@@ -1058,6 +1058,54 @@ class Post < ApplicationRecord
|
||||
none
|
||||
end
|
||||
|
||||
def is_matches(value, current_user = User.anonymous)
|
||||
case value.downcase
|
||||
when "parent"
|
||||
where(has_children: true)
|
||||
when "child"
|
||||
where.not(parent: nil)
|
||||
when "sfw"
|
||||
where.not(rating: ["q", "e"])
|
||||
when "nsfw"
|
||||
where(rating: ["q", "e"])
|
||||
when *AutocompleteService::POST_STATUSES
|
||||
status_matches(value, current_user)
|
||||
when *MediaAsset::FILE_TYPES
|
||||
attribute_matches(value, :file_ext, :enum)
|
||||
when *Post::RATINGS.values.map(&:downcase)
|
||||
rating_matches(value)
|
||||
else
|
||||
none
|
||||
end
|
||||
end
|
||||
|
||||
def has_matches(value)
|
||||
case value.downcase
|
||||
when "parent"
|
||||
where.not(parent: nil)
|
||||
when "child", "children"
|
||||
where(has_children: true)
|
||||
when "source"
|
||||
where.not(source: "")
|
||||
when "appeals"
|
||||
where(id: PostAppeal.select(:post_id))
|
||||
when "flags"
|
||||
where(id: PostFlag.by_users.select(:post_id))
|
||||
when "replacements"
|
||||
where(id: PostReplacement.select(:post_id))
|
||||
when "comments"
|
||||
where(id: Comment.undeleted.select(:post_id))
|
||||
when "commentary"
|
||||
where(id: ArtistCommentary.undeleted.select(:post_id))
|
||||
when "notes"
|
||||
where(id: Note.active.select(:post_id))
|
||||
when "pools"
|
||||
where(id: Pool.undeleted.select("unnest(post_ids)"))
|
||||
else
|
||||
none
|
||||
end
|
||||
end
|
||||
|
||||
def status_matches(status, current_user = User.anonymous)
|
||||
case status.downcase
|
||||
when "pending"
|
||||
|
||||
@@ -736,6 +736,98 @@ class PostQueryBuilderTest < ActiveSupport::TestCase
|
||||
assert_tag_match([], "duration:>1")
|
||||
end
|
||||
|
||||
should "return posts for the is:<status> metatag" do
|
||||
pending = create(:post, is_pending: true)
|
||||
flagged = create(:post, is_flagged: true)
|
||||
deleted = create(:post, is_deleted: true)
|
||||
banned = create(:post, is_banned: true)
|
||||
appealed = create(:post, is_deleted: true)
|
||||
appeal = create(:post_appeal, post: appealed)
|
||||
|
||||
assert_tag_match([appealed, flagged, pending], "is:modqueue")
|
||||
assert_tag_match([pending], "is:pending")
|
||||
assert_tag_match([flagged], "is:flagged")
|
||||
assert_tag_match([appealed], "is:appealed")
|
||||
assert_tag_match([appealed, deleted], "is:deleted")
|
||||
assert_tag_match([banned], "is:banned")
|
||||
assert_tag_match([banned], "is:active")
|
||||
assert_tag_match([banned], "is:active is:banned")
|
||||
end
|
||||
|
||||
should "return posts for the is:<rating> metatag" do
|
||||
s = create(:post, rating: "s")
|
||||
q = create(:post, rating: "q")
|
||||
e = create(:post, rating: "e")
|
||||
all = [e, q, s]
|
||||
|
||||
assert_tag_match([s], "is:safe")
|
||||
assert_tag_match([q], "is:questionable")
|
||||
assert_tag_match([e], "is:explicit")
|
||||
assert_tag_match([s], "is:sfw")
|
||||
assert_tag_match([e, q], "is:nsfw")
|
||||
end
|
||||
|
||||
should "return posts for the is:<filetype> metatag" do
|
||||
jpg = create(:post, file_ext: "jpg")
|
||||
png = create(:post, file_ext: "png")
|
||||
gif = create(:post, file_ext: "gif")
|
||||
mp4 = create(:post, file_ext: "mp4")
|
||||
webm = create(:post, file_ext: "webm")
|
||||
swf = create(:post, file_ext: "swf")
|
||||
zip = create(:post, file_ext: "zip")
|
||||
|
||||
assert_tag_match([jpg], "is:jpg")
|
||||
assert_tag_match([png], "is:png")
|
||||
assert_tag_match([gif], "is:gif")
|
||||
assert_tag_match([mp4], "is:mp4")
|
||||
assert_tag_match([webm], "is:webm")
|
||||
assert_tag_match([swf], "is:swf")
|
||||
assert_tag_match([zip], "is:zip")
|
||||
end
|
||||
|
||||
should "return posts for the is:<parent> metatag" do
|
||||
parent = create(:post)
|
||||
child = create(:post, parent: parent)
|
||||
|
||||
assert_tag_match([parent], "is:parent")
|
||||
assert_tag_match([child], "is:child")
|
||||
assert_tag_match([], "is:blah")
|
||||
end
|
||||
|
||||
should "return posts for the has:<value> metatag" do
|
||||
parent = create(:post)
|
||||
child = create(:post, parent: parent)
|
||||
|
||||
appeal = create(:post_appeal)
|
||||
flag = create(:post_flag)
|
||||
replacement = create(:post_replacement)
|
||||
comment = create(:comment)
|
||||
commentary = create(:artist_commentary)
|
||||
note = create(:note)
|
||||
|
||||
pooled = create(:post)
|
||||
pool = create(:pool, post_ids: [pooled.id])
|
||||
|
||||
assert_tag_match([child], "has:parent")
|
||||
assert_tag_match([parent], "has:child")
|
||||
assert_tag_match([parent], "has:children")
|
||||
assert_tag_match([appeal.post], "has:appeals")
|
||||
assert_tag_match([flag.post], "has:flags")
|
||||
assert_tag_match([replacement.post], "has:replacements")
|
||||
assert_tag_match([comment.post], "has:comments")
|
||||
assert_tag_match([commentary.post], "has:commentary")
|
||||
assert_tag_match([note.post], "has:notes")
|
||||
assert_tag_match([pooled], "has:pools")
|
||||
assert_tag_match([], "has:blah")
|
||||
end
|
||||
|
||||
should "return posts for the has:<source> metatag" do
|
||||
post1 = create(:post, source: "blah")
|
||||
post2 = create(:post, source: nil)
|
||||
|
||||
assert_tag_match([post1], "has:source")
|
||||
end
|
||||
|
||||
should "return posts for the status:<type> metatag" do
|
||||
pending = create(:post, is_pending: true)
|
||||
flagged = create(:post, is_flagged: true)
|
||||
|
||||
Reference in New Issue
Block a user