-
Notifications
You must be signed in to change notification settings - Fork 322
/
Copy pathdotclear.rb
183 lines (147 loc) · 6.15 KB
/
dotclear.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# frozen_string_literal: true
module JekyllImport
module Importers
class Dotclear < Importer
class << self
def specify_options(c)
c.option "datafile", "--datafile PATH", "Dotclear export file."
c.option "mediafolder", "--mediafolder DIR", "Dotclear media export folder (unpacked media.zip)."
end
def require_deps
JekyllImport.require_with_fallback(%w())
end
def validate(opts)
file_path = opts["datafile"]
log_undefined_flag_error("datafile") if file_path.nil? || file_path.empty?
file_path = File.expand_path(file_path)
if File.open(file_path, "rb", &:readline).start_with?("///DOTCLEAR|")
@data = read_export(file_path)
Jekyll.logger.info "Export File:", file_path
else
Jekyll.logger.abort_with "Import Error:", "#{file_path.inspect} is not a valid Dotclear export file!"
end
assets = @data["media"]
return if !assets || assets.empty?
Jekyll.logger.info "", "Media files detected in export data."
media_dir = opts["mediafolder"]
log_undefined_flag_error("mediafolder") if media_dir.nil? || media_dir.empty?
media_dir = File.expand_path(media_dir)
log_invalid_media_dir_error(media_dir) if !File.directory?(media_dir) || Dir.empty?(media_dir)
end
def process(opts)
import_posts
import_assets(opts["mediafolder"])
Jekyll.logger.info "", "and, done!"
end
private
# Parse backup sections into a Hash of arrays.
#
# Each section is of following shape:
#
# [key alpha,beta,gamma,...]
# lorem,ipsum,dolor,...
# red,blue,green,...
#
# Returns Hash of shape:
#
# {key => [{alpha => lorem,...}, {alpha => red,...}]}
#
def read_export(file)
ignored_sections = %w(category comment link setting)
File.read(file, :encoding => "utf-8").split("\n\n").each_with_object({}) do |section, data|
next unless %r!^\[(?<key>.*?) (?<header>.*)\]\n(?<rows>.*)!m =~ section
next if ignored_sections.include?(key)
headers = header.split(",")
data[key] = rows.each_line.with_object([]) do |line, bucket|
bucket << headers.zip(sanitize_line!(line)).to_h
end
data
end
end
def register_post_tags
@data["meta"].each_with_object({}) do |entry, tags|
next unless entry["meta_type"] == "tag"
post_id = entry["post_id"]
tags[post_id] ||= []
tags[post_id] << entry["meta_id"]
end
end
def log_undefined_flag_error(label)
Jekyll.logger.abort_with "Import Error:", "--#{label} flag cannot be undefined, null or empty!"
end
def log_invalid_media_dir_error(media_dir)
Jekyll.logger.error "Import Error:", "--mediafolder should be a non-empty directory."
Jekyll.logger.abort_with "", "Please check #{media_dir.inspect}."
end
def sanitize_line!(line)
line.strip!
line.split('","').tap do |items|
items[0].delete_prefix!('"')
items[-1].delete_suffix!('"')
end
end
# -
REPLACE_MAP = {
'\"' => '"',
'\r\n' => "\n",
'\n' => "\n",
"/dotclear/public/" => "/assets/dotclear/",
"/public/" => "/assets/dotclear/",
}.freeze
REPLACE_RE = Regexp.union(REPLACE_MAP.keys)
private_constant :REPLACE_MAP, :REPLACE_RE
# -
def adjust_post_contents!(content)
content.strip!
content.gsub!(REPLACE_RE, REPLACE_MAP)
content
end
def import_posts
tags = register_post_tags
posts = @data["post"]
FileUtils.mkdir_p("_drafts") unless posts.empty?
Jekyll.logger.info "Importing posts.."
posts.each do |post|
date, title = post.values_at("post_creadt", "post_title")
path = File.join("_drafts", Date.parse(date).strftime("%Y-%m-%d-") + Jekyll::Utils.slugify(title) + ".html")
excerpt = adjust_post_contents!(post["post_excerpt_xhtml"].to_s)
excerpt = nil if excerpt.empty?
# Unlike the paradigm in Jekyll-generated HTML, `post_content_xhtml` in the export data
# doesn't begin with `post_excerpt_xhtml`.
# Instead of checking whether the excerpt content exists elsewhere in the exported content
# string, always prepend excerpt onto content with an empty line in between.
content = [excerpt, post["post_content_xhtml"]].tap(&:compact!).join("\n\n")
front_matter_data = {
"layout" => "post",
"title" => title,
"date" => date,
"lang" => post["post_lang"],
"tags" => tags[post["post_id"]],
"original_url" => post["post_url"], # URL as included in the export-file.
"excerpt" => excerpt,
}.tap(&:compact!)
Jekyll.logger.info "Creating:", path
File.write(path, "#{YAML.dump(front_matter_data)}---\n\n#{adjust_post_contents!(content)}\n")
end
end
def import_assets(src_dir)
assets = @data["media"]
FileUtils.mkdir_p("assets/dotclear") if assets && !assets.empty?
Jekyll.logger.info "Importing assets.."
assets.each do |asset|
file_path = File.join(src_dir, asset["media_file"])
if File.exist?(file_path)
dest_path = File.join("assets/dotclear", asset["media_file"])
FileUtils.mkdir_p(File.dirname(dest_path))
Jekyll.logger.info "Copying:", file_path
Jekyll.logger.info "To:", dest_path
FileUtils.cp_r file_path, dest_path
else
Jekyll.logger.info "Not found:", file_path
end
end
end
end
end
end
end