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.
Files changed (50) hide show
  1. checksums.yaml +6 -14
  2. data/History.markdown +18 -0
  3. data/README.markdown +12 -1
  4. data/jekyll-import.gemspec +31 -25
  5. data/lib/jekyll-import.rb +50 -1
  6. data/lib/jekyll-import/importer.rb +11 -0
  7. data/lib/jekyll-import/importers.rb +10 -0
  8. data/lib/jekyll-import/importers/csv.rb +50 -0
  9. data/lib/jekyll-import/importers/drupal6.rb +139 -0
  10. data/lib/jekyll-import/importers/drupal7.rb +102 -0
  11. data/lib/jekyll-import/importers/enki.rb +76 -0
  12. data/lib/jekyll-import/importers/google_reader.rb +68 -0
  13. data/lib/jekyll-import/importers/joomla.rb +83 -0
  14. data/lib/jekyll-import/importers/jrnl.rb +127 -0
  15. data/lib/jekyll-import/importers/marley.rb +72 -0
  16. data/lib/jekyll-import/importers/mephisto.rb +109 -0
  17. data/lib/jekyll-import/importers/mt.rb +169 -0
  18. data/lib/jekyll-import/importers/posterous.rb +139 -0
  19. data/lib/jekyll-import/importers/rss.rb +71 -0
  20. data/lib/jekyll-import/importers/s9y.rb +67 -0
  21. data/lib/jekyll-import/importers/textpattern.rb +76 -0
  22. data/lib/jekyll-import/importers/tumblr.rb +265 -0
  23. data/lib/jekyll-import/importers/typo.rb +89 -0
  24. data/lib/jekyll-import/importers/wordpress.rb +323 -0
  25. data/lib/jekyll-import/importers/wordpressdotcom.rb +97 -0
  26. data/lib/jekyll/commands/import.rb +1 -0
  27. data/test/helper.rb +3 -1
  28. data/test/test_jrnl_importer.rb +39 -0
  29. data/test/test_mt_importer.rb +16 -16
  30. data/test/test_tumblr_importer.rb +61 -0
  31. data/test/test_wordpress_importer.rb +1 -1
  32. data/test/test_wordpressdotcom_importer.rb +1 -1
  33. metadata +53 -32
  34. data/lib/jekyll/jekyll-import/csv.rb +0 -30
  35. data/lib/jekyll/jekyll-import/drupal6.rb +0 -112
  36. data/lib/jekyll/jekyll-import/drupal7.rb +0 -74
  37. data/lib/jekyll/jekyll-import/enki.rb +0 -49
  38. data/lib/jekyll/jekyll-import/google_reader.rb +0 -61
  39. data/lib/jekyll/jekyll-import/joomla.rb +0 -53
  40. data/lib/jekyll/jekyll-import/marley.rb +0 -52
  41. data/lib/jekyll/jekyll-import/mephisto.rb +0 -84
  42. data/lib/jekyll/jekyll-import/mt.rb +0 -142
  43. data/lib/jekyll/jekyll-import/posterous.rb +0 -122
  44. data/lib/jekyll/jekyll-import/rss.rb +0 -63
  45. data/lib/jekyll/jekyll-import/s9y.rb +0 -59
  46. data/lib/jekyll/jekyll-import/textpattern.rb +0 -58
  47. data/lib/jekyll/jekyll-import/tumblr.rb +0 -242
  48. data/lib/jekyll/jekyll-import/typo.rb +0 -69
  49. data/lib/jekyll/jekyll-import/wordpress.rb +0 -299
  50. 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(/(&|&amp;)/, ' 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