jekyll-import 0.19.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e0c80e9a51361d575c8282c0dd6f813d24cf36423fcbe891a4dcda8c2410003
4
- data.tar.gz: 8067c22cc55a955283967999590cecc4ea5f6fd212005b62919d811cb76bcc9b
3
+ metadata.gz: e0098ade7230382d4787c65ab3c17607680119727e1e9cdca587274d94e3e084
4
+ data.tar.gz: 8f1050dfa7170aa7b08462c0132ab959ffdcda31e8c469929ce123aecb64909a
5
5
  SHA512:
6
- metadata.gz: 9ea2f084457d3192cece261e1e98511e4cf0219e62afb5d3c2b50f8c957e76fe82f85ae9b5c55beebf4c8b6c15f8acbd17da00dca245253af5ddc3a9a84888b7
7
- data.tar.gz: f5abebbb51120ba3877075eaf82f4a02c07e3253011b75c996e3f1c9aa5ab9533e66775f5041702bda739513ad1e5fa600775c5e3b7e60d452422edc85507aec
6
+ metadata.gz: f32b5ac48f88293a4703c7ece13d4a9886c598b4491fa9e8b750b02f4dfab405291c095355885f823a9b23551bc69e73015487371daa9fb6a9db7ec4783b88f0
7
+ data.tar.gz: 0ba737a7d8ff767eb1bcce6bffa8af357d6c66793b36e682edfcce05245e21dd0a12d5a3d250118ae9b00dc40857db50884d2365e2419a86efce7d97e0ac52af
@@ -64,7 +64,7 @@ module Jekyll
64
64
 
65
65
  def abort_on_invalid_migrator(migrator)
66
66
  warn "Sorry, '#{migrator}' isn't a valid migrator. Valid choices:"
67
- IMPORTERS.keys.each { |k| warn "* #{k}" }
67
+ IMPORTERS.each_key { |k| warn "* #{k}" }
68
68
  raise "'#{migrator}' is not a valid migrator."
69
69
  end
70
70
  end
@@ -12,7 +12,7 @@ module JekyllImport
12
12
 
13
13
  def self.stringify_keys(hash)
14
14
  the_hash = hash.clone
15
- the_hash.keys.each do |key|
15
+ hash.each_key do |key|
16
16
  the_hash[(key.to_s rescue key) || key] = the_hash.delete(key)
17
17
  end
18
18
  the_hash
@@ -11,11 +11,8 @@ module JekyllImport
11
11
  end
12
12
 
13
13
  def self.validate(options)
14
- if options["source"].nil?
15
- raise "Missing mandatory option: --source"
16
- elsif !File.exist?(options["source"])
17
- raise Errno::ENOENT, "File not found: #{options["source"]}"
18
- end
14
+ raise "Missing mandatory option: --source" if options["source"].nil?
15
+ raise Errno::ENOENT, "File not found: #{options["source"]}" unless File.exist?(options["source"])
19
16
  end
20
17
 
21
18
  def self.require_deps
@@ -42,7 +39,6 @@ module JekyllImport
42
39
  source = options.fetch("source")
43
40
 
44
41
  listener = BloggerAtomStreamListener.new
45
-
46
42
  listener.leave_blogger_info = !options.fetch("no-blogger-info", false)
47
43
  listener.comments = options.fetch("comments", false)
48
44
 
@@ -52,7 +48,6 @@ module JekyllImport
52
48
  end
53
49
 
54
50
  options["original-url-base"] = listener.original_url_base
55
-
56
51
  postprocess(options)
57
52
  end
58
53
 
@@ -63,32 +58,32 @@ module JekyllImport
63
58
  # Returns nothing.
64
59
  def self.postprocess(options)
65
60
  # Replace internal link URL
66
- if options.fetch("replace-internal-link", false)
67
- original_url_base = options.fetch("original-url-base", nil)
68
- if original_url_base
69
- orig_url_pattern = Regexp.new(" href=([\"\'])(?:#{Regexp.escape(original_url_base)})?/([0-9]{4})/([0-9]{2})/([^\"\']+\.html)\\1")
70
-
71
- Dir.glob("_posts/*.*") do |filename|
72
- body = nil
73
- File.open(filename, "r") do |f|
74
- f.flock(File::LOCK_SH)
75
- body = f.read
76
- end
61
+ return unless options.fetch("replace-internal-link", false)
77
62
 
78
- body.gsub!(orig_url_pattern) do
79
- # for post_url
80
- quote = Regexp.last_match(1)
81
- post_file = Dir.glob("_posts/#{Regexp.last_match(2)}-#{Regexp.last_match(3)}-*-#{Regexp.last_match(4).to_s.tr("/", "-")}").first
82
- raise "Could not found: _posts/#{Regexp.last_match(2)}-#{Regexp.last_match(3)}-*-#{Regexp.last_match(4).to_s.tr("/", "-")}" if post_file.nil?
63
+ original_url_base = options.fetch("original-url-base", nil)
64
+ return unless original_url_base
83
65
 
84
- " href=#{quote}{{ site.baseurl }}{% post_url #{File.basename(post_file, ".html")} %}#{quote}"
85
- end
66
+ orig_url_pattern = Regexp.new(" href=([\"\'])(?:#{Regexp.escape(original_url_base)})?/([0-9]{4})/([0-9]{2})/([^\"\']+\.html)\\1")
86
67
 
87
- File.open(filename, "w") do |f|
88
- f.flock(File::LOCK_EX)
89
- f << body
90
- end
91
- end
68
+ Dir.glob("_posts/*.*") do |filename|
69
+ body = nil
70
+ File.open(filename, "r") do |f|
71
+ f.flock(File::LOCK_SH)
72
+ body = f.read
73
+ end
74
+
75
+ body.gsub!(orig_url_pattern) do
76
+ # for post_url
77
+ quote = Regexp.last_match(1)
78
+ post_file = Dir.glob("_posts/#{Regexp.last_match(2)}-#{Regexp.last_match(3)}-*-#{Regexp.last_match(4).to_s.tr("/", "-")}").first
79
+ raise "Could not found: _posts/#{Regexp.last_match(2)}-#{Regexp.last_match(3)}-*-#{Regexp.last_match(4).to_s.tr("/", "-")}" if post_file.nil?
80
+
81
+ " href=#{quote}{{ site.baseurl }}{% post_url #{File.basename(post_file, ".html")} %}#{quote}"
82
+ end
83
+
84
+ File.open(filename, "w") do |f|
85
+ f.flock(File::LOCK_EX)
86
+ f << body
92
87
  end
93
88
  end
94
89
  end
@@ -118,9 +113,7 @@ module JekyllImport
118
113
 
119
114
  @in_entry_elem = { :meta => {}, :body => nil }
120
115
  when "title"
121
- if @in_entry_elem
122
- raise 'only <title type="text"></title> is supported' if attrs["type"] != "text"
123
- end
116
+ raise 'only <title type="text"></title> is supported' if @in_entry_elem && attrs["type"] != "text"
124
117
  when "category"
125
118
  if @in_entry_elem
126
119
  if attrs["scheme"] == "http://www.blogger.com/atom/ns#"
@@ -150,25 +143,23 @@ module JekyllImport
150
143
  end
151
144
 
152
145
  def text(text)
153
- if @in_entry_elem
154
- case @tag_bread.last
155
- when "id"
156
- @in_entry_elem[:meta][:id] = text
157
- when "published"
158
- @in_entry_elem[:meta][:published] = text
159
- when "updated"
160
- @in_entry_elem[:meta][:updated] = text
161
- when "title"
162
- @in_entry_elem[:meta][:title] = text
163
- when "content"
164
- @in_entry_elem[:body] = text
165
- when "name"
166
- @in_entry_elem[:meta][:author] = text if @tag_bread[-2..-1] == %w(author name)
167
- when "app:draft"
168
- if @tag_bread[-2..-1] == %w(app:control app:draft)
169
- @in_entry_elem[:meta][:draft] = true if text == "yes"
170
- end
171
- end
146
+ return unless @in_entry_elem
147
+
148
+ case @tag_bread.last
149
+ when "id"
150
+ @in_entry_elem[:meta][:id] = text
151
+ when "published"
152
+ @in_entry_elem[:meta][:published] = text
153
+ when "updated"
154
+ @in_entry_elem[:meta][:updated] = text
155
+ when "title"
156
+ @in_entry_elem[:meta][:title] = text
157
+ when "content"
158
+ @in_entry_elem[:body] = text
159
+ when "name"
160
+ @in_entry_elem[:meta][:author] = text if @tag_bread[-2..-1] == %w(author name)
161
+ when "app:draft"
162
+ @in_entry_elem[:meta][:draft] = true if @tag_bread[-2..-1] == %w(app:control app:draft) && text == "yes"
172
163
  end
173
164
  end
174
165
 
@@ -186,7 +177,7 @@ module JekyllImport
186
177
 
187
178
  FileUtils.mkdir_p(target_dir)
188
179
 
189
- file_name = URI.decode("#{post_data[:filename]}.html")
180
+ file_name = URI.decode_www_form_component("#{post_data[:filename]}.html")
190
181
  File.open(File.join(target_dir, file_name), "w") do |f|
191
182
  f.flock(File::LOCK_EX)
192
183
 
@@ -258,25 +249,22 @@ module JekyllImport
258
249
  body = @in_entry_elem[:body]
259
250
 
260
251
  # body escaping associated with liquid
261
- body.gsub!(%r!{{!, '{{ "{{" }}') if body =~ %r!{{!
262
- body.gsub!(%r!{%!, '{{ "{%" }}') if body =~ %r!{%!
252
+ body.gsub!(%r!{{!, '{{ "{{" }}') if %r!{{!.match?(body)
253
+ body.gsub!(%r!{%!, '{{ "{%" }}') if %r!{%!.match?(body)
263
254
 
264
255
  { :filename => filename, :header => header, :body => body }
265
256
  elsif @in_entry_elem[:meta][:kind] == "comment"
266
257
  timestamp = Time.parse(@in_entry_elem[:meta][:published]).strftime("%Y-%m-%d")
267
- if @in_entry_elem[:meta][:original_url]
268
- @comment_seq ||= 1
258
+ raise "Original URL is missing" unless @in_entry_elem[:meta][:original_url]
269
259
 
270
- original_uri = URI.parse(@in_entry_elem[:meta][:original_url])
271
- original_path = original_uri.path.to_s
272
- filename = format("%s-%s-%s", timestamp, File.basename(original_path, File.extname(original_path)), @comment_seq)
260
+ @comment_seq ||= 1
273
261
 
274
- @comment_seq += 1
262
+ original_uri = URI.parse(@in_entry_elem[:meta][:original_url])
263
+ original_path = original_uri.path.to_s
264
+ filename = format("%s-%s-%s", timestamp, File.basename(original_path, File.extname(original_path)), @comment_seq)
275
265
 
276
- @original_url_base = "#{original_uri.scheme}://#{original_uri.host}"
277
- else
278
- raise "Original URL is missing"
279
- end
266
+ @comment_seq += 1
267
+ @original_url_base = "#{original_uri.scheme}://#{original_uri.host}"
280
268
 
281
269
  header = {
282
270
  "date" => @in_entry_elem[:meta][:published],
@@ -291,8 +279,8 @@ module JekyllImport
291
279
  body = @in_entry_elem[:body]
292
280
 
293
281
  # body escaping associated with liquid
294
- body.gsub!(%r!{{!, '{{ "{{" }}') if body =~ %r!{{!
295
- body.gsub!(%r!{%!, '{{ "{%" }}') if body =~ %r!{%!
282
+ body.gsub!(%r!{{!, '{{ "{{" }}') if %r!{{!.match?(body)
283
+ body.gsub!(%r!{%!, '{{ "{%" }}') if %r!{%!.match?(body)
296
284
 
297
285
  { :filename => filename, :header => header, :body => body }
298
286
  end
@@ -19,15 +19,17 @@ module JekyllImport
19
19
  nr.teaser,
20
20
  n.created,
21
21
  n.status,
22
+ ua.dst AS alias,
22
23
  n.type,
23
24
  GROUP_CONCAT( td.name SEPARATOR '|' ) AS 'tags'
24
- FROM #{prefix}node_revisions AS nr,
25
+ FROM #{prefix}node_revisions AS nr, url_alias AS ua,
25
26
  #{prefix}node AS n
26
27
  LEFT OUTER JOIN #{prefix}term_node AS tn ON tn.nid = n.nid
27
28
  LEFT OUTER JOIN #{prefix}term_data AS td ON tn.tid = td.tid
28
29
  WHERE (#{types})
29
30
  AND n.vid = nr.vid
30
- GROUP BY n.nid
31
+ AND ua.src = CONCAT( 'node/', n.nid)
32
+ GROUP BY n.nid, ua.dst
31
33
  SQL
32
34
 
33
35
  query
@@ -44,9 +46,11 @@ SQL
44
46
 
45
47
  data = {
46
48
  "excerpt" => summary,
47
- "categories" => tags.split("|"),
49
+ "categories" => tags.split("|").uniq,
48
50
  }
49
51
 
52
+ data["permalink"] = "/" + sql_post_data[:alias] if sql_post_data[:alias]
53
+
50
54
  [data, content]
51
55
  end
52
56
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll-import/importers/drupal_common"
4
+
5
+ module JekyllImport
6
+ module Importers
7
+ class Drupal8 < Importer
8
+ include DrupalCommon
9
+ extend DrupalCommon::ClassMethods
10
+
11
+ def self.build_query(prefix, types, engine)
12
+ types = types.join("' OR n.type = '")
13
+ types = "n.type = '#{types}'"
14
+
15
+ tag_group = if engine == "postgresql"
16
+ <<POSTGRESQL
17
+ (SELECT STRING_AGG(td.name, '|')
18
+ FROM #{prefix}taxonomy_term_field_data td, #{prefix}taxonomy_index ti
19
+ WHERE ti.tid = td.tid AND ti.nid = n.nid) AS tags
20
+ POSTGRESQL
21
+ else
22
+ <<SQL
23
+ (SELECT GROUP_CONCAT(td.name SEPARATOR '|')
24
+ FROM #{prefix}taxonomy_term_field_data td, #{prefix}taxonomy_index ti
25
+ WHERE ti.tid = td.tid AND ti.nid = n.nid) AS 'tags'
26
+ SQL
27
+ end
28
+
29
+ query = <<QUERY
30
+ SELECT n.nid,
31
+ n.title,
32
+ nb.body_value,
33
+ nb.body_summary,
34
+ n.created,
35
+ n.status,
36
+ n.type,
37
+ #{tag_group}
38
+ FROM #{prefix}node_field_data AS n
39
+ LEFT JOIN #{prefix}node__body AS nb
40
+ ON nb.entity_id = n.nid
41
+ WHERE (#{types})
42
+ QUERY
43
+
44
+ query
45
+ end
46
+
47
+ def self.aliases_query(prefix)
48
+ "SELECT source, alias FROM #{prefix}url_alias WHERE source = ?"
49
+ end
50
+
51
+ def self.post_data(sql_post_data)
52
+ content = sql_post_data[:body_value].to_s
53
+ summary = sql_post_data[:body_summary].to_s
54
+ tags = (sql_post_data[:tags] || "").downcase.strip
55
+
56
+ data = {
57
+ "excerpt" => summary,
58
+ "categories" => tags.split("|"),
59
+ }
60
+
61
+ [data, content]
62
+ end
63
+ end
64
+ end
65
+ end
@@ -68,6 +68,7 @@ module JekyllImport
68
68
  src_dir = conf["source"]
69
69
 
70
70
  dirs = {
71
+ :_aliases => src_dir,
71
72
  :_posts => File.join(src_dir, "_posts").to_s,
72
73
  :_drafts => File.join(src_dir, "_drafts").to_s,
73
74
  :_layouts => Jekyll.sanitized_path(src_dir, conf["layouts_dir"].to_s),
@@ -146,10 +147,10 @@ module JekyllImport
146
147
 
147
148
  if partition.first.length.positive?
148
149
  dir = "#{partition.first}/"
149
- FileUtils.mkdir_p partition.first
150
+ FileUtils.mkdir_p "#{dirs[:_aliases]}/#{dir}"
150
151
  end
151
152
 
152
- File.open("#{dir}#{file}.md", "w") do |f|
153
+ File.open("#{dirs[:_aliases]}/#{dir}#{file}.md", "w") do |f|
153
154
  f.puts "---"
154
155
  f.puts "layout: refresh"
155
156
  f.puts "permalink: #{dir}#{file}/"
@@ -48,7 +48,7 @@ module JekyllImport
48
48
  title = file_content.scan(regexp[:title]).first.to_s.strip
49
49
  prerex = file_content.scan(regexp[:perex]).first.to_s.strip
50
50
  published_on = DateTime.parse(post[:published_on]) rescue File.mtime(File.dirname(f))
51
- meta = meta_content ? YAML.safe_load(meta_content.scan(regexp[:meta]).to_s) : {}
51
+ meta = meta_content ? YAML.safe_load(meta_content.scan(regexp[:meta]).to_s) : {}
52
52
  meta["title"] = title
53
53
  meta["layout"] = "post"
54
54
 
@@ -31,7 +31,7 @@ module JekyllImport
31
31
  rubygems
32
32
  sequel
33
33
  mysql2
34
- fastercsv
34
+ csv
35
35
  fileutils
36
36
  ))
37
37
  end
@@ -69,7 +69,7 @@ module JekyllImport
69
69
  # comments:: If true, output comments in _comments directory, like the one
70
70
  # mentioned at https://github.com/mpalmer/jekyll-static-comments/
71
71
  def self.process(options)
72
- options = default_options.merge(options)
72
+ options = default_options.merge(options)
73
73
 
74
74
  comments = options.fetch("comments")
75
75
  posts_name_by_id = {} if comments
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JekyllImport
4
+ module Importers
5
+ class Pluxml < Importer
6
+ def self.require_deps
7
+ JekyllImport.require_with_fallback(%w(
8
+ nokogiri
9
+ fileutils
10
+ safe_yaml
11
+ ))
12
+ end
13
+
14
+ def self.specify_options(c)
15
+ c.option "source", "--source NAME", "The PluXML data directory to import"
16
+ c.option "layout", "--layout NAME", "The layout to apply"
17
+ c.option "avoid_liquid", "--avoid_liquid true", "Will add render_with_liquid: false in frontmatter"
18
+ end
19
+
20
+ def self.validate(options)
21
+ abort "Missing mandatory option --source." if options["source"].nil?
22
+ # no layout option, layout by default is post
23
+ options["layout"] = "post" if options["layout"].nil?
24
+ # no avoid_liquid option, avoid_liquid by default is false
25
+ options["avoid_liquid"] = false if options["avoid_liquid"].nil?
26
+ end
27
+
28
+ def self.process(options)
29
+ source = options.fetch("source")
30
+ layout = options.fetch("layout")
31
+ avoid_liquid = options.fetch("avoid_liquid")
32
+
33
+ FileUtils.mkdir_p("_posts")
34
+ FileUtils.mkdir_p("_drafts")
35
+
36
+ # for each XML file in source location
37
+ Dir.glob("*.xml", :base => source).each do |df|
38
+ df = File.join(source, df)
39
+ filename = File.basename(df, ".*")
40
+
41
+ # prepare post file name in Jekyll format
42
+ a_filename = filename.split(".")
43
+ post_name = a_filename.pop
44
+ file_date = a_filename.pop
45
+ post_date = file_date[0..3] + "-" + file_date[4..5] + "-" + file_date[6..7]
46
+
47
+ # if draft, only take post name
48
+ if filename.split(".")[1].split(",")[0] == "draft"
49
+ directory = "_drafts"
50
+ name = post_name.to_s
51
+ # if post, post date precede post name
52
+ else
53
+ directory = "_posts"
54
+ name = "#{post_date}-#{post_name}"
55
+ end
56
+
57
+ xml = File.open(df) { |f| Nokogiri::XML(f) }
58
+ raise "There doesn't appear to be any XML items at the source (#{df}) provided." unless xml
59
+
60
+ doc = xml.xpath("document")
61
+ header = {
62
+ "layout" => layout,
63
+ "title" => doc.xpath("title").text,
64
+ "tags" => doc.xpath("tags").text.split(", "),
65
+ }
66
+ header["render_with_liquid"] = false if avoid_liquid
67
+
68
+ path = File.join(directory, "#{name}.html")
69
+ File.open(path, "w") do |f|
70
+ f.puts header.to_yaml
71
+ f.puts "---\n\n"
72
+ f.puts doc.xpath("chapo").text
73
+ f.puts doc.xpath("content").text
74
+ end
75
+
76
+ Jekyll.logger.info "Wrote file #{path} successfully!"
77
+ end
78
+ nil
79
+ end
80
+ end
81
+ end
82
+ end
@@ -6,6 +6,7 @@ module JekyllImport
6
6
  def self.specify_options(c)
7
7
  c.option "source", "--source NAME", "The RSS file or URL to import"
8
8
  c.option "tag", "--tag NAME", "Add a tag to posts"
9
+ c.option "render_audio", "--render_audio", "Render <audio> element as necessary"
9
10
  end
10
11
 
11
12
  def self.validate(options)
@@ -30,8 +31,6 @@ module JekyllImport
30
31
  # Returns nothing.
31
32
  def self.process(options)
32
33
  source = options.fetch("source")
33
- frontmatter = options.fetch("frontmatter", [])
34
- body = options.fetch("body", ["description"])
35
34
 
36
35
  content = ""
37
36
  open(source) { |s| content = s.read }
@@ -40,37 +39,56 @@ module JekyllImport
40
39
  raise "There doesn't appear to be any RSS items at the source (#{source}) provided." unless rss
41
40
 
42
41
  rss.items.each do |item|
43
- formatted_date = item.date.strftime("%Y-%m-%d")
44
- post_name = Jekyll::Utils.slugify(item.title, :mode => "latin")
45
- name = "#{formatted_date}-#{post_name}"
42
+ write_rss_item(item, options)
43
+ end
44
+ end
46
45
 
47
- header = {
48
- "layout" => "post",
49
- "title" => item.title,
50
- }
46
+ def self.write_rss_item(item, options)
47
+ frontmatter = options.fetch("frontmatter", [])
48
+ body = options.fetch("body", ["description"])
49
+ render_audio = options.fetch("render_audio", false)
51
50
 
52
- header["tag"] = options["tag"] unless options["tag"].nil? || options["tag"].empty?
51
+ formatted_date = item.date.strftime("%Y-%m-%d")
52
+ post_name = Jekyll::Utils.slugify(item.title, :mode => "latin")
53
+ name = "#{formatted_date}-#{post_name}"
54
+ audio = render_audio && item.enclosure.url
53
55
 
54
- frontmatter.each do |value|
55
- header[value] = item.send(value)
56
- end
56
+ header = {
57
+ "layout" => "post",
58
+ "title" => item.title,
59
+ }
57
60
 
58
- output = +""
61
+ header["tag"] = options["tag"] unless options["tag"].nil? || options["tag"].empty?
59
62
 
60
- body.each do |row|
61
- output << item.send(row).to_s
62
- end
63
+ frontmatter.each do |value|
64
+ header[value] = item.send(value)
65
+ end
63
66
 
64
- output.strip!
65
- output = item.content_encoded if output.empty?
67
+ output = +""
68
+
69
+ body.each do |row|
70
+ output << item.send(row).to_s
71
+ end
66
72
 
67
- FileUtils.mkdir_p("_posts")
73
+ output.strip!
74
+ output = item.content_encoded if output.empty?
68
75
 
69
- File.open("_posts/#{name}.html", "w") do |f|
70
- f.puts header.to_yaml
71
- f.puts "---\n\n"
72
- f.puts output
76
+ FileUtils.mkdir_p("_posts")
77
+
78
+ File.open("_posts/#{name}.html", "w") do |f|
79
+ f.puts header.to_yaml
80
+ f.puts "---\n\n"
81
+
82
+ if audio
83
+ f.puts <<~HTML
84
+ <audio controls="">
85
+ <source src="#{audio}" type="audio/mpeg">
86
+ Your browser does not support the audio element.
87
+ </audio>
88
+ HTML
73
89
  end
90
+
91
+ f.puts output
74
92
  end
75
93
  end
76
94
  end
@@ -11,25 +11,31 @@ module JekyllImport
11
11
  fileutils
12
12
  safe_yaml
13
13
  unidecode
14
+ nokogiri
14
15
  )
15
16
  )
16
17
  end
17
18
 
18
19
  def self.specify_options(c)
19
- c.option "dbname", "--dbname DB", "Database name (default: '')"
20
- c.option "socket", "--socket SOCKET", "Database socket (default: '')"
21
- c.option "user", "--user USER", "Database user name (default: '')"
22
- c.option "password", "--password PW", "Database user's password (default: '')"
23
- c.option "host", "--host HOST", "Database host name (default: 'localhost')"
24
- c.option "port", "--port PORT", "Custom database port connect to (default: 3306)"
25
- c.option "table_prefix", "--table_prefix PREFIX", "Table prefix name (default: 'serendipity_')"
26
- c.option "clean_entities", "--clean_entities", "Whether to clean entities (default: true)"
27
- c.option "comments", "--comments", "Whether to import comments (default: true)"
28
- c.option "categories", "--categories", "Whether to import categories (default: true)"
29
- c.option "tags", "--tags", "Whether to import tags (default: true)"
30
- c.option "drafts", "--drafts", "Whether to export drafts as well"
31
- c.option "markdown", "--markdown", "convert into markdown format (default: false)"
32
- c.option "permalinks", "--permalinks", "preserve S9Y permalinks (default: false)"
20
+ c.option "dbname", "--dbname DB", "Database name (default: '')"
21
+ c.option "socket", "--socket SOCKET", "Database socket (default: '')"
22
+ c.option "user", "--user USER", "Database user name (default: '')"
23
+ c.option "password", "--password PW", "Database user's password (default: '')"
24
+ c.option "host", "--host HOST", "Database host name (default: 'localhost')"
25
+ c.option "port", "--port PORT", "Custom database port connect to (default: 3306)"
26
+ c.option "table_prefix", "--table_prefix PREFIX", "Table prefix name (default: 'serendipity_')"
27
+ c.option "clean_entities", "--clean_entities", "Whether to clean entities (default: true)"
28
+ c.option "comments", "--comments", "Whether to import comments (default: true)"
29
+ c.option "categories", "--categories", "Whether to import categories (default: true)"
30
+ c.option "tags", "--tags", "Whether to import tags (default: true)"
31
+ c.option "drafts", "--drafts", "Whether to export drafts as well"
32
+ c.option "markdown", "--markdown", "convert into markdown format (default: false)"
33
+ c.option "permalinks", "--permalinks", "preserve S9Y permalinks (default: false)"
34
+ c.option "excerpt_separator", "--excerpt_separator", "Demarkation for excerpts (default: '<a id=\"extended\"></a>')"
35
+ c.option "includeentry", "--includeentry", "Replace macros from the includeentry plugin (default: false)"
36
+ c.option "imgfig", "--imgfig", "Replace nested img and youtube divs with HTML figure tags (default: true)"
37
+ c.option "linebreak", "--linebreak", "Line break processing: wp, nokogiri, ignore (default: wp)"
38
+ c.option "relative", "--relative", "Convert links with this prefix to relative (default:nil)"
33
39
  end
34
40
 
35
41
  # Main migrator function. Call this to perform the migration.
@@ -56,36 +62,64 @@ module JekyllImport
56
62
  # :categories:: If true, save the post's categories in its
57
63
  # YAML front matter. Default: true.
58
64
  # :tags:: If true, save the post's tags in its
59
- # YAML front matter. Default: true.
65
+ # YAML front matter, in lowercase. Default: true.
60
66
  # :extension:: Set the post extension. Default: "html"
61
67
  # :drafts:: If true, export drafts as well
62
68
  # Default: true.
63
69
  # :markdown:: If true, convert the content to markdown
64
70
  # Default: false
65
71
  # :permalinks:: If true, save the post's original permalink in its
66
- # YAML front matter. Default: false.
72
+ # YAML front matter. If the 'entryproperties' plugin
73
+ # was used, its permalink will become the canonical
74
+ # permalink, and any other will become redirects.
75
+ # Default: false.
76
+ # :excerpt_separator:: A string to use to separate the excerpt (body
77
+ # in S9Y) from the rest of the article (extended
78
+ # body in S9Y). Default: "<a id=\"extended\"></a>".
79
+ # :includentry:: Replace macros from the includentry plugin - these are
80
+ # the [s9y-include-entry] and [s9y-include-block] macros.
81
+ # Default: false.
82
+ # :imgfig:: Replace S9Y image-comment divs with an HTML figure
83
+ # div and figcaption, if applicable. Works for img and
84
+ # iframe.
85
+ # Default: true.
67
86
  #
87
+ # :linebreak:: When set to the default "wp", line breaks in entries
88
+ # will be processed WordPress style, by replacing double
89
+ # line breaks with HTML p tags, and remaining single
90
+ # line breaks with HTML br tags. When set to "nokogiri",
91
+ # entries will be loaded into Nokogiri and formatted as
92
+ # an XHTML fragment. When set to "ignore", line breaks
93
+ # will not be replaced at all.
94
+ # Default: wp
95
+ # :relative:: Replace absolute links (http://:relative:/foo)
96
+ # to relative links (/foo).
97
+
68
98
  def self.process(opts)
69
99
  options = {
70
- :user => opts.fetch("user", ""),
71
- :pass => opts.fetch("password", ""),
72
- :host => opts.fetch("host", "localhost"),
73
- :port => opts.fetch("port", 3306),
74
- :socket => opts.fetch("socket", nil),
75
- :dbname => opts.fetch("dbname", ""),
76
- :table_prefix => opts.fetch("table_prefix", "serendipity_"),
77
- :clean_entities => opts.fetch("clean_entities", true),
78
- :comments => opts.fetch("comments", true),
79
- :categories => opts.fetch("categories", true),
80
- :tags => opts.fetch("tags", true),
81
- :extension => opts.fetch("extension", "html"),
82
- :drafts => opts.fetch("drafts", true),
83
- :markdown => opts.fetch("markdown", false),
84
- :permalinks => opts.fetch("permalinks", false),
100
+ :user => opts.fetch("user", ""),
101
+ :pass => opts.fetch("password", ""),
102
+ :host => opts.fetch("host", "localhost"),
103
+ :port => opts.fetch("port", 3306),
104
+ :socket => opts.fetch("socket", nil),
105
+ :dbname => opts.fetch("dbname", ""),
106
+ :table_prefix => opts.fetch("table_prefix", "serendipity_"),
107
+ :clean_entities => opts.fetch("clean_entities", true),
108
+ :comments => opts.fetch("comments", true),
109
+ :categories => opts.fetch("categories", true),
110
+ :tags => opts.fetch("tags", true),
111
+ :extension => opts.fetch("extension", "html"),
112
+ :drafts => opts.fetch("drafts", true),
113
+ :markdown => opts.fetch("markdown", false),
114
+ :permalinks => opts.fetch("permalinks", false),
115
+ :excerpt_separator => opts.fetch("excerpt_separator", "<a id=\"extended\"></a>"),
116
+ :includeentry => opts.fetch("includeentry", false),
117
+ :imgfig => opts.fetch("imgfig", true),
118
+ :linebreak => opts.fetch("linebreak", "wp"),
119
+ :relative => opts.fetch("relative", nil),
85
120
  }
86
121
 
87
122
  options[:clean_entities] = require_if_available("htmlentities", "clean_entities") if options[:clean_entities]
88
-
89
123
  options[:markdown] = require_if_available("reverse_markdown", "markdown") if options[:markdown]
90
124
 
91
125
  FileUtils.mkdir_p("_posts")
@@ -120,6 +154,7 @@ module JekyllImport
120
154
 
121
155
  posts_query = "
122
156
  SELECT
157
+ 'post' AS `type`,
123
158
  entries.ID AS `id`,
124
159
  entries.isdraft AS `isdraft`,
125
160
  entries.title AS `title`,
@@ -154,36 +189,41 @@ module JekyllImport
154
189
  name = format("%02d-%02d-%02d-%s.%s", date.year, date.month, date.day, slug, extension)
155
190
 
156
191
  content = post[:body].to_s
157
- content += "\n\n" + post[:body_extended].to_s unless post[:body_extended].to_s.empty?
192
+ extended_content = post[:body_extended].to_s
158
193
 
194
+ content += options[:excerpt_separator] + extended_content unless extended_content.nil? || extended_content.strip.empty?
195
+
196
+ content = process_includeentry(content, db, options) if options[:includeentry]
197
+ content = process_img_div(content) if options[:imgfig]
159
198
  content = clean_entities(content) if options[:clean_entities]
199
+ content = content.gsub(%r!href=(["'])http://#{options[:relative]}!, 'href=\1') if options[:relative]
160
200
 
161
201
  content = ReverseMarkdown.convert(content) if options[:markdown]
162
202
 
163
203
  categories = process_categories(db, options, post)
164
204
  comments = process_comments(db, options, post)
165
205
  tags = process_tags(db, options, post)
166
- permalink = process_permalink(db, options, post)
206
+ all_permalinks = process_permalink(db, options, post)
207
+ primary_permalink = all_permalinks.shift
208
+ supplemental_permalinks = all_permalinks unless all_permalinks.empty?
167
209
 
168
210
  # Get the relevant fields as a hash, delete empty fields and
169
211
  # convert to YAML for the header.
170
212
  data = {
171
- "layout" => post[:type].to_s,
172
- "status" => status.to_s,
173
- "published" => status.to_s == "draft" ? nil : (status.to_s == "published"),
174
- "title" => title.to_s,
175
- "author" => {
176
- "display_name" => post[:author].to_s,
177
- "login" => post[:author_login].to_s,
178
- "email" => post[:author_email].to_s,
179
- },
180
- "author_login" => post[:author_login].to_s,
181
- "author_email" => post[:author_email].to_s,
182
- "date" => date.to_s,
183
- "permalink" => options[:permalinks] ? permalink : nil,
184
- "categories" => options[:categories] ? categories : nil,
185
- "tags" => options[:tags] ? tags : nil,
186
- "comments" => options[:comments] ? comments : nil,
213
+ "layout" => post[:type].to_s,
214
+ "status" => status.to_s,
215
+ "published" => status.to_s == "draft" ? nil : (status.to_s == "published"),
216
+ "title" => title.to_s,
217
+ "author" => post[:author].to_s,
218
+ "author_login" => post[:author_login].to_s,
219
+ "author_email" => post[:author_email].to_s,
220
+ "date" => date.to_s,
221
+ "permalink" => options[:permalinks] ? primary_permalink : nil,
222
+ "redirect_from" => options[:permalinks] ? supplemental_permalinks : nil,
223
+ "categories" => options[:categories] ? categories : nil,
224
+ "tags" => options[:tags] ? tags : nil,
225
+ "comments" => options[:comments] ? comments : nil,
226
+ "excerpt_separator" => extended_content.empty? ? nil : options[:excerpt_separator],
187
227
  }.delete_if { |_k, v| v.nil? || v == "" }.to_yaml
188
228
 
189
229
  if post[:type] == "page"
@@ -195,11 +235,21 @@ module JekyllImport
195
235
  filename = "_posts/#{name}"
196
236
  end
197
237
 
238
+ content = case options[:linebreak]
239
+ when "nokogiri"
240
+ Nokogiri::HTML.fragment(content).to_xhtml
241
+ when "ignore"
242
+ content
243
+ else
244
+ # "wp" is the only remaining option, and the default
245
+ Util.wpautop(content)
246
+ end
247
+
198
248
  # Write out the data and content to file
199
249
  File.open(filename, "w") do |f|
200
250
  f.puts data
201
251
  f.puts "---"
202
- f.puts Util.wpautop(content)
252
+ f.puts content
203
253
  end
204
254
  end
205
255
 
@@ -207,10 +257,154 @@ module JekyllImport
207
257
  require gem_name
208
258
  true
209
259
  rescue LoadError
210
- warn "Could not require '#{gem_name}', so the :#{option_name} option is now disabled."
260
+ Jekyll.logger.warn "s9y database:", "Could not require '#{gem_name}', so the :#{option_name} option is now disabled."
211
261
  true
212
262
  end
213
263
 
264
+ def self.process_includeentry(text, db, options)
265
+ return text unless options[:includeentry]
266
+
267
+ result = text
268
+
269
+ px = options[:table_prefix]
270
+
271
+ props = text.scan(%r!(\[s9y-include-entry:([0-9]+):([^:]+)\])!)
272
+ blocks = text.scan(%r!(\[s9y-include-block:([0-9]+):?([^:]+)?\])!)
273
+
274
+ props.each do |match|
275
+ macro = match[0]
276
+ id = match[1]
277
+ replacement = ""
278
+ if match[2].start_with?("prop=")
279
+ prop = match[2].sub("prop=", "")
280
+ cquery = get_property_query(px, id, prop)
281
+ else
282
+ prop = match[2]
283
+ cquery = get_value_query(px, id, prop)
284
+ end
285
+ db[cquery].each do |row|
286
+ replacement << row[:txt]
287
+ end
288
+ result = result.sub(macro, replacement)
289
+ end
290
+
291
+ blocks.each do |match|
292
+ macro = match[0]
293
+ id = match[1]
294
+ replacement = ""
295
+ # match[2] *could* be 'template', but we can't run it through Smarty, so we ignore it
296
+ cquery = %(
297
+ SELECT
298
+ px.body AS `txt`
299
+ FROM
300
+ #{px}staticblocks AS px
301
+ WHERE
302
+ id = '#{id}'
303
+ )
304
+ db[cquery].each do |row|
305
+ replacement << row[:txt]
306
+ end
307
+ result = result.sub(macro, replacement)
308
+ end
309
+
310
+ result
311
+ end
312
+
313
+ def get_property_query(px, id, prop)
314
+ %(
315
+ SELECT
316
+ px.value AS `txt`
317
+ FROM
318
+ #{px}entryproperties AS px
319
+ WHERE
320
+ entryid = '#{id}' AND
321
+ property = '#{prop}'
322
+ )
323
+ end
324
+
325
+ def get_value_query(px, id, prop)
326
+ %(
327
+ SELECT
328
+ px.#{prop} AS `txt`
329
+ FROM
330
+ #{px}entries AS px
331
+ WHERE
332
+ entryid = '#{id}'
333
+ )
334
+ end
335
+
336
+ # Replace .serendipity_imageComment_* blocks
337
+ def self.process_img_div(text)
338
+ caption_classes = [
339
+ ".serendipity_imageComment_left",
340
+ ".serendipity_imageComment_right",
341
+ ".serendipity_imageComment_center",
342
+ ]
343
+
344
+ noko = Nokogiri::HTML.fragment(text)
345
+ noko.css(caption_classes.join(",")).each do |imgcaption|
346
+ block_attrs = get_block_attrs(imgcaption)
347
+
348
+ # Is this a thumbnail to a bigger/other image?
349
+ big_link = imgcaption.at_css(".serendipity_image_link")
350
+ big_link ||= imgcaption.at_xpath(".//a[.//img]")
351
+
352
+ # The caption (if any) may have raw HTML
353
+ caption_elem = imgcaption.at_css(".serendipity_imageComment_txt")
354
+ caption = ""
355
+ caption = "<figcaption>#{caption_elem.inner_html}</figcaption>" if caption_elem
356
+
357
+ image_node = imgcaption.at_css("img")
358
+ if image_node
359
+ attrs = get_media_attrs(image_node)
360
+ media = "<img #{attrs}/>"
361
+ else
362
+ iframe_node = imgcaption.at_css("iframe")
363
+ if iframe_node
364
+ attrs = get_media_attrs(iframe_node)
365
+ media = "<iframe #{attrs}'></iframe>"
366
+ else
367
+ Jekyll.logger.warn "s9y database:", "Unrecognized media block: #{imgcaption}"
368
+ return text
369
+ end
370
+ end
371
+
372
+ # Wrap media in link, if any
373
+ if big_link
374
+ big = big_link.attribute("href")
375
+ media = "<a href='#{big}'>#{media}</a>"
376
+ end
377
+
378
+ # Replace HTML with clean media source, wrapped in figure
379
+ imgcaption.replace("<figure #{block_attrs}#{media}#{caption}</figure>")
380
+ end
381
+
382
+ noko.to_s
383
+ end
384
+
385
+ def get_media_attrs(node)
386
+ width = node.attribute("width")
387
+ width = "width='#{width}'" if width
388
+ height = node.attribute("height")
389
+ height = "height='#{height}'" if height
390
+ alt = node.attribute("alt")
391
+ alt = "alt='#{alt}'" if alt
392
+ src = "src='" + node.attribute("src") + "'"
393
+ [src, width, height, alt].join(" ")
394
+ end
395
+
396
+ def get_block_attrs(imgcaption)
397
+ # Extract block-level attributes
398
+ float = imgcaption.attribute("class").value.sub("serendipity_imageComment_", "")
399
+ float = "class='figure-#{float}'"
400
+ style = imgcaption.attribute("style")
401
+ style = " style='#{style.value}'" if style
402
+ # Don't lose good data
403
+ mdbnum = imgcaption.search(".//comment()").text.strip.sub("s9ymdb:", "")
404
+ mdb = "<!-- mdb='#{mdbnum}' -->" if mdbnum
405
+ [float, style, mdb].join(" ")
406
+ end
407
+
214
408
  def self.process_categories(db, options, post)
215
409
  return [] unless options[:categories]
216
410
 
@@ -278,7 +472,7 @@ module JekyllImport
278
472
  end
279
473
 
280
474
  def self.process_tags(db, options, post)
281
- return [] unless options[:categories]
475
+ return [] unless options[:tags]
282
476
 
283
477
  px = options[:table_prefix]
284
478
 
@@ -293,18 +487,36 @@ module JekyllImport
293
487
 
294
488
  db[cquery].each_with_object([]) do |tag, tags|
295
489
  tags << if options[:clean_entities]
296
- clean_entities(tag[:name])
490
+ clean_entities(tag[:name]).downcase
297
491
  else
298
- tag[:name]
492
+ tag[:name].downcase
299
493
  end
300
494
  end
301
495
  end
302
496
 
303
497
  def self.process_permalink(db, options, post)
304
- return unless options[:permalinks]
498
+ return [] unless options[:permalinks]
499
+
500
+ permalinks = []
305
501
 
306
502
  px = options[:table_prefix]
307
503
 
504
+ if db.table_exists?("#{px}entryproperties")
505
+ pquery = %(
506
+ SELECT
507
+ props.value AS `permalink`
508
+ FROM
509
+ #{px}entryproperties AS props
510
+ WHERE
511
+ props.entryid = '#{post[:id]}' AND
512
+ props.property = 'permalink'
513
+ )
514
+ db[pquery].each do |link|
515
+ plink = link[:permalink].to_s
516
+ permalinks << plink unless plink.end_with? "/UNKNOWN.html"
517
+ end
518
+ end
519
+
308
520
  cquery = %(
309
521
  SELECT
310
522
  permalinks.permalink AS `permalink`
@@ -316,8 +528,10 @@ module JekyllImport
316
528
  )
317
529
 
318
530
  db[cquery].each do |link|
319
- return "/#{link[:permalink]}"
531
+ permalinks << "/#{link[:permalink]}"
320
532
  end
533
+
534
+ permalinks
321
535
  end
322
536
 
323
537
  def self.clean_entities(text)
@@ -266,7 +266,7 @@ module JekyllImport
266
266
  lang = "python"
267
267
  start = i
268
268
  elsif block
269
- lang = "javascript" if line =~ %r!;$!
269
+ lang = "javascript" if %r!;$!.match?(line)
270
270
  block = line =~ indent && i < lines.size - 1 # Also handle EOF
271
271
  unless block
272
272
  lines[start] = "{% highlight #{lang} %}"
@@ -283,7 +283,7 @@ module JekyllImport
283
283
  return url unless @grab_images
284
284
 
285
285
  path = "tumblr_files/#{url.split("/").last}"
286
- path += ext unless path =~ %r!#{ext}$!
286
+ path += ext unless %r!#{ext}$!.match?(path)
287
287
  FileUtils.mkdir_p "tumblr_files"
288
288
 
289
289
  # Don't fetch if we've already cached this file
@@ -55,7 +55,7 @@ module JekyllImport
55
55
  raise "Unknown database server '#{server}'"
56
56
  end
57
57
  db[SQL].each do |post|
58
- next unless post[:state] =~ %r!published!i
58
+ next unless %r!published!i.match?(post[:state])
59
59
 
60
60
  post[:slug] = "no slug" if post[:slug].nil?
61
61
 
@@ -77,8 +77,8 @@ module JekyllImport
77
77
 
78
78
  File.open("_posts/#{name}", "w") do |f|
79
79
  f.puts({ "layout" => "post",
80
- "title" => (post[:title]&.to_s&.force_encoding("UTF-8")),
81
- "tags" => (post[:keywords]&.to_s&.force_encoding("UTF-8")),
80
+ "title" => post[:title]&.to_s&.force_encoding("UTF-8"),
81
+ "tags" => post[:keywords]&.to_s&.force_encoding("UTF-8"),
82
82
  "typo_id" => post[:id], }.delete_if { |_k, v| v.nil? || v == "" }.to_yaml)
83
83
  f.puts "---"
84
84
  f.puts post[:body].delete("\r")
@@ -30,14 +30,15 @@ module JekyllImport
30
30
  images.each do |i|
31
31
  uri = i["src"]
32
32
 
33
- i["src"] = format("{{ site.baseurl }}/%s/%s", assets_folder, File.basename(uri))
34
33
  dst = File.join(assets_folder, File.basename(uri))
34
+ i["src"] = File.join("{{ site.baseurl }}", dst)
35
35
  Jekyll.logger.info uri
36
36
  if File.exist?(dst)
37
37
  Jekyll.logger.info "Already in cache. Clean assets folder if you want a redownload."
38
38
  next
39
39
  end
40
40
  begin
41
+ FileUtils.mkdir_p assets_folder
41
42
  OpenURI.open_uri(uri, :allow_redirections => :safe) do |f|
42
43
  File.open(dst, "wb") do |out|
43
44
  out.puts f.read
@@ -191,7 +192,16 @@ module JekyllImport
191
192
  content = Hpricot(item.text_for("content:encoded"))
192
193
  header["excerpt"] = item.excerpt if item.excerpt
193
194
 
194
- download_images(item.title, content, assets_folder) if fetch
195
+ if fetch
196
+ # Put the images into a /yyyy/mm/ subfolder to reduce clashes
197
+ assets_dir_path = if item.published_at
198
+ File.join(assets_folder, item.published_at.strftime("/%Y/%m"))
199
+ else
200
+ assets_folder
201
+ end
202
+
203
+ download_images(item.title, content, assets_dir_path)
204
+ end
195
205
 
196
206
  FileUtils.mkdir_p item.directory_name
197
207
  File.open(File.join(item.directory_name, item.file_name), "w") do |f|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllImport
4
- VERSION = "0.19.0"
4
+ VERSION = "0.21.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-import
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Preston-Werner
@@ -10,22 +10,8 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2019-06-24 00:00:00.000000000 Z
13
+ date: 2021-11-01 00:00:00.000000000 Z
14
14
  dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: fastercsv
17
- requirement: !ruby/object:Gem::Requirement
18
- requirements:
19
- - - "~>"
20
- - !ruby/object:Gem::Version
21
- version: '1.0'
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- requirements:
26
- - - "~>"
27
- - !ruby/object:Gem::Version
28
- version: '1.0'
29
15
  - !ruby/object:Gem::Dependency
30
16
  name: jekyll
31
17
  requirement: !ruby/object:Gem::Requirement
@@ -74,20 +60,6 @@ dependencies:
74
60
  - - "~>"
75
61
  - !ruby/object:Gem::Version
76
62
  version: '1.0'
77
- - !ruby/object:Gem::Dependency
78
- name: activesupport
79
- requirement: !ruby/object:Gem::Requirement
80
- requirements:
81
- - - "~>"
82
- - !ruby/object:Gem::Version
83
- version: '4.2'
84
- type: :development
85
- prerelease: false
86
- version_requirements: !ruby/object:Gem::Requirement
87
- requirements:
88
- - - "~>"
89
- - !ruby/object:Gem::Version
90
- version: '4.2'
91
63
  - !ruby/object:Gem::Dependency
92
64
  name: bundler
93
65
  requirement: !ruby/object:Gem::Requirement
@@ -164,14 +136,14 @@ dependencies:
164
136
  requirements:
165
137
  - - "~>"
166
138
  - !ruby/object:Gem::Version
167
- version: '0.4'
139
+ version: 0.11.0
168
140
  type: :development
169
141
  prerelease: false
170
142
  version_requirements: !ruby/object:Gem::Requirement
171
143
  requirements:
172
144
  - - "~>"
173
145
  - !ruby/object:Gem::Version
174
- version: '0.4'
146
+ version: 0.11.0
175
147
  - !ruby/object:Gem::Dependency
176
148
  name: shoulda
177
149
  requirement: !ruby/object:Gem::Requirement
@@ -373,6 +345,7 @@ files:
373
345
  - lib/jekyll-import/importers/dotclear.rb
374
346
  - lib/jekyll-import/importers/drupal6.rb
375
347
  - lib/jekyll-import/importers/drupal7.rb
348
+ - lib/jekyll-import/importers/drupal8.rb
376
349
  - lib/jekyll-import/importers/drupal_common.rb
377
350
  - lib/jekyll-import/importers/easyblog.rb
378
351
  - lib/jekyll-import/importers/enki.rb
@@ -384,6 +357,7 @@ files:
384
357
  - lib/jekyll-import/importers/marley.rb
385
358
  - lib/jekyll-import/importers/mephisto.rb
386
359
  - lib/jekyll-import/importers/mt.rb
360
+ - lib/jekyll-import/importers/pluxml.rb
387
361
  - lib/jekyll-import/importers/posterous.rb
388
362
  - lib/jekyll-import/importers/roller.rb
389
363
  - lib/jekyll-import/importers/rss.rb
@@ -410,14 +384,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
410
384
  requirements:
411
385
  - - ">="
412
386
  - !ruby/object:Gem::Version
413
- version: 2.3.0
387
+ version: 2.4.0
414
388
  required_rubygems_version: !ruby/object:Gem::Requirement
415
389
  requirements:
416
390
  - - ">="
417
391
  - !ruby/object:Gem::Version
418
392
  version: '0'
419
393
  requirements: []
420
- rubygems_version: 3.0.3
394
+ rubygems_version: 3.1.6
421
395
  signing_key:
422
396
  specification_version: 4
423
397
  summary: Import command for Jekyll (static site generator).