このコミットが含まれているのは:
@@ -770,31 +770,15 @@ class TagsController < ApplicationController
|
|||||||
TagVersioning.ensure_snapshot!(old_owner_tag, created_by_user: current_user)
|
TagVersioning.ensure_snapshot!(old_owner_tag, created_by_user: current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
current_tag_name.wiki_page&.update!(tag_name_id: promoted_tag_name.id)
|
current_tag_name&.update!(primary_flg: false, auto_generated: false)
|
||||||
current_tag_name.association(:wiki_page).reset
|
promoted_tag_name.update!(tag:, primary_flg: true, auto_generated: false)
|
||||||
|
tag.association(:tag_names).reset
|
||||||
current_tag_name.update!(canonical_id: promoted_tag_name.id,
|
|
||||||
primary_flg: false, auto_generated: false)
|
|
||||||
promoted_tag_name.update!(tag:, canonical_id: nil, primary_flg: true, auto_generated: false)
|
|
||||||
|
|
||||||
TagName.where(tag:,
|
|
||||||
language_code: current_tag_name.language_code,
|
|
||||||
primary_flg: false)
|
|
||||||
.where.not(id: promoted_tag_name.id)
|
|
||||||
.find_each do |alias_tag_name|
|
|
||||||
alias_tag_name.update!(canonical_id: promoted_tag_name.id, primary_flg: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
tag.update!(tag_name_id: promoted_tag_name.id)
|
|
||||||
current_tag_name.association(:wiki_page).reset
|
|
||||||
current_tag_name.association(:tag).reset
|
|
||||||
current_tag_name.reload.update!(canonical_id: promoted_tag_name.id, primary_flg: false)
|
|
||||||
|
|
||||||
return unless old_owner_tag && old_owner_tag != tag
|
return unless old_owner_tag && old_owner_tag != tag
|
||||||
|
|
||||||
record_tag_version!(old_owner_tag.reload, event_type: :update,
|
record_tag_version!(old_owner_tag.reload, event_type: :update,
|
||||||
created_by_user: current_user,
|
created_by_user: current_user,
|
||||||
language_code: current_tag_name.language_code)
|
language_code: promoted_tag_name.language_code)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_aliases! locale, tag, alias_names
|
def update_aliases! locale, tag, alias_names
|
||||||
@@ -802,20 +786,15 @@ class TagsController < ApplicationController
|
|||||||
|
|
||||||
affected_tags = [tag]
|
affected_tags = [tag]
|
||||||
|
|
||||||
primary_tag_name = TagName.find_by!(tag:,
|
|
||||||
language_code: locale.language_code,
|
|
||||||
primary_flg: true)
|
|
||||||
current_aliases = tag.tag_names.where(language_code: locale.language_code,
|
current_aliases = tag.tag_names.where(language_code: locale.language_code,
|
||||||
primary_flg: false).to_a
|
primary_flg: false).to_a
|
||||||
|
|
||||||
current_aliases.each do |alias_tag_name|
|
requested_aliases = TagName.where(
|
||||||
next if alias_names.include?(alias_tag_name.name)
|
language_code: locale.language_code, name: alias_names)
|
||||||
|
.includes(:tag).index_by(&:name)
|
||||||
affected_tags << alias_tag_name.tag
|
|
||||||
end
|
|
||||||
|
|
||||||
alias_names.each do |alias_name|
|
alias_names.each do |alias_name|
|
||||||
alias_tag_name = TagName.find_by(language_code: locale.language_code, name: alias_name)
|
alias_tag_name = requested_aliases[alias_name]
|
||||||
if alias_tag_name&.primary? && alias_tag_name.tag_id && alias_tag_name.tag_id != tag.id
|
if alias_tag_name&.primary? && alias_tag_name.tag_id && alias_tag_name.tag_id != tag.id
|
||||||
alias_tag_name.errors.add(:name, 'その名前は既に使はれてゐます.')
|
alias_tag_name.errors.add(:name, 'その名前は既に使はれてゐます.')
|
||||||
raise ActiveRecord::RecordInvalid, alias_tag_name
|
raise ActiveRecord::RecordInvalid, alias_tag_name
|
||||||
@@ -831,16 +810,15 @@ class TagsController < ApplicationController
|
|||||||
current_aliases.each do |alias_tag_name|
|
current_aliases.each do |alias_tag_name|
|
||||||
next if alias_names.include?(alias_tag_name.name)
|
next if alias_names.include?(alias_tag_name.name)
|
||||||
|
|
||||||
alias_tag_name.update!(canonical_id: nil, tag_id: nil)
|
alias_tag_name.destroy!
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_names.each do |alias_name|
|
alias_names.each do |alias_name|
|
||||||
alias_tag_name = TagName.find_or_initialize_by(
|
alias_tag_name = requested_aliases[alias_name] || TagName.new(
|
||||||
language_code: locale.language_code,
|
language_code: locale.language_code,
|
||||||
name: alias_name)
|
name: alias_name)
|
||||||
alias_tag_name.script_code = locale.script_code
|
alias_tag_name.script_code = locale.script_code
|
||||||
alias_tag_name.update!(tag:, primary_flg: false, auto_generated: false,
|
alias_tag_name.update!(tag:, primary_flg: false, auto_generated: false)
|
||||||
canonical_id: primary_tag_name.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
affected_tags.compact.uniq.each do |affected_tag|
|
affected_tags.compact.uniq.each do |affected_tag|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class WikiPagesController < ApplicationController
|
class WikiPagesController < ApplicationController
|
||||||
# 互換性のための Facade API
|
# 互換性のための Facade API
|
||||||
|
|
||||||
rescue_from Wiki::Commit::Conflict, with: :render_wiki_conflict
|
rescue_from TagDescriptionOperation::Conflict, with: :render_wiki_conflict
|
||||||
|
|
||||||
def index
|
def index
|
||||||
title = params[:title].to_s.strip
|
title = params[:title].to_s.strip
|
||||||
@@ -38,7 +38,7 @@ class WikiPagesController < ApplicationController
|
|||||||
tag_group = TagGroup.find_by(id: params[:id])
|
tag_group = TagGroup.find_by(id: params[:id])
|
||||||
return render_tag_group tag_group if tag_group
|
return render_tag_group tag_group if tag_group
|
||||||
|
|
||||||
render_wiki_page_or_404 nil
|
head :not_found
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_by_title
|
def show_by_title
|
||||||
@@ -55,7 +55,7 @@ class WikiPagesController < ApplicationController
|
|||||||
|
|
||||||
return render_wiki_page_by_tag_description tag_description if tag_description
|
return render_wiki_page_by_tag_description tag_description if tag_description
|
||||||
|
|
||||||
render_wiki_page_or_404 nil
|
head :not_found
|
||||||
end
|
end
|
||||||
|
|
||||||
def exists
|
def exists
|
||||||
@@ -134,15 +134,15 @@ class WikiPagesController < ApplicationController
|
|||||||
if title.start_with?(TagGroupRepr::PREFIX)
|
if title.start_with?(TagGroupRepr::PREFIX)
|
||||||
name = title.delete_prefix(TagGroupRepr::PREFIX)
|
name = title.delete_prefix(TagGroupRepr::PREFIX)
|
||||||
parsed = TagGroupMarkdownParser.parse(body:, group: nil)
|
parsed = TagGroupMarkdownParser.parse(body:, group: nil)
|
||||||
group = TagGroupOperation.create!(name:, **parsed, actor: current_user,
|
group = TagGroupOperation.create!(
|
||||||
require_compatibility: true)
|
name:, **parsed.except(:compatibility_body), actor: current_user)
|
||||||
version = current_group_version(group)
|
version = current_group_version(group)
|
||||||
return render_group_representation(TagGroupRepr.one(group), version,
|
return render_group_representation(TagGroupRepr.one(group), version,
|
||||||
body: parsed.fetch(:compatibility_body),
|
body: parsed.fetch(:compatibility_body),
|
||||||
status: :created)
|
status: :created)
|
||||||
end
|
end
|
||||||
|
|
||||||
page =
|
description =
|
||||||
if title.include?(':')
|
if title.include?(':')
|
||||||
return render_unprocessable_entity field: :title
|
return render_unprocessable_entity field: :title
|
||||||
else
|
else
|
||||||
@@ -153,7 +153,6 @@ class WikiPagesController < ApplicationController
|
|||||||
tag: tag_name.tag, body:, created_by_user: current_user, message:)
|
tag: tag_name.tag, body:, created_by_user: current_user, message:)
|
||||||
end
|
end
|
||||||
|
|
||||||
description = TagDescription.find_by!(tag_id: page.tag_name.tag_id, language_code: 'ja')
|
|
||||||
render json: WikiPageRepr.tag_description(description), status: :created
|
render json: WikiPageRepr.tag_description(description), status: :created
|
||||||
rescue ActiveRecord::RecordInvalid => e
|
rescue ActiveRecord::RecordInvalid => e
|
||||||
render_validation_error e.record
|
render_validation_error e.record
|
||||||
@@ -179,22 +178,21 @@ class WikiPagesController < ApplicationController
|
|||||||
|
|
||||||
name = title.delete_prefix(TagGroupRepr::PREFIX)
|
name = title.delete_prefix(TagGroupRepr::PREFIX)
|
||||||
parsed = TagGroupMarkdownParser.parse(body:, group:)
|
parsed = TagGroupMarkdownParser.parse(body:, group:)
|
||||||
TagGroupOperation.update!(group:, name:, **parsed, actor: current_user,
|
TagGroupOperation.update!(
|
||||||
require_compatibility: true)
|
group:, name:, **parsed.except(:compatibility_body), actor: current_user)
|
||||||
return head :ok
|
return head :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
return render_unprocessable_entity field: :title if title.include?(':')
|
return render_unprocessable_entity field: :title if title.include?(':')
|
||||||
description = TagDescription.find_by(id: params[:id])
|
description = TagDescription.find_by(id: params[:id])
|
||||||
unless description
|
unless description
|
||||||
return render_unprocessable_entity field: :title if group || WikiPage.exists?(id: params[:id])
|
return render_unprocessable_entity field: :title if group
|
||||||
return head :not_found
|
return head :not_found
|
||||||
end
|
end
|
||||||
tag = description.tag
|
tag = description.tag
|
||||||
tag_name = TagDescriptionOperation.compatibility_tag_name(tag)
|
tag_name = tag.tag_name('ja')
|
||||||
if tag_name.name.include?(':') || (group && title != tag_name.name)
|
return render_unprocessable_entity field: :title unless tag_name
|
||||||
return render_unprocessable_entity field: :title
|
return render_unprocessable_entity field: :title if tag_name.name.include?(':')
|
||||||
end
|
|
||||||
|
|
||||||
base_revision_id = params[:base_revision_id].presence
|
base_revision_id = params[:base_revision_id].presence
|
||||||
|
|
||||||
@@ -251,26 +249,6 @@ class WikiPagesController < ApplicationController
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def legacy_group_page? page
|
|
||||||
return false unless page
|
|
||||||
return true if page.title.start_with?(TagGroupRepr::PREFIX)
|
|
||||||
|
|
||||||
prefix = ApplicationRecord.sanitize_sql_like(TagGroupRepr::PREFIX)
|
|
||||||
page.wiki_versions.where('title LIKE ?', "#{ prefix }%").exists?
|
|
||||||
end
|
|
||||||
|
|
||||||
def legacy_update_identified? page, title, group
|
|
||||||
return false unless page
|
|
||||||
return true if page.title == title
|
|
||||||
return false if title == group.name
|
|
||||||
|
|
||||||
namespace = page.title.split(':', 2)
|
|
||||||
return true if namespace.size == 2 && title.start_with?("#{ namespace.first }:")
|
|
||||||
|
|
||||||
base = params[:base_revision_id].presence
|
|
||||||
base && page.wiki_revisions.exists?(id: base)
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_tag_group group
|
def render_tag_group group
|
||||||
version = if params[:version].present?
|
version = if params[:version].present?
|
||||||
group_version(group, params[:version])
|
group_version(group, params[:version])
|
||||||
@@ -357,27 +335,6 @@ class WikiPagesController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_wiki_page_or_404 page
|
|
||||||
return head :not_found unless page
|
|
||||||
|
|
||||||
rev = find_revision(page)
|
|
||||||
return head :not_found unless rev
|
|
||||||
|
|
||||||
if rev.redirect?
|
|
||||||
return (
|
|
||||||
redirect_to wiki_page_by_title_path(title: rev.redirect_page.title),
|
|
||||||
status: :moved_permanently)
|
|
||||||
end
|
|
||||||
|
|
||||||
body = rev.body
|
|
||||||
revision_id = rev.id
|
|
||||||
pred = page.pred_revision_id(revision_id)
|
|
||||||
succ = page.succ_revision_id(revision_id)
|
|
||||||
updated_at = rev.created_at
|
|
||||||
|
|
||||||
render json: WikiPageRepr.base(page).merge(body:, revision_id:, pred:, succ:, updated_at:)
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_wiki_page_by_tag_description description
|
def render_wiki_page_by_tag_description description
|
||||||
versions = description_versions(description)
|
versions = description_versions(description)
|
||||||
historical = params[:version].present?
|
historical = params[:version].present?
|
||||||
@@ -432,14 +389,6 @@ class WikiPagesController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_revision page
|
|
||||||
if params[:version].present?
|
|
||||||
page.wiki_revisions.find_by(id: params[:version])
|
|
||||||
else
|
|
||||||
page.current_revision
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_wiki_conflict err
|
def render_wiki_conflict err
|
||||||
render json: { error: 'conflict', message: err.message }, status: :conflict
|
render json: { error: 'conflict', message: err.message }, status: :conflict
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,14 +13,13 @@ class Locale < ApplicationRecord
|
|||||||
def generate_tag_names!
|
def generate_tag_names!
|
||||||
ApplicationRecord.transaction do
|
ApplicationRecord.transaction do
|
||||||
tag_ids = TagName.where(language_code:, primary_flg: true).pluck(:tag_id)
|
tag_ids = TagName.where(language_code:, primary_flg: true).pluck(:tag_id)
|
||||||
Tag.where.not(id: tag_ids).find_each do
|
Tag.where.not(id: tag_ids).includes(:tag_names).find_each do
|
||||||
TagName.create!(tag_id: _1.id,
|
TagName.create!(tag_id: _1.id,
|
||||||
language_code:,
|
language_code:,
|
||||||
name: TagName.generate_name(self, _1, _1.tag_name('ja')),
|
name: TagName.generate_name(self, _1, _1.tag_name('ja')),
|
||||||
script_code:,
|
script_code:,
|
||||||
primary_flg: true,
|
primary_flg: true,
|
||||||
auto_generated: true,
|
auto_generated: true)
|
||||||
canonical_id: nil)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+11
-19
@@ -91,7 +91,6 @@ class Tag < ApplicationRecord
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
def name(language_code) = tag_name(language_code)&.name
|
def name(language_code) = tag_name(language_code)&.name
|
||||||
def wiki_page = tag_name('ja')&.wiki_page
|
|
||||||
|
|
||||||
def system?
|
def system?
|
||||||
tag_name('ja')&.name.in?(SYSTEM_TAG_NAMES)
|
tag_name('ja')&.name.in?(SYSTEM_TAG_NAMES)
|
||||||
@@ -260,16 +259,13 @@ class Tag < ApplicationRecord
|
|||||||
name = name.to_s.strip
|
name = name.to_s.strip
|
||||||
|
|
||||||
ApplicationRecord.transaction do
|
ApplicationRecord.transaction do
|
||||||
tn = TagName.find_or_create_by!(language_code:, name:) do
|
tn = TagName.find_by(language_code:, name:)
|
||||||
_1.script_code = locale.script_code
|
next tn.tag if tn
|
||||||
_1.primary_flg = true
|
|
||||||
end
|
|
||||||
|
|
||||||
tag = tn.tag
|
tag = Tag.create!(category:)
|
||||||
next tag if tag
|
tn = TagName.create!(tag:, language_code:, name:,
|
||||||
|
script_code: locale.script_code, primary_flg: true,
|
||||||
tag = Tag.create!(tag_name_id: tn.id, category:)
|
auto_generated: false)
|
||||||
tn.update!(tag:, primary_flg: true, canonical_id: nil, auto_generated: false)
|
|
||||||
generate_tag_names!(locale, tag, tn)
|
generate_tag_names!(locale, tag, tn)
|
||||||
tag.association(:tag_names).reset
|
tag.association(:tag_names).reset
|
||||||
|
|
||||||
@@ -303,20 +299,17 @@ class Tag < ApplicationRecord
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
source_tag_name = source_tag.tag_name('ja')
|
if source_tag.tag_descriptions.exists?
|
||||||
|
source_tag.errors.add(:base, :invalid)
|
||||||
if source_tag_name.wiki_page.present?
|
raise ActiveRecord::RecordInvalid, source_tag
|
||||||
raise ActiveRecord::RecordInvalid.new(source_tag_name)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
TagVersioning.record!(source_tag, event_type: :discard, created_by_user:)
|
TagVersioning.record!(source_tag, event_type: :discard, created_by_user:)
|
||||||
source_tag.tag_names.update_all(tag_id: target_tag.id, primary_flg: false,
|
source_tag.tag_names.update_all(tag_id: target_tag.id, primary_flg: false,
|
||||||
|
auto_generated: false,
|
||||||
updated_at: Time.current)
|
updated_at: Time.current)
|
||||||
source_tag.destroy!
|
source_tag.destroy!
|
||||||
|
|
||||||
source_tag_name.update_columns(canonical_id: target_tag.tag_name_id,
|
|
||||||
updated_at: Time.current)
|
|
||||||
|
|
||||||
TagVersioning.record!(target_tag, event_type: :update, created_by_user:)
|
TagVersioning.record!(target_tag, event_type: :update, created_by_user:)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -376,8 +369,7 @@ class Tag < ApplicationRecord
|
|||||||
name: TagName.generate_name(locale, tag, base_tag_name),
|
name: TagName.generate_name(locale, tag, base_tag_name),
|
||||||
script_code:,
|
script_code:,
|
||||||
primary_flg: true,
|
primary_flg: true,
|
||||||
auto_generated: true,
|
auto_generated: true)
|
||||||
canonical_id: nil)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
class TagName < ApplicationRecord
|
class TagName < ApplicationRecord
|
||||||
belongs_to :language, foreign_key: :language_code, primary_key: :code
|
belongs_to :language, foreign_key: :language_code, primary_key: :code
|
||||||
belongs_to :tag, optional: true
|
belongs_to :tag
|
||||||
has_one :wiki_page
|
|
||||||
|
|
||||||
validates :name, presence: true,
|
validates :name, presence: true,
|
||||||
length: { maximum: 255 },
|
length: { maximum: 255 },
|
||||||
uniqueness: { scope: :language_code }
|
uniqueness: { scope: :language_code }
|
||||||
|
|
||||||
validate :alias_name_must_not_have_prefix
|
validate :alias_name_must_not_have_prefix
|
||||||
validate :alias_must_not_have_wiki_page
|
|
||||||
validate :name_must_be_sanitised
|
validate :name_must_be_sanitised
|
||||||
validate :name_mustnt_start_with_nico
|
validate :name_mustnt_start_with_nico
|
||||||
validate :generated_name_must_be_reserved
|
validate :generated_name_must_be_reserved
|
||||||
@@ -21,14 +19,16 @@ class TagName < ApplicationRecord
|
|||||||
|
|
||||||
tns = TagName.where(language_code: locale.language_code, name: names).index_by(&:name)
|
tns = TagName.where(language_code: locale.language_code, name: names).index_by(&:name)
|
||||||
|
|
||||||
|
primary_names = TagName.where(
|
||||||
|
language_code: locale.language_code, tag_id: tns.values.map(&:tag_id),
|
||||||
|
primary_flg: true).pluck(:tag_id, :name).to_h
|
||||||
|
|
||||||
names.map { |name|
|
names.map { |name|
|
||||||
tag_name = tns[name]
|
tag_name = tns[name]
|
||||||
if !tag_name || tag_name.primary? || tag_name.tag_id.nil?
|
if !tag_name || tag_name.primary?
|
||||||
name
|
name
|
||||||
else
|
else
|
||||||
TagName.find_by(language_code: locale.language_code,
|
primary_names[tag_name.tag_id] || name
|
||||||
tag_id: tag_name.tag_id,
|
|
||||||
primary_flg: true)&.name || name
|
|
||||||
end
|
end
|
||||||
}.uniq
|
}.uniq
|
||||||
end
|
end
|
||||||
@@ -53,12 +53,6 @@ class TagName < ApplicationRecord
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def alias_must_not_have_wiki_page
|
|
||||||
if !(primary?) && wiki_page
|
|
||||||
errors.add :primary_flg, 'Wiki 参照がある名前はエーリアスになれません.'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def name_must_be_sanitised
|
def name_must_be_sanitised
|
||||||
if name? && name != TagNameSanitisationRule.sanitise(name)
|
if name? && name != TagNameSanitisationRule.sanitise(name)
|
||||||
errors.add :name, '名前に使用できない文字が含まれてゐます.'
|
errors.add :name, '名前に使用できない文字が含まれてゐます.'
|
||||||
|
|||||||
@@ -33,13 +33,7 @@ class TagNameSanitisationRule < ApplicationRecord
|
|||||||
existing_tag = existing_tn.tag
|
existing_tag = existing_tn.tag
|
||||||
source_tag = tn.tag
|
source_tag = tn.tag
|
||||||
|
|
||||||
if existing_tag
|
Tag.merge_tags!(existing_tag, source_tag) if existing_tag != source_tag
|
||||||
Tag.merge_tags!(existing_tag, source_tag) if tn.tag
|
|
||||||
elsif source_tag
|
|
||||||
tn.update_columns(tag_id: nil, updated_at: Time.current)
|
|
||||||
source_tag.update_columns(tag_name_id: existing_tn.id, updated_at: Time.current)
|
|
||||||
existing_tn.update_columns(tag_id: source_tag.id, updated_at: Time.current)
|
|
||||||
end
|
|
||||||
tn.destroy!
|
tn.destroy!
|
||||||
|
|
||||||
next
|
next
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ class User < ApplicationRecord
|
|||||||
has_many :ip_addresses, through: :user_ips
|
has_many :ip_addresses, through: :user_ips
|
||||||
has_many :user_post_views, dependent: :destroy
|
has_many :user_post_views, dependent: :destroy
|
||||||
has_many :viewed_posts, through: :user_post_views, source: :post
|
has_many :viewed_posts, through: :user_post_views, source: :post
|
||||||
has_many :created_wiki_pages,
|
has_many :created_tag_descriptions, class_name: 'TagDescription',
|
||||||
class_name: 'WikiPage', foreign_key: :created_user_id, dependent: :nullify
|
foreign_key: :created_by_user_id, dependent: :nullify
|
||||||
has_many :updated_wiki_pages,
|
has_many :updated_tag_descriptions, class_name: 'TagDescription',
|
||||||
class_name: 'WikiPage', foreign_key: :updated_user_id, dependent: :nullify
|
foreign_key: :updated_by_user_id, dependent: :nullify
|
||||||
|
|
||||||
def viewed?(post) = user_post_views.exists?(post_id: post.id)
|
def viewed?(post) = user_post_views.exists?(post_id: post.id)
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
class WikiLine < ApplicationRecord
|
|
||||||
has_many :wiki_revision_lines, dependent: :restrict_with_exception
|
|
||||||
|
|
||||||
validates :sha256, presence: true, uniqueness: true, length: { is: 64 }
|
|
||||||
validates :body, presence: true
|
|
||||||
|
|
||||||
def self.upsert_by_body! body
|
|
||||||
sha = Digest::SHA256.hexdigest(body)
|
|
||||||
now = Time.current
|
|
||||||
|
|
||||||
upsert(sha256: sha, body:, created_at: now, updated_at: now)
|
|
||||||
|
|
||||||
find_by!(sha256: sha)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
require 'set'
|
|
||||||
|
|
||||||
|
|
||||||
class WikiPage < ApplicationRecord
|
|
||||||
include MyDiscard
|
|
||||||
|
|
||||||
has_many :wiki_revisions, dependent: :destroy
|
|
||||||
belongs_to :created_user, class_name: 'User'
|
|
||||||
belongs_to :updated_user, class_name: 'User'
|
|
||||||
|
|
||||||
has_many :redirected_from_revisions,
|
|
||||||
class_name: 'WikiRevision',
|
|
||||||
foreign_key: :redirect_page_id,
|
|
||||||
dependent: :nullify
|
|
||||||
|
|
||||||
has_many :wiki_versions
|
|
||||||
|
|
||||||
attribute :version_no, :integer, default: 1
|
|
||||||
|
|
||||||
belongs_to :tag_name
|
|
||||||
validates :tag_name, presence: true
|
|
||||||
validates :body, presence: true
|
|
||||||
|
|
||||||
def title = tag_name.name
|
|
||||||
def deprecated_at = tag_name.tag&.deprecated_at
|
|
||||||
|
|
||||||
def title= val
|
|
||||||
(self.tag_name ||= build_tag_name).name = val
|
|
||||||
end
|
|
||||||
|
|
||||||
def current_revision = wiki_revisions.order(id: :desc).first
|
|
||||||
|
|
||||||
def resolve_redirect limit: 10
|
|
||||||
page = self
|
|
||||||
visited = Set.new
|
|
||||||
|
|
||||||
limit.times do
|
|
||||||
return page if visited.include?(page.id)
|
|
||||||
|
|
||||||
visited.add(page.id)
|
|
||||||
|
|
||||||
rev = page.current_revision
|
|
||||||
return page if !(rev&.redirect?) || !(rev.redirect_page)
|
|
||||||
|
|
||||||
page = rev.redirect_page
|
|
||||||
end
|
|
||||||
|
|
||||||
page
|
|
||||||
end
|
|
||||||
|
|
||||||
def pred_revision_id revision_id
|
|
||||||
wiki_revisions.where('id < ?', revision_id).order(id: :desc).limit(1).pick(:id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def succ_revision_id revision_id
|
|
||||||
wiki_revisions.where('id > ?', revision_id).order(id: :asc).limit(1).pick(:id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
class WikiRevision < ApplicationRecord
|
|
||||||
belongs_to :wiki_page
|
|
||||||
belongs_to :base_revision, class_name: 'WikiRevision', optional: true
|
|
||||||
belongs_to :created_user, class_name: 'User'
|
|
||||||
belongs_to :redirect_page, class_name: 'WikiPage', optional: true
|
|
||||||
|
|
||||||
has_many :wiki_revision_lines, dependent: :delete_all
|
|
||||||
has_many :wiki_lines, through: :wiki_revision_lines
|
|
||||||
|
|
||||||
enum :kind, content: 0, redirect: 1
|
|
||||||
|
|
||||||
validates :kind, presence: true
|
|
||||||
validates :lines_count, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
|
||||||
validates :tree_sha256, length: { is: 64 }, allow_nil: true
|
|
||||||
|
|
||||||
validate :kind_consistency
|
|
||||||
|
|
||||||
def body
|
|
||||||
return unless content?
|
|
||||||
|
|
||||||
wiki_revision_lines
|
|
||||||
.includes(:wiki_line)
|
|
||||||
.order(:position)
|
|
||||||
.map { |rev| rev.wiki_line.body }
|
|
||||||
.join("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def kind_consistency
|
|
||||||
if content?
|
|
||||||
if tree_sha256.blank?
|
|
||||||
errors.add(:tree_sha256, '種類がページの場合は必須です.')
|
|
||||||
end
|
|
||||||
|
|
||||||
if redirect_page_id.present?
|
|
||||||
errors.add(:redirect_page_id, '種類がページの場合は空である必要があります.')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if redirect?
|
|
||||||
if redirect_page_id.blank?
|
|
||||||
errors.add(:redirect_page_id, '種類がリダイレクトの場合は必須です.')
|
|
||||||
end
|
|
||||||
|
|
||||||
if tree_sha256.present?
|
|
||||||
errors.add(:tree_sha256, '種類がリダイレクトの場合は空である必要があります.')
|
|
||||||
end
|
|
||||||
|
|
||||||
if lines_count.to_i > 0
|
|
||||||
errors.add(:lines_count, '種類がリダイレクトの場合は 0 である必要があります.')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
class WikiRevisionLine < ApplicationRecord
|
|
||||||
belongs_to :wiki_revision
|
|
||||||
belongs_to :wiki_line
|
|
||||||
|
|
||||||
validates :position, presence: true,
|
|
||||||
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
|
||||||
validates :position, uniqueness: { scope: :wiki_revision_id }
|
|
||||||
end
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
class WikiVersion < ApplicationRecord
|
|
||||||
include VersionRecord
|
|
||||||
|
|
||||||
belongs_to :wiki_page
|
|
||||||
|
|
||||||
validates :title, presence: true
|
|
||||||
validates :body, presence: true
|
|
||||||
end
|
|
||||||
@@ -2,14 +2,8 @@
|
|||||||
|
|
||||||
|
|
||||||
module WikiPageRepr
|
module WikiPageRepr
|
||||||
BASE = { methods: [:title, :deprecated_at] }.freeze
|
|
||||||
|
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
def base wiki_page
|
|
||||||
wiki_page.as_json(BASE)
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag_description description
|
def tag_description description
|
||||||
tag = description.tag
|
tag = description.tag
|
||||||
{ 'id' => description.id,
|
{ 'id' => description.id,
|
||||||
@@ -29,8 +23,4 @@ module WikiPageRepr
|
|||||||
def tag_group tag_group
|
def tag_group tag_group
|
||||||
TagGroupRepr.tag_group(tag_group).merge('resource_type' => 'tag_group')
|
TagGroupRepr.tag_group(tag_group).merge('resource_type' => 'tag_group')
|
||||||
end
|
end
|
||||||
|
|
||||||
def many wiki_pages
|
|
||||||
wiki_pages.map { |p| base(p) }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
class TagDescriptionOperation
|
class TagDescriptionOperation
|
||||||
LANGUAGE_CODE = 'ja'
|
LANGUAGE_CODE = 'ja'
|
||||||
|
|
||||||
|
class Conflict < StandardError
|
||||||
|
;
|
||||||
|
end
|
||||||
|
|
||||||
def self.create!(tag:, body:, created_by_user:, message: nil)
|
def self.create!(tag:, body:, created_by_user:, message: nil)
|
||||||
ApplicationRecord.transaction do
|
ApplicationRecord.transaction do
|
||||||
tag.lock!
|
tag.lock!
|
||||||
raise ActiveRecord::RecordNotUnique if TagDescription.exists?(
|
raise ActiveRecord::RecordNotUnique if TagDescription.exists?(
|
||||||
tag_id: tag.id, language_code: LANGUAGE_CODE)
|
tag_id: tag.id, language_code: LANGUAGE_CODE)
|
||||||
|
|
||||||
page = Wiki::CompatibilityWriter.create!(
|
description = TagDescription.new(tag:, language_code: LANGUAGE_CODE)
|
||||||
tag_name: compatibility_tag_name(tag), body:, created_by_user:, message:)
|
body = normalise_body!(description, body)
|
||||||
revision = page.wiki_revisions.order(id: :desc).first
|
record_version!(description:, body:, user: created_by_user)
|
||||||
record_version!(tag:, revision:, body: revision.body, user: created_by_user,
|
description
|
||||||
description: nil)
|
|
||||||
page
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -22,51 +24,47 @@ class TagDescriptionOperation
|
|||||||
tag.lock!
|
tag.lock!
|
||||||
description = TagDescription.lock.find_by!(
|
description = TagDescription.lock.find_by!(
|
||||||
tag_id: tag.id, language_code: LANGUAGE_CODE)
|
tag_id: tag.id, language_code: LANGUAGE_CODE)
|
||||||
tag_name = compatibility_tag_name(tag)
|
body = normalise_body!(description, body)
|
||||||
page = WikiPage.lock.find_by(tag_name_id: tag_name.id)
|
latest = TagDescriptionVersion.find_by(
|
||||||
unless page
|
tag_id: tag.id, language_code: LANGUAGE_CODE,
|
||||||
versions = TagDescriptionVersion.where(tag_id: tag.id, language_code: LANGUAGE_CODE)
|
version_no: description.version_no)
|
||||||
if base_revision_id.present? && versions.maximum(:id) != base_revision_id.to_i
|
if base_revision_id.present? && latest&.id != base_revision_id.to_i
|
||||||
raise Wiki::Commit::Conflict, '競合が発生してゐます.'
|
raise Conflict, '競合が発生してゐます.'
|
||||||
end
|
|
||||||
page = Wiki::CompatibilityWriter.create!(
|
|
||||||
tag_name:, body:, created_by_user: updated_by_user, message:)
|
|
||||||
revision = page.current_revision
|
|
||||||
record_version!(tag:, revision:, body: revision.body,
|
|
||||||
user: updated_by_user, description:)
|
|
||||||
next revision
|
|
||||||
end
|
end
|
||||||
revision = Wiki::CompatibilityWriter.update!(
|
next if latest && normalise_body(description.body) == body
|
||||||
page:, body:, updated_by_user:, message:,
|
|
||||||
base_revision_id:)
|
|
||||||
next unless revision
|
|
||||||
|
|
||||||
record_version!(tag:, revision:, body: revision.body,
|
record_version!(description:, body:, user: updated_by_user)
|
||||||
user: updated_by_user, description:)
|
|
||||||
revision
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.compatibility_tag_name tag
|
def self.record_version! description:, body:, user:
|
||||||
tag.tag_name(LANGUAGE_CODE) || TagName.find_by!(id: tag.tag_name_id,
|
creating = description.new_record?
|
||||||
language_code: LANGUAGE_CODE)
|
version_no = creating ? 1 : description.version_no + 1
|
||||||
|
version = TagDescriptionVersion.create!(
|
||||||
|
tag_id: description.tag_id, language_code: description.language_code,
|
||||||
|
version_no:, event_type: creating ? 'create' : 'update', body:,
|
||||||
|
source_language_code: description.source_language_code,
|
||||||
|
source_version_no: description.source_version_no,
|
||||||
|
created_by_user_id: user.id, created_at: Time.current)
|
||||||
|
description.created_by_user_id = user.id if creating
|
||||||
|
description.update!(body:, version_no:, updated_by_user_id: user.id)
|
||||||
|
version
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.record_version! tag:, revision:, body:, user:, description:
|
|
||||||
version_no = description ? description.version_no + 1 : 1
|
|
||||||
TagDescriptionVersion.create!(
|
|
||||||
id: revision.id, tag_id: tag.id, language_code: LANGUAGE_CODE,
|
|
||||||
version_no:, event_type: description ? 'update' : 'create', body:,
|
|
||||||
created_by_user_id: user.id, created_at: revision.created_at)
|
|
||||||
if description
|
|
||||||
description.update!(body:, version_no:, updated_by_user_id: user.id)
|
|
||||||
else
|
|
||||||
TagDescription.create!(
|
|
||||||
tag_id: tag.id,
|
|
||||||
language_code: LANGUAGE_CODE, version_no:, body:,
|
|
||||||
created_by_user_id: user.id, updated_by_user_id: user.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private_class_method :record_version!
|
private_class_method :record_version!
|
||||||
|
|
||||||
|
def self.normalise_body body
|
||||||
|
body.to_s.gsub(/\r\n?/, "\n")
|
||||||
|
.encode('UTF-8', invalid: :replace, undef: :replace, replace: '🖕')
|
||||||
|
.sub(/\n+\z/, '')
|
||||||
|
end
|
||||||
|
private_class_method :normalise_body
|
||||||
|
|
||||||
|
def self.normalise_body! description, body
|
||||||
|
normalised = normalise_body(body)
|
||||||
|
return normalised if normalised.present?
|
||||||
|
|
||||||
|
description.errors.add(:body, :blank)
|
||||||
|
raise ActiveRecord::RecordInvalid, description
|
||||||
|
end
|
||||||
|
private_class_method :normalise_body!
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
class TagGroupOperation
|
class TagGroupOperation
|
||||||
MAX_PLACEMENT_DEPTH = 5
|
MAX_PLACEMENT_DEPTH = 5
|
||||||
PREFIX = TagGroupRepr::PREFIX
|
|
||||||
|
|
||||||
def self.create!(name:, description:, members:, placements:, actor:,
|
def self.create!(name:, description:, members:, placements:, actor:)
|
||||||
require_compatibility: false, compatibility_body: nil)
|
ApplicationRecord.transaction do
|
||||||
group, sync_issue = ApplicationRecord.transaction do
|
|
||||||
group = TagGroup.new(version_no: 1, created_by_user: actor,
|
group = TagGroup.new(version_no: 1, created_by_user: actor,
|
||||||
updated_by_user: actor)
|
updated_by_user: actor)
|
||||||
attributes = prepare!(group:, name:, description:, members:, placements:)
|
attributes = prepare!(group:, name:, description:, members:, placements:)
|
||||||
@@ -13,21 +11,15 @@ class TagGroupOperation
|
|||||||
group.save!
|
group.save!
|
||||||
persist_structure!(group:, attributes:, actor:)
|
persist_structure!(group:, attributes:, actor:)
|
||||||
persist_version!(group:, attributes:, actor:, event_type: 'create')
|
persist_version!(group:, attributes:, actor:, event_type: 'create')
|
||||||
issue = sync_compatibility!(group:, actor:, old_name: nil,
|
group
|
||||||
require_compatibility:, compatibility_body:)
|
|
||||||
[group, issue]
|
|
||||||
end
|
end
|
||||||
report_sync_issue(group, actor, sync_issue)
|
|
||||||
group
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.update!(group:, name:, description:, members:, placements:, actor:,
|
def self.update!(group:, name:, description:, members:, placements:, actor:)
|
||||||
require_compatibility: false, compatibility_body: nil)
|
ApplicationRecord.transaction do
|
||||||
group, sync_issue = ApplicationRecord.transaction do
|
|
||||||
group.lock!
|
group.lock!
|
||||||
old_name = group.name
|
|
||||||
attributes = prepare!(group:, name:, description:, members:, placements:)
|
attributes = prepare!(group:, name:, description:, members:, placements:)
|
||||||
next [group, nil] if unchanged?(group, attributes)
|
next group if unchanged?(group, attributes)
|
||||||
|
|
||||||
group.update!(name: attributes.fetch(:name),
|
group.update!(name: attributes.fetch(:name),
|
||||||
description: attributes.fetch(:description),
|
description: attributes.fetch(:description),
|
||||||
@@ -35,12 +27,8 @@ class TagGroupOperation
|
|||||||
updated_by_user: actor)
|
updated_by_user: actor)
|
||||||
persist_structure!(group:, attributes:, actor:)
|
persist_structure!(group:, attributes:, actor:)
|
||||||
persist_version!(group:, attributes:, actor:, event_type: 'update')
|
persist_version!(group:, attributes:, actor:, event_type: 'update')
|
||||||
issue = sync_compatibility!(group:, actor:, old_name:,
|
group
|
||||||
require_compatibility:, compatibility_body:)
|
|
||||||
[group, issue]
|
|
||||||
end
|
end
|
||||||
report_sync_issue(group, actor, sync_issue)
|
|
||||||
group
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.prepare!(group:, name:, description:, members:, placements:)
|
def self.prepare!(group:, name:, description:, members:, placements:)
|
||||||
@@ -313,57 +301,6 @@ class TagGroupOperation
|
|||||||
end
|
end
|
||||||
private_class_method :persist_version!
|
private_class_method :persist_version!
|
||||||
|
|
||||||
def self.sync_compatibility!(group:, actor:, old_name:,
|
|
||||||
require_compatibility:, compatibility_body:)
|
|
||||||
representation = TagGroupRepr.one(group)
|
|
||||||
body = begin
|
|
||||||
TagGroupMarkdownRenderer.render(representation)
|
|
||||||
rescue TagGroupMarkdownRenderer::UnsupportedStructure => error
|
|
||||||
if compatibility_body.present?
|
|
||||||
compatibility_body
|
|
||||||
elsif require_compatibility
|
|
||||||
invalid!(group, :base, error.message)
|
|
||||||
else
|
|
||||||
return error.message
|
|
||||||
end
|
|
||||||
end
|
|
||||||
invalid!(group, :base, '本文は必須です.') if body.blank?
|
|
||||||
|
|
||||||
title = "#{ PREFIX }#{ group.name }"
|
|
||||||
old_title = old_name && "#{ PREFIX }#{ old_name }"
|
|
||||||
page = if old_title
|
|
||||||
WikiPage.joins(:tag_name).find_by(tag_names: { name: old_title })
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
page ||= WikiPage.joins(:tag_name).find_by(tag_names: { name: title })
|
|
||||||
|
|
||||||
if page
|
|
||||||
tag_name = page.tag_name
|
|
||||||
tag_name.update!(name: title) if tag_name.name != title
|
|
||||||
Wiki::Commit.legacy_content!(page:, body:, created_user: actor, message: nil,
|
|
||||||
base_revision_id: nil)
|
|
||||||
else
|
|
||||||
tag_name = TagName.find_or_initialize_by(language_code: 'ja', name: title)
|
|
||||||
tag_name.script_code = 'Jpan'
|
|
||||||
tag_name.primary_flg = true
|
|
||||||
tag_name.auto_generated = false
|
|
||||||
tag_name.save!
|
|
||||||
Wiki::CompatibilityWriter.create!(tag_name:, body:, created_by_user: actor)
|
|
||||||
end
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
private_class_method :sync_compatibility!
|
|
||||||
|
|
||||||
def self.report_sync_issue(group, actor, reason)
|
|
||||||
return unless reason
|
|
||||||
|
|
||||||
payload = { status: 'unsupported_structure', tag_group_id: group.id,
|
|
||||||
version_no: group.version_no, user_id: actor&.id, reason: }
|
|
||||||
Rails.logger.warn("tag_group_wiki_sync_skipped #{ payload.to_json }")
|
|
||||||
end
|
|
||||||
private_class_method :report_sync_issue
|
|
||||||
|
|
||||||
def self.placement_references(nodes)
|
def self.placement_references(nodes)
|
||||||
nodes.flat_map do |node|
|
nodes.flat_map do |node|
|
||||||
own = if node.key?('tag_id')
|
own = if node.key?('tag_id')
|
||||||
|
|||||||
@@ -1,164 +0,0 @@
|
|||||||
require 'digest'
|
|
||||||
|
|
||||||
|
|
||||||
module Wiki
|
|
||||||
class Commit
|
|
||||||
class Conflict < StandardError
|
|
||||||
;
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.create_content! tag_name:, body:, created_by_user:, message: nil
|
|
||||||
if tag_name.tag && tag_name.language_code == 'ja' && !tag_name.name.include?(':')
|
|
||||||
return TagDescriptionOperation.create!(
|
|
||||||
tag: tag_name.tag, body:, created_by_user:, message:)
|
|
||||||
end
|
|
||||||
|
|
||||||
tag_name.errors.add(:base, :invalid)
|
|
||||||
raise ActiveRecord::RecordInvalid, tag_name
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.create_legacy_content! tag_name:, body:, created_by_user:, message: nil
|
|
||||||
normalised = normalise_body(body)
|
|
||||||
|
|
||||||
page = WikiPage.new(tag_name:,
|
|
||||||
body: normalised,
|
|
||||||
created_user: created_by_user,
|
|
||||||
updated_user: created_by_user)
|
|
||||||
|
|
||||||
if normalised.blank?
|
|
||||||
page.errors.add(:body, :blank)
|
|
||||||
raise ActiveRecord::RecordInvalid, page
|
|
||||||
end
|
|
||||||
|
|
||||||
ActiveRecord::Base.transaction do
|
|
||||||
page.save!
|
|
||||||
|
|
||||||
new(page:, created_user: created_by_user).content!(
|
|
||||||
body: normalised,
|
|
||||||
message:,
|
|
||||||
base_revision_id: nil)
|
|
||||||
|
|
||||||
page
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.content! page:, body:, created_user:, message: nil, base_revision_id: nil
|
|
||||||
description = TagDescription.find_by(tag_id: page.tag_name.tag_id, language_code: 'ja')
|
|
||||||
description ||= TagDescription.joins(:tag).find_by(
|
|
||||||
tags: { tag_name_id: page.tag_name_id }, language_code: 'ja')
|
|
||||||
if description && !page.title.include?(':')
|
|
||||||
return TagDescriptionOperation.update!(
|
|
||||||
tag: description.tag, body:, updated_by_user: created_user,
|
|
||||||
message:, base_revision_id:)
|
|
||||||
end
|
|
||||||
|
|
||||||
page.errors.add(:base, :invalid)
|
|
||||||
raise ActiveRecord::RecordInvalid, page
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.legacy_content! page:, body:, created_user:, message: nil,
|
|
||||||
base_revision_id: nil
|
|
||||||
new(page:, created_user:).content!(body:, message:, base_revision_id:)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.redirect! page:, redirect_page:, created_user:, message: nil, base_revision_id: nil
|
|
||||||
new(page:, created_user:).redirect!(redirect_page:, message:, base_revision_id:)
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize page:, created_user:
|
|
||||||
@page = page
|
|
||||||
@created_user = created_user
|
|
||||||
end
|
|
||||||
|
|
||||||
def content! body:, message:, base_revision_id:
|
|
||||||
normalised = self.class.normalise_body(body)
|
|
||||||
if normalised.blank?
|
|
||||||
@page.errors.add(:body, :blank)
|
|
||||||
raise ActiveRecord::RecordInvalid, @page
|
|
||||||
end
|
|
||||||
|
|
||||||
lines = split_lines(normalised)
|
|
||||||
|
|
||||||
line_shas = lines.map { |line| Digest::SHA256.hexdigest(line) }
|
|
||||||
tree_sha = Digest::SHA256.hexdigest(line_shas.join(','))
|
|
||||||
|
|
||||||
ActiveRecord::Base.transaction do
|
|
||||||
@page.lock!
|
|
||||||
|
|
||||||
if base_revision_id.present?
|
|
||||||
current_id = @page.wiki_revisions.maximum(:id)
|
|
||||||
if current_id && current_id != base_revision_id.to_i
|
|
||||||
raise Conflict,
|
|
||||||
"競合が発生してゐます" +
|
|
||||||
"(現在の Id.:#{ current_id },ベース Id.:#{ base_revision_id })."
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
line_id_by_sha = upsert_lines!(lines, line_shas)
|
|
||||||
line_ids = line_shas.map { |sha| line_id_by_sha.fetch(sha) }
|
|
||||||
@page.update!(body: normalised)
|
|
||||||
|
|
||||||
WikiVersionRecorder.record!(
|
|
||||||
page: @page,
|
|
||||||
event_type: @page.wiki_versions.exists? ? :update : :create,
|
|
||||||
reason: message,
|
|
||||||
created_by_user: @created_user)
|
|
||||||
|
|
||||||
rev = WikiRevision.create!(
|
|
||||||
wiki_page: @page,
|
|
||||||
base_revision_id:,
|
|
||||||
created_user: @created_user,
|
|
||||||
kind: :content,
|
|
||||||
redirect_page_id: nil,
|
|
||||||
message:,
|
|
||||||
lines_count: lines.length,
|
|
||||||
tree_sha256: tree_sha)
|
|
||||||
|
|
||||||
rows = line_ids.each_with_index.map do |line_id, pos|
|
|
||||||
{ wiki_revision_id: rev.id, wiki_line_id: line_id, position: pos }
|
|
||||||
end
|
|
||||||
WikiRevisionLine.insert_all!(rows) if rows.any?
|
|
||||||
rev
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def redirect!(redirect_page:, message:, base_revision_id:) = raise '廃止しました.'
|
|
||||||
|
|
||||||
def self.normalise_body body
|
|
||||||
s = body.to_s
|
|
||||||
s.gsub!(/\r\n?/, "\n")
|
|
||||||
s.encode('UTF-8', invalid: :replace, undef: :replace, replace: '🖕')
|
|
||||||
s.gsub(/\n+$/, '')
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def split_lines(body) = body.split("\n")
|
|
||||||
|
|
||||||
def upsert_lines! lines, line_shas
|
|
||||||
now = Time.current
|
|
||||||
|
|
||||||
id_by_sha = WikiLine.where(sha256: line_shas).pluck(:sha256, :id).to_h
|
|
||||||
|
|
||||||
missing_by_sha = { }
|
|
||||||
|
|
||||||
line_shas.each_with_index do |sha, i|
|
|
||||||
next if id_by_sha.key?(sha)
|
|
||||||
next if missing_by_sha.key?(sha)
|
|
||||||
|
|
||||||
missing_by_sha[sha] = {
|
|
||||||
sha256: sha,
|
|
||||||
body: lines[i],
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now }
|
|
||||||
end
|
|
||||||
|
|
||||||
if missing_by_sha.any?
|
|
||||||
WikiLine.upsert_all(missing_by_sha.values)
|
|
||||||
id_by_sha = WikiLine.where(sha256: line_shas).pluck(:sha256, :id).to_h
|
|
||||||
end
|
|
||||||
|
|
||||||
id_by_sha
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
module Wiki
|
|
||||||
class CompatibilityWriter
|
|
||||||
def self.create! tag_name:, body:, created_by_user:, message: nil
|
|
||||||
ApplicationRecord.transaction do
|
|
||||||
tag_name.lock!
|
|
||||||
page = WikiPage.lock.find_by(tag_name_id: tag_name.id)
|
|
||||||
if page
|
|
||||||
update!(page:, body:, updated_by_user: created_by_user, message:)
|
|
||||||
page
|
|
||||||
else
|
|
||||||
Commit.create_legacy_content!(tag_name:, body:, created_by_user:, message:)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.update! page:, body:, updated_by_user:, message: nil,
|
|
||||||
base_revision_id: nil
|
|
||||||
normalised = Commit.normalise_body(body)
|
|
||||||
if normalised.blank?
|
|
||||||
return Commit.legacy_content!(
|
|
||||||
page:, body: normalised, created_user: updated_by_user, message:,
|
|
||||||
base_revision_id:)
|
|
||||||
end
|
|
||||||
|
|
||||||
if base_revision_id.present?
|
|
||||||
current_id = page.wiki_revisions.maximum(:id)
|
|
||||||
if current_id && current_id != base_revision_id.to_i
|
|
||||||
raise Commit::Conflict,
|
|
||||||
"競合が発生してゐます" +
|
|
||||||
"(現在の Id.:#{ current_id },ベース Id.:#{ base_revision_id })."
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if Commit.normalise_body(page.body) == normalised && page.wiki_revisions.exists?
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
Commit.legacy_content!(
|
|
||||||
page:, body: normalised, created_user: updated_by_user, message:,
|
|
||||||
base_revision_id:)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
class WikiVersionRecorder < VersionRecorder
|
|
||||||
def self.record! page:, event_type:, reason: nil, created_by_user:
|
|
||||||
new(page:, event_type:, reason:, created_by_user:).record!
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize page:, event_type:, reason: nil, created_by_user:
|
|
||||||
@reason = reason
|
|
||||||
super(record: page, event_type:, created_by_user:)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def version_class = WikiVersion
|
|
||||||
def version_association = :wiki_versions
|
|
||||||
def record_key = :wiki_page
|
|
||||||
|
|
||||||
def snapshot_attributes = {
|
|
||||||
title: @record.title,
|
|
||||||
body: @record.body,
|
|
||||||
reason: @reason }
|
|
||||||
end
|
|
||||||
新しいイシューから参照
ユーザーをブロックする