lib/comfortable_mexican_sofa/seeds/page/importer.rb
# frozen_string_literal: true
module ComfortableMexicanSofa::Seeds::Page
class Importer < ComfortableMexicanSofa::Seeds::Importer
# tracking target page linking. Since we might be linking to something that
# doesn't exist yet, we'll defer linking to the end of import
attr_accessor :target_pages
def initialize(from, to = from)
super
self.path = ::File.join(ComfortableMexicanSofa.config.seeds_path, from, "pages/")
end
def import!
import_page(File.join(path, "index/"), nil)
link_target_pages
# Remove pages not found in seeds
site.pages.where("id NOT IN (?)", seed_ids).destroy_all
end
private
# Recursive function that will be called for each child page (subfolder)
def import_page(path, parent)
slug = path.split("/").last
# setting page record
page =
if parent.present?
child = site.pages.where(slug: slug).first_or_initialize
child.parent = parent
child
else
site.pages.root || site.pages.new(slug: slug)
end
content_path = File.join(path, "content.html")
# If file is newer than page record we'll process it
if fresh_seed?(page, content_path)
# reading file content in, resulting in a hash
fragments_hash = parse_file_content(content_path)
# parsing attributes section
attributes_yaml = fragments_hash.delete("attributes")
attrs = YAML.safe_load(attributes_yaml)
# applying attributes
layout = site.layouts.find_by(identifier: attrs.delete("layout")) || parent.try(:layout)
category_ids = category_names_to_ids(page, attrs.delete("categories"))
target_page = attrs.delete("target_page")
page.attributes = attrs.merge(
layout: layout,
category_ids: category_ids
)
# applying fragments
old_frag_identifiers = page.fragments.pluck(:identifier)
new_frag_identifiers, fragments_attributes =
construct_fragments_attributes(fragments_hash, page, path)
page.fragments_attributes = fragments_attributes
if page.save
message = "[CMS SEEDS] Imported Page \t #{page.full_path}"
ComfortableMexicanSofa.logger.info(message)
# defering target page linking
if target_page.present?
self.target_pages ||= {}
self.target_pages[page.id] = target_page
end
# cleaning up old fragments
page.fragments.where(identifier: old_frag_identifiers - new_frag_identifiers).destroy_all
else
message = "[CMS SEEDS] Failed to import Page \n#{page.errors.inspect}"
ComfortableMexicanSofa.logger.warn(message)
end
end
import_translations(path, page)
# Tracking what page from seeds we're working with. So we can remove pages
# that are no longer in seeds
seed_ids << page.id
# importing child pages (if there are any)
Dir["#{path}*/"].each do |page_path|
import_page(page_path, page)
end
end
# Importing translations for given page. They look like `content.locale.html`
def import_translations(path, page)
old_translations = page.translations.pluck(:locale)
new_translations = []
Dir["#{path}content.*.html"].each do |file_path|
locale = File.basename(file_path).match(%r{content\.(\w+)\.html})[1]
new_translations << locale
translation = page.translations.where(locale: locale).first_or_initialize
next unless fresh_seed?(translation, file_path)
# reading file content in, resulting in a hash
fragments_hash = parse_file_content(file_path)
# parsing attributes section
attributes_yaml = fragments_hash.delete("attributes")
attrs = YAML.safe_load(attributes_yaml)
# applying attributes
layout = site.layouts.find_by(identifier: attrs.delete("layout")) || page.try(:layout)
translation.attributes = attrs.merge(
layout: layout
)
# applying fragments
old_frag_identifiers = translation.fragments.pluck(:identifier)
new_frag_identifiers, fragments_attributes =
construct_fragments_attributes(fragments_hash, translation, path)
translation.fragments_attributes = fragments_attributes
if translation.save
message = "[CMS SEEDS] Imported Translation \t #{locale}"
ComfortableMexicanSofa.logger.info(message)
# cleaning up old fragments
frags_to_remove = old_frag_identifiers - new_frag_identifiers
translation.fragments.where(identifier: frags_to_remove).destroy_all
else
message = "[CMS SEEDS] Failed to import Translation \n#{locale}"
ComfortableMexicanSofa.logger.warn(message)
end
end
# Cleaning up removed translations
translations_to_remove = old_translations - new_translations
page.translations.where(locale: translations_to_remove).destroy_all
end
# Constructing frag attributes hash that can be assigned to page or translation
# also returning list of frag identifiers so we can destroy old ones
def construct_fragments_attributes(hash, record, path)
frag_identifiers = []
frag_attributes = hash.collect do |frag_header, frag_content|
tag, identifier = frag_header.split
frag_hash = {
identifier: identifier,
tag: tag
}
# tracking fragments that need removing later
frag_identifiers << identifier
# based on tag we need to cram content in proper place and proper format
case tag
when "date", "datetime"
frag_hash[:datetime] = frag_content
when "checkbox"
frag_hash[:boolean] = frag_content
when "file", "files"
files, file_ids_destroy = files_content(record, identifier, path, frag_content)
frag_hash[:files] = files
frag_hash[:file_ids_destroy] = file_ids_destroy
else
frag_hash[:content] = frag_content
end
frag_hash
end
[frag_identifiers, frag_attributes]
end
# Preparing fragment attachments. Returns hashes with file data for
# ActiveStorage and a list of ids of old attachements to destroy
def files_content(record, identifier, path, frag_content)
# preparing attachments
files = frag_content.split("\n").collect do |filename|
file_handler = File.open(File.join(path, filename))
{
io: file_handler,
filename: filename,
content_type: MimeMagic.by_magic(file_handler)
}
end
# ensuring that old attachments get removed
ids_destroy = []
if (frag = record.fragments.find_by(identifier: identifier))
ids_destroy = frag.attachments.pluck(:id)
end
[files, ids_destroy]
end
def link_target_pages
return unless self.target_pages.present?
self.target_pages.each do |page_id, target|
if (target = site.pages.find_by(full_path: target))
@site.pages.find(page_id).update_column(:target_page_id, target.id)
end
end
end
end
end