jekyll-reloaded 0.12
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.
- data/Gemfile +2 -0
- data/History.txt +321 -0
- data/LICENSE +21 -0
- data/README.textile +41 -0
- data/Rakefile +161 -0
- data/bin/jekyll +289 -0
- data/cucumber.yml +1 -0
- data/features/create_sites.feature +112 -0
- data/features/embed_filters.feature +60 -0
- data/features/markdown.feature +30 -0
- data/features/pagination.feature +27 -0
- data/features/permalinks.feature +65 -0
- data/features/post_data.feature +153 -0
- data/features/site_configuration.feature +145 -0
- data/features/site_data.feature +82 -0
- data/features/step_definitions/jekyll_steps.rb +145 -0
- data/features/support/env.rb +19 -0
- data/jekyll.gemspec +146 -0
- data/lib/guard/jekyll.rb +57 -0
- data/lib/jekyll/converter.rb +50 -0
- data/lib/jekyll/converters/identity.rb +22 -0
- data/lib/jekyll/converters/markdown.rb +125 -0
- data/lib/jekyll/converters/textile.rb +50 -0
- data/lib/jekyll/convertible.rb +116 -0
- data/lib/jekyll/core_ext.rb +52 -0
- data/lib/jekyll/errors.rb +6 -0
- data/lib/jekyll/filters.rb +118 -0
- data/lib/jekyll/generator.rb +7 -0
- data/lib/jekyll/generators/pagination.rb +113 -0
- data/lib/jekyll/layout.rb +51 -0
- data/lib/jekyll/live_site.rb +216 -0
- data/lib/jekyll/migrators/csv.rb +26 -0
- data/lib/jekyll/migrators/drupal.rb +103 -0
- data/lib/jekyll/migrators/enki.rb +49 -0
- data/lib/jekyll/migrators/joomla.rb +53 -0
- data/lib/jekyll/migrators/marley.rb +52 -0
- data/lib/jekyll/migrators/mephisto.rb +84 -0
- data/lib/jekyll/migrators/mt.rb +86 -0
- data/lib/jekyll/migrators/posterous.rb +67 -0
- data/lib/jekyll/migrators/rss.rb +47 -0
- data/lib/jekyll/migrators/textpattern.rb +58 -0
- data/lib/jekyll/migrators/tumblr.rb +195 -0
- data/lib/jekyll/migrators/typo.rb +51 -0
- data/lib/jekyll/migrators/wordpress.rb +294 -0
- data/lib/jekyll/migrators/wordpressdotcom.rb +70 -0
- data/lib/jekyll/page.rb +160 -0
- data/lib/jekyll/plugin.rb +77 -0
- data/lib/jekyll/post.rb +262 -0
- data/lib/jekyll/site.rb +339 -0
- data/lib/jekyll/static_file.rb +77 -0
- data/lib/jekyll/tags/highlight.rb +118 -0
- data/lib/jekyll/tags/include.rb +37 -0
- data/lib/jekyll/tags/post_url.rb +38 -0
- data/lib/jekyll.rb +134 -0
- data/test/helper.rb +34 -0
- data/test/source/.htaccess +8 -0
- data/test/source/_includes/sig.markdown +3 -0
- data/test/source/_layouts/default.html +27 -0
- data/test/source/_layouts/simple.html +1 -0
- data/test/source/_posts/2008-02-02-not-published.textile +8 -0
- data/test/source/_posts/2008-02-02-published.textile +8 -0
- data/test/source/_posts/2008-10-18-foo-bar.textile +8 -0
- data/test/source/_posts/2008-11-21-complex.textile +8 -0
- data/test/source/_posts/2008-12-03-permalinked-post.textile +9 -0
- data/test/source/_posts/2008-12-13-include.markdown +8 -0
- data/test/source/_posts/2009-01-27-array-categories.textile +10 -0
- data/test/source/_posts/2009-01-27-categories.textile +7 -0
- data/test/source/_posts/2009-01-27-category.textile +7 -0
- data/test/source/_posts/2009-01-27-empty-categories.textile +7 -0
- data/test/source/_posts/2009-01-27-empty-category.textile +7 -0
- data/test/source/_posts/2009-03-12-hash-#1.markdown +6 -0
- data/test/source/_posts/2009-05-18-empty-tag.textile +6 -0
- data/test/source/_posts/2009-05-18-empty-tags.textile +6 -0
- data/test/source/_posts/2009-05-18-tag.textile +6 -0
- data/test/source/_posts/2009-05-18-tags.textile +9 -0
- data/test/source/_posts/2009-06-22-empty-yaml.textile +3 -0
- data/test/source/_posts/2009-06-22-no-yaml.textile +1 -0
- data/test/source/_posts/2010-01-08-triple-dash.markdown +5 -0
- data/test/source/_posts/2010-01-09-date-override.textile +7 -0
- data/test/source/_posts/2010-01-09-time-override.textile +7 -0
- data/test/source/_posts/2010-01-09-timezone-override.textile +7 -0
- data/test/source/_posts/2010-01-16-override-data.textile +4 -0
- data/test/source/_posts/2011-04-12-md-extension.md +7 -0
- data/test/source/_posts/2011-04-12-text-extension.text +0 -0
- data/test/source/about.html +6 -0
- data/test/source/category/_posts/2008-9-23-categories.textile +6 -0
- data/test/source/contacts.html +5 -0
- data/test/source/css/screen.css +76 -0
- data/test/source/deal.with.dots.html +7 -0
- data/test/source/foo/_posts/bar/2008-12-12-topical-post.textile +8 -0
- data/test/source/index.html +22 -0
- data/test/source/sitemap.xml +32 -0
- data/test/source/win/_posts/2009-05-24-yaml-linebreak.markdown +7 -0
- data/test/source/z_category/_posts/2008-9-23-categories.textile +6 -0
- data/test/suite.rb +11 -0
- data/test/test_configuration.rb +29 -0
- data/test/test_core_ext.rb +66 -0
- data/test/test_filters.rb +62 -0
- data/test/test_generated_site.rb +72 -0
- data/test/test_kramdown.rb +23 -0
- data/test/test_page.rb +117 -0
- data/test/test_pager.rb +113 -0
- data/test/test_post.rb +450 -0
- data/test/test_rdiscount.rb +18 -0
- data/test/test_redcarpet.rb +21 -0
- data/test/test_redcloth.rb +86 -0
- data/test/test_site.rb +220 -0
- data/test/test_tags.rb +201 -0
- metadata +332 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'sequel'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
# NOTE: This migrator is made for Joomla 1.5 databases.
|
|
7
|
+
# NOTE: This converter requires Sequel and the MySQL gems.
|
|
8
|
+
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
|
|
9
|
+
# installed, running the following commands should work:
|
|
10
|
+
# $ sudo gem install sequel
|
|
11
|
+
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
|
|
12
|
+
|
|
13
|
+
module Jekyll
|
|
14
|
+
module Joomla
|
|
15
|
+
def self.process(dbname, user, pass, host = 'localhost', table_prefix = 'jos_', section = '1')
|
|
16
|
+
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
|
17
|
+
|
|
18
|
+
FileUtils.mkdir_p("_posts")
|
|
19
|
+
|
|
20
|
+
# Reads a MySQL database via Sequel and creates a post file for each
|
|
21
|
+
# post in wp_posts that has post_status = 'publish'. This restriction is
|
|
22
|
+
# made because 'draft' posts are not guaranteed to have valid dates.
|
|
23
|
+
query = "SELECT `title`, `alias`, CONCAT(`introtext`,`fulltext`) as content, `created`, `id` FROM #{table_prefix}content WHERE state = '0' OR state = '1' AND sectionid = '#{section}'"
|
|
24
|
+
|
|
25
|
+
db[query].each do |post|
|
|
26
|
+
# Get required fields and construct Jekyll compatible name.
|
|
27
|
+
title = post[:title]
|
|
28
|
+
slug = post[:alias]
|
|
29
|
+
date = post[:created]
|
|
30
|
+
content = post[:content]
|
|
31
|
+
name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month, date.day,
|
|
32
|
+
slug]
|
|
33
|
+
|
|
34
|
+
# Get the relevant fields as a hash, delete empty fields and convert
|
|
35
|
+
# to YAML for the header.
|
|
36
|
+
data = {
|
|
37
|
+
'layout' => 'post',
|
|
38
|
+
'title' => title.to_s,
|
|
39
|
+
'joomla_id' => post[:id],
|
|
40
|
+
'joomla_url' => post[:alias],
|
|
41
|
+
'date' => date
|
|
42
|
+
}.delete_if { |k,v| v.nil? || v == '' }.to_yaml
|
|
43
|
+
|
|
44
|
+
# Write out the data and content to file
|
|
45
|
+
File.open("_posts/#{name}", "w") do |f|
|
|
46
|
+
f.puts data
|
|
47
|
+
f.puts "---"
|
|
48
|
+
f.puts content
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
module Jekyll
|
|
5
|
+
module Marley
|
|
6
|
+
def self.regexp
|
|
7
|
+
{ :id => /^\d{0,4}-{0,1}(.*)$/,
|
|
8
|
+
:title => /^#\s*(.*)\s+$/,
|
|
9
|
+
:title_with_date => /^#\s*(.*)\s+\(([0-9\/]+)\)$/,
|
|
10
|
+
:published_on => /.*\s+\(([0-9\/]+)\)$/,
|
|
11
|
+
:perex => /^([^\#\n]+\n)$/,
|
|
12
|
+
:meta => /^\{\{\n(.*)\}\}\n$/mi # Multiline Regexp
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.process(marley_data_dir)
|
|
17
|
+
raise ArgumentError, "marley dir #{marley_data_dir} not found" unless File.directory?(marley_data_dir)
|
|
18
|
+
|
|
19
|
+
FileUtils.mkdir_p "_posts"
|
|
20
|
+
|
|
21
|
+
posts = 0
|
|
22
|
+
Dir["#{marley_data_dir}/**/*.txt"].each do |f|
|
|
23
|
+
next unless File.exists?(f)
|
|
24
|
+
|
|
25
|
+
#copied over from marley's app/lib/post.rb
|
|
26
|
+
file_content = File.read(f)
|
|
27
|
+
meta_content = file_content.slice!( self.regexp[:meta] )
|
|
28
|
+
body = file_content.sub( self.regexp[:title], '').sub( self.regexp[:perex], '').strip
|
|
29
|
+
|
|
30
|
+
title = file_content.scan( self.regexp[:title] ).first.to_s.strip
|
|
31
|
+
prerex = file_content.scan( self.regexp[:perex] ).first.to_s.strip
|
|
32
|
+
published_on = DateTime.parse( post[:published_on] ) rescue File.mtime( File.dirname(f) )
|
|
33
|
+
meta = ( meta_content ) ? YAML::load( meta_content.scan( self.regexp[:meta]).to_s ) : {}
|
|
34
|
+
meta['title'] = title
|
|
35
|
+
meta['layout'] = 'post'
|
|
36
|
+
|
|
37
|
+
formatted_date = published_on.strftime('%Y-%m-%d')
|
|
38
|
+
post_name = File.dirname(f).split(%r{/}).last.gsub(/\A\d+-/, '')
|
|
39
|
+
|
|
40
|
+
name = "#{formatted_date}-#{post_name}"
|
|
41
|
+
File.open("_posts/#{name}.markdown", "w") do |f|
|
|
42
|
+
f.puts meta.to_yaml
|
|
43
|
+
f.puts "---\n"
|
|
44
|
+
f.puts "\n#{prerex}\n\n" if prerex
|
|
45
|
+
f.puts body
|
|
46
|
+
end
|
|
47
|
+
posts += 1
|
|
48
|
+
end
|
|
49
|
+
"Created #{posts} posts!"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Quickly hacked together my Michael Ivey
|
|
2
|
+
# Based on mt.rb by Nick Gerakines, open source and publically
|
|
3
|
+
# available under the MIT license. Use this module at your own risk.
|
|
4
|
+
|
|
5
|
+
require 'rubygems'
|
|
6
|
+
require 'sequel'
|
|
7
|
+
require 'fastercsv'
|
|
8
|
+
require 'fileutils'
|
|
9
|
+
require File.join(File.dirname(__FILE__),"csv.rb")
|
|
10
|
+
|
|
11
|
+
# NOTE: This converter requires Sequel and the MySQL gems.
|
|
12
|
+
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
|
|
13
|
+
# installed, running the following commands should work:
|
|
14
|
+
# $ sudo gem install sequel
|
|
15
|
+
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
|
|
16
|
+
|
|
17
|
+
module Jekyll
|
|
18
|
+
module Mephisto
|
|
19
|
+
#Accepts a hash with database config variables, exports mephisto posts into a csv
|
|
20
|
+
#export PGPASSWORD if you must
|
|
21
|
+
def self.postgres(c)
|
|
22
|
+
sql = <<-SQL
|
|
23
|
+
BEGIN;
|
|
24
|
+
CREATE TEMP TABLE jekyll AS
|
|
25
|
+
SELECT title, permalink, body, published_at, filter FROM contents
|
|
26
|
+
WHERE user_id = 1 AND type = 'Article' ORDER BY published_at;
|
|
27
|
+
COPY jekyll TO STDOUT WITH CSV HEADER;
|
|
28
|
+
ROLLBACK;
|
|
29
|
+
SQL
|
|
30
|
+
command = %Q(psql -h #{c[:host] || "localhost"} -c "#{sql.strip}" #{c[:database]} #{c[:username]} -o #{c[:filename] || "posts.csv"})
|
|
31
|
+
puts command
|
|
32
|
+
`#{command}`
|
|
33
|
+
CSV.process
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# This query will pull blog posts from all entries across all blogs. If
|
|
37
|
+
# you've got unpublished, deleted or otherwise hidden posts please sift
|
|
38
|
+
# through the created posts to make sure nothing is accidently published.
|
|
39
|
+
QUERY = "SELECT id, \
|
|
40
|
+
permalink, \
|
|
41
|
+
body, \
|
|
42
|
+
published_at, \
|
|
43
|
+
title \
|
|
44
|
+
FROM contents \
|
|
45
|
+
WHERE user_id = 1 AND \
|
|
46
|
+
type = 'Article' AND \
|
|
47
|
+
published_at IS NOT NULL \
|
|
48
|
+
ORDER BY published_at"
|
|
49
|
+
|
|
50
|
+
def self.process(dbname, user, pass, host = 'localhost')
|
|
51
|
+
db = Sequel.mysql(dbname, :user => user,
|
|
52
|
+
:password => pass,
|
|
53
|
+
:host => host,
|
|
54
|
+
:encoding => 'utf8')
|
|
55
|
+
|
|
56
|
+
FileUtils.mkdir_p "_posts"
|
|
57
|
+
|
|
58
|
+
db[QUERY].each do |post|
|
|
59
|
+
title = post[:title]
|
|
60
|
+
slug = post[:permalink]
|
|
61
|
+
date = post[:published_at]
|
|
62
|
+
content = post[:body]
|
|
63
|
+
|
|
64
|
+
# Ideally, this script would determine the post format (markdown,
|
|
65
|
+
# html, etc) and create files with proper extensions. At this point
|
|
66
|
+
# it just assumes that markdown will be acceptable.
|
|
67
|
+
name = [date.year, date.month, date.day, slug].join('-') + ".markdown"
|
|
68
|
+
|
|
69
|
+
data = {
|
|
70
|
+
'layout' => 'post',
|
|
71
|
+
'title' => title.to_s,
|
|
72
|
+
'mt_id' => post[:entry_id],
|
|
73
|
+
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
|
|
74
|
+
|
|
75
|
+
File.open("_posts/#{name}", "w") do |f|
|
|
76
|
+
f.puts data
|
|
77
|
+
f.puts "---"
|
|
78
|
+
f.puts content
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Created by Nick Gerakines, open source and publically available under the
|
|
2
|
+
# MIT license. Use this module at your own risk.
|
|
3
|
+
# I'm an Erlang/Perl/C++ guy so please forgive my dirty ruby.
|
|
4
|
+
|
|
5
|
+
require 'rubygems'
|
|
6
|
+
require 'sequel'
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
require 'yaml'
|
|
9
|
+
|
|
10
|
+
# NOTE: This converter requires Sequel and the MySQL gems.
|
|
11
|
+
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
|
|
12
|
+
# installed, running the following commands should work:
|
|
13
|
+
# $ sudo gem install sequel
|
|
14
|
+
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
|
|
15
|
+
|
|
16
|
+
module Jekyll
|
|
17
|
+
module MT
|
|
18
|
+
# This query will pull blog posts from all entries across all blogs. If
|
|
19
|
+
# you've got unpublished, deleted or otherwise hidden posts please sift
|
|
20
|
+
# through the created posts to make sure nothing is accidently published.
|
|
21
|
+
QUERY = "SELECT entry_id, \
|
|
22
|
+
entry_basename, \
|
|
23
|
+
entry_text, \
|
|
24
|
+
entry_text_more, \
|
|
25
|
+
entry_authored_on, \
|
|
26
|
+
entry_title, \
|
|
27
|
+
entry_convert_breaks \
|
|
28
|
+
FROM mt_entry"
|
|
29
|
+
|
|
30
|
+
def self.process(dbname, user, pass, host = 'localhost')
|
|
31
|
+
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
|
32
|
+
|
|
33
|
+
FileUtils.mkdir_p "_posts"
|
|
34
|
+
|
|
35
|
+
db[QUERY].each do |post|
|
|
36
|
+
title = post[:entry_title]
|
|
37
|
+
slug = post[:entry_basename].gsub(/_/, '-')
|
|
38
|
+
date = post[:entry_authored_on]
|
|
39
|
+
content = post[:entry_text]
|
|
40
|
+
more_content = post[:entry_text_more]
|
|
41
|
+
entry_convert_breaks = post[:entry_convert_breaks]
|
|
42
|
+
|
|
43
|
+
# Be sure to include the body and extended body.
|
|
44
|
+
if more_content != nil
|
|
45
|
+
content = content + " \n" + more_content
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Ideally, this script would determine the post format (markdown,
|
|
49
|
+
# html, etc) and create files with proper extensions. At this point
|
|
50
|
+
# it just assumes that markdown will be acceptable.
|
|
51
|
+
name = [date.year, date.month, date.day, slug].join('-') + '.' +
|
|
52
|
+
self.suffix(entry_convert_breaks)
|
|
53
|
+
|
|
54
|
+
data = {
|
|
55
|
+
'layout' => 'post',
|
|
56
|
+
'title' => title.to_s,
|
|
57
|
+
'mt_id' => post[:entry_id],
|
|
58
|
+
'date' => date
|
|
59
|
+
}.delete_if { |k,v| v.nil? || v == '' }.to_yaml
|
|
60
|
+
|
|
61
|
+
File.open("_posts/#{name}", "w") do |f|
|
|
62
|
+
f.puts data
|
|
63
|
+
f.puts "---"
|
|
64
|
+
f.puts content
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.suffix(entry_type)
|
|
70
|
+
if entry_type.nil? || entry_type.include?("markdown")
|
|
71
|
+
# The markdown plugin I have saves this as
|
|
72
|
+
# "markdown_with_smarty_pants", so I just look for "markdown".
|
|
73
|
+
"markdown"
|
|
74
|
+
elsif entry_type.include?("textile")
|
|
75
|
+
# This is saved as "textile_2" on my installation of MT 5.1.
|
|
76
|
+
"textile"
|
|
77
|
+
elsif entry_type == "0" || entry_type.include?("richtext")
|
|
78
|
+
# Richtext looks to me like it's saved as HTML, so I include it here.
|
|
79
|
+
"html"
|
|
80
|
+
else
|
|
81
|
+
# Other values might need custom work.
|
|
82
|
+
entry_type
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'jekyll'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'uri'
|
|
6
|
+
require "json"
|
|
7
|
+
|
|
8
|
+
# ruby -r './lib/jekyll/migrators/posterous.rb' -e 'Jekyll::Posterous.process(email, pass, api_key, blog)'
|
|
9
|
+
|
|
10
|
+
module Jekyll
|
|
11
|
+
module Posterous
|
|
12
|
+
def self.fetch(uri_str, limit = 10)
|
|
13
|
+
# You should choose better exception.
|
|
14
|
+
raise ArgumentError, 'Stuck in a redirect loop. Please double check your email and password' if limit == 0
|
|
15
|
+
|
|
16
|
+
response = nil
|
|
17
|
+
Net::HTTP.start('posterous.com') do |http|
|
|
18
|
+
req = Net::HTTP::Get.new(uri_str)
|
|
19
|
+
req.basic_auth @email, @pass
|
|
20
|
+
response = http.request(req)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
case response
|
|
24
|
+
when Net::HTTPSuccess then response
|
|
25
|
+
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
|
|
26
|
+
else response.error!
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.process(email, pass, api_token, blog = 'primary')
|
|
31
|
+
@email, @pass, @api_token = email, pass, api_token
|
|
32
|
+
FileUtils.mkdir_p "_posts"
|
|
33
|
+
|
|
34
|
+
posts = JSON.parse(self.fetch("/api/v2/users/me/sites/#{blog}/posts?api_token=#{@api_token}").body)
|
|
35
|
+
page = 1
|
|
36
|
+
|
|
37
|
+
while posts.any?
|
|
38
|
+
posts.each do |post|
|
|
39
|
+
title = post["title"]
|
|
40
|
+
slug = title.gsub(/[^[:alnum:]]+/, '-').downcase
|
|
41
|
+
date = Date.parse(post["display_date"])
|
|
42
|
+
content = post["body_html"]
|
|
43
|
+
published = !post["is_private"]
|
|
44
|
+
name = "%02d-%02d-%02d-%s.html" % [date.year, date.month, date.day, slug]
|
|
45
|
+
|
|
46
|
+
# Get the relevant fields as a hash, delete empty fields and convert
|
|
47
|
+
# to YAML for the header
|
|
48
|
+
data = {
|
|
49
|
+
'layout' => 'post',
|
|
50
|
+
'title' => title.to_s,
|
|
51
|
+
'published' => published
|
|
52
|
+
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
|
|
53
|
+
|
|
54
|
+
# Write out the data and content to file
|
|
55
|
+
File.open("_posts/#{name}", "w") do |f|
|
|
56
|
+
f.puts data
|
|
57
|
+
f.puts "---"
|
|
58
|
+
f.puts content
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
page += 1
|
|
63
|
+
posts = JSON.parse(self.fetch("/api/v2/users/me/sites/#{blog}/posts?api_token=#{@api_token}&page=#{page}").body)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Created by Kendall Buchanan (https://github.com/kendagriff) on 2011-12-22.
|
|
2
|
+
# Use at your own risk. The end.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# (URL)
|
|
6
|
+
# ruby -r '_import/rss.rb' -e "Jekyll::MigrateRSS.process('http://yourdomain.com/your-favorite-feed.xml')"
|
|
7
|
+
#
|
|
8
|
+
# (Local file)
|
|
9
|
+
# ruby -r '_import/rss.rb' -e "Jekyll::MigrateRSS.process('./somefile/on/your/computer.xml')"
|
|
10
|
+
|
|
11
|
+
require 'rubygems'
|
|
12
|
+
require 'rss/1.0'
|
|
13
|
+
require 'rss/2.0'
|
|
14
|
+
require 'open-uri'
|
|
15
|
+
require 'fileutils'
|
|
16
|
+
require 'yaml'
|
|
17
|
+
|
|
18
|
+
module Jekyll
|
|
19
|
+
module MigrateRSS
|
|
20
|
+
|
|
21
|
+
# The `source` argument may be a URL or a local file.
|
|
22
|
+
def self.process(source)
|
|
23
|
+
content = ""
|
|
24
|
+
open(source) { |s| content = s.read }
|
|
25
|
+
rss = RSS::Parser.parse(content, false)
|
|
26
|
+
|
|
27
|
+
raise "There doesn't appear to be any RSS items at the source (#{source}) provided." unless rss
|
|
28
|
+
|
|
29
|
+
rss.items.each do |item|
|
|
30
|
+
formatted_date = item.date.strftime('%Y-%m-%d')
|
|
31
|
+
post_name = item.title.split(%r{ |!|/|:|&|-|$|,}).map { |i| i.downcase if i != '' }.compact.join('-')
|
|
32
|
+
name = "#{formatted_date}-#{post_name}"
|
|
33
|
+
|
|
34
|
+
header = {
|
|
35
|
+
'layout' => 'post',
|
|
36
|
+
'title' => item.title
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
File.open("_posts/#{name}.html", "w") do |f|
|
|
40
|
+
f.puts header.to_yaml
|
|
41
|
+
f.puts "---\n"
|
|
42
|
+
f.puts item.description
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'sequel'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
# NOTE: This converter requires Sequel and the MySQL gems.
|
|
7
|
+
# The MySQL gem can be difficult to install on OS X. Once you have MySQL
|
|
8
|
+
# installed, running the following commands should work:
|
|
9
|
+
# $ sudo gem install sequel
|
|
10
|
+
# $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
|
|
11
|
+
|
|
12
|
+
module Jekyll
|
|
13
|
+
module TextPattern
|
|
14
|
+
# Reads a MySQL database via Sequel and creates a post file for each post.
|
|
15
|
+
# The only posts selected are those with a status of 4 or 5, which means
|
|
16
|
+
# "live" and "sticky" respectively.
|
|
17
|
+
# Other statuses are 1 => draft, 2 => hidden and 3 => pending.
|
|
18
|
+
QUERY = "SELECT Title, \
|
|
19
|
+
url_title, \
|
|
20
|
+
Posted, \
|
|
21
|
+
Body, \
|
|
22
|
+
Keywords \
|
|
23
|
+
FROM textpattern \
|
|
24
|
+
WHERE Status = '4' OR \
|
|
25
|
+
Status = '5'"
|
|
26
|
+
|
|
27
|
+
def self.process(dbname, user, pass, host = 'localhost')
|
|
28
|
+
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
|
29
|
+
|
|
30
|
+
FileUtils.mkdir_p "_posts"
|
|
31
|
+
|
|
32
|
+
db[QUERY].each do |post|
|
|
33
|
+
# Get required fields and construct Jekyll compatible name.
|
|
34
|
+
title = post[:Title]
|
|
35
|
+
slug = post[:url_title]
|
|
36
|
+
date = post[:Posted]
|
|
37
|
+
content = post[:Body]
|
|
38
|
+
|
|
39
|
+
name = [date.strftime("%Y-%m-%d"), slug].join('-') + ".textile"
|
|
40
|
+
|
|
41
|
+
# Get the relevant fields as a hash, delete empty fields and convert
|
|
42
|
+
# to YAML for the header.
|
|
43
|
+
data = {
|
|
44
|
+
'layout' => 'post',
|
|
45
|
+
'title' => title.to_s,
|
|
46
|
+
'tags' => post[:Keywords].split(',')
|
|
47
|
+
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
|
|
48
|
+
|
|
49
|
+
# Write out the data and content to file.
|
|
50
|
+
File.open("_posts/#{name}", "w") do |f|
|
|
51
|
+
f.puts data
|
|
52
|
+
f.puts "---"
|
|
53
|
+
f.puts content
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'open-uri'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'nokogiri'
|
|
5
|
+
require 'date'
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'uri'
|
|
8
|
+
require 'jekyll'
|
|
9
|
+
|
|
10
|
+
module Jekyll
|
|
11
|
+
module Tumblr
|
|
12
|
+
def self.process(url, format = "html", grab_images = false,
|
|
13
|
+
add_highlights = false, rewrite_urls = true)
|
|
14
|
+
@grab_images = grab_images
|
|
15
|
+
FileUtils.mkdir_p "_posts/tumblr"
|
|
16
|
+
url += "/api/read/json/"
|
|
17
|
+
per_page = 50
|
|
18
|
+
posts = []
|
|
19
|
+
# Two passes are required so that we can rewrite URLs.
|
|
20
|
+
# First pass builds up an array of each post as a hash.
|
|
21
|
+
begin
|
|
22
|
+
current_page = (current_page || -1) + 1
|
|
23
|
+
feed = open(url + "?num=#{per_page}&start=#{current_page * per_page}")
|
|
24
|
+
json = feed.readlines.join("\n")[21...-2] # Strip Tumblr's JSONP chars.
|
|
25
|
+
blog = JSON.parse(json)
|
|
26
|
+
puts "Page: #{current_page + 1} - Posts: #{blog["posts"].size}"
|
|
27
|
+
posts += blog["posts"].map { |post| post_to_hash(post, format) }
|
|
28
|
+
end until blog["posts"].size < per_page
|
|
29
|
+
# Rewrite URLs and create redirects.
|
|
30
|
+
posts = rewrite_urls_and_redirects posts if rewrite_urls
|
|
31
|
+
# Second pass for writing post files.
|
|
32
|
+
posts.each do |post|
|
|
33
|
+
if format == "md"
|
|
34
|
+
post[:content] = html_to_markdown post[:content]
|
|
35
|
+
post[:content] = add_syntax_highlights post[:content] if add_highlights
|
|
36
|
+
end
|
|
37
|
+
File.open("_posts/tumblr/#{post[:name]}", "w") do |f|
|
|
38
|
+
f.puts post[:header].to_yaml + "---\n" + post[:content]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# Converts each type of Tumblr post to a hash with all required
|
|
46
|
+
# data for Jekyll.
|
|
47
|
+
def self.post_to_hash(post, format)
|
|
48
|
+
case post['type']
|
|
49
|
+
when "regular"
|
|
50
|
+
title = post["regular-title"]
|
|
51
|
+
content = post["regular-body"]
|
|
52
|
+
when "link"
|
|
53
|
+
title = post["link-text"] || post["link-url"]
|
|
54
|
+
content = "<a href=\"#{post["link-url"]}\">#{title}</a>"
|
|
55
|
+
unless post["link-description"].nil?
|
|
56
|
+
content << "<br/>" + post["link-description"]
|
|
57
|
+
end
|
|
58
|
+
when "photo"
|
|
59
|
+
title = post["photo-caption"]
|
|
60
|
+
max_size = post.keys.map{ |k| k.gsub("photo-url-", "").to_i }.max
|
|
61
|
+
url = post["photo-url"] || post["photo-url-#{max_size}"]
|
|
62
|
+
ext = "." + post[post.keys.select { |k|
|
|
63
|
+
k =~ /^photo-url-/ && post[k].split("/").last =~ /\./
|
|
64
|
+
}.first].split(".").last
|
|
65
|
+
content = "<img src=\"#{save_file(url, ext)}\"/>"
|
|
66
|
+
unless post["photo-link-url"].nil?
|
|
67
|
+
content = "<a href=\"#{post["photo-link-url"]}\">#{content}</a>"
|
|
68
|
+
end
|
|
69
|
+
when "audio"
|
|
70
|
+
if !post["id3-title"].nil?
|
|
71
|
+
title = post["id3-title"]
|
|
72
|
+
content = post.at["audio-player"] + "<br/>" + post["audio-caption"]
|
|
73
|
+
else
|
|
74
|
+
title = post["audio-caption"]
|
|
75
|
+
content = post.at["audio-player"]
|
|
76
|
+
end
|
|
77
|
+
when "quote"
|
|
78
|
+
title = post["quote-text"]
|
|
79
|
+
content = "<blockquote>#{post["quote-text"]}</blockquote>"
|
|
80
|
+
unless post["quote-source"].nil?
|
|
81
|
+
content << "—" + post["quote-source"]
|
|
82
|
+
end
|
|
83
|
+
when "conversation"
|
|
84
|
+
title = post["conversation-title"]
|
|
85
|
+
content = "<section><dialog>"
|
|
86
|
+
post["conversation"]["line"].each do |line|
|
|
87
|
+
content << "<dt>#{line['label']}</dt><dd>#{line}</dd>"
|
|
88
|
+
end
|
|
89
|
+
content << "</section></dialog>"
|
|
90
|
+
when "video"
|
|
91
|
+
title = post["video-title"]
|
|
92
|
+
content = post["video-player"]
|
|
93
|
+
unless post["video-caption"].nil?
|
|
94
|
+
content << "<br/>" + post["video-caption"]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
date = Date.parse(post['date']).to_s
|
|
98
|
+
title = Nokogiri::HTML(title).text
|
|
99
|
+
slug = title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
|
|
100
|
+
{
|
|
101
|
+
:name => "#{date}-#{slug}.#{format}",
|
|
102
|
+
:header => {
|
|
103
|
+
"layout" => "post",
|
|
104
|
+
"title" => title,
|
|
105
|
+
"tags" => post["tags"],
|
|
106
|
+
},
|
|
107
|
+
:content => content,
|
|
108
|
+
:url => post["url"],
|
|
109
|
+
:slug => post["url-with-slug"],
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Create a Hash of old urls => new urls, for rewriting and
|
|
114
|
+
# redirects, and replace urls in each post. Instantiate Jekyll
|
|
115
|
+
# site/posts to get the correct permalink format.
|
|
116
|
+
def self.rewrite_urls_and_redirects(posts)
|
|
117
|
+
site = Jekyll::Site.new(Jekyll.configuration({}))
|
|
118
|
+
dir = File.join(File.dirname(__FILE__), "..")
|
|
119
|
+
urls = Hash[posts.map { |post|
|
|
120
|
+
# Create an initial empty file for the post so that
|
|
121
|
+
# we can instantiate a post object.
|
|
122
|
+
File.open("_posts/tumblr/#{post[:name]}", "w")
|
|
123
|
+
tumblr_url = URI.parse(post[:slug]).path
|
|
124
|
+
jekyll_url = Jekyll::Post.new(site, dir, "", "tumblr/" + post[:name]).url
|
|
125
|
+
redirect_dir = tumblr_url.sub(/\//, "") + "/"
|
|
126
|
+
FileUtils.mkdir_p redirect_dir
|
|
127
|
+
File.open(redirect_dir + "index.html", "w") do |f|
|
|
128
|
+
f.puts "<html><head><meta http-equiv='Refresh' content='0; " +
|
|
129
|
+
"url=#{jekyll_url}'></head><body></body></html>"
|
|
130
|
+
end
|
|
131
|
+
[tumblr_url, jekyll_url]
|
|
132
|
+
}]
|
|
133
|
+
posts.map { |post|
|
|
134
|
+
urls.each do |tumblr_url, jekyll_url|
|
|
135
|
+
post[:content].gsub!(/#{tumblr_url}/i, jekyll_url)
|
|
136
|
+
end
|
|
137
|
+
post
|
|
138
|
+
}
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Uses Python's html2text to convert a post's content to
|
|
142
|
+
# markdown. Preserve HTML tables as per the markdown docs.
|
|
143
|
+
def self.html_to_markdown(content)
|
|
144
|
+
preserve = ["table", "tr", "th", "td"]
|
|
145
|
+
preserve.each do |tag|
|
|
146
|
+
content.gsub!(/<#{tag}/i, "$$" + tag)
|
|
147
|
+
content.gsub!(/<\/#{tag}/i, "||" + tag)
|
|
148
|
+
end
|
|
149
|
+
content = %x[echo '#{content.gsub("'", "''")}' | html2text]
|
|
150
|
+
preserve.each do |tag|
|
|
151
|
+
content.gsub!("$$" + tag, "<" + tag)
|
|
152
|
+
content.gsub!("||" + tag, "</" + tag)
|
|
153
|
+
end
|
|
154
|
+
content
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Adds pygments highlight tags to code blocks in posts that use
|
|
158
|
+
# markdown format. This doesn't guess the language of the code
|
|
159
|
+
# block, so you should modify this to suit your own content.
|
|
160
|
+
# For example, my code block only contain Python and JavaScript,
|
|
161
|
+
# so I can assume the block is JavaScript if it contains a
|
|
162
|
+
# semi-colon.
|
|
163
|
+
def self.add_syntax_highlights(content)
|
|
164
|
+
lines = content.split("\n")
|
|
165
|
+
block, indent, lang, start = false, /^ /, nil, nil
|
|
166
|
+
lines.each_with_index do |line, i|
|
|
167
|
+
if !block && line =~ indent
|
|
168
|
+
block = true
|
|
169
|
+
lang = "python"
|
|
170
|
+
start = i
|
|
171
|
+
elsif block
|
|
172
|
+
lang = "javascript" if line =~ /;$/
|
|
173
|
+
block = line =~ indent && i < lines.size - 1 # Also handle EOF
|
|
174
|
+
if !block
|
|
175
|
+
lines[start] = "{% highlight #{lang} %}"
|
|
176
|
+
lines[i - 1] = "{% endhighlight %}"
|
|
177
|
+
end
|
|
178
|
+
lines[i] = lines[i].sub(indent, "")
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
lines.join("\n")
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def self.save_file(url, ext)
|
|
185
|
+
if @grab_images
|
|
186
|
+
path = "tumblr_files/#{url.split('/').last}"
|
|
187
|
+
path += ext unless path =~ /#{ext}$/
|
|
188
|
+
FileUtils.mkdir_p "tumblr_files"
|
|
189
|
+
File.open(path, "w") { |f| f.write(open(url).read) }
|
|
190
|
+
url = "/" + path
|
|
191
|
+
end
|
|
192
|
+
url
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|