mirror of
https://github.com/discourse/discourse.git
synced 2024-12-24 22:25:03 +08:00
272 lines
7.8 KiB
Ruby
272 lines
7.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require File.join(Rails.root, "script", "import_scripts", "base.rb")
|
|
|
|
module ImportExport
|
|
class Importer < ImportScripts::Base
|
|
def initialize(data)
|
|
@users = data[:users]
|
|
@groups = data[:groups]
|
|
@categories = data[:categories]
|
|
@topics = data[:topics]
|
|
@translation_overrides = data[:translation_overrides]
|
|
|
|
# To support legacy `category_export` script
|
|
if data[:category].present?
|
|
@categories = [] if @categories.blank?
|
|
@categories << data[:category]
|
|
end
|
|
end
|
|
|
|
def perform
|
|
RateLimiter.disable
|
|
|
|
import_users
|
|
import_groups
|
|
import_categories
|
|
import_topics
|
|
import_translation_overrides
|
|
|
|
self
|
|
ensure
|
|
RateLimiter.enable
|
|
end
|
|
|
|
def import_users
|
|
return if @users.blank?
|
|
|
|
puts "Importing users..."
|
|
|
|
@users.each do |u|
|
|
import_id = "#{u[:id]}#{import_source}"
|
|
existing = User.with_email(u[:email]).first
|
|
|
|
if existing
|
|
if existing.custom_fields["import_id"] != import_id
|
|
existing.custom_fields["import_id"] = import_id
|
|
existing.save!
|
|
end
|
|
else
|
|
u = create_user(u, import_id) # see ImportScripts::Base
|
|
end
|
|
end
|
|
|
|
self
|
|
end
|
|
|
|
def import_groups
|
|
return if @groups.blank?
|
|
|
|
puts "Importing groups..."
|
|
|
|
@groups.each do |group_data|
|
|
g = group_data.dup
|
|
user_ids = g.delete(:user_ids)
|
|
external_id = g.delete(:id)
|
|
new_group = Group.find_by_name(g[:name]) || Group.create!(g)
|
|
user_ids.each do |external_user_id|
|
|
begin
|
|
new_group.add(User.find(new_user_id(external_user_id)))
|
|
rescue StandardError
|
|
ActiveRecord::RecordNotUnique
|
|
end
|
|
end
|
|
end
|
|
|
|
self
|
|
end
|
|
|
|
def import_categories
|
|
return if @categories.blank?
|
|
|
|
puts "Importing categories..."
|
|
|
|
import_ids = @categories.collect { |c| "#{c[:id]}#{import_source}" }
|
|
existing_categories =
|
|
CategoryCustomField
|
|
.where("name = 'import_id' AND value IN (?)", import_ids)
|
|
.select(:category_id, :value)
|
|
.to_a
|
|
existing_category_ids = existing_categories.pluck(:value)
|
|
|
|
levels = category_levels
|
|
max_level = levels.values.max
|
|
if SiteSetting.max_category_nesting < max_level
|
|
puts "Setting max_category_nesting to #{max_level}..."
|
|
SiteSetting.max_category_nesting = max_level
|
|
end
|
|
|
|
fix_permissions
|
|
|
|
@categories.reject! { |c| existing_category_ids.include? c[:id].to_s }
|
|
@categories.sort_by! { |c| levels[c[:id]] || 0 }
|
|
|
|
@categories.each do |cat_attrs|
|
|
begin
|
|
id = cat_attrs.delete(:id)
|
|
permissions = cat_attrs.delete(:permissions_params)
|
|
|
|
category = Category.new(cat_attrs)
|
|
category.parent_category_id =
|
|
new_category_id(cat_attrs[:parent_category_id]) if cat_attrs[
|
|
:parent_category_id
|
|
].present?
|
|
category.user_id = new_user_id(cat_attrs[:user_id])
|
|
import_id = "#{id}#{import_source}"
|
|
category.custom_fields["import_id"] = import_id
|
|
category.permissions = permissions
|
|
category.save!
|
|
existing_categories << { category_id: category.id, value: import_id }
|
|
|
|
if cat_attrs[:description].present?
|
|
post = category.topic.ordered_posts.first
|
|
post.raw = cat_attrs[:description]
|
|
post.skip_validation = true
|
|
post.save!
|
|
post.rebake!
|
|
end
|
|
rescue => e
|
|
puts "Failed to import category (ID = #{id}, name = #{cat_attrs[:name]}): #{e.message}"
|
|
end
|
|
end
|
|
|
|
self
|
|
end
|
|
|
|
def import_topics
|
|
return if @topics.blank?
|
|
|
|
puts "Importing topics...", ""
|
|
|
|
@topics.each do |t|
|
|
puts ""
|
|
print t[:title]
|
|
|
|
first_post_attrs =
|
|
t[:posts].first.merge(t.slice(*(TopicExporter::TOPIC_ATTRS - %i[id category_id])))
|
|
|
|
first_post_attrs[:user_id] = new_user_id(first_post_attrs[:user_id])
|
|
first_post_attrs[:category] = new_category_id(t[:category_id])
|
|
|
|
import_id = "#{first_post_attrs[:id]}#{import_source}"
|
|
first_post = PostCustomField.where(name: "import_id", value: import_id).first&.post
|
|
|
|
first_post = create_post(first_post_attrs, import_id) unless first_post
|
|
|
|
topic_id = first_post.topic_id
|
|
|
|
t[:posts].each_with_index do |post_data, i|
|
|
next if i == 0
|
|
print "."
|
|
post_import_id = "#{post_data[:id]}#{import_source}"
|
|
existing = PostCustomField.where(name: "import_id", value: post_import_id).first&.post
|
|
unless existing
|
|
# see ImportScripts::Base
|
|
create_post(
|
|
post_data.merge(topic_id: topic_id, user_id: new_user_id(post_data[:user_id])),
|
|
post_import_id,
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
puts ""
|
|
|
|
self
|
|
end
|
|
|
|
def import_translation_overrides
|
|
return if @translation_overrides.blank?
|
|
|
|
puts "Importing translation overrides..."
|
|
|
|
@translation_overrides.each do |tu|
|
|
TranslationOverride.upsert!(tu[:locale], tu[:translation_key], tu[:value])
|
|
end
|
|
|
|
TranslationOverride.reload_all_overrides!
|
|
end
|
|
|
|
def new_user_id(external_user_id)
|
|
ucf =
|
|
UserCustomField.where(name: "import_id", value: "#{external_user_id}#{import_source}").first
|
|
ucf ? ucf.user_id : Discourse::SYSTEM_USER_ID
|
|
end
|
|
|
|
def new_category_id(external_category_id)
|
|
CategoryCustomField
|
|
.where(name: "import_id", value: "#{external_category_id}#{import_source}")
|
|
.first
|
|
&.category_id
|
|
end
|
|
|
|
def import_source
|
|
@_import_source ||= "#{ENV["IMPORT_SOURCE"] || ""}"
|
|
end
|
|
|
|
def category_levels
|
|
@levels ||=
|
|
begin
|
|
levels = {}
|
|
|
|
# Incomplete backups may lack definitions for some parent categories
|
|
# which would cause an infinite loop below.
|
|
parent_ids = @categories.map { |category| category[:parent_category_id] }.uniq
|
|
category_ids = @categories.map { |category| category[:id] }.uniq
|
|
(parent_ids - category_ids).each { |id| levels[id] = 0 }
|
|
|
|
loop do
|
|
changed = false
|
|
|
|
@categories.each do |category|
|
|
if !levels[category[:id]]
|
|
if !category[:parent_category_id]
|
|
levels[category[:id]] = 1
|
|
elsif levels[category[:parent_category_id]]
|
|
levels[category[:id]] = levels[category[:parent_category_id]] + 1
|
|
end
|
|
|
|
changed = true
|
|
end
|
|
end
|
|
|
|
break if !changed
|
|
end
|
|
|
|
levels
|
|
end
|
|
end
|
|
|
|
def fix_permissions
|
|
categories_by_id = @categories.to_h { |category| [category[:id], category] }
|
|
|
|
@categories.each do |category|
|
|
if category[:permissions_params].blank?
|
|
category[:permissions_params] = { "everyone" => CategoryGroup.permission_types[:full] }
|
|
end
|
|
end
|
|
|
|
max_level = category_levels.values.max
|
|
max_level.times do
|
|
@categories.each do |category|
|
|
parent_category = categories_by_id[category[:parent_category_id]]
|
|
if !parent_category || !parent_category[:permissions_params] ||
|
|
parent_category[:permissions_params][:everyone]
|
|
next
|
|
end
|
|
|
|
parent_groups = parent_category[:permissions_params].map(&:first)
|
|
child_groups = category[:permissions_params].map(&:first)
|
|
|
|
only_subcategory_groups = child_groups - parent_groups
|
|
if only_subcategory_groups.present?
|
|
parent_category[:permissions_params].merge!(
|
|
category[:permissions_params].slice(*only_subcategory_groups),
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|