jekyll-import 0.1.0.beta3 → 0.1.0.beta4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +6 -14
- data/History.markdown +18 -0
- data/README.markdown +12 -1
- data/jekyll-import.gemspec +31 -25
- data/lib/jekyll-import.rb +50 -1
- data/lib/jekyll-import/importer.rb +11 -0
- data/lib/jekyll-import/importers.rb +10 -0
- data/lib/jekyll-import/importers/csv.rb +50 -0
- data/lib/jekyll-import/importers/drupal6.rb +139 -0
- data/lib/jekyll-import/importers/drupal7.rb +102 -0
- data/lib/jekyll-import/importers/enki.rb +76 -0
- data/lib/jekyll-import/importers/google_reader.rb +68 -0
- data/lib/jekyll-import/importers/joomla.rb +83 -0
- data/lib/jekyll-import/importers/jrnl.rb +127 -0
- data/lib/jekyll-import/importers/marley.rb +72 -0
- data/lib/jekyll-import/importers/mephisto.rb +109 -0
- data/lib/jekyll-import/importers/mt.rb +169 -0
- data/lib/jekyll-import/importers/posterous.rb +139 -0
- data/lib/jekyll-import/importers/rss.rb +71 -0
- data/lib/jekyll-import/importers/s9y.rb +67 -0
- data/lib/jekyll-import/importers/textpattern.rb +76 -0
- data/lib/jekyll-import/importers/tumblr.rb +265 -0
- data/lib/jekyll-import/importers/typo.rb +89 -0
- data/lib/jekyll-import/importers/wordpress.rb +323 -0
- data/lib/jekyll-import/importers/wordpressdotcom.rb +97 -0
- data/lib/jekyll/commands/import.rb +1 -0
- data/test/helper.rb +3 -1
- data/test/test_jrnl_importer.rb +39 -0
- data/test/test_mt_importer.rb +16 -16
- data/test/test_tumblr_importer.rb +61 -0
- data/test/test_wordpress_importer.rb +1 -1
- data/test/test_wordpressdotcom_importer.rb +1 -1
- metadata +53 -32
- data/lib/jekyll/jekyll-import/csv.rb +0 -30
- data/lib/jekyll/jekyll-import/drupal6.rb +0 -112
- data/lib/jekyll/jekyll-import/drupal7.rb +0 -74
- data/lib/jekyll/jekyll-import/enki.rb +0 -49
- data/lib/jekyll/jekyll-import/google_reader.rb +0 -61
- data/lib/jekyll/jekyll-import/joomla.rb +0 -53
- data/lib/jekyll/jekyll-import/marley.rb +0 -52
- data/lib/jekyll/jekyll-import/mephisto.rb +0 -84
- data/lib/jekyll/jekyll-import/mt.rb +0 -142
- data/lib/jekyll/jekyll-import/posterous.rb +0 -122
- data/lib/jekyll/jekyll-import/rss.rb +0 -63
- data/lib/jekyll/jekyll-import/s9y.rb +0 -59
- data/lib/jekyll/jekyll-import/textpattern.rb +0 -58
- data/lib/jekyll/jekyll-import/tumblr.rb +0 -242
- data/lib/jekyll/jekyll-import/typo.rb +0 -69
- data/lib/jekyll/jekyll-import/wordpress.rb +0 -299
- data/lib/jekyll/jekyll-import/wordpressdotcom.rb +0 -84
@@ -0,0 +1,102 @@
|
|
1
|
+
# NOTE: This converter requires Sequel and the MySQL gems.
|
2
|
+
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
|
3
|
+
# installed, running the following commands should work:
|
4
|
+
# $ sudo gem install sequel
|
5
|
+
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
|
6
|
+
|
7
|
+
module JekyllImport
|
8
|
+
module Importers
|
9
|
+
class Drupal7 < Importer
|
10
|
+
# Reads a MySQL database via Sequel and creates a post file for each story
|
11
|
+
# and blog node.
|
12
|
+
QUERY = "SELECT n.nid, \
|
13
|
+
n.title, \
|
14
|
+
fdb.body_value, \
|
15
|
+
n.created, \
|
16
|
+
n.status \
|
17
|
+
FROM node AS n, \
|
18
|
+
field_data_body AS fdb \
|
19
|
+
WHERE (n.type = 'blog' OR n.type = 'story' OR n.type = 'article') \
|
20
|
+
AND n.nid = fdb.entity_id \
|
21
|
+
AND n.vid = fdb.revision_id"
|
22
|
+
|
23
|
+
def self.validate(options)
|
24
|
+
%w[dbname user].each do |option|
|
25
|
+
if options[option].nil?
|
26
|
+
abort "Missing mandatory option --#{option}."
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.specify_options(c)
|
32
|
+
c.option 'dbname', '--dbname DB', 'Database name'
|
33
|
+
c.option 'user', '--user USER', 'Database user name'
|
34
|
+
c.option 'password', '--password PW', 'Database user\'s password (default: "")'
|
35
|
+
c.option 'host', '--host HOST', 'Database host name (default: "localhost")'
|
36
|
+
c.option 'prefix', '--prefix PREFIX', 'Table prefix name'
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.require_deps
|
40
|
+
JekyllImport.require_with_fallback(%w[
|
41
|
+
rubygems
|
42
|
+
sequel
|
43
|
+
fileutils
|
44
|
+
safe_yaml
|
45
|
+
])
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.process(options)
|
49
|
+
dbname = options.fetch('dbname')
|
50
|
+
user = options.fetch('user')
|
51
|
+
pass = options.fetch('password', "")
|
52
|
+
host = options.fetch('host', "localhost")
|
53
|
+
prefix = options.fetch('prefix', "")
|
54
|
+
|
55
|
+
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
56
|
+
|
57
|
+
unless prefix.empty?
|
58
|
+
QUERY[" node "] = " " + prefix + "node "
|
59
|
+
QUERY[" field_data_body "] = " " + prefix + "field_data_body "
|
60
|
+
end
|
61
|
+
|
62
|
+
FileUtils.mkdir_p "_posts"
|
63
|
+
FileUtils.mkdir_p "_drafts"
|
64
|
+
FileUtils.mkdir_p "_layouts"
|
65
|
+
|
66
|
+
db[QUERY].each do |post|
|
67
|
+
# Get required fields and construct Jekyll compatible name
|
68
|
+
node_id = post[:nid]
|
69
|
+
title = post[:title]
|
70
|
+
content = post[:body_value]
|
71
|
+
created = post[:created]
|
72
|
+
time = Time.at(created)
|
73
|
+
is_published = post[:status] == 1
|
74
|
+
dir = is_published ? "_posts" : "_drafts"
|
75
|
+
slug = title.strip.downcase.gsub(/(&|&)/, ' and ').gsub(/[\s\.\/\\]/, '-').gsub(/[^\w-]/, '').gsub(/[-_]{2,}/, '-').gsub(/^[-_]/, '').gsub(/[-_]$/, '')
|
76
|
+
name = time.strftime("%Y-%m-%d-") + slug + '.md'
|
77
|
+
|
78
|
+
# Get the relevant fields as a hash, delete empty fields and convert
|
79
|
+
# to YAML for the header
|
80
|
+
data = {
|
81
|
+
'layout' => 'default',
|
82
|
+
'title' => title.to_s,
|
83
|
+
'created' => created,
|
84
|
+
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
|
85
|
+
|
86
|
+
# Write out the data and content to file
|
87
|
+
File.open("#{dir}/#{name}", "w") do |f|
|
88
|
+
f.puts data
|
89
|
+
f.puts "---"
|
90
|
+
f.puts content
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
# TODO: Make dirs & files for nodes of type 'page'
|
96
|
+
# Make refresh pages for these as well
|
97
|
+
|
98
|
+
# TODO: Make refresh dirs & files according to entries in url_alias table
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Adapted by Rodrigo Pinto <rodrigopqn@gmail.com>
|
2
|
+
# Based on typo.rb by Toby DiPasquale
|
3
|
+
|
4
|
+
module JekyllImport
|
5
|
+
module Importers
|
6
|
+
class Enki < Importer
|
7
|
+
SQL = <<-EOS
|
8
|
+
SELECT p.id,
|
9
|
+
p.title,
|
10
|
+
p.slug,
|
11
|
+
p.body,
|
12
|
+
p.published_at as date,
|
13
|
+
p.cached_tag_list as tags
|
14
|
+
FROM posts p
|
15
|
+
EOS
|
16
|
+
|
17
|
+
def self.validate(options)
|
18
|
+
%w[dbname user].each do |option|
|
19
|
+
if options[option].nil?
|
20
|
+
abort "Missing mandatory option --#{option}."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.specify_options(c)
|
26
|
+
c.option 'dbname', '--dbname', 'Database name'
|
27
|
+
c.option 'user', '--user', 'Database name'
|
28
|
+
c.option 'password', '--password', 'Database name (default: "")'
|
29
|
+
c.option 'host', '--host', 'Database name'
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.require_deps
|
33
|
+
JekyllImport.require_with_fallback(%w[
|
34
|
+
rubygems
|
35
|
+
sequel
|
36
|
+
fileutils
|
37
|
+
pg
|
38
|
+
])
|
39
|
+
end
|
40
|
+
|
41
|
+
# Just working with postgres, but can be easily adapted
|
42
|
+
# to work with both mysql and postgres.
|
43
|
+
def self.process(options)
|
44
|
+
dbname = options.fetch('dbname')
|
45
|
+
user = options.fetch('user')
|
46
|
+
pass = options.fetch('password', "")
|
47
|
+
host = options.fetch('host', "localhost")
|
48
|
+
|
49
|
+
FileUtils.mkdir_p('_posts')
|
50
|
+
db = Sequel.postgres(:database => dbname,
|
51
|
+
:user => user,
|
52
|
+
:password => pass,
|
53
|
+
:host => host,
|
54
|
+
:encoding => 'utf8')
|
55
|
+
|
56
|
+
db[SQL].each do |post|
|
57
|
+
name = [ sprintf("%.04d", post[:date].year),
|
58
|
+
sprintf("%.02d", post[:date].month),
|
59
|
+
sprintf("%.02d", post[:date].day),
|
60
|
+
post[:slug].strip ].join('-')
|
61
|
+
name += '.textile'
|
62
|
+
|
63
|
+
File.open("_posts/#{name}", 'w') do |f|
|
64
|
+
f.puts({ 'layout' => 'post',
|
65
|
+
'title' => post[:title].to_s,
|
66
|
+
'enki_id' => post[:id],
|
67
|
+
'categories' => post[:tags]
|
68
|
+
}.delete_if { |k, v| v.nil? || v == '' }.to_yaml)
|
69
|
+
f.puts '---'
|
70
|
+
f.puts post[:body].delete("\r")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Usage:
|
2
|
+
# (Local file)
|
3
|
+
# ruby -r 'jekyll/jekyll-import/rss' -e "JekyllImport::GoogleReader.process(:source => './somefile/on/your/computer.xml')"
|
4
|
+
|
5
|
+
module JekyllImport
|
6
|
+
module Importers
|
7
|
+
class GoogleReader < Importer
|
8
|
+
def self.validate(options)
|
9
|
+
if options['source'].nil?
|
10
|
+
abort "Missing mandatory option --source."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.specify_options(c)
|
15
|
+
c.option 'source', '--source', 'Source XML file of Google Reader export'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.require_deps
|
19
|
+
JekyllImport.require_with_fallback(%w[
|
20
|
+
rubygems
|
21
|
+
rss
|
22
|
+
fileutils
|
23
|
+
safe_yaml
|
24
|
+
open-url
|
25
|
+
rexml/document
|
26
|
+
date
|
27
|
+
])
|
28
|
+
end
|
29
|
+
|
30
|
+
# Process the import.
|
31
|
+
#
|
32
|
+
# source - a URL or a local file String.
|
33
|
+
#
|
34
|
+
# Returns nothing.
|
35
|
+
def self.process(options)
|
36
|
+
source = options.fetch('source')
|
37
|
+
|
38
|
+
open(source) do |content|
|
39
|
+
feed = RSS::Parser.parse(content)
|
40
|
+
|
41
|
+
raise "There doesn't appear to be any RSS items at the source (#{source}) provided." unless feed
|
42
|
+
|
43
|
+
feed.items.each do |item|
|
44
|
+
title = item.title.content.to_s
|
45
|
+
formatted_date = Date.parse(item.published.to_s)
|
46
|
+
post_name = title.split(%r{ |!|/|:|&|-|$|,}).map do |i|
|
47
|
+
i.downcase if i != ''
|
48
|
+
end.compact.join('-')
|
49
|
+
name = "#{formatted_date}-#{post_name}"
|
50
|
+
|
51
|
+
header = {
|
52
|
+
'layout' => 'post',
|
53
|
+
'title' => title
|
54
|
+
}
|
55
|
+
|
56
|
+
FileUtils.mkdir_p("_posts")
|
57
|
+
|
58
|
+
File.open("_posts/#{name}.html", "w") do |f|
|
59
|
+
f.puts header.to_yaml
|
60
|
+
f.puts "---\n\n"
|
61
|
+
f.puts item.content.content.to_s
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# NOTE: This migrator is made for Joomla 1.5 databases.
|
2
|
+
# NOTE: This converter requires Sequel and the MySQL gems.
|
3
|
+
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
|
4
|
+
# installed, running the following commands should work:
|
5
|
+
# $ sudo gem install sequel
|
6
|
+
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
|
7
|
+
|
8
|
+
module JekyllImport
|
9
|
+
module Importers
|
10
|
+
class Joomla < Importer
|
11
|
+
def self.validate(options)
|
12
|
+
%w[dbname user].each do |option|
|
13
|
+
if options[option].nil?
|
14
|
+
abort "Missing mandatory option --#{option}."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.specify_options(c)
|
20
|
+
c.option 'dbname', '--dbname', 'Database name'
|
21
|
+
c.option 'user', '--user', 'Database user name'
|
22
|
+
c.option 'password', '--password', "Database user's password (default: '')"
|
23
|
+
c.option 'host', '--host', 'Database host name'
|
24
|
+
c.option 'section', '--section', 'Table prefix name'
|
25
|
+
c.option 'prefix', '--prefix', 'Table prefix name'
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.require_deps
|
29
|
+
JekyllImport.require_with_fallback(%w[
|
30
|
+
rubygems
|
31
|
+
sequel
|
32
|
+
fileutils
|
33
|
+
safe_yaml
|
34
|
+
])
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.process(options)
|
38
|
+
dbname = options.fetch('dbname')
|
39
|
+
user = options.fetch('user')
|
40
|
+
pass = options.fetch('password', '')
|
41
|
+
host = options.fetch('host', "localhost")
|
42
|
+
section = options.fetch('section', '1')
|
43
|
+
table_prefix = options.fetch('prefix', "jos_")
|
44
|
+
|
45
|
+
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
46
|
+
|
47
|
+
FileUtils.mkdir_p("_posts")
|
48
|
+
|
49
|
+
# Reads a MySQL database via Sequel and creates a post file for each
|
50
|
+
# post in wp_posts that has post_status = 'publish'. This restriction is
|
51
|
+
# made because 'draft' posts are not guaranteed to have valid dates.
|
52
|
+
query = "SELECT `title`, `alias`, CONCAT(`introtext`,`fulltext`) as content, `created`, `id` FROM #{table_prefix}content WHERE state = '0' OR state = '1' AND sectionid = '#{section}'"
|
53
|
+
|
54
|
+
db[query].each do |post|
|
55
|
+
# Get required fields and construct Jekyll compatible name.
|
56
|
+
title = post[:title]
|
57
|
+
slug = post[:alias]
|
58
|
+
date = post[:created]
|
59
|
+
content = post[:content]
|
60
|
+
name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month, date.day,
|
61
|
+
slug]
|
62
|
+
|
63
|
+
# Get the relevant fields as a hash, delete empty fields and convert
|
64
|
+
# to YAML for the header.
|
65
|
+
data = {
|
66
|
+
'layout' => 'post',
|
67
|
+
'title' => title.to_s,
|
68
|
+
'joomla_id' => post[:id],
|
69
|
+
'joomla_url' => post[:alias],
|
70
|
+
'date' => date
|
71
|
+
}.delete_if { |k,v| v.nil? || v == '' }.to_yaml
|
72
|
+
|
73
|
+
# Write out the data and content to file
|
74
|
+
File.open("_posts/#{name}", "w") do |f|
|
75
|
+
f.puts data
|
76
|
+
f.puts "---"
|
77
|
+
f.puts content
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# Author: Aniket Pant <me@aniketpant.com>
|
2
|
+
|
3
|
+
module JekyllImport
|
4
|
+
module Importers
|
5
|
+
class Jrnl < Importer
|
6
|
+
|
7
|
+
def self.require_deps
|
8
|
+
JekyllImport.require_with_fallback(%w[
|
9
|
+
time
|
10
|
+
rubygems
|
11
|
+
safe_yaml
|
12
|
+
])
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.specify_options(c)
|
16
|
+
c.option 'file', '--file FILENAME', 'Journal file (default: "~/journal.txt")'
|
17
|
+
c.option 'time_format', '--time_format FORMAT', 'Time format of your journal (default: "%Y-%m-%d %H:%M")'
|
18
|
+
c.option 'extension', '--extension EXT', 'Output extension (default: "md")'
|
19
|
+
c.option 'layout', '--layout NAME', 'Output post layout (default: "post")'
|
20
|
+
end
|
21
|
+
|
22
|
+
# Reads a jrnl file and creates a new post for each entry
|
23
|
+
# The following overrides are available:
|
24
|
+
# :file path to input file
|
25
|
+
# :time_format the format used by the jrnl configuration
|
26
|
+
# :extension the extension format of the output files
|
27
|
+
# :layout explicitly set the layout of the output
|
28
|
+
def self.process(options)
|
29
|
+
file = options.fetch('file', "~/journal.txt")
|
30
|
+
time_format = options.fetch('time_format', "%Y-%m-%d %H:%M")
|
31
|
+
extension = options.fetch('extension', "md")
|
32
|
+
layout = options.fetch('layout', "post")
|
33
|
+
|
34
|
+
date_length = Time.now.strftime(time_format).length
|
35
|
+
|
36
|
+
# convert relative to absolute if needed
|
37
|
+
file = File.expand_path(file)
|
38
|
+
|
39
|
+
abort "The jrnl file was not found. Please make sure '#{file}' exists. You can specify a different file using the --file switch." unless File.file?(file)
|
40
|
+
|
41
|
+
input = File.read(file)
|
42
|
+
entries = input.split("\n\n");
|
43
|
+
|
44
|
+
entries.each do |entry|
|
45
|
+
# split dateline and body
|
46
|
+
# content[0] has the date and title
|
47
|
+
# content[1] has the post body
|
48
|
+
content = entry.split("\n")
|
49
|
+
|
50
|
+
body = get_post_content(content)
|
51
|
+
date = get_date(content[0], date_length)
|
52
|
+
title = get_title(content[0], date_length)
|
53
|
+
slug = create_slug(title)
|
54
|
+
filename = create_filename(date, slug, extension)
|
55
|
+
meta = create_meta(layout, title, date) # prepare YAML meta data
|
56
|
+
|
57
|
+
write_file(filename, meta, body) # write to file
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# strip body from jrnl entry
|
62
|
+
def self.get_post_content(content)
|
63
|
+
return content[1]
|
64
|
+
end
|
65
|
+
|
66
|
+
# strip timestamp from the dateline
|
67
|
+
def self.get_date(content, offset)
|
68
|
+
return content[0, offset]
|
69
|
+
end
|
70
|
+
|
71
|
+
# strip title from the dateline
|
72
|
+
def self.get_title(content, offset)
|
73
|
+
return content[offset + 1, content.length]
|
74
|
+
end
|
75
|
+
|
76
|
+
# generate slug
|
77
|
+
def self.create_slug(title)
|
78
|
+
return title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
|
79
|
+
end
|
80
|
+
|
81
|
+
# generate filename
|
82
|
+
def self.create_filename(date, slug, extension)
|
83
|
+
return "#{Time.parse(date).strftime("%Y-%m-%d")}-#{slug}.#{extension}"
|
84
|
+
end
|
85
|
+
|
86
|
+
# Prepare YAML meta data
|
87
|
+
#
|
88
|
+
# layout - name of the layout
|
89
|
+
# title - title of the entry
|
90
|
+
# date - date of entry creation
|
91
|
+
#
|
92
|
+
# Examples
|
93
|
+
#
|
94
|
+
# create_meta("post", "Entry 1", "2013-01-01 13:00")
|
95
|
+
# # => "---\nlayout: post\ntitle: Entry 1\ndate: 2013-01-01 13:00\n"
|
96
|
+
#
|
97
|
+
# Returns array converted to YAML
|
98
|
+
def self.create_meta(layout, title, date)
|
99
|
+
data = {
|
100
|
+
'layout' => layout,
|
101
|
+
'title' => title,
|
102
|
+
'date' => Time.parse(date).strftime("%Y-%m-%d %H:%M %z")
|
103
|
+
}.to_yaml
|
104
|
+
return data;
|
105
|
+
end
|
106
|
+
|
107
|
+
# Writes given data to file
|
108
|
+
#
|
109
|
+
# filename - name of the output file
|
110
|
+
# meta - YAML header data
|
111
|
+
# body - jrnl entry content
|
112
|
+
#
|
113
|
+
# Examples
|
114
|
+
#
|
115
|
+
# write_file("2013-01-01-entry-1.md", "---\nlayout: post\ntitle: Entry 1\ndate: 2013-01-01 13:00\n", "This is the first entry for my new journal")
|
116
|
+
#
|
117
|
+
# Writes file to _posts/filename
|
118
|
+
def self.write_file(filename, meta, body)
|
119
|
+
File.open("_posts/#{filename}", "w") do |f|
|
120
|
+
f.puts meta
|
121
|
+
f.puts "---\n\n"
|
122
|
+
f.puts body
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|