Merge remote-tracking branch 'origin/main' into feature/047
This commit is contained in:
@@ -1,6 +1,14 @@
|
||||
class IpAddress < ApplicationRecord
|
||||
validates :ip_address, presence: true, length: { maximum: 16 }
|
||||
validates :banned, inclusion: { in: [true, false] }
|
||||
|
||||
has_many :users
|
||||
has_many :user_ips, dependent: :destroy
|
||||
has_many :users, through: :user_ips
|
||||
|
||||
def banned? = banned_at?
|
||||
def banned = banned?
|
||||
|
||||
def banned= value
|
||||
bool = ActiveModel::Type::Boolean.new.cast(value)
|
||||
self.banned_at = bool ? banned_at || Time.current : nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
class Material < ApplicationRecord
|
||||
include MyDiscard
|
||||
|
||||
default_scope -> { kept }
|
||||
|
||||
belongs_to :parent, class_name: 'Material', optional: true
|
||||
has_many :children, class_name: 'Material', foreign_key: :parent_id, dependent: :nullify
|
||||
|
||||
belongs_to :tag, optional: true
|
||||
belongs_to :created_by_user, class_name: 'User', optional: true
|
||||
belongs_to :updated_by_user, class_name: 'User', optional: true
|
||||
|
||||
has_one_attached :file, dependent: :purge
|
||||
|
||||
validates :tag_id, presence: true, uniqueness: true
|
||||
|
||||
validate :file_must_be_attached
|
||||
validate :tag_must_be_material_category
|
||||
|
||||
def content_type
|
||||
return nil unless file&.attached?
|
||||
|
||||
file.blob.content_type
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def file_must_be_attached
|
||||
return if url.present? || file.attached?
|
||||
|
||||
errors.add(:url, 'URL かファイルのどちらかは必須です.')
|
||||
end
|
||||
|
||||
def tag_must_be_material_category
|
||||
return if tag.blank? || tag.character? || tag.material?
|
||||
|
||||
errors.add(:tag, '素材カテゴリのタグを指定してください.')
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,11 @@
|
||||
module MyDiscard
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included { include Discard::Model }
|
||||
included do
|
||||
include Discard::Model
|
||||
|
||||
default_scope -> { kept }
|
||||
end
|
||||
|
||||
class_methods do
|
||||
def find_undiscard_or_create_by! attrs, &block
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class NicoTagVersion < ApplicationRecord
|
||||
include VersionRecord
|
||||
|
||||
belongs_to :tag
|
||||
|
||||
validates :name, presence: true
|
||||
end
|
||||
@@ -1,7 +1,6 @@
|
||||
class Post < ApplicationRecord
|
||||
require 'mini_magick'
|
||||
|
||||
belongs_to :parent, class_name: 'Post', optional: true, foreign_key: 'parent_id'
|
||||
belongs_to :uploaded_user, class_name: 'User', optional: true
|
||||
|
||||
has_many :post_tags, dependent: :destroy, inverse_of: :post
|
||||
@@ -11,6 +10,7 @@ class Post < ApplicationRecord
|
||||
|
||||
has_many :user_post_views, dependent: :delete_all
|
||||
has_many :post_similarities, dependent: :delete_all
|
||||
has_many :post_versions
|
||||
|
||||
has_one_attached :thumbnail
|
||||
|
||||
@@ -30,6 +30,8 @@ class Post < ApplicationRecord
|
||||
super(options).merge(thumbnail: nil)
|
||||
end
|
||||
|
||||
def snapshot_tag_names = tags.joins(:tag_name).order('tag_names.name').pluck('tag_names.name')
|
||||
|
||||
def related limit: nil
|
||||
ids = post_similarities.order(cos: :desc)
|
||||
ids = ids.limit(limit) if limit
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
class PostVersion < ApplicationRecord
|
||||
include VersionRecord
|
||||
|
||||
belongs_to :post
|
||||
|
||||
validates :url, presence: true
|
||||
|
||||
validate :validate_original_created_range
|
||||
|
||||
private
|
||||
|
||||
def validate_original_created_range
|
||||
f = original_created_from
|
||||
b = original_created_before
|
||||
return if f.blank? || b.blank?
|
||||
|
||||
if f >= b
|
||||
errors.add :original_created_before, 'オリジナルの作成日時の順番がをかしぃです.'
|
||||
end
|
||||
end
|
||||
end
|
||||
+43
-24
@@ -1,3 +1,6 @@
|
||||
require 'set'
|
||||
|
||||
|
||||
class Tag < ApplicationRecord
|
||||
include MyDiscard
|
||||
|
||||
@@ -5,8 +8,6 @@ class Tag < ApplicationRecord
|
||||
;
|
||||
end
|
||||
|
||||
default_scope -> { kept }
|
||||
|
||||
has_many :post_tags, inverse_of: :tag
|
||||
has_many :active_post_tags, -> { kept }, class_name: 'PostTag', inverse_of: :tag
|
||||
has_many :post_tags_with_discarded, -> { with_discarded }, class_name: 'PostTag'
|
||||
@@ -31,6 +32,10 @@ class Tag < ApplicationRecord
|
||||
class_name: 'TagSimilarity', foreign_key: :target_tag_id, dependent: :delete_all
|
||||
|
||||
has_many :deerjikists, dependent: :delete_all
|
||||
has_many :materials
|
||||
|
||||
has_many :tag_versions
|
||||
has_many :nico_tag_versions
|
||||
|
||||
belongs_to :tag_name
|
||||
delegate :wiki_page, to: :tag_name
|
||||
@@ -72,27 +77,18 @@ class Tag < ApplicationRecord
|
||||
|
||||
def has_wiki = wiki_page.present?
|
||||
|
||||
def self.tagme
|
||||
@tagme ||= find_or_create_by_tag_name!('タグ希望', category: :meta)
|
||||
end
|
||||
def material_id = materials.first&.id
|
||||
|
||||
def self.bot
|
||||
@bot ||= find_or_create_by_tag_name!('bot操作', category: :meta)
|
||||
end
|
||||
def self.tagme = find_or_create_by_tag_name!('タグ希望', category: :meta)
|
||||
def self.bot = find_or_create_by_tag_name!('bot操作', category: :meta)
|
||||
def self.no_deerjikist = find_or_create_by_tag_name!('ニジラー情報不詳', category: :meta)
|
||||
def self.video = find_or_create_by_tag_name!('動画', category: :meta)
|
||||
def self.niconico = find_or_create_by_tag_name!('ニコニコ', category: :meta)
|
||||
def self.youtube = find_or_create_by_tag_name!('YouTube', category: :meta)
|
||||
|
||||
def self.no_deerjikist
|
||||
@no_deerjikist ||= find_or_create_by_tag_name!('ニジラー情報不詳', category: :meta)
|
||||
end
|
||||
|
||||
def self.video
|
||||
@video ||= find_or_create_by_tag_name!('動画', category: :meta)
|
||||
end
|
||||
|
||||
def self.niconico
|
||||
@niconico ||= find_or_create_by_tag_name!('ニコニコ', category: :meta)
|
||||
end
|
||||
|
||||
def self.normalise_tags tag_names, with_tagme: true, deny_nico: true
|
||||
def self.normalise_tags tag_names, with_tagme: true,
|
||||
with_no_deerjikist: true,
|
||||
deny_nico: true
|
||||
if deny_nico && tag_names.any? { |n| n.downcase.start_with?('nico:') }
|
||||
raise NicoTagNormalisationError
|
||||
end
|
||||
@@ -106,7 +102,7 @@ class Tag < ApplicationRecord
|
||||
end
|
||||
|
||||
tags << Tag.tagme if with_tagme && tags.size < 10 && tags.none?(Tag.tagme)
|
||||
tags << Tag.no_deerjikist if tags.all? { |t| !(t.deerjikist?) }
|
||||
tags << Tag.no_deerjikist if with_no_deerjikist && tags.all? { |t| !(t.deerjikist?) }
|
||||
tags.uniq(&:id)
|
||||
end
|
||||
|
||||
@@ -144,18 +140,25 @@ class Tag < ApplicationRecord
|
||||
retry
|
||||
end
|
||||
|
||||
def self.merge_tags! target_tag, source_tags
|
||||
def self.merge_tags! target_tag, source_tags, created_by_user: nil
|
||||
target_tag => Tag
|
||||
|
||||
affected_post_ids = Set.new
|
||||
|
||||
Tag.transaction do
|
||||
TagVersioning.ensure_snapshot!(target_tag, created_by_user:)
|
||||
|
||||
Array(source_tags).compact.uniq.each do |source_tag|
|
||||
source_tag => Tag
|
||||
|
||||
next if source_tag == target_tag
|
||||
|
||||
TagVersioning.ensure_snapshot!(source_tag, created_by_user:)
|
||||
|
||||
source_tag.post_tags.kept.find_each do |source_pt|
|
||||
post_id = source_pt.post_id
|
||||
source_pt.discard_by!(nil)
|
||||
affected_post_ids << post_id
|
||||
source_pt.discard_by!(created_by_user)
|
||||
unless PostTag.kept.exists?(post_id:, tag: target_tag)
|
||||
PostTag.create!(post_id:, tag: target_tag)
|
||||
end
|
||||
@@ -167,6 +170,7 @@ class Tag < ApplicationRecord
|
||||
raise ActiveRecord::RecordInvalid.new(source_tag_name)
|
||||
end
|
||||
|
||||
TagVersioning.record!(source_tag, event_type: :discard, created_by_user:)
|
||||
source_tag.discard!
|
||||
|
||||
if source_tag.nico?
|
||||
@@ -175,6 +179,13 @@ class Tag < ApplicationRecord
|
||||
source_tag_name.update_columns(canonical_id: target_tag.tag_name_id,
|
||||
updated_at: Time.current)
|
||||
end
|
||||
|
||||
TagVersioning.record!(target_tag, event_type: :update, created_by_user:)
|
||||
end
|
||||
|
||||
Post.where(id: affected_post_ids.to_a).find_each do |post|
|
||||
PostVersionRecorder.ensure_snapshot!(post, created_by_user:)
|
||||
PostVersionRecorder.record!(post:, event_type: :update, created_by_user:)
|
||||
end
|
||||
|
||||
# 投稿件数を再集計
|
||||
@@ -184,6 +195,14 @@ class Tag < ApplicationRecord
|
||||
target_tag.reload
|
||||
end
|
||||
|
||||
def snapshot_aliases = tag_name.aliases.kept.order(:name).pluck(:name)
|
||||
|
||||
def snapshot_parent_tag_ids = parents.order(:id).pluck(:id)
|
||||
|
||||
def snapshot_linked_tag_names
|
||||
linked_tags.joins(:tag_name).order('tag_names.name').pluck('tag_names.name')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def nico_tag_name_must_start_with_nico
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
class TagName < ApplicationRecord
|
||||
include MyDiscard
|
||||
|
||||
default_scope -> { kept }
|
||||
|
||||
has_one :tag
|
||||
has_one :wiki_page
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
class TagVersion < ApplicationRecord
|
||||
include VersionRecord
|
||||
|
||||
belongs_to :tag
|
||||
|
||||
enum :category, { deerjikist: 'deerjikist',
|
||||
meme: 'meme',
|
||||
character: 'character',
|
||||
general: 'general',
|
||||
material: 'material',
|
||||
meta: 'meta' }, validate: true
|
||||
|
||||
validates :name, presence: true
|
||||
validates :category, presence: true
|
||||
end
|
||||
@@ -4,7 +4,6 @@ class User < ApplicationRecord
|
||||
validates :name, length: { maximum: 255 }
|
||||
validates :inheritance_code, presence: true, length: { maximum: 64 }
|
||||
validates :role, presence: true, inclusion: { in: roles.keys }
|
||||
validates :banned, inclusion: { in: [true, false] }
|
||||
|
||||
has_many :created_posts,
|
||||
class_name: 'Post', foreign_key: :uploaded_user_id, dependent: :nullify
|
||||
@@ -18,6 +17,14 @@ class User < ApplicationRecord
|
||||
has_many :updated_wiki_pages,
|
||||
class_name: 'WikiPage', foreign_key: :updated_user_id, dependent: :nullify
|
||||
|
||||
def banned? = banned_at?
|
||||
def banned = banned?
|
||||
|
||||
def banned= value
|
||||
bool = ActiveModel::Type::Boolean.new.cast(value)
|
||||
self.banned_at = bool ? (banned_at || Time.current) : nil
|
||||
end
|
||||
|
||||
def viewed?(post) = user_post_views.exists?(post_id: post.id)
|
||||
def gte_member? = member? || admin?
|
||||
end
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
module VersionRecord
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def readonly? = persisted?
|
||||
|
||||
included do
|
||||
belongs_to :created_by_user, class_name: 'User', optional: true
|
||||
|
||||
enum :event_type, { create: 'create',
|
||||
update: 'update',
|
||||
discard: 'discard',
|
||||
restore: 'restore' }, prefix: true, validate: true
|
||||
|
||||
validates :version_no, presence: true, numericality: { only_integer: true, greater_than: 0 }
|
||||
validates :event_type, presence: true
|
||||
|
||||
scope :chronological, -> { order(:version_no, :id) }
|
||||
end
|
||||
end
|
||||
@@ -4,8 +4,6 @@ require 'set'
|
||||
class WikiPage < ApplicationRecord
|
||||
include MyDiscard
|
||||
|
||||
default_scope -> { kept }
|
||||
|
||||
has_many :wiki_revisions, dependent: :destroy
|
||||
belongs_to :created_user, class_name: 'User'
|
||||
belongs_to :updated_user, class_name: 'User'
|
||||
@@ -17,8 +15,11 @@ class WikiPage < ApplicationRecord
|
||||
|
||||
has_many :assets, class_name: 'WikiAsset', dependent: :destroy
|
||||
|
||||
has_many :wiki_versions
|
||||
|
||||
belongs_to :tag_name
|
||||
validates :tag_name, presence: true
|
||||
validates :body, presence: true
|
||||
|
||||
def title = tag_name.name
|
||||
|
||||
@@ -28,11 +29,6 @@ class WikiPage < ApplicationRecord
|
||||
|
||||
def current_revision = wiki_revisions.order(id: :desc).first
|
||||
|
||||
def body
|
||||
rev = current_revision
|
||||
rev.body if rev&.content?
|
||||
end
|
||||
|
||||
def resolve_redirect limit: 10
|
||||
page = self
|
||||
visited = Set.new
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
class WikiVersion < ApplicationRecord
|
||||
include VersionRecord
|
||||
|
||||
belongs_to :wiki_page
|
||||
|
||||
validates :title, presence: true
|
||||
validates :body, presence: true
|
||||
end
|
||||
Reference in New Issue
Block a user