jekyll 0.12.1 → 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of jekyll might be problematic. Click here for more details.
- data/CONTRIBUTING.md +67 -0
- data/Gemfile +1 -1
- data/History.txt +50 -6
- data/README.textile +10 -6
- data/Rakefile +74 -36
- data/bin/jekyll +78 -276
- data/cucumber.yml +3 -1
- data/features/create_sites.feature +1 -1
- data/features/drafts.feature +25 -0
- data/features/site_configuration.feature +1 -1
- data/features/step_definitions/jekyll_steps.rb +13 -3
- data/features/support/env.rb +3 -1
- data/jekyll.gemspec +73 -17
- data/lib/jekyll.rb +31 -21
- data/lib/jekyll/command.rb +12 -0
- data/lib/jekyll/commands/build.rb +81 -0
- data/lib/jekyll/commands/serve.rb +28 -0
- data/lib/jekyll/converter.rb +1 -3
- data/lib/jekyll/converters/identity.rb +13 -14
- data/lib/jekyll/converters/markdown.rb +128 -128
- data/lib/jekyll/converters/textile.rb +37 -37
- data/lib/jekyll/convertible.rb +6 -4
- data/lib/jekyll/core_ext.rb +9 -1
- data/lib/jekyll/draft.rb +35 -0
- data/lib/jekyll/errors.rb +1 -3
- data/lib/jekyll/filters.rb +13 -4
- data/lib/jekyll/generator.rb +1 -4
- data/lib/jekyll/generators/pagination.rb +46 -46
- data/lib/jekyll/layout.rb +0 -2
- data/lib/jekyll/mime.types +1588 -0
- data/lib/jekyll/page.rb +24 -8
- data/lib/jekyll/plugin.rb +0 -2
- data/lib/jekyll/post.rb +66 -40
- data/lib/jekyll/site.rb +96 -20
- data/lib/jekyll/static_file.rb +0 -2
- data/lib/jekyll/tags/gist.rb +19 -0
- data/lib/jekyll/tags/highlight.rb +63 -62
- data/lib/jekyll/tags/include.rb +25 -25
- data/lib/jekyll/tags/post_url.rb +30 -25
- data/script/bootstrap +2 -0
- data/site/.gitignore +4 -0
- data/site/CNAME +1 -0
- data/site/README +1 -0
- data/site/_config.yml +5 -0
- data/site/_includes/analytics.html +32 -0
- data/site/_includes/docs_contents.html +82 -0
- data/site/_includes/footer.html +15 -0
- data/site/_includes/header.html +26 -0
- data/site/_includes/section_nav.html +22 -0
- data/site/_includes/top.html +14 -0
- data/site/_layouts/default.html +12 -0
- data/site/_layouts/docs.html +21 -0
- data/site/_posts/2012-07-01-configuration.md +277 -0
- data/site/_posts/2012-07-01-contributing.md +66 -0
- data/site/_posts/2012-07-01-deployment-methods.md +108 -0
- data/site/_posts/2012-07-01-extras.md +103 -0
- data/site/_posts/2012-07-01-frontmatter.md +120 -0
- data/site/_posts/2012-07-01-github-pages.md +34 -0
- data/site/_posts/2012-07-01-heroku.md +8 -0
- data/site/_posts/2012-07-01-home.md +47 -0
- data/site/_posts/2012-07-01-installation.md +43 -0
- data/site/_posts/2012-07-01-migrations.md +180 -0
- data/site/_posts/2012-07-01-pages.md +62 -0
- data/site/_posts/2012-07-01-pagination.md +116 -0
- data/site/_posts/2012-07-01-permalinks.md +163 -0
- data/site/_posts/2012-07-01-plugins.md +384 -0
- data/site/_posts/2012-07-01-posts.md +106 -0
- data/site/_posts/2012-07-01-resources.md +49 -0
- data/site/_posts/2012-07-01-sites.md +28 -0
- data/site/_posts/2012-07-01-structure.md +95 -0
- data/site/_posts/2012-07-01-templates.md +217 -0
- data/site/_posts/2012-07-01-troubleshooting.md +108 -0
- data/site/_posts/2012-07-01-usage.md +38 -0
- data/site/_posts/2012-07-01-variables.md +166 -0
- data/site/css/grid.css +62 -0
- data/site/css/normalize.css +504 -0
- data/site/css/pygments.css +70 -0
- data/site/css/style.css +697 -0
- data/site/docs/index.html +11 -0
- data/site/favicon.png +0 -0
- data/site/img/article-footer.png +0 -0
- data/site/img/footer-arrow.png +0 -0
- data/site/img/footer-logo.png +0 -0
- data/site/img/logo-2x.png +0 -0
- data/site/img/octojekyll.png +0 -0
- data/site/img/tube.png +0 -0
- data/site/img/tube1x.png +0 -0
- data/site/index.html +77 -0
- data/site/js/modernizr-2.5.3.min.js +4 -0
- data/test/fixtures/broken_front_matter2.erb +4 -0
- data/test/fixtures/broken_front_matter3.erb +7 -0
- data/test/fixtures/exploit_front_matter.erb +4 -0
- data/test/helper.rb +16 -0
- data/test/source/_posts/2013-01-12-nil-layout.textile +6 -0
- data/test/source/_posts/2013-01-12-no-layout.textile +5 -0
- data/test/source/contacts/bar.html +5 -0
- data/test/source/contacts/index.html +5 -0
- data/test/test_configuration.rb +7 -8
- data/test/test_convertible.rb +29 -0
- data/test/test_core_ext.rb +22 -0
- data/test/test_generated_site.rb +1 -1
- data/test/test_kramdown.rb +3 -3
- data/test/test_page.rb +88 -2
- data/test/test_post.rb +42 -6
- data/test/test_rdiscount.rb +1 -1
- data/test/test_redcarpet.rb +1 -1
- data/test/test_redcloth.rb +6 -6
- data/test/test_site.rb +73 -8
- data/test/test_tags.rb +36 -13
- metadata +150 -19
- data/lib/jekyll/migrators/csv.rb +0 -26
- data/lib/jekyll/migrators/drupal.rb +0 -103
- data/lib/jekyll/migrators/enki.rb +0 -49
- data/lib/jekyll/migrators/joomla.rb +0 -53
- data/lib/jekyll/migrators/marley.rb +0 -52
- data/lib/jekyll/migrators/mephisto.rb +0 -84
- data/lib/jekyll/migrators/mt.rb +0 -86
- data/lib/jekyll/migrators/posterous.rb +0 -67
- data/lib/jekyll/migrators/rss.rb +0 -47
- data/lib/jekyll/migrators/textpattern.rb +0 -58
- data/lib/jekyll/migrators/tumblr.rb +0 -195
- data/lib/jekyll/migrators/typo.rb +0 -51
- data/lib/jekyll/migrators/wordpress.rb +0 -294
- data/lib/jekyll/migrators/wordpressdotcom.rb +0 -70
@@ -1,67 +0,0 @@
|
|
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
|
data/lib/jekyll/migrators/rss.rb
DELETED
@@ -1,47 +0,0 @@
|
|
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
|
@@ -1,58 +0,0 @@
|
|
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
|
@@ -1,195 +0,0 @@
|
|
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
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# Author: Toby DiPasquale <toby@cbcg.net>
|
2
|
-
require 'fileutils'
|
3
|
-
require 'rubygems'
|
4
|
-
require 'sequel'
|
5
|
-
require 'yaml'
|
6
|
-
|
7
|
-
module Jekyll
|
8
|
-
module Typo
|
9
|
-
# This SQL *should* work for both MySQL and PostgreSQL, but I haven't
|
10
|
-
# tested PostgreSQL yet (as of 2008-12-16).
|
11
|
-
SQL = <<-EOS
|
12
|
-
SELECT c.id id,
|
13
|
-
c.title title,
|
14
|
-
c.permalink slug,
|
15
|
-
c.body body,
|
16
|
-
c.published_at date,
|
17
|
-
c.state state,
|
18
|
-
COALESCE(tf.name, 'html') filter
|
19
|
-
FROM contents c
|
20
|
-
LEFT OUTER JOIN text_filters tf
|
21
|
-
ON c.text_filter_id = tf.id
|
22
|
-
EOS
|
23
|
-
|
24
|
-
def self.process dbname, user, pass, host='localhost'
|
25
|
-
FileUtils.mkdir_p '_posts'
|
26
|
-
db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
|
27
|
-
db[SQL].each do |post|
|
28
|
-
next unless post[:state] =~ /published/
|
29
|
-
|
30
|
-
name = [ sprintf("%.04d", post[:date].year),
|
31
|
-
sprintf("%.02d", post[:date].month),
|
32
|
-
sprintf("%.02d", post[:date].day),
|
33
|
-
post[:slug].strip ].join('-')
|
34
|
-
|
35
|
-
# Can have more than one text filter in this field, but we just want
|
36
|
-
# the first one for this.
|
37
|
-
name += '.' + post[:filter].split(' ')[0]
|
38
|
-
|
39
|
-
File.open("_posts/#{name}", 'w') do |f|
|
40
|
-
f.puts({ 'layout' => 'post',
|
41
|
-
'title' => post[:title].to_s,
|
42
|
-
'typo_id' => post[:id]
|
43
|
-
}.delete_if { |k, v| v.nil? || v == '' }.to_yaml)
|
44
|
-
f.puts '---'
|
45
|
-
f.puts post[:body].delete("\r")
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
end
|
@@ -1,294 +0,0 @@
|
|
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 WordPress
|
14
|
-
|
15
|
-
# Main migrator function. Call this to perform the migration.
|
16
|
-
#
|
17
|
-
# dbname:: The name of the database
|
18
|
-
# user:: The database user name
|
19
|
-
# pass:: The database user's password
|
20
|
-
# host:: The address of the MySQL database host. Default: 'localhost'
|
21
|
-
# options:: A hash table of configuration options.
|
22
|
-
#
|
23
|
-
# Supported options are:
|
24
|
-
#
|
25
|
-
# :table_prefix:: Prefix of database tables used by WordPress.
|
26
|
-
# Default: 'wp_'
|
27
|
-
# :clean_entities:: If true, convert non-ASCII characters to HTML
|
28
|
-
# entities in the posts, comments, titles, and
|
29
|
-
# names. Requires the 'htmlentities' gem to
|
30
|
-
# work. Default: true.
|
31
|
-
# :comments:: If true, migrate post comments too. Comments
|
32
|
-
# are saved in the post's YAML front matter.
|
33
|
-
# Default: true.
|
34
|
-
# :categories:: If true, save the post's categories in its
|
35
|
-
# YAML front matter.
|
36
|
-
# :tags:: If true, save the post's tags in its
|
37
|
-
# YAML front matter.
|
38
|
-
# :more_excerpt:: If true, when a post has no excerpt but
|
39
|
-
# does have a <!-- more --> tag, use the
|
40
|
-
# preceding post content as the excerpt.
|
41
|
-
# Default: true.
|
42
|
-
# :more_anchor:: If true, convert a <!-- more --> tag into
|
43
|
-
# two HTML anchors with ids "more" and
|
44
|
-
# "more-NNN" (where NNN is the post number).
|
45
|
-
# Default: true.
|
46
|
-
# :status:: Array of allowed post statuses. Only
|
47
|
-
# posts with matching status will be migrated.
|
48
|
-
# Known statuses are :publish, :draft, :private,
|
49
|
-
# and :revision. If this is nil or an empty
|
50
|
-
# array, all posts are migrated regardless of
|
51
|
-
# status. Default: [:publish].
|
52
|
-
#
|
53
|
-
def self.process(dbname, user, pass, host='localhost', options={})
|
54
|
-
options = {
|
55
|
-
:table_prefix => 'wp_',
|
56
|
-
:clean_entities => true,
|
57
|
-
:comments => true,
|
58
|
-
:categories => true,
|
59
|
-
:tags => true,
|
60
|
-
:more_excerpt => true,
|
61
|
-
:more_anchor => true,
|
62
|
-
:status => [:publish] # :draft, :private, :revision
|
63
|
-
}.merge(options)
|
64
|
-
|
65
|
-
if options[:clean_entities]
|
66
|
-
begin
|
67
|
-
require 'htmlentities'
|
68
|
-
rescue LoadError
|
69
|
-
STDERR.puts "Could not require 'htmlentities', so the " +
|
70
|
-
":clean_entities option is now disabled."
|
71
|
-
options[:clean_entities] = false
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
FileUtils.mkdir_p("_posts")
|
76
|
-
|
77
|
-
db = Sequel.mysql(dbname, :user => user, :password => pass,
|
78
|
-
:host => host, :encoding => 'utf8')
|
79
|
-
|
80
|
-
px = options[:table_prefix]
|
81
|
-
|
82
|
-
posts_query = "
|
83
|
-
SELECT
|
84
|
-
posts.ID AS `id`,
|
85
|
-
posts.guid AS `guid`,
|
86
|
-
posts.post_type AS `type`,
|
87
|
-
posts.post_status AS `status`,
|
88
|
-
posts.post_title AS `title`,
|
89
|
-
posts.post_name AS `slug`,
|
90
|
-
posts.post_date AS `date`,
|
91
|
-
posts.post_content AS `content`,
|
92
|
-
posts.post_excerpt AS `excerpt`,
|
93
|
-
posts.comment_count AS `comment_count`,
|
94
|
-
users.display_name AS `author`,
|
95
|
-
users.user_login AS `author_login`,
|
96
|
-
users.user_email AS `author_email`,
|
97
|
-
users.user_url AS `author_url`
|
98
|
-
FROM #{px}posts AS `posts`
|
99
|
-
LEFT JOIN #{px}users AS `users`
|
100
|
-
ON posts.post_author = users.ID"
|
101
|
-
|
102
|
-
if options[:status] and not options[:status].empty?
|
103
|
-
status = options[:status][0]
|
104
|
-
posts_query << "
|
105
|
-
WHERE posts.post_status = '#{status.to_s}'"
|
106
|
-
options[:status][1..-1].each do |status|
|
107
|
-
posts_query << " OR
|
108
|
-
posts.post_status = '#{status.to_s}'"
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
db[posts_query].each do |post|
|
113
|
-
process_post(post, db, options)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
|
118
|
-
def self.process_post(post, db, options)
|
119
|
-
px = options[:table_prefix]
|
120
|
-
|
121
|
-
title = post[:title]
|
122
|
-
if options[:clean_entities]
|
123
|
-
title = clean_entities(title)
|
124
|
-
end
|
125
|
-
|
126
|
-
slug = post[:slug]
|
127
|
-
if !slug or slug.empty?
|
128
|
-
slug = sluggify(title)
|
129
|
-
end
|
130
|
-
|
131
|
-
date = post[:date] || Time.now
|
132
|
-
name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month,
|
133
|
-
date.day, slug]
|
134
|
-
content = post[:content].to_s
|
135
|
-
if options[:clean_entities]
|
136
|
-
content = clean_entities(content)
|
137
|
-
end
|
138
|
-
|
139
|
-
excerpt = post[:excerpt].to_s
|
140
|
-
|
141
|
-
more_index = content.index(/<!-- *more *-->/)
|
142
|
-
more_anchor = nil
|
143
|
-
if more_index
|
144
|
-
if options[:more_excerpt] and
|
145
|
-
(post[:excerpt].nil? or post[:excerpt].empty?)
|
146
|
-
excerpt = content[0...more_index]
|
147
|
-
end
|
148
|
-
if options[:more_anchor]
|
149
|
-
more_link = "more"
|
150
|
-
content.sub!(/<!-- *more *-->/,
|
151
|
-
"<a id=\"more\"></a>" +
|
152
|
-
"<a id=\"more-#{post[:id]}\"></a>")
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
categories = []
|
157
|
-
tags = []
|
158
|
-
|
159
|
-
if options[:categories] or options[:tags]
|
160
|
-
|
161
|
-
cquery =
|
162
|
-
"SELECT
|
163
|
-
terms.name AS `name`,
|
164
|
-
ttax.taxonomy AS `type`
|
165
|
-
FROM
|
166
|
-
#{px}terms AS `terms`,
|
167
|
-
#{px}term_relationships AS `trels`,
|
168
|
-
#{px}term_taxonomy AS `ttax`
|
169
|
-
WHERE
|
170
|
-
trels.object_id = '#{post[:id]}' AND
|
171
|
-
trels.term_taxonomy_id = ttax.term_taxonomy_id AND
|
172
|
-
terms.term_id = ttax.term_id"
|
173
|
-
|
174
|
-
db[cquery].each do |term|
|
175
|
-
if options[:categories] and term[:type] == "category"
|
176
|
-
if options[:clean_entities]
|
177
|
-
categories << clean_entities(term[:name])
|
178
|
-
else
|
179
|
-
categories << term[:name]
|
180
|
-
end
|
181
|
-
elsif options[:tags] and term[:type] == "post_tag"
|
182
|
-
if options[:clean_entities]
|
183
|
-
tags << clean_entities(term[:name])
|
184
|
-
else
|
185
|
-
tags << term[:name]
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
comments = []
|
192
|
-
|
193
|
-
if options[:comments] and post[:comment_count].to_i > 0
|
194
|
-
cquery =
|
195
|
-
"SELECT
|
196
|
-
comment_ID AS `id`,
|
197
|
-
comment_author AS `author`,
|
198
|
-
comment_author_email AS `author_email`,
|
199
|
-
comment_author_url AS `author_url`,
|
200
|
-
comment_date AS `date`,
|
201
|
-
comment_date_gmt AS `date_gmt`,
|
202
|
-
comment_content AS `content`
|
203
|
-
FROM #{px}comments
|
204
|
-
WHERE
|
205
|
-
comment_post_ID = '#{post[:id]}' AND
|
206
|
-
comment_approved != 'spam'"
|
207
|
-
|
208
|
-
|
209
|
-
db[cquery].each do |comment|
|
210
|
-
|
211
|
-
comcontent = comment[:content].to_s
|
212
|
-
if comcontent.respond_to?(:force_encoding)
|
213
|
-
comcontent.force_encoding("UTF-8")
|
214
|
-
end
|
215
|
-
if options[:clean_entities]
|
216
|
-
comcontent = clean_entities(comcontent)
|
217
|
-
end
|
218
|
-
comauthor = comment[:author].to_s
|
219
|
-
if options[:clean_entities]
|
220
|
-
comauthor = clean_entities(comauthor)
|
221
|
-
end
|
222
|
-
|
223
|
-
comments << {
|
224
|
-
'id' => comment[:id].to_i,
|
225
|
-
'author' => comauthor,
|
226
|
-
'author_email' => comment[:author_email].to_s,
|
227
|
-
'author_url' => comment[:author_url].to_s,
|
228
|
-
'date' => comment[:date].to_s,
|
229
|
-
'date_gmt' => comment[:date_gmt].to_s,
|
230
|
-
'content' => comcontent,
|
231
|
-
}
|
232
|
-
end
|
233
|
-
|
234
|
-
comments.sort!{ |a,b| a['id'] <=> b['id'] }
|
235
|
-
end
|
236
|
-
|
237
|
-
# Get the relevant fields as a hash, delete empty fields and
|
238
|
-
# convert to YAML for the header.
|
239
|
-
data = {
|
240
|
-
'layout' => post[:type].to_s,
|
241
|
-
'status' => post[:status].to_s,
|
242
|
-
'published' => (post[:status].to_s == "publish"),
|
243
|
-
'title' => title.to_s,
|
244
|
-
'author' => post[:author].to_s,
|
245
|
-
'author_login' => post[:author_login].to_s,
|
246
|
-
'author_email' => post[:author_email].to_s,
|
247
|
-
'author_url' => post[:author_url].to_s,
|
248
|
-
'excerpt' => excerpt,
|
249
|
-
'more_anchor' => more_anchor,
|
250
|
-
'wordpress_id' => post[:id],
|
251
|
-
'wordpress_url' => post[:guid].to_s,
|
252
|
-
'date' => date,
|
253
|
-
'categories' => options[:categories] ? categories : nil,
|
254
|
-
'tags' => options[:tags] ? tags : nil,
|
255
|
-
'comments' => options[:comments] ? comments : nil,
|
256
|
-
}.delete_if { |k,v| v.nil? || v == '' }.to_yaml
|
257
|
-
|
258
|
-
# Write out the data and content to file
|
259
|
-
File.open("_posts/#{name}", "w") do |f|
|
260
|
-
f.puts data
|
261
|
-
f.puts "---"
|
262
|
-
f.puts content
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
|
267
|
-
def self.clean_entities( text )
|
268
|
-
if text.respond_to?(:force_encoding)
|
269
|
-
text.force_encoding("UTF-8")
|
270
|
-
end
|
271
|
-
text = HTMLEntities.new.encode(text, :named)
|
272
|
-
# We don't want to convert these, it would break all
|
273
|
-
# HTML tags in the post and comments.
|
274
|
-
text.gsub!("&", "&")
|
275
|
-
text.gsub!("<", "<")
|
276
|
-
text.gsub!(">", ">")
|
277
|
-
text.gsub!(""", '"')
|
278
|
-
text.gsub!("'", "'")
|
279
|
-
text
|
280
|
-
end
|
281
|
-
|
282
|
-
|
283
|
-
def self.sluggify( title )
|
284
|
-
begin
|
285
|
-
require 'unidecode'
|
286
|
-
title = title.to_ascii
|
287
|
-
rescue LoadError
|
288
|
-
STDERR.puts "Could not require 'unidecode'. If your post titles have non-ASCII characters, you could get nicer permalinks by installing unidecode."
|
289
|
-
end
|
290
|
-
title.downcase.gsub(/[^0-9A-Za-z]+/, " ").strip.gsub(" ", "-")
|
291
|
-
end
|
292
|
-
|
293
|
-
end
|
294
|
-
end
|