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,51 @@
|
|
|
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
|
|
@@ -0,0 +1,294 @@
|
|
|
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
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'hpricot'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
require 'yaml'
|
|
7
|
+
require 'time'
|
|
8
|
+
|
|
9
|
+
module Jekyll
|
|
10
|
+
# This importer takes a wordpress.xml file, which can be exported from your
|
|
11
|
+
# wordpress.com blog (/wp-admin/export.php).
|
|
12
|
+
module WordpressDotCom
|
|
13
|
+
def self.process(filename = "wordpress.xml")
|
|
14
|
+
import_count = Hash.new(0)
|
|
15
|
+
doc = Hpricot::XML(File.read(filename))
|
|
16
|
+
|
|
17
|
+
(doc/:channel/:item).each do |item|
|
|
18
|
+
title = item.at(:title).inner_text.strip
|
|
19
|
+
permalink_title = item.at('wp:post_name').inner_text
|
|
20
|
+
# Fallback to "prettified" title if post_name is empty (can happen)
|
|
21
|
+
if permalink_title == ""
|
|
22
|
+
permalink_title = title.downcase.split.join('-')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
date = Time.parse(item.at('wp:post_date').inner_text)
|
|
26
|
+
status = item.at('wp:status').inner_text
|
|
27
|
+
|
|
28
|
+
if status == "publish"
|
|
29
|
+
published = true
|
|
30
|
+
else
|
|
31
|
+
published = false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
type = item.at('wp:post_type').inner_text
|
|
35
|
+
tags = (item/:category).map{|c| c.inner_text}.reject{|c| c == 'Uncategorized'}.uniq
|
|
36
|
+
|
|
37
|
+
metas = Hash.new
|
|
38
|
+
item.search("wp:postmeta").each do |meta|
|
|
39
|
+
key = meta.at('wp:meta_key').inner_text
|
|
40
|
+
value = meta.at('wp:meta_value').inner_text
|
|
41
|
+
metas[key] = value;
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
name = "#{date.strftime('%Y-%m-%d')}-#{permalink_title}.html"
|
|
45
|
+
header = {
|
|
46
|
+
'layout' => type,
|
|
47
|
+
'title' => title,
|
|
48
|
+
'tags' => tags,
|
|
49
|
+
'status' => status,
|
|
50
|
+
'type' => type,
|
|
51
|
+
'published' => published,
|
|
52
|
+
'meta' => metas
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
FileUtils.mkdir_p "_#{type}s"
|
|
56
|
+
File.open("_#{type}s/#{name}", "w") do |f|
|
|
57
|
+
f.puts header.to_yaml
|
|
58
|
+
f.puts '---'
|
|
59
|
+
f.puts item.at('content:encoded').inner_text
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
import_count[type] += 1
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
import_count.each do |key, value|
|
|
66
|
+
puts "Imported #{value} #{key}s"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/jekyll/page.rb
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
module Jekyll
|
|
2
|
+
|
|
3
|
+
class Page
|
|
4
|
+
include Convertible
|
|
5
|
+
|
|
6
|
+
attr_writer :dir
|
|
7
|
+
attr_accessor :site, :pager
|
|
8
|
+
attr_accessor :name, :ext, :basename
|
|
9
|
+
attr_accessor :data, :content, :output
|
|
10
|
+
|
|
11
|
+
# Initialize a new Page.
|
|
12
|
+
#
|
|
13
|
+
# site - The Site object.
|
|
14
|
+
# source - The String path to the source.
|
|
15
|
+
# dir - The String path between the source and the file.
|
|
16
|
+
# name - The String filename of the file.
|
|
17
|
+
def initialize(site, source, dir, name)
|
|
18
|
+
@site = site
|
|
19
|
+
@dir = dir
|
|
20
|
+
@base = File.join(source, @dir)
|
|
21
|
+
@name = name
|
|
22
|
+
|
|
23
|
+
self.process(name)
|
|
24
|
+
self.read_yaml(@base, name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# The source filename for this page.
|
|
28
|
+
def filename
|
|
29
|
+
File.join(@base, name)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# The generated directory into which the page will be placed
|
|
33
|
+
# upon generation. This is derived from the permalink or, if
|
|
34
|
+
# permalink is absent, we be '/'
|
|
35
|
+
#
|
|
36
|
+
# Returns the String destination directory.
|
|
37
|
+
def dir
|
|
38
|
+
url[-1, 1] == '/' ? url : File.dirname(url)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# The full path and filename of the post. Defined in the YAML of the post
|
|
42
|
+
# body.
|
|
43
|
+
#
|
|
44
|
+
# Returns the String permalink or nil if none has been set.
|
|
45
|
+
def permalink
|
|
46
|
+
self.data && self.data['permalink']
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The template of the permalink.
|
|
50
|
+
#
|
|
51
|
+
# Returns the template String.
|
|
52
|
+
def template
|
|
53
|
+
if self.site.permalink_style == :pretty && !index? && html?
|
|
54
|
+
"/:basename/"
|
|
55
|
+
else
|
|
56
|
+
"/:basename:output_ext"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The generated relative url of this page. e.g. /about.html.
|
|
61
|
+
#
|
|
62
|
+
# Returns the String url.
|
|
63
|
+
def url
|
|
64
|
+
return @url if @url
|
|
65
|
+
|
|
66
|
+
url = if permalink
|
|
67
|
+
permalink
|
|
68
|
+
else
|
|
69
|
+
{
|
|
70
|
+
"basename" => self.basename,
|
|
71
|
+
"output_ext" => self.output_ext,
|
|
72
|
+
}.inject(template) { |result, token|
|
|
73
|
+
result.gsub(/:#{token.first}/, token.last)
|
|
74
|
+
}.gsub(/\/\//, "/")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# sanitize url
|
|
78
|
+
@url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
|
|
79
|
+
@url += "/" if url =~ /\/$/
|
|
80
|
+
@url
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Extract information from the page filename.
|
|
84
|
+
#
|
|
85
|
+
# name - The String filename of the page file.
|
|
86
|
+
#
|
|
87
|
+
# Returns nothing.
|
|
88
|
+
def process(name)
|
|
89
|
+
self.ext = File.extname(name)
|
|
90
|
+
self.basename = name[0 .. -self.ext.length-1]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Add any necessary layouts to this post
|
|
94
|
+
#
|
|
95
|
+
# layouts - The Hash of {"name" => "layout"}.
|
|
96
|
+
# site_payload - The site payload Hash.
|
|
97
|
+
#
|
|
98
|
+
# Returns nothing.
|
|
99
|
+
def render(layouts, site_payload)
|
|
100
|
+
payload = {
|
|
101
|
+
"page" => self.to_liquid,
|
|
102
|
+
'paginator' => pager.to_liquid
|
|
103
|
+
}.deep_merge(site_payload)
|
|
104
|
+
|
|
105
|
+
do_layout(payload, layouts)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Convert this Page's data to a Hash suitable for use by Liquid.
|
|
109
|
+
#
|
|
110
|
+
# Returns the Hash representation of this Page.
|
|
111
|
+
def to_liquid
|
|
112
|
+
self.data.deep_merge({
|
|
113
|
+
"url" => File.join(@dir, self.url),
|
|
114
|
+
"content" => self.content })
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Obtain destination path.
|
|
118
|
+
#
|
|
119
|
+
# dest - The String path to the destination dir.
|
|
120
|
+
#
|
|
121
|
+
# Returns the destination file path String.
|
|
122
|
+
def destination(dest)
|
|
123
|
+
# The url needs to be unescaped in order to preserve the correct
|
|
124
|
+
# filename.
|
|
125
|
+
path = File.join(dest, @dir, CGI.unescape(self.url))
|
|
126
|
+
path = File.join(path, "index.html") if self.url =~ /\/$/
|
|
127
|
+
path
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Write the generated page file to the destination directory.
|
|
131
|
+
#
|
|
132
|
+
# dest - The String path to the destination dir.
|
|
133
|
+
#
|
|
134
|
+
# Returns nothing.
|
|
135
|
+
def write(dest)
|
|
136
|
+
path = destination(dest)
|
|
137
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
138
|
+
File.open(path, 'w') do |f|
|
|
139
|
+
f.write(self.output)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Returns the object as a debug String.
|
|
144
|
+
def inspect
|
|
145
|
+
"#<Jekyll:Page @name=#{self.name.inspect}>"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Returns the Boolean of whether this Page is HTML or not.
|
|
149
|
+
def html?
|
|
150
|
+
output_ext == '.html'
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Returns the Boolean of whether this Page is an index file or not.
|
|
154
|
+
def index?
|
|
155
|
+
basename == 'index'
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module Jekyll
|
|
2
|
+
|
|
3
|
+
class Plugin
|
|
4
|
+
PRIORITIES = { :lowest => -100,
|
|
5
|
+
:low => -10,
|
|
6
|
+
:normal => 0,
|
|
7
|
+
:high => 10,
|
|
8
|
+
:highest => 100 }
|
|
9
|
+
|
|
10
|
+
# Install a hook so that subclasses are recorded. This method is only
|
|
11
|
+
# ever called by Ruby itself.
|
|
12
|
+
#
|
|
13
|
+
# base - The Class subclass.
|
|
14
|
+
#
|
|
15
|
+
# Returns nothing.
|
|
16
|
+
def self.inherited(base)
|
|
17
|
+
subclasses << base
|
|
18
|
+
subclasses.sort!
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# The list of Classes that have been subclassed.
|
|
22
|
+
#
|
|
23
|
+
# Returns an Array of Class objects.
|
|
24
|
+
def self.subclasses
|
|
25
|
+
@subclasses ||= []
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Get or set the priority of this plugin. When called without an
|
|
29
|
+
# argument it returns the priority. When an argument is given, it will
|
|
30
|
+
# set the priority.
|
|
31
|
+
#
|
|
32
|
+
# priority - The Symbol priority (default: nil). Valid options are:
|
|
33
|
+
# :lowest, :low, :normal, :high, :highest
|
|
34
|
+
#
|
|
35
|
+
# Returns the Symbol priority.
|
|
36
|
+
def self.priority(priority = nil)
|
|
37
|
+
@priority ||= nil
|
|
38
|
+
if priority && PRIORITIES.has_key?(priority)
|
|
39
|
+
@priority = priority
|
|
40
|
+
end
|
|
41
|
+
@priority || :normal
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Get or set the safety of this plugin. When called without an argument
|
|
45
|
+
# it returns the safety. When an argument is given, it will set the
|
|
46
|
+
# safety.
|
|
47
|
+
#
|
|
48
|
+
# safe - The Boolean safety (default: nil).
|
|
49
|
+
#
|
|
50
|
+
# Returns the safety Boolean.
|
|
51
|
+
def self.safe(safe = nil)
|
|
52
|
+
if safe
|
|
53
|
+
@safe = safe
|
|
54
|
+
end
|
|
55
|
+
@safe || false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Spaceship is priority [higher -> lower]
|
|
59
|
+
#
|
|
60
|
+
# other - The class to be compared.
|
|
61
|
+
#
|
|
62
|
+
# Returns -1, 0, 1.
|
|
63
|
+
def self.<=>(other)
|
|
64
|
+
PRIORITIES[other.priority] <=> PRIORITIES[self.priority]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Initialize a new plugin. This should be overridden by the subclass.
|
|
68
|
+
#
|
|
69
|
+
# config - The Hash of configuration options.
|
|
70
|
+
#
|
|
71
|
+
# Returns a new instance.
|
|
72
|
+
def initialize(config = {})
|
|
73
|
+
# no-op for default
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|