Merge remote-tracking branch 'origin/main' into feature/047
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
class MaterialsController < ApplicationController
|
||||
def index
|
||||
page = (params[:page].presence || 1).to_i
|
||||
limit = (params[:limit].presence || 20).to_i
|
||||
|
||||
page = 1 if page < 1
|
||||
limit = 1 if limit < 1
|
||||
|
||||
offset = (page - 1) * limit
|
||||
|
||||
tag_id = params[:tag_id].presence
|
||||
parent_id = params[:parent_id].presence
|
||||
|
||||
q = Material.includes(:tag, :created_by_user).with_attached_file
|
||||
q = q.where(tag_id:) if tag_id
|
||||
q = q.where(parent_id:) if parent_id
|
||||
|
||||
count = q.count
|
||||
materials = q.order(created_at: :desc, id: :desc).limit(limit).offset(offset)
|
||||
|
||||
render json: { materials: MaterialRepr.many(materials, host: request.base_url), count: count }
|
||||
end
|
||||
|
||||
def show
|
||||
material =
|
||||
Material
|
||||
.includes(:tag)
|
||||
.with_attached_file
|
||||
.find_by(id: params[:id])
|
||||
return head :not_found unless material
|
||||
|
||||
wiki_page_body = material.tag.tag_name.wiki_page&.current_revision&.body
|
||||
|
||||
render json: MaterialRepr.base(material, host: request.base_url).merge(wiki_page_body:)
|
||||
end
|
||||
|
||||
def create
|
||||
return head :unauthorized unless current_user
|
||||
|
||||
tag_name_raw = params[:tag].to_s.strip
|
||||
file = params[:file]
|
||||
url = params[:url].to_s.strip.presence
|
||||
return head :bad_request if tag_name_raw.blank? || (file.blank? && url.blank?)
|
||||
|
||||
tag_name = TagName.find_undiscard_or_create_by!(name: tag_name_raw)
|
||||
tag = tag_name.tag
|
||||
tag = Tag.create!(tag_name:, category: :material) unless tag
|
||||
|
||||
material = Material.new(tag:, url:,
|
||||
created_by_user: current_user,
|
||||
updated_by_user: current_user)
|
||||
material.file.attach(file)
|
||||
|
||||
if material.save
|
||||
render json: MaterialRepr.base(material, host: request.base_url), status: :created
|
||||
else
|
||||
render json: { errors: material.errors.full_messages }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
return head :unauthorized unless current_user
|
||||
return head :forbidden unless current_user.gte_member?
|
||||
|
||||
material = Material.with_attached_file.find_by(id: params[:id])
|
||||
return head :not_found unless material
|
||||
|
||||
tag_name_raw = params[:tag].to_s.strip
|
||||
file = params[:file]
|
||||
url = params[:url].to_s.strip.presence
|
||||
return head :bad_request if tag_name_raw.blank? || (file.blank? && url.blank?)
|
||||
|
||||
tag_name = TagName.find_undiscard_or_create_by!(name: tag_name_raw)
|
||||
tag = tag_name.tag
|
||||
tag = Tag.create!(tag_name:, category: :material) unless tag
|
||||
|
||||
material.update!(tag:, url:, updated_by_user: current_user)
|
||||
if file
|
||||
material.file.attach(file)
|
||||
else
|
||||
material.file.purge
|
||||
end
|
||||
|
||||
if material.save
|
||||
render json: MaterialRepr.base(material, host: request.base_url)
|
||||
else
|
||||
render json: { errors: material.errors.full_messages }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
return head :unauthorized unless current_user
|
||||
return head :forbidden unless current_user.gte_member?
|
||||
|
||||
material = Material.find_by(id: params[:id])
|
||||
return head :not_found unless material
|
||||
|
||||
material.discard
|
||||
head :no_content
|
||||
end
|
||||
end
|
||||
@@ -30,14 +30,21 @@ class NicoTagsController < ApplicationController
|
||||
id = params[:id].to_i
|
||||
|
||||
tag = Tag.find(id)
|
||||
return head :bad_request if tag.category != 'nico'
|
||||
return head :bad_request unless tag.nico?
|
||||
|
||||
linked_tag_names = params[:tags].to_s.split(' ')
|
||||
linked_tags = Tag.normalise_tags(linked_tag_names, with_tagme: false)
|
||||
return head :bad_request if linked_tags.any? { |t| t.category == 'nico' }
|
||||
linked_tag_names = params[:tags].to_s.split
|
||||
linked_tags = Tag.normalise_tags(linked_tag_names, with_tagme: false,
|
||||
with_no_deerjikist: false)
|
||||
return head :bad_request if linked_tags.any? { |t| t.nico? }
|
||||
|
||||
tag.linked_tags = linked_tags
|
||||
tag.save!
|
||||
ApplicationRecord.transaction do
|
||||
TagVersioning.record_tag_snapshots!(linked_tags, created_by_user: current_user)
|
||||
|
||||
tag.linked_tags = linked_tags
|
||||
tag.save!
|
||||
|
||||
NicoTagVersionRecorder.record!(tag:, event_type: :update, created_by_user: current_user)
|
||||
end
|
||||
|
||||
render json: tag.linked_tags.map { |t| TagRepr.base(t) }, status: :ok
|
||||
end
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
class PostVersionsController < ApplicationController
|
||||
def index
|
||||
post_id = params[:post].presence
|
||||
tag_id = params[:tag].presence
|
||||
page = (params[:page].presence || 1).to_i
|
||||
limit = (params[:limit].presence || 20).to_i
|
||||
|
||||
page = 1 if page < 1
|
||||
limit = 1 if limit < 1
|
||||
|
||||
offset = (page - 1) * limit
|
||||
|
||||
tag_name =
|
||||
if tag_id
|
||||
TagName.joins(:tag).find_by(tag: { id: tag_id })
|
||||
end
|
||||
return render json: { versions: [], count: 0 } if tag_id && tag_name.blank?
|
||||
|
||||
q = PostVersion.joins(<<~SQL.squish)
|
||||
LEFT JOIN
|
||||
post_versions prev
|
||||
ON
|
||||
prev.post_id = post_versions.post_id
|
||||
AND prev.version_no = post_versions.version_no - 1
|
||||
SQL
|
||||
.select('post_versions.*', 'prev.title AS prev_title', 'prev.url AS prev_url',
|
||||
'prev.thumbnail_base AS prev_thumbnail_base', 'prev.tags AS prev_tags',
|
||||
'prev.original_created_from AS prev_original_created_from',
|
||||
'prev.original_created_before AS prev_original_created_before')
|
||||
q = q.where('post_versions.post_id = ?', post_id) if post_id
|
||||
if tag_name
|
||||
escaped = ActiveRecord::Base.sanitize_sql_like(tag_name.name)
|
||||
q = q.where(("CONCAT(' ', post_versions.tags, ' ') LIKE :kw " +
|
||||
"OR CONCAT(' ', prev.tags, ' ') LIKE :kw"),
|
||||
kw: "% #{ escaped } %")
|
||||
end
|
||||
|
||||
count = q.except(:select, :order, :limit, :offset).count
|
||||
|
||||
versions = q.order(Arel.sql('post_versions.created_at DESC, post_versions.id DESC'))
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
|
||||
render json: { versions: serialise_versions(versions), count: }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def serialise_versions rows
|
||||
user_ids = rows.map(&:created_by_user_id).compact.uniq
|
||||
users_by_id = User.where(id: user_ids).pluck(:id, :name).to_h
|
||||
|
||||
rows.map do |row|
|
||||
cur_tags = split_tags(row.tags)
|
||||
prev_tags = split_tags(row.attributes['prev_tags'])
|
||||
|
||||
{
|
||||
post_id: row.post_id,
|
||||
version_no: row.version_no,
|
||||
event_type: row.event_type,
|
||||
title: {
|
||||
current: row.title,
|
||||
prev: row.attributes['prev_title']
|
||||
},
|
||||
url: {
|
||||
current: row.url,
|
||||
prev: row.attributes['prev_url']
|
||||
},
|
||||
thumbnail: {
|
||||
current: nil,
|
||||
prev: nil
|
||||
},
|
||||
thumbnail_base: {
|
||||
current: row.thumbnail_base,
|
||||
prev: row.attributes['prev_thumbnail_base']
|
||||
},
|
||||
tags: build_version_tags(cur_tags, prev_tags),
|
||||
original_created_from: {
|
||||
current: row.original_created_from&.iso8601,
|
||||
prev: row.attributes['prev_original_created_from']&.iso8601
|
||||
},
|
||||
original_created_before: {
|
||||
current: row.original_created_before&.iso8601,
|
||||
prev: row.attributes['prev_original_created_before']&.iso8601
|
||||
},
|
||||
created_at: row.created_at.iso8601,
|
||||
created_by_user:
|
||||
if row.created_by_user_id
|
||||
{
|
||||
id: row.created_by_user_id,
|
||||
name: users_by_id[row.created_by_user_id]
|
||||
}
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def build_version_tags(cur_tags, prev_tags)
|
||||
(cur_tags | prev_tags).map do |name|
|
||||
type =
|
||||
if cur_tags.include?(name) && prev_tags.include?(name)
|
||||
'context'
|
||||
elsif cur_tags.include?(name)
|
||||
'added'
|
||||
else
|
||||
'removed'
|
||||
end
|
||||
|
||||
{
|
||||
name:,
|
||||
type:
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def split_tags(tags)
|
||||
tags.to_s.split(/\s+/).reject(&:blank?)
|
||||
end
|
||||
end
|
||||
@@ -44,7 +44,7 @@ class PostsController < ApplicationController
|
||||
filtered_posts
|
||||
.joins("LEFT JOIN (#{ pt_max_sql }) pt_max ON pt_max.post_id = posts.id")
|
||||
.reselect('posts.*', Arel.sql("#{ updated_at_all_sql } AS updated_at_all"))
|
||||
.preload(tags: { tag_name: :wiki_page })
|
||||
.preload(tags: [:materials, { tag_name: :wiki_page }])
|
||||
.with_attached_thumbnail
|
||||
|
||||
q = q.where('posts.url LIKE ?', "%#{ url }%") if url
|
||||
@@ -95,7 +95,7 @@ class PostsController < ApplicationController
|
||||
end
|
||||
|
||||
def random
|
||||
post = filtered_posts.preload(tags: { tag_name: :wiki_page })
|
||||
post = filtered_posts.preload(tags: [:materials, { tag_name: :wiki_page }])
|
||||
.order('RAND()')
|
||||
.first
|
||||
return head :not_found unless post
|
||||
@@ -104,7 +104,7 @@ class PostsController < ApplicationController
|
||||
end
|
||||
|
||||
def show
|
||||
post = Post.includes(tags: { tag_name: :wiki_page }).find_by(id: params[:id])
|
||||
post = Post.includes(tags: [:materials, { tag_name: :wiki_page }]).find_by(id: params[:id])
|
||||
return head :not_found unless post
|
||||
|
||||
render json: PostRepr.base(post, current_user)
|
||||
@@ -127,17 +127,22 @@ class PostsController < ApplicationController
|
||||
post = Post.new(title:, url:, thumbnail_base: nil, uploaded_user: current_user,
|
||||
original_created_from:, original_created_before:)
|
||||
post.thumbnail.attach(thumbnail)
|
||||
if post.save
|
||||
post.resized_thumbnail!
|
||||
|
||||
ApplicationRecord.transaction do
|
||||
post.save!
|
||||
tags = Tag.normalise_tags(tag_names)
|
||||
TagVersioning.record_tag_snapshots!(tags, created_by_user: current_user)
|
||||
|
||||
tags = Tag.expand_parent_tags(tags)
|
||||
sync_post_tags!(post, tags)
|
||||
|
||||
post.reload
|
||||
render json: PostRepr.base(post), status: :created
|
||||
else
|
||||
render json: { errors: post.errors.full_messages }, status: :unprocessable_entity
|
||||
post.resized_thumbnail!
|
||||
PostVersionRecorder.record!(post:, event_type: :create, created_by_user: current_user)
|
||||
end
|
||||
|
||||
post.reload
|
||||
render json: PostRepr.base(post), status: :created
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
render json: { errors: post.errors.full_messages }, status: :unprocessable_entity
|
||||
rescue Tag::NicoTagNormalisationError
|
||||
head :bad_request
|
||||
end
|
||||
@@ -166,19 +171,27 @@ class PostsController < ApplicationController
|
||||
original_created_before = params[:original_created_before]
|
||||
|
||||
post = Post.find(params[:id].to_i)
|
||||
if post.update(title:, original_created_from:, original_created_before:)
|
||||
tags = post.tags.where(category: 'nico').to_a +
|
||||
Tag.normalise_tags(tag_names, with_tagme: false)
|
||||
|
||||
ApplicationRecord.transaction do
|
||||
PostVersionRecorder.ensure_snapshot!(post, created_by_user: current_user)
|
||||
|
||||
post.update!(title:, original_created_from:, original_created_before:)
|
||||
|
||||
normalised_tags = Tag.normalise_tags(tag_names, with_tagme: false)
|
||||
TagVersioning.record_tag_snapshots!(normalised_tags, created_by_user: current_user)
|
||||
|
||||
tags = post.tags.nico.to_a + normalised_tags
|
||||
tags = Tag.expand_parent_tags(tags)
|
||||
sync_post_tags!(post, tags)
|
||||
|
||||
post.reload
|
||||
json = post.as_json
|
||||
json['tags'] = build_tag_tree_for(post.tags)
|
||||
render json:, status: :ok
|
||||
else
|
||||
render json: post.errors, status: :unprocessable_entity
|
||||
PostVersionRecorder.record!(post:, event_type: :update, created_by_user: current_user)
|
||||
end
|
||||
|
||||
post.reload
|
||||
json = post.as_json
|
||||
json['tags'] = build_tag_tree_for(post.tags)
|
||||
render json:, status: :ok
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
render json: post.errors, status: :unprocessable_entity
|
||||
rescue Tag::NicoTagNormalisationError
|
||||
head :bad_request
|
||||
end
|
||||
@@ -198,7 +211,7 @@ class PostsController < ApplicationController
|
||||
pts = pts.where(post_id: id) if id.present?
|
||||
pts = pts.where(tag_id:) if tag_id.present?
|
||||
pts = pts.includes(:post, :created_user, :deleted_user,
|
||||
tag: { tag_name: :wiki_page })
|
||||
tag: [:materials, { tag_name: :wiki_page }])
|
||||
|
||||
events = []
|
||||
pts.each do |pt|
|
||||
|
||||
@@ -7,7 +7,16 @@ class TagChildrenController < ApplicationController
|
||||
child_id = params[:child_id]
|
||||
return head :bad_request if parent_id.blank? || child_id.blank?
|
||||
|
||||
Tag.find(parent_id).children << Tag.find(child_id) rescue nil
|
||||
parent = Tag.find(parent_id)
|
||||
child = Tag.find(child_id)
|
||||
return head :bad_request if parent.nico? || child.nico?
|
||||
|
||||
ApplicationRecord.transaction do
|
||||
TagVersioning.ensure_snapshot!(child, created_by_user: current_user)
|
||||
|
||||
TagImplication.find_or_create_by!(parent_tag: parent, tag: child)
|
||||
TagVersionRecorder.record!(tag: child, event_type: :update, created_by_user: current_user)
|
||||
end
|
||||
|
||||
head :no_content
|
||||
end
|
||||
@@ -20,7 +29,16 @@ class TagChildrenController < ApplicationController
|
||||
child_id = params[:child_id]
|
||||
return head :bad_request if parent_id.blank? || child_id.blank?
|
||||
|
||||
Tag.find(parent_id).children.delete(Tag.find(child_id)) rescue nil
|
||||
parent = Tag.find(parent_id)
|
||||
child = Tag.find(child_id)
|
||||
return head :bad_request if parent.nico? || child.nico?
|
||||
|
||||
ApplicationRecord.transaction do
|
||||
TagVersioning.ensure_snapshot!(child, created_by_user: current_user)
|
||||
|
||||
TagImplication.find_by(parent_tag: parent, tag: child)&.destroy!
|
||||
TagVersionRecorder.record!(tag: child, event_type: :update, created_by_user: current_user)
|
||||
end
|
||||
|
||||
head :no_content
|
||||
end
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
class TagVersionsController < ApplicationController
|
||||
def index
|
||||
tag_id = params[:id].presence
|
||||
page = (params[:page].presence || 1).to_i
|
||||
limit = (params[:limit].presence || 20).to_i
|
||||
|
||||
page = 1 if page < 1
|
||||
limit = 1 if limit < 1
|
||||
|
||||
offset = (page - 1) * limit
|
||||
|
||||
q = TagVersion.joins(<<~SQL.squish)
|
||||
LEFT JOIN
|
||||
tag_versions prev
|
||||
ON
|
||||
prev.tag_id = tag_versions.tag_id
|
||||
AND prev.version_no = tag_versions.version_no - 1
|
||||
SQL
|
||||
.select('tag_versions.*', 'prev.name AS prev_name', 'prev.category AS prev_category',
|
||||
'prev.aliases AS prev_aliases', 'prev.parent_tag_ids AS prev_parent_tag_ids')
|
||||
q = q.where('tag_versions.tag_id = ?', tag_id) if tag_id
|
||||
|
||||
count = q.except(:select, :order, :limit, :offset).count
|
||||
|
||||
versions = q.order(Arel.sql('tag_versions.created_at DESC, tag_versions.id DESC'))
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
|
||||
render json: { versions: serialise_versions(versions), count: }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def serialise_versions rows
|
||||
user_ids = rows.map(&:created_by_user_id).compact.uniq
|
||||
users_by_id = User.where(id: user_ids).pluck(:id, :name).to_h
|
||||
|
||||
rows.map do |row|
|
||||
cur_aliases = split_values(row.aliases)
|
||||
prev_aliases = split_values(row.attributes['prev_aliases'])
|
||||
|
||||
cur_parent_tag_ids = split_parent_tag_ids(row.parent_tag_ids)
|
||||
prev_parent_tag_ids = split_parent_tag_ids(row.attributes['prev_parent_tag_ids'])
|
||||
|
||||
all_parent_tag_ids = (cur_parent_tag_ids | prev_parent_tag_ids)
|
||||
|
||||
tags_by_id =
|
||||
Tag
|
||||
.includes(:tag_name, :materials, { tag_name: :wiki_page })
|
||||
.where(id: all_parent_tag_ids)
|
||||
.index_by(&:id)
|
||||
|
||||
parent_tags =
|
||||
build_version_values(cur_parent_tag_ids, prev_parent_tag_ids, key: :tag_id)
|
||||
.map do |h|
|
||||
{ tag: TagRepr.base(tags_by_id[h[:tag_id]]),
|
||||
type: h[:type] }
|
||||
end
|
||||
|
||||
{ tag_id: row.tag_id,
|
||||
version_no: row.version_no,
|
||||
event_type: row.event_type,
|
||||
name: { current: row.name, prev: row.attributes['prev_name'] },
|
||||
category: { current: row.category, prev: row.attributes['prev_category'] },
|
||||
aliases: build_version_values(cur_aliases, prev_aliases, key: :name),
|
||||
parent_tags:,
|
||||
created_at: row.created_at.iso8601,
|
||||
created_by_user: row.created_by_user_id &&
|
||||
{ id: row.created_by_user_id,
|
||||
name: users_by_id[row.created_by_user_id] } }
|
||||
end
|
||||
end
|
||||
|
||||
def build_version_values cur_values, prev_values, key:
|
||||
(cur_values | prev_values).map do |value|
|
||||
type =
|
||||
if cur_values.include?(value) && prev_values.include?(value)
|
||||
'context'
|
||||
elsif cur_values.include?(value)
|
||||
'added'
|
||||
else
|
||||
'removed'
|
||||
end
|
||||
|
||||
{ key => value, type: }
|
||||
end
|
||||
end
|
||||
|
||||
def split_values(values) = values.to_s.split(/\s+/).reject(&:blank?)
|
||||
|
||||
def split_parent_tag_ids(values) = split_values(values).map(&:to_i)
|
||||
end
|
||||
@@ -33,11 +33,11 @@ class TagsController < ApplicationController
|
||||
else
|
||||
Tag.joins(:tag_name)
|
||||
end
|
||||
.includes(:tag_name, tag_name: :wiki_page)
|
||||
.includes(:tag_name, :materials, tag_name: :wiki_page)
|
||||
q = q.where(posts: { id: post_id }) if post_id.present?
|
||||
|
||||
q = q.where('tag_names.name LIKE ?', "%#{ name }%") if name
|
||||
q = q.where(category: category) if category
|
||||
q = q.where(category:) if category
|
||||
q = q.where('tags.post_count >= ?', post_count_between[0]) if post_count_between[0]
|
||||
q = q.where('tags.post_count <= ?', post_count_between[1]) if post_count_between[1]
|
||||
q = q.where('tags.created_at >= ?', created_between[0]) if created_between[0]
|
||||
@@ -66,7 +66,45 @@ class TagsController < ApplicationController
|
||||
.offset(offset)
|
||||
.to_a
|
||||
|
||||
render json: { tags: TagRepr.base(tags), count: q.size }
|
||||
render json: { tags: TagRepr.many(tags), count: q.size }
|
||||
end
|
||||
|
||||
def with_depth
|
||||
parent_tag_id = params[:parent].to_i
|
||||
parent_tag_id = nil if parent_tag_id <= 0
|
||||
|
||||
tag_ids =
|
||||
if parent_tag_id
|
||||
TagImplication.where(parent_tag_id:).select(:tag_id)
|
||||
else
|
||||
Tag.where.not(id: TagImplication.select(:tag_id)).select(:id)
|
||||
end
|
||||
|
||||
tags =
|
||||
Tag
|
||||
.joins(:tag_name)
|
||||
.includes(:tag_name, :materials, tag_name: :wiki_page)
|
||||
.where(category: [:meme, :character, :material])
|
||||
.where(id: tag_ids)
|
||||
.order('tag_names.name')
|
||||
.distinct
|
||||
.to_a
|
||||
|
||||
has_children_tag_ids =
|
||||
if tags.empty?
|
||||
[]
|
||||
else
|
||||
TagImplication
|
||||
.joins(:tag)
|
||||
.where(parent_tag_id: tags.map(&:id),
|
||||
tags: { category: [:meme, :character, :material] })
|
||||
.distinct
|
||||
.pluck(:parent_tag_id)
|
||||
end
|
||||
|
||||
render json: tags.map { |tag|
|
||||
TagRepr.base(tag).merge(has_children: has_children_tag_ids.include?(tag.id), children: [])
|
||||
}
|
||||
end
|
||||
|
||||
def autocomplete
|
||||
@@ -90,7 +128,7 @@ class TagsController < ApplicationController
|
||||
end
|
||||
|
||||
base = Tag.joins(:tag_name)
|
||||
.includes(:tag_name, tag_name: :wiki_page)
|
||||
.includes(:tag_name, :materials, tag_name: :wiki_page)
|
||||
base = base.where('tags.post_count > 0') if present_only
|
||||
|
||||
canonical_hit =
|
||||
@@ -115,7 +153,7 @@ class TagsController < ApplicationController
|
||||
|
||||
def show
|
||||
tag = Tag.joins(:tag_name)
|
||||
.includes(:tag_name, tag_name: :wiki_page)
|
||||
.includes(:tag_name, :materials, tag_name: :wiki_page)
|
||||
.find_by(id: params[:id])
|
||||
if tag
|
||||
render json: TagRepr.base(tag)
|
||||
@@ -129,7 +167,7 @@ class TagsController < ApplicationController
|
||||
return head :bad_request if name.blank?
|
||||
|
||||
tag = Tag.joins(:tag_name)
|
||||
.includes(:tag_name, tag_name: :wiki_page)
|
||||
.includes(:tag_name, :materials, tag_name: :wiki_page)
|
||||
.find_by(tag_names: { name: })
|
||||
if tag
|
||||
render json: TagRepr.base(tag)
|
||||
@@ -159,6 +197,72 @@ class TagsController < ApplicationController
|
||||
render json: DeerjikistRepr.many(tag.deerjikists)
|
||||
end
|
||||
|
||||
def materials_by_name
|
||||
name = params[:name].to_s.strip
|
||||
return head :bad_request if name.blank?
|
||||
|
||||
tag = Tag.joins(:tag_name)
|
||||
.includes(:tag_name, :materials, tag_name: :wiki_page)
|
||||
.find_by(tag_names: { name: })
|
||||
return head :not_found unless tag
|
||||
|
||||
render json: build_tag_children(tag)
|
||||
end
|
||||
|
||||
def update_all
|
||||
return head :unauthorized unless current_user
|
||||
return head :forbidden unless current_user.gte_member?
|
||||
|
||||
tag = Tag.find_by(id: params[:id])
|
||||
return head :not_found unless tag
|
||||
|
||||
name = params[:name].to_s.strip
|
||||
category = params[:category].to_s.strip
|
||||
return head :unprocessable_entity if name.blank? || category.blank?
|
||||
|
||||
if name != tag.name &&
|
||||
tag.in?([Tag.tagme, Tag.bot, Tag.no_deerjikist, Tag.video, Tag.niconico])
|
||||
return render json: { error: 'システム・タグの名称は変更できません.' },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
|
||||
if tag.nico? || category == 'nico'
|
||||
return render json: { error: 'ニコタグは変更できません.' },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
|
||||
alias_names = params[:aliases].to_s.split.uniq
|
||||
parent_names = params[:parent_tags].to_s.split.uniq
|
||||
|
||||
ApplicationRecord.transaction do
|
||||
TagVersioning.ensure_snapshot!(tag, created_by_user: current_user)
|
||||
|
||||
old_name = tag.name
|
||||
name_changed = name != old_name
|
||||
wiki_page = tag.tag_name.wiki_page if name_changed
|
||||
|
||||
tag.update!(category:)
|
||||
tag.tag_name.update!(name:)
|
||||
|
||||
alias_names << old_name if name_changed
|
||||
alias_names.delete(name)
|
||||
|
||||
update_aliases!(tag, alias_names)
|
||||
update_parent_tags!(tag, parent_names)
|
||||
|
||||
tag.reload
|
||||
|
||||
record_tag_version!(
|
||||
tag,
|
||||
event_type: :update,
|
||||
created_by_user: current_user,
|
||||
name_changed:,
|
||||
wiki_page:)
|
||||
end
|
||||
|
||||
render json: TagRepr.base(tag.reload)
|
||||
end
|
||||
|
||||
def update
|
||||
return head :unauthorized unless current_user
|
||||
return head :forbidden unless current_user.gte_member?
|
||||
@@ -168,14 +272,123 @@ class TagsController < ApplicationController
|
||||
|
||||
tag = Tag.find(params[:id])
|
||||
|
||||
if name.present?
|
||||
tag.tag_name.update!(name:)
|
||||
if tag.nico? || (category.present? && category == 'nico')
|
||||
return render json: { error: 'ニコタグは変更できません.' },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
|
||||
if category.present?
|
||||
tag.update!(category:)
|
||||
ApplicationRecord.transaction do
|
||||
TagVersioning.ensure_snapshot!(tag, created_by_user: current_user)
|
||||
|
||||
old_name = tag.name
|
||||
name_changed = name.present? && name != old_name
|
||||
wiki_page = tag.tag_name.wiki_page if name_changed
|
||||
|
||||
tag.tag_name.update!(name:) if name.present?
|
||||
tag.update!(category:) if category.present?
|
||||
|
||||
tag.reload
|
||||
|
||||
record_tag_version!(
|
||||
tag,
|
||||
event_type: :update,
|
||||
created_by_user: current_user,
|
||||
name_changed:,
|
||||
wiki_page:)
|
||||
end
|
||||
|
||||
render json: TagRepr.base(tag)
|
||||
render json: TagRepr.base(tag.reload)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_tag_children tag
|
||||
material = tag.materials.first
|
||||
file = nil
|
||||
content_type = nil
|
||||
if material&.file&.attached?
|
||||
file = rails_storage_proxy_url(material.file, only_path: false)
|
||||
content_type = material.file.blob.content_type
|
||||
end
|
||||
|
||||
TagRepr.base(tag).merge(
|
||||
children: tag.children.sort_by { _1.name }.map { build_tag_children(_1) },
|
||||
material: material.as_json&.merge(file:, content_type:))
|
||||
end
|
||||
|
||||
def record_tag_version! tag, event_type:, created_by_user:, name_changed: false, wiki_page: nil
|
||||
if tag.nico?
|
||||
NicoTagVersionRecorder.record!(tag:, event_type:, created_by_user:)
|
||||
return
|
||||
end
|
||||
|
||||
TagVersionRecorder.record!(tag:, event_type:, created_by_user:)
|
||||
|
||||
return unless name_changed
|
||||
|
||||
wiki_page ||= tag.tag_name.wiki_page
|
||||
return unless wiki_page&.wiki_versions&.exists?
|
||||
|
||||
WikiVersionRecorder.record!(
|
||||
page: wiki_page,
|
||||
event_type: :update,
|
||||
created_by_user:)
|
||||
end
|
||||
|
||||
def update_aliases! tag, alias_names
|
||||
alias_names = alias_names.uniq
|
||||
|
||||
affected_tags = [tag]
|
||||
|
||||
current_aliases = tag.tag_name.aliases.to_a
|
||||
|
||||
current_aliases.each do |alias_tag_name|
|
||||
next if alias_names.include?(alias_tag_name.name)
|
||||
|
||||
affected_tags << alias_tag_name.canonical&.tag
|
||||
end
|
||||
|
||||
alias_names.each do |alias_name|
|
||||
alias_tag_name = TagName.find_undiscard_or_create_by!(name: alias_name)
|
||||
affected_tags << alias_tag_name.canonical&.tag
|
||||
end
|
||||
|
||||
affected_tags.compact.uniq.each do |affected_tag|
|
||||
TagVersioning.ensure_snapshot!(affected_tag, created_by_user: current_user)
|
||||
end
|
||||
|
||||
current_aliases.each do |alias_tag_name|
|
||||
next if alias_names.include?(alias_tag_name.name)
|
||||
|
||||
alias_tag_name.update!(canonical: nil)
|
||||
end
|
||||
|
||||
alias_names.each do |alias_name|
|
||||
alias_tag_name = TagName.find_undiscard_or_create_by!(name: alias_name)
|
||||
alias_tag_name.update!(canonical: tag.tag_name)
|
||||
end
|
||||
|
||||
affected_tags.compact.uniq.each do |affected_tag|
|
||||
record_tag_version!(affected_tag, event_type: :update, created_by_user: current_user)
|
||||
end
|
||||
end
|
||||
|
||||
def update_parent_tags! tag, parent_names
|
||||
parent_tags = Tag.normalise_tags(parent_names, with_tagme: false,
|
||||
with_no_deerjikist: false,
|
||||
deny_nico: true)
|
||||
|
||||
old_parent_tags = tag.parents.to_a
|
||||
|
||||
TagVersioning.record_tag_snapshots!((old_parent_tags + parent_tags).uniq,
|
||||
created_by_user: current_user)
|
||||
|
||||
tag.reversed_tag_implications.destroy_all
|
||||
|
||||
parent_tags.each do |parent_tag|
|
||||
next if parent_tag == tag
|
||||
|
||||
TagImplication.create!(tag:, parent_tag:)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
class UsersController < ApplicationController
|
||||
def create
|
||||
user = User.create!(inheritance_code: SecureRandom.uuid, role: 'guest')
|
||||
return head :unprocessable_entity if request.remote_ip.blank?
|
||||
|
||||
user = nil
|
||||
|
||||
User.transaction do
|
||||
user = User.create!(inheritance_code: SecureRandom.uuid, role: :guest)
|
||||
attach_ip_address!(user)
|
||||
end
|
||||
|
||||
render json: { code: user.inheritance_code,
|
||||
user: user.slice(:id, :name, :inheritance_code, :role) }
|
||||
user: user.slice(:id, :name, :inheritance_code, :role) },
|
||||
status: :created
|
||||
end
|
||||
|
||||
def verify
|
||||
ip_bin = IPAddr.new(request.remote_ip).hton
|
||||
ip_address = IpAddress.find_or_create_by!(ip_address: ip_bin)
|
||||
|
||||
user = User.find_by(inheritance_code: params[:code])
|
||||
return render json: { valid: false } unless user
|
||||
|
||||
UserIp.find_or_create_by!(user:, ip_address:)
|
||||
return head :unprocessable_entity if request.remote_ip.blank?
|
||||
|
||||
attach_ip_address!(user)
|
||||
|
||||
render json: { valid: true, user: user.slice(:id, :name, :inheritance_code, :role) }
|
||||
end
|
||||
@@ -41,9 +49,18 @@ class UsersController < ApplicationController
|
||||
return head :bad_request if name.blank?
|
||||
|
||||
if user.update(name:)
|
||||
render json: user.slice(:id, :name, :inheritance_code, :role), status: :created
|
||||
render json: user.slice(:id, :name, :inheritance_code, :role), status: :ok
|
||||
else
|
||||
render json: user.errors, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def attach_ip_address! user
|
||||
ip_bin = IPAddr.new(request.remote_ip).hton
|
||||
ip_address = IpAddress.create_or_find_by!(ip_address: ip_bin)
|
||||
|
||||
UserIp.create_or_find_by!(user:, ip_address:)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -85,22 +85,24 @@ class WikiPagesController < ApplicationController
|
||||
return head :unauthorized unless current_user
|
||||
return head :forbidden unless current_user.gte_member?
|
||||
|
||||
name = params[:title]&.strip
|
||||
title = params[:title].to_s.strip
|
||||
body = params[:body].to_s
|
||||
message = params[:message].presence
|
||||
|
||||
return head :unprocessable_entity if name.blank? || body.blank?
|
||||
return head :unprocessable_entity if title.blank? || body.blank?
|
||||
|
||||
tag_name = TagName.find_undiscard_or_create_by!(name:)
|
||||
page = WikiPage.new(tag_name:, created_user: current_user, updated_user: current_user)
|
||||
if page.save
|
||||
message = params[:message].presence
|
||||
Wiki::Commit.content!(page:, body:, created_user: current_user, message:)
|
||||
tag_name = TagName.find_undiscard_or_create_by!(name: title)
|
||||
|
||||
render json: WikiPageRepr.base(page).merge(body:), status: :created
|
||||
else
|
||||
render json: { errors: page.errors.full_messages },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
page =
|
||||
Wiki::Commit.create_content!(
|
||||
tag_name:,
|
||||
body:,
|
||||
created_by_user: current_user,
|
||||
message:)
|
||||
|
||||
render json: WikiPageRepr.base(page), status: :created
|
||||
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
|
||||
head :unprocessable_entity
|
||||
end
|
||||
|
||||
def update
|
||||
@@ -113,10 +115,32 @@ class WikiPagesController < ApplicationController
|
||||
return head :unprocessable_entity if title.blank? || body.blank?
|
||||
|
||||
page = WikiPage.find(params[:id])
|
||||
base_revision_id = page.current_revision.id
|
||||
base_revision_id = params[:base_revision_id].presence
|
||||
|
||||
if params[:title].present? && params[:title].strip != page.title
|
||||
return head :unprocessable_entity
|
||||
ApplicationRecord.transaction do
|
||||
page.lock!
|
||||
|
||||
old_title = page.title
|
||||
|
||||
tag = Tag.find_by(tag_name_id: page.tag_name_id)
|
||||
|
||||
if tag && title != old_title
|
||||
TagVersioning.ensure_snapshot!(tag, created_by_user: current_user)
|
||||
end
|
||||
|
||||
page.tag_name.update!(name: title) if title != old_title
|
||||
|
||||
message = params[:message].presence
|
||||
Wiki::Commit.content!(page:,
|
||||
body:,
|
||||
created_user: current_user,
|
||||
message:,
|
||||
base_revision_id:)
|
||||
|
||||
if tag && title != old_title
|
||||
tag.reload
|
||||
TagVersionRecorder.record!(tag:, event_type: :update, created_by_user: current_user)
|
||||
end
|
||||
end
|
||||
|
||||
message = params[:message].presence
|
||||
|
||||
Reference in New Issue
Block a user