sutty-migration 0.3.5 → 0.4.0rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +23 -53
- data/lib/jekyll-wordpress-sql-migration.rb +132 -0
- data/lib/jekyll-wordpress-xml-migration.rb +88 -0
- data/lib/sutty_migration/data.rb +3 -3
- data/lib/sutty_migration/wordpress.rb +6 -52
- data/lib/sutty_migration/wordpress_xml/attachment.rb +81 -13
- data/lib/sutty_migration/wordpress_xml/post.rb +119 -17
- data/lib/sutty_migration/wordpress_xml.rb +21 -10
- metadata +70 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 41536e494891ca8e5b5a6ea4f98dcf2cdd780bb8f1a16cdfc10bcee9f5195eb6
|
|
4
|
+
data.tar.gz: d73e41285e4bfd4080aad2beb3b9b3a29332ee0f31e64b4438fd0bcc7a479952
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b7e64d51403a731be28d4eae8385747dee6abc99e3e5c7b8220240a673b1dee555584706ef0d10f203751eacb51b89022c02ec1d9019b3854a547650f9489e1d
|
|
7
|
+
data.tar.gz: f7ae65bde83caa3404d7de2b825fe751fdc0490fda3c399f4c98f2cf320e037323172b065ec82e9e7af4a5e09ecc5c27e095f3b955c19268e1fe0cc55522a557
|
data/README.md
CHANGED
|
@@ -91,75 +91,45 @@ SQLite syntax, since `mysql2sqlite` doesn't support this yet.
|
|
|
91
91
|
Wordpress websites can include lots of posts and metadata, depending on
|
|
92
92
|
the amount of plugins installed. We don't have an official way of
|
|
93
93
|
dumping everything into Jekyll, because you will probably want to move
|
|
94
|
-
things around. You can write a plugin like this
|
|
94
|
+
things around. You can write a plugin like this to inspect what's
|
|
95
|
+
happening on every stage:
|
|
95
96
|
|
|
96
97
|
```ruby
|
|
97
98
|
# _plugins/wordpress.rb
|
|
98
99
|
# frozen_string_literal: true
|
|
99
100
|
|
|
100
|
-
require '
|
|
101
|
-
require '
|
|
102
|
-
require 'jekyll-write-and-commit-changes'
|
|
101
|
+
require 'jekyll-wordpress-sql-migration'
|
|
102
|
+
require 'pry'
|
|
103
103
|
|
|
104
|
-
Jekyll::Hooks.register :
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
# Download all files
|
|
108
|
-
wp.download_all
|
|
109
|
-
|
|
110
|
-
wp.posts(layout: 'post').each do |post|
|
|
111
|
-
doc = Jekyll::Document.create(site: site, title: post[:post_title], date: post[:post_date], collection: 'posts')
|
|
112
|
-
doc.content = post[:content]
|
|
113
|
-
doc.save
|
|
114
|
-
end
|
|
104
|
+
Jekyll::Hooks.register :wordpress_post, :post_init do |item|
|
|
105
|
+
binding.pry
|
|
115
106
|
end
|
|
116
107
|
```
|
|
117
108
|
|
|
109
|
+
Available hooks are `:wordpress_attachment`, `:wordpress_post` and
|
|
110
|
+
`:migration` for the `:post_init`, `:pre_render`, `:post_convert`,
|
|
111
|
+
`:post_render` and `:post_write` stages.
|
|
112
|
+
|
|
113
|
+
And then set `JEKYLL_ENV=migration` for the migration to run.
|
|
114
|
+
|
|
115
|
+
If the website is still available (although probably doesn't),
|
|
116
|
+
attachments will be downloaded to `wp-content/`. If you have them from
|
|
117
|
+
a backup, put them there so the plugin finds them.
|
|
118
|
+
|
|
118
119
|
### WordPress XML
|
|
119
120
|
|
|
120
121
|
If you have the XML dump from a WordPress site, you can migrate content
|
|
121
|
-
by
|
|
122
|
+
by using the XML migration plugin and hooking to its steps.
|
|
122
123
|
|
|
123
124
|
```ruby
|
|
125
|
+
# _plugins/wordpress.rb
|
|
124
126
|
# frozen_string_literal: true
|
|
125
127
|
|
|
126
|
-
require '
|
|
127
|
-
require '
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
# Run after reading the site
|
|
132
|
-
Jekyll::Hooks.register :site, :post_read do |site|
|
|
133
|
-
# Put the XML dump at _files/wordpress.xml
|
|
134
|
-
xml = SuttyMigration::WordpressXml.new site: site, file: '_files/wordpress.xml'
|
|
135
|
-
|
|
136
|
-
# Download all files
|
|
137
|
-
xml.attachments.values.map(&:download)
|
|
138
|
-
|
|
139
|
-
# Migrate posts. You can move metadata around and recover
|
|
140
|
-
# relationships or any info your theme requires.
|
|
141
|
-
xml.posts.values.each do |post|
|
|
142
|
-
# Update documents already migrated.
|
|
143
|
-
doc = Jekyll::Document.find_or_create(site: site, collection: locale, title: post.title, slug: post.slug, date: post.date)
|
|
144
|
-
# Don't change the UUIDv4
|
|
145
|
-
d.data['uuid'] ||= SecureRandom.uuid
|
|
146
|
-
d.data['draft'] = post.draft?
|
|
147
|
-
d.data['layout'] = 'post'
|
|
148
|
-
d.data['last_modified_at'] = post.last_modified_at
|
|
149
|
-
|
|
150
|
-
d.data['categories'] = post.categories.map { |c| c[:title] }
|
|
151
|
-
d.data['tags'] = post.tags.map { |t| t[:title] }
|
|
152
|
-
|
|
153
|
-
d.data['author'] = post.author[:email]
|
|
154
|
-
d.data['description'] = post.description
|
|
155
|
-
d.content = post.content
|
|
156
|
-
|
|
157
|
-
doc.save
|
|
158
|
-
rescue => e
|
|
159
|
-
Jekyll.logger.warn "Couldn't migrate #{post.title}"
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
exit # Stop here
|
|
128
|
+
require 'jekyll-wordpress-xml-migration'
|
|
129
|
+
require 'pry'
|
|
130
|
+
|
|
131
|
+
Jekyll::Hooks.register :wordpress_post, :post_init do |item|
|
|
132
|
+
binding.pry
|
|
163
133
|
end
|
|
164
134
|
```
|
|
165
135
|
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Enable this plugin if you have an SQL dump backup of your Wordpress
|
|
4
|
+
# site. To avoid having to run a MySQL/MariaDB database, we convert the
|
|
5
|
+
# dump into an SQLite database with our fork of
|
|
6
|
+
# [mysql2sqlite](https://git.sutty.abyaya.la/misc/mysql2sqlite)
|
|
7
|
+
#
|
|
8
|
+
# Put the SQLite file on _files/wordpress.sqlite3 and set the URL on the
|
|
9
|
+
# `url` field on `_config.yml`. If the new website doesn't match the
|
|
10
|
+
# URL, you can use `wordpress_url`.
|
|
11
|
+
#
|
|
12
|
+
# If the prefix for Wordpress tables is different than "wp_", set
|
|
13
|
+
# `wordpress_prefix` on `_config.yml`.
|
|
14
|
+
#
|
|
15
|
+
# ```sh
|
|
16
|
+
# JEKYLL_ENV=migration bundle exec jekyll build
|
|
17
|
+
# ```
|
|
18
|
+
#
|
|
19
|
+
# ## Hooks
|
|
20
|
+
#
|
|
21
|
+
# Hook to `:migration` if you need to access and modify data during the
|
|
22
|
+
# migration process:
|
|
23
|
+
#
|
|
24
|
+
# * `:post_init`: will receive the `Hash`
|
|
25
|
+
# * `:pre_render`: will receive the `SuttyMigration::WordpressXml::Post`
|
|
26
|
+
# * `:post_convert`: will receive the `Jekyll::Document` after setting front matter
|
|
27
|
+
# * `:post_render`: will receive the `Jekyll::Document` after sanitizing the content
|
|
28
|
+
# * `:post_write`: will receive the `Jekyll::Document` after writing to disk
|
|
29
|
+
|
|
30
|
+
Jekyll::Hooks.register :site, :post_read, priority: :high do |site|
|
|
31
|
+
next unless Jekyll.env == 'migration'
|
|
32
|
+
|
|
33
|
+
database = '_files/wordpress.sqlite3'
|
|
34
|
+
|
|
35
|
+
Jekyll.logger.abort_with 'Migration:', "#{database} doesn't exist!" unless File.exist? database
|
|
36
|
+
|
|
37
|
+
require 'sutty_migration/wordpress'
|
|
38
|
+
require 'sutty_migration/wordpress_xml/post'
|
|
39
|
+
require 'sutty_migration/wordpress_xml/attachment'
|
|
40
|
+
require 'sutty_migration/jekyll/document_creator'
|
|
41
|
+
require 'jekyll-write-and-commit-changes'
|
|
42
|
+
|
|
43
|
+
url = site.config['wordpress_url'] || site.config['url']
|
|
44
|
+
prefix = site.config['wordpress_prefix'] || 'wp_'
|
|
45
|
+
|
|
46
|
+
wordpress = SuttyMigration::Wordpress.new(site:, url:, database:, prefix:)
|
|
47
|
+
migrated_posts = []
|
|
48
|
+
|
|
49
|
+
Jekyll::Hooks.trigger :migration, :post_init, wordpress
|
|
50
|
+
|
|
51
|
+
wordpress.posts(layout: 'attachment').each do |item|
|
|
52
|
+
Jekyll::Hooks.trigger :wordpress_attachment, :post_init, item
|
|
53
|
+
|
|
54
|
+
attachment = SuttyMigration::WordpressXml::Attachment.new(wordpress:, item:)
|
|
55
|
+
|
|
56
|
+
# Download and convert to Sutty's public/
|
|
57
|
+
Jekyll::Hooks.trigger :wordpress_attachment, :pre_render, attachment
|
|
58
|
+
# Index attachments by id, public path and original destination so
|
|
59
|
+
# we can find them later.
|
|
60
|
+
wordpress.attachments[attachment.attachment_url] =
|
|
61
|
+
wordpress.attachments[attachment.public_path] =
|
|
62
|
+
wordpress.attachments[item[:id]] =
|
|
63
|
+
attachment
|
|
64
|
+
Jekyll::Hooks.trigger :wordpress_attachment, :post_convert, attachment
|
|
65
|
+
Jekyll::Hooks.trigger :wordpress_attachment, :post_render, attachment
|
|
66
|
+
|
|
67
|
+
Jekyll::Hooks.trigger :wordpress_attachment, :post_write, attachment
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Jekyll::Hooks.trigger :migration, :pre_render, wordpress
|
|
71
|
+
|
|
72
|
+
# We have to instantiate Posts from a Hash instead of an XML object.
|
|
73
|
+
wordpress.posts(layout: 'post').each do |item|
|
|
74
|
+
Jekyll::Hooks.trigger :wordpress_post, :post_init, item
|
|
75
|
+
|
|
76
|
+
item[:title] = 'Draft' if item[:title].empty?
|
|
77
|
+
item[:slug] = Jekyll::Utils.slugify(item[:title], mode: 'latin') if item[:slug].empty?
|
|
78
|
+
|
|
79
|
+
post = SuttyMigration::WordpressXml::Post.new(wordpress:, item:).tap do |p|
|
|
80
|
+
p.categories = [item.dig(:terms, :category)].flatten.compact
|
|
81
|
+
p.tags = [item.dig(:terms, :post_tag)].flatten.compact
|
|
82
|
+
p.date = item[:date] || item[:last_modified_at]
|
|
83
|
+
p.last_modified_at = item[:last_modified_at]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
Jekyll::Hooks.trigger :wordpress_post, :pre_render, post
|
|
87
|
+
|
|
88
|
+
Jekyll::Document.find_or_create(site:, slug: post.slug, title: post.title, date: post.date,
|
|
89
|
+
collection: 'posts').tap do |doc|
|
|
90
|
+
# If you want to customize the layout, change it with a :post_init
|
|
91
|
+
# hook.
|
|
92
|
+
doc.data['layout'] = item[:layout]
|
|
93
|
+
doc.data['created_at'] = post.date
|
|
94
|
+
doc.data['last_modified_at'] = post.last_modified_at&.localtime
|
|
95
|
+
doc.data['uuid'] ||= SecureRandom.uuid
|
|
96
|
+
doc.data['categories'] = post.categories
|
|
97
|
+
doc.data['tags'] = post.tags
|
|
98
|
+
doc.data['draft'] = post.draft?
|
|
99
|
+
|
|
100
|
+
unless (attachment_id = item.dig(:front_matter, :_thumbnail_id).to_i).zero?
|
|
101
|
+
attachment = wordpress.attachments[attachment_id]
|
|
102
|
+
|
|
103
|
+
if attachment
|
|
104
|
+
doc.data['image'] = {
|
|
105
|
+
'path' => attachment.public_path,
|
|
106
|
+
'description' => ''
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
Jekyll::Hooks.trigger :wordpress_post, :post_convert, doc
|
|
112
|
+
|
|
113
|
+
doc.content = post.beautiful_content
|
|
114
|
+
|
|
115
|
+
Jekyll::Hooks.trigger :wordpress_post, :post_render, doc
|
|
116
|
+
|
|
117
|
+
migrated_posts << doc
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
Jekyll::Hooks.trigger :migration, :post_convert, wordpress
|
|
122
|
+
Jekyll::Hooks.trigger :migration, :post_render, migrated_posts
|
|
123
|
+
|
|
124
|
+
# Save them all at the end
|
|
125
|
+
migrated_posts.each do |doc|
|
|
126
|
+
doc.save
|
|
127
|
+
|
|
128
|
+
Jekyll::Hooks.trigger :wordpress_post, :post_write, doc
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
Jekyll::Hooks.trigger :migration, :post_write, migrated_posts
|
|
132
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Enable this plugin if you have an XML backup of your Wordpress site.
|
|
4
|
+
# Although this is the easiest path, it's not always possible to have
|
|
5
|
+
# one, either because the website is not working or producing the XML
|
|
6
|
+
# dump gets canceled by PHP resource usage limits.
|
|
7
|
+
#
|
|
8
|
+
# Put the XML file on _files/wordpress.xml and set the URL on the `url`
|
|
9
|
+
# field on _config.yml. If the new website doesn't match the URL, you
|
|
10
|
+
# can use `wordpress_url`.
|
|
11
|
+
#
|
|
12
|
+
# JEKYLL_ENV=migration bundle exec jekyll build
|
|
13
|
+
Jekyll::Hooks.register :site, :post_read, priority: :high do |site|
|
|
14
|
+
next unless Jekyll.env == 'migration'
|
|
15
|
+
|
|
16
|
+
file = '_files/wordpress.xml'
|
|
17
|
+
|
|
18
|
+
Jekyll.logger.abort_with 'Migration:', "#{file} is missing!" unless File.exist? file
|
|
19
|
+
|
|
20
|
+
require 'sutty_migration/wordpress_xml'
|
|
21
|
+
require 'sutty_migration/jekyll/document_creator'
|
|
22
|
+
require 'jekyll-write-and-commit-changes'
|
|
23
|
+
|
|
24
|
+
wordpress = SuttyMigration::WordpressXml.new(site:, file:)
|
|
25
|
+
migrated_posts = []
|
|
26
|
+
|
|
27
|
+
Jekyll::Hooks.trigger :migration, :post_init, wordpress
|
|
28
|
+
|
|
29
|
+
wordpress.attachments.values.uniq.each do |attachment|
|
|
30
|
+
Jekyll::Hooks.trigger :wordpress_attachment, :post_init, attachment.item
|
|
31
|
+
|
|
32
|
+
Jekyll::Hooks.trigger :wordpress_attachment, :pre_render, attachment
|
|
33
|
+
# Download and convert to Sutty's public/
|
|
34
|
+
attachment.public_path
|
|
35
|
+
Jekyll::Hooks.trigger :wordpress_attachment, :post_convert, attachment
|
|
36
|
+
Jekyll::Hooks.trigger :wordpress_attachment, :post_render, attachment
|
|
37
|
+
Jekyll::Hooks.trigger :wordpress_attachment, :post_write, attachment
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
wordpress.posts.each_value do |post|
|
|
41
|
+
Jekyll::Hooks.trigger :wordpress_post, :post_init, post.item
|
|
42
|
+
Jekyll::Hooks.trigger :wordpress_post, :pre_render, post
|
|
43
|
+
Jekyll::Document.find_or_create(site:, slug: post.slug, title: post.title, date: post.date,
|
|
44
|
+
collection: 'posts').tap do |doc|
|
|
45
|
+
# If you want to customize the layout, change it with a :post_init
|
|
46
|
+
# hook.
|
|
47
|
+
doc.data['layout'] = 'post'
|
|
48
|
+
doc.data['permalink'] = post.permalink
|
|
49
|
+
doc.data['created_at'] = post.date
|
|
50
|
+
doc.data['last_modified_at'] = post.last_modified_at&.localtime
|
|
51
|
+
doc.data['uuid'] ||= SecureRandom.uuid
|
|
52
|
+
doc.data['categories'] = post.categories.map { _1[:title] }
|
|
53
|
+
doc.data['tags'] = post.tags.map { _1[:title] }
|
|
54
|
+
doc.data['draft'] = post.draft?
|
|
55
|
+
|
|
56
|
+
unless (attachment_id = post.meta['_thumbnail_id'].to_i).zero?
|
|
57
|
+
attachment = wordpress.attachments[attachment_id]
|
|
58
|
+
|
|
59
|
+
if attachment
|
|
60
|
+
doc.data['image'] = {
|
|
61
|
+
'path' => attachment.public_path,
|
|
62
|
+
'description' => ''
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
Jekyll::Hooks.trigger :wordpress_post, :post_convert, doc
|
|
68
|
+
|
|
69
|
+
doc.content = post.beautiful_content
|
|
70
|
+
|
|
71
|
+
Jekyll::Hooks.trigger :wordpress_post, :post_render, doc
|
|
72
|
+
|
|
73
|
+
migrated_posts << doc
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
Jekyll::Hooks.trigger :migration, :post_convert, wordpress
|
|
78
|
+
Jekyll::Hooks.trigger :migration, :post_render, migrated_posts
|
|
79
|
+
|
|
80
|
+
# Save them all at the end
|
|
81
|
+
migrated_posts.each do |doc|
|
|
82
|
+
doc.save
|
|
83
|
+
|
|
84
|
+
Jekyll::Hooks.trigger :wordpress_post, :post_write, doc
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
Jekyll::Hooks.trigger :migration, :post_write, migrated_posts
|
|
88
|
+
end
|
data/lib/sutty_migration/data.rb
CHANGED
|
@@ -27,9 +27,9 @@ Jekyll::Hooks.register :site, :post_read, priority: :low do |site|
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
document ||= begin
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
data = row.slice(*%w[date slug title]).transform_keys(&:to_sym)
|
|
31
|
+
Jekyll::Document.find_or_create(site: site, collection: 'posts', **data)
|
|
32
|
+
end
|
|
33
33
|
next unless document
|
|
34
34
|
|
|
35
35
|
row.each do |attribute, value|
|
|
@@ -17,7 +17,7 @@ module SuttyMigration
|
|
|
17
17
|
# migration plugin where you can convert data by yourself. We may add
|
|
18
18
|
# this feature in the future.
|
|
19
19
|
class Wordpress
|
|
20
|
-
attr_reader :site, :prefix, :limit, :url, :wp, :database, :multisite
|
|
20
|
+
attr_reader :site, :prefix, :limit, :url, :wp, :database, :multisite, :attachments
|
|
21
21
|
|
|
22
22
|
# @param :site [Jekyll::Site] Jekyll site
|
|
23
23
|
# @param :url [String] Wordpress site URL (must be up for downloads)
|
|
@@ -32,13 +32,14 @@ module SuttyMigration
|
|
|
32
32
|
@url = url.freeze
|
|
33
33
|
@database = database || File.join(site.source, '_data', 'wordpress.sqlite3')
|
|
34
34
|
@multisite = multisite
|
|
35
|
+
@attachments = {}
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
# Generate database connections for a multisite WP
|
|
38
39
|
#
|
|
39
40
|
# @return [Hash] { "ID" => SuttyMigration::Wordpress }
|
|
40
41
|
def blogs
|
|
41
|
-
@blogs ||= wp["select * from #{prefix}blogs"].to_a.
|
|
42
|
+
@blogs ||= wp["select * from #{prefix}blogs"].to_a.to_h do |blog|
|
|
42
43
|
url = "https://#{blog[:domain]}#{blog[:path]}"
|
|
43
44
|
pfx = "#{prefix}#{blog[:blog_id]}_" if blog[:blog_id] > 1
|
|
44
45
|
pfx ||= prefix
|
|
@@ -46,11 +47,11 @@ module SuttyMigration
|
|
|
46
47
|
[blog[:blog_id],
|
|
47
48
|
blog.merge(db: self.class.new(site: site, url: url, prefix: pfx, database: database, limit: limit,
|
|
48
49
|
multisite: self))]
|
|
49
|
-
end
|
|
50
|
+
end
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
def options
|
|
53
|
-
@options ||= wp["select option_name, option_value from #{prefix}options"].to_a.
|
|
54
|
+
@options ||= wp["select option_name, option_value from #{prefix}options"].to_a.to_h(&:values).transform_keys(&:to_sym)
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
# Open the database.
|
|
@@ -62,53 +63,6 @@ module SuttyMigration
|
|
|
62
63
|
end
|
|
63
64
|
end
|
|
64
65
|
|
|
65
|
-
# Download all attachments. Adds the local path to them.
|
|
66
|
-
#
|
|
67
|
-
# @param :progress [Boolean] Toggle progress bar
|
|
68
|
-
# @return [Nil]
|
|
69
|
-
def download_all(progress: true)
|
|
70
|
-
posts(layout: 'attachment').each do |attachment|
|
|
71
|
-
attachment[:front_matter]['file_path'] = download(url: attachment[:guid], progress: progress)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Downloads a file if needed, optionally showing a progress bar.
|
|
76
|
-
#
|
|
77
|
-
# @param :url [String] File URL
|
|
78
|
-
# @param :progress [Boolean] Toggle progress bar
|
|
79
|
-
# @return [String] File local path
|
|
80
|
-
def download(url:, progress: true)
|
|
81
|
-
uri = URI(url)
|
|
82
|
-
dest = uri.path.sub(%r{\A/}, '')
|
|
83
|
-
full = File.join(site.source, dest)
|
|
84
|
-
|
|
85
|
-
return dest if File.exist? full
|
|
86
|
-
|
|
87
|
-
::Jekyll.logger.info "Downloading #{dest}"
|
|
88
|
-
|
|
89
|
-
FileUtils.mkdir_p File.dirname(full)
|
|
90
|
-
|
|
91
|
-
File.open(full, 'w') do |f|
|
|
92
|
-
if progress
|
|
93
|
-
head = Faraday.head(url)
|
|
94
|
-
content_length = head.headers['content-length'].to_i
|
|
95
|
-
progress = ProgressBar.create(title: File.basename(dest), total: content_length, output: $stderr)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
Faraday.get(url) do |req|
|
|
99
|
-
req.options.on_data = proc do |chunk, downloaded_bytes|
|
|
100
|
-
f.write chunk
|
|
101
|
-
|
|
102
|
-
if progress
|
|
103
|
-
progress.progress = downloaded_bytes > content_length ? content_length : downloaded_bytes
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
dest
|
|
110
|
-
end
|
|
111
|
-
|
|
112
66
|
# List post types
|
|
113
67
|
#
|
|
114
68
|
# @return [Array]
|
|
@@ -129,7 +83,7 @@ module SuttyMigration
|
|
|
129
83
|
end
|
|
130
84
|
|
|
131
85
|
wp[post_query(**options)].each_page(limit).to_a.map(&:to_a).flatten.tap do |p|
|
|
132
|
-
p.
|
|
86
|
+
p.each do |post|
|
|
133
87
|
# Sequel parses dates on localtime
|
|
134
88
|
post[:date] = ::Jekyll::Utils.parse_date(post[:date]) unless post[:date].blank?
|
|
135
89
|
unless post[:last_modified_at].blank?
|
|
@@ -4,6 +4,7 @@ require_relative 'post'
|
|
|
4
4
|
require 'php-serialize'
|
|
5
5
|
require 'faraday'
|
|
6
6
|
require 'progressbar'
|
|
7
|
+
require 'addressable'
|
|
7
8
|
|
|
8
9
|
module SuttyMigration
|
|
9
10
|
class WordpressXml
|
|
@@ -13,14 +14,22 @@ module SuttyMigration
|
|
|
13
14
|
#
|
|
14
15
|
# @return [String]
|
|
15
16
|
def attachment_url
|
|
16
|
-
@attachment_url ||= attribute_value
|
|
17
|
+
@attachment_url ||= attribute_value('attachment_url') || attribute_value('guid')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def parsed_url
|
|
21
|
+
@parsed_url ||= Addressable::URI.parse(attachment_url)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def normalized_url
|
|
25
|
+
@normalized_url ||= parsed_url.normalize
|
|
17
26
|
end
|
|
18
27
|
|
|
19
28
|
# File destination
|
|
20
29
|
#
|
|
21
30
|
# @return [String]
|
|
22
31
|
def dest
|
|
23
|
-
@dest ||= CGI.unescape(
|
|
32
|
+
@dest ||= CGI.unescape(parsed_url.path.sub(%r{\A/}, '').unicode_normalize)
|
|
24
33
|
end
|
|
25
34
|
|
|
26
35
|
# Metadata, with file information as a Hash
|
|
@@ -40,30 +49,89 @@ module SuttyMigration
|
|
|
40
49
|
def download(progress: true)
|
|
41
50
|
return true if File.exist? dest
|
|
42
51
|
|
|
43
|
-
::Jekyll.logger.info 'Downloading:', dest
|
|
44
|
-
|
|
45
52
|
FileUtils.mkdir_p File.dirname(dest)
|
|
46
53
|
|
|
47
54
|
File.open(dest, 'w') do |f|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
head = Faraday.head(normalized_url)
|
|
56
|
+
|
|
57
|
+
if head.status > 399
|
|
58
|
+
::Jekyll.logger.warn 'Error:', "#{head.status} #{normalized_url}"
|
|
59
|
+
return false
|
|
52
60
|
end
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
content_length = head.headers['content-length'].to_i
|
|
63
|
+
|
|
64
|
+
if content_length.zero?
|
|
65
|
+
::Jekyll.logger.warn 'Error:', "Content length is zero for #{normalized_url}"
|
|
66
|
+
return false
|
|
67
|
+
end
|
|
57
68
|
|
|
58
|
-
|
|
59
|
-
|
|
69
|
+
::Jekyll.logger.info 'Downloading:', dest
|
|
70
|
+
|
|
71
|
+
progress_bar =
|
|
72
|
+
(ProgressBar.create(title: File.basename(dest), total: content_length, output: $stderr) if progress)
|
|
73
|
+
|
|
74
|
+
begin
|
|
75
|
+
Faraday.get(normalized_url) do |req|
|
|
76
|
+
req.options.on_data = proc do |chunk, downloaded_bytes|
|
|
77
|
+
if downloaded_bytes > content_length
|
|
78
|
+
raise Faraday::Error, 'File is bigger than informed, aborting download'
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
f.write chunk
|
|
82
|
+
|
|
83
|
+
progress_bar&.progress = downloaded_bytes
|
|
60
84
|
end
|
|
61
85
|
end
|
|
86
|
+
rescue Faraday::Error => e
|
|
87
|
+
::Jekyll.logger.warn 'Error:', e.message
|
|
62
88
|
end
|
|
89
|
+
|
|
90
|
+
progress_bar&.stop
|
|
63
91
|
end
|
|
64
92
|
|
|
65
93
|
File.exist? dest
|
|
66
94
|
end
|
|
95
|
+
|
|
96
|
+
# Local path following Sutty's conventions. Instead of producing
|
|
97
|
+
# a SecureRandom.base36 key, we use the first 28 characters of the
|
|
98
|
+
# digest to obtain constand names.
|
|
99
|
+
#
|
|
100
|
+
# It then hardlinks the original file.
|
|
101
|
+
#
|
|
102
|
+
# @return [String]
|
|
103
|
+
def public_path
|
|
104
|
+
return unless digest
|
|
105
|
+
|
|
106
|
+
@public_path ||= File.join('public', digest[..27], dest.split('/').last).tap do
|
|
107
|
+
next if File.exist? _1
|
|
108
|
+
|
|
109
|
+
# If the directory exists and it's not empty, but the file is
|
|
110
|
+
# not there, we either have the same file with different names
|
|
111
|
+
# or old thumbnails.
|
|
112
|
+
dir = File.dirname(_1)
|
|
113
|
+
if File.directory?(dir) && !Dir.empty?(dir)
|
|
114
|
+
::Jekyll.logger.warn 'Migration:',
|
|
115
|
+
"#{dir} is not empty, you probably have the same file with different names"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
FileUtils.mkdir_p(File.dirname(_1))
|
|
119
|
+
FileUtils.ln(dest, _1)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Download and produce a digest. Produce an error if the file
|
|
124
|
+
# can't be downloaded.
|
|
125
|
+
#
|
|
126
|
+
# @return [String]
|
|
127
|
+
def digest
|
|
128
|
+
@digest ||=
|
|
129
|
+
begin
|
|
130
|
+
require 'digest'
|
|
131
|
+
|
|
132
|
+
Digest::SHA2.file(dest).hexdigest if download
|
|
133
|
+
end
|
|
134
|
+
end
|
|
67
135
|
end
|
|
68
136
|
end
|
|
69
137
|
end
|
|
@@ -1,18 +1,37 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'rails-html-sanitizer'
|
|
3
4
|
require 'wordpress_formatting/wpautop'
|
|
5
|
+
require 'htmlbeautifier'
|
|
4
6
|
require 'jekyll/utils'
|
|
7
|
+
require 'nokogiri'
|
|
5
8
|
|
|
6
9
|
module SuttyMigration
|
|
7
10
|
class WordpressXml
|
|
8
11
|
# Represents a WordPress post
|
|
9
12
|
class Post
|
|
10
13
|
attr_reader :wordpress, :item
|
|
14
|
+
attr_writer :tags, :categories, :author, :meta, :date, :last_modified_at
|
|
11
15
|
|
|
16
|
+
SANITIZER = Rails::HTML::Sanitizer.safe_list_sanitizer.new
|
|
17
|
+
# This follows https://www.npmjs.com/package/@suttyweb/editor and
|
|
18
|
+
# also allows some known WP tags, you can add your own during
|
|
19
|
+
# plugin initilization.
|
|
20
|
+
#
|
|
21
|
+
# rubocop:disable Style/MutableConstant
|
|
22
|
+
TAGS = %w[a aside audio blockquote br caption code del em figcaption figure h1 h2 h3 h4 h5 h6 hr iframe img li mark ol p
|
|
23
|
+
pre small strong sub sup table tbody td tfoot th thead tr u ul video]
|
|
24
|
+
ATTRIBUTES = %w[alt colspan controls data-align data-id data-lang data-multimedia data-multimedia-inner
|
|
25
|
+
data-turbo href id name referrerpolicy rel reversed rowspan src start target title type]
|
|
26
|
+
# rubocop:enable Style/MutableConstant
|
|
12
27
|
SLASH = '/'
|
|
28
|
+
THUMBNAIL_PREFIX = /-\d+x\d+(\.....?)\z/
|
|
29
|
+
# TODO: Move to wordpress-formatting
|
|
30
|
+
WORDPRESS_ELEMENT_START = /\[(\w+ [^\]]+)\]/
|
|
31
|
+
WORDPRESS_ELEMENT_END = %r{\[(/\w+)\]}
|
|
13
32
|
|
|
14
33
|
# @param :wordpress [SuttyMigration::WordpressXml]
|
|
15
|
-
# @param :item [Nokogiri::XML::Element]
|
|
34
|
+
# @param :item [Nokogiri::XML::Element, Hash]
|
|
16
35
|
def initialize(wordpress:, item:)
|
|
17
36
|
@wordpress = wordpress
|
|
18
37
|
@item = item
|
|
@@ -31,17 +50,17 @@ module SuttyMigration
|
|
|
31
50
|
|
|
32
51
|
# Permalink. Absolute URL to the post.
|
|
33
52
|
#
|
|
34
|
-
# @return [String]
|
|
53
|
+
# @return [String, nil]
|
|
35
54
|
def permalink
|
|
36
55
|
@permalink ||=
|
|
37
56
|
begin
|
|
38
|
-
p = attribute_value('link')
|
|
57
|
+
p = attribute_value('link')&.sub(wordpress.url, '')&.split('?', 2)&.first
|
|
39
58
|
|
|
40
|
-
|
|
41
|
-
p << SLASH
|
|
42
|
-
end
|
|
59
|
+
unless p.nil? || p.blank?
|
|
60
|
+
p << SLASH if !p.end_with?(SLASH) && File.extname(p).empty?
|
|
43
61
|
|
|
44
|
-
|
|
62
|
+
p.squeeze(SLASH)
|
|
63
|
+
end
|
|
45
64
|
end
|
|
46
65
|
end
|
|
47
66
|
|
|
@@ -63,7 +82,9 @@ module SuttyMigration
|
|
|
63
82
|
#
|
|
64
83
|
# @return [String]
|
|
65
84
|
def slug
|
|
66
|
-
@slug ||= attribute_value('post_name').tr(SLASH, '-').squeeze('-').sub(/\A-+/, '').sub(
|
|
85
|
+
@slug ||= (attribute_value('post_name') || attribute_value('slug')).to_s.tr(SLASH, '-').squeeze('-').sub(/\A-+/, '').sub(
|
|
86
|
+
/-+\z/, ''
|
|
87
|
+
)
|
|
67
88
|
end
|
|
68
89
|
|
|
69
90
|
# Publication date.
|
|
@@ -83,19 +104,93 @@ module SuttyMigration
|
|
|
83
104
|
#
|
|
84
105
|
# @return [Time, nil]
|
|
85
106
|
def last_modified_at
|
|
86
|
-
@last_modified_at ||=
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
107
|
+
@last_modified_at ||= %w[post_modified_gmt post_modified].map do |date_attr|
|
|
108
|
+
::Jekyll::Utils.parse_date attribute_value(date_attr)
|
|
109
|
+
rescue StandardError
|
|
110
|
+
end.compact.first
|
|
90
111
|
end
|
|
91
112
|
|
|
92
|
-
# Content as HTML,
|
|
113
|
+
# Content as an HTML string, converting Wordpress [tags] to <tags>
|
|
93
114
|
#
|
|
94
115
|
# @return [String]
|
|
95
116
|
def content
|
|
96
|
-
@content ||= WordpressFormatting::Wpautop.wpautop(attribute_value('encoded')).gsub(
|
|
97
|
-
|
|
98
|
-
|
|
117
|
+
@content ||= WordpressFormatting::Wpautop.wpautop((attribute_value('encoded') || attribute_value('content')).gsub(WORDPRESS_ELEMENT_START, '<\\1>').gsub(
|
|
118
|
+
WORDPRESS_ELEMENT_END, '<\\1>'
|
|
119
|
+
))
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Remove extraneous code
|
|
123
|
+
#
|
|
124
|
+
# @return [String]
|
|
125
|
+
def sanitized_content
|
|
126
|
+
@sanitized_content ||= SANITIZER.sanitize(content.to_s, tags: TAGS, attributes: ATTRIBUTES)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Returns a fragment that can be queried using Nokogiri and
|
|
130
|
+
# transformed for final migration. Applies common transformations
|
|
131
|
+
# too.
|
|
132
|
+
#
|
|
133
|
+
# @return [Nokogiri::HTML]
|
|
134
|
+
def parsed_content
|
|
135
|
+
@parsed_content ||= Nokogiri::HTML.fragment(sanitized_content).tap do |html|
|
|
136
|
+
html.css('[src]').each do |element|
|
|
137
|
+
# Remove the thumbnail suffix, we only want the original files
|
|
138
|
+
element['src'] = element['src'].sub(THUMBNAIL_PREFIX, '\\1')
|
|
139
|
+
|
|
140
|
+
if element['src'].start_with? wordpress.url
|
|
141
|
+
attachment = wordpress.attachments[CGI.unescape element['src']]
|
|
142
|
+
|
|
143
|
+
unless attachment
|
|
144
|
+
::Jekyll.logger.warn 'Error:', "Not found #{element['src']}"
|
|
145
|
+
next
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
element['src'] = attachment.public_path
|
|
149
|
+
|
|
150
|
+
# Move the element into a <figure> like our editor does
|
|
151
|
+
unless element.parent && element.parent.name == '#document-fragment'
|
|
152
|
+
parent = element.parent
|
|
153
|
+
|
|
154
|
+
parent = parent.parent until parent.parent.nil? || parent.parent.name == '#document-fragment'
|
|
155
|
+
|
|
156
|
+
figure = parent.add_previous_sibling('<figure></figure>').first
|
|
157
|
+
figure.add_child element
|
|
158
|
+
# This was the [caption] WP element
|
|
159
|
+
figcaption = html.css('caption').first
|
|
160
|
+
|
|
161
|
+
if figcaption
|
|
162
|
+
figcaption.name = 'figcaption'
|
|
163
|
+
figure.add_child figcaption
|
|
164
|
+
element['alt'] = figcaption.text.strip
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Iframes are converted to an autolinked paragraph so
|
|
169
|
+
# jekyll-embed-urls can pick them up during build
|
|
170
|
+
elsif element.name == 'iframe'
|
|
171
|
+
element.parent.name = 'p'
|
|
172
|
+
element.name = 'a'
|
|
173
|
+
element['href'] = element['src']
|
|
174
|
+
if element['href'].include? 'youtube.com/embed/'
|
|
175
|
+
element['href'] = Addressable::URI.parse(element['href']).tap do |uri|
|
|
176
|
+
uri.query = URI.encode_www_form(URI.decode_www_form(uri.query.to_s).tap do |query|
|
|
177
|
+
query << ['v', uri.path.split('/').last]
|
|
178
|
+
end)
|
|
179
|
+
uri.path = '/watch'
|
|
180
|
+
end.to_s
|
|
181
|
+
end
|
|
182
|
+
element.delete 'src'
|
|
183
|
+
element.inner_html = element['href']
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Beautifies the parsed content.
|
|
190
|
+
#
|
|
191
|
+
# @return [String]
|
|
192
|
+
def beautiful_content
|
|
193
|
+
HtmlBeautifier.beautify(parsed_content.to_s.squeeze(' ').gsub(/>\s+</, '><'))
|
|
99
194
|
end
|
|
100
195
|
|
|
101
196
|
# Author attributes.
|
|
@@ -176,9 +271,16 @@ module SuttyMigration
|
|
|
176
271
|
|
|
177
272
|
# Get a value from the attribute
|
|
178
273
|
#
|
|
274
|
+
# @param key [String, Symbol]
|
|
179
275
|
# @return [String, nil]
|
|
180
276
|
def attribute_value(key)
|
|
181
|
-
item
|
|
277
|
+
(case item
|
|
278
|
+
when Hash
|
|
279
|
+
item[key.to_sym]
|
|
280
|
+
else
|
|
281
|
+
item.at_css(key.to_s)&.text
|
|
282
|
+
end
|
|
283
|
+
)&.unicode_normalize&.strip
|
|
182
284
|
end
|
|
183
285
|
end
|
|
184
286
|
end
|
|
@@ -15,7 +15,13 @@ module SuttyMigration
|
|
|
15
15
|
def initialize(site:, file:)
|
|
16
16
|
@site = site
|
|
17
17
|
@file = file
|
|
18
|
-
@xml = Nokogiri::XML File.read(file)
|
|
18
|
+
@xml = Nokogiri::XML File.read(file).delete("\b").delete("\u0002")
|
|
19
|
+
|
|
20
|
+
if xml.errors.empty?
|
|
21
|
+
::Jekyll.logger.info 'Migration:', "#{file} parsed correctly"
|
|
22
|
+
else
|
|
23
|
+
::Jekyll.logger.warn 'Migration:', "#{xml.errors.count} parsing errors found on #{file}!"
|
|
24
|
+
end
|
|
19
25
|
|
|
20
26
|
# Make things easier by removing namespaces.
|
|
21
27
|
xml.remove_namespaces!
|
|
@@ -108,8 +114,8 @@ module SuttyMigration
|
|
|
108
114
|
#
|
|
109
115
|
# @return [Hash]
|
|
110
116
|
def posts
|
|
111
|
-
@posts ||= items_find_by('post_type', 'post').map do |
|
|
112
|
-
{ attribute_value(
|
|
117
|
+
@posts ||= items_find_by('post_type', 'post').map do |item|
|
|
118
|
+
{ attribute_value(item, 'post_id').to_i => Post.new(wordpress: self, item:) }
|
|
113
119
|
end.reduce(&:merge)
|
|
114
120
|
end
|
|
115
121
|
|
|
@@ -117,18 +123,23 @@ module SuttyMigration
|
|
|
117
123
|
#
|
|
118
124
|
# @return [Hash]
|
|
119
125
|
def pages
|
|
120
|
-
@pages ||= items_find_by('post_type', 'page').
|
|
121
|
-
|
|
122
|
-
end
|
|
126
|
+
@pages ||= items_find_by('post_type', 'page').to_h do |item|
|
|
127
|
+
[attribute_value(page, 'post_id').to_i, Post.new(wordpress: self, item:)]
|
|
128
|
+
end
|
|
123
129
|
end
|
|
124
130
|
|
|
125
|
-
# Attachments, indexed by ID
|
|
131
|
+
# Attachments, indexed by URL and ID
|
|
126
132
|
#
|
|
127
133
|
# @return [Hash]
|
|
128
134
|
def attachments
|
|
129
|
-
@attachments ||= items_find_by('post_type', 'attachment').map do |
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
@attachments ||= items_find_by('post_type', 'attachment').map do |item|
|
|
136
|
+
attachment = Attachment.new(wordpress: self, item:)
|
|
137
|
+
|
|
138
|
+
[
|
|
139
|
+
[attribute_value(item, 'post_id').to_i, attachment],
|
|
140
|
+
[attribute_value(item, 'attachment_url'), attachment]
|
|
141
|
+
]
|
|
142
|
+
end.flatten(1).to_h
|
|
132
143
|
end
|
|
133
144
|
|
|
134
145
|
# Find items by attribute and value
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sutty-migration
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0rc1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- f
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jekyll
|
|
@@ -38,6 +38,20 @@ dependencies:
|
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '0.1'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: addressable
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
56
|
name: fast_blank
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -150,6 +164,34 @@ dependencies:
|
|
|
150
164
|
- - "~>"
|
|
151
165
|
- !ruby/object:Gem::Version
|
|
152
166
|
version: 1.3.0
|
|
167
|
+
- !ruby/object:Gem::Dependency
|
|
168
|
+
name: htmlbeautifier
|
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ">="
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '0'
|
|
174
|
+
type: :runtime
|
|
175
|
+
prerelease: false
|
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
177
|
+
requirements:
|
|
178
|
+
- - ">="
|
|
179
|
+
- !ruby/object:Gem::Version
|
|
180
|
+
version: '0'
|
|
181
|
+
- !ruby/object:Gem::Dependency
|
|
182
|
+
name: rails-html-sanitizer
|
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
|
184
|
+
requirements:
|
|
185
|
+
- - ">="
|
|
186
|
+
- !ruby/object:Gem::Version
|
|
187
|
+
version: '0'
|
|
188
|
+
type: :runtime
|
|
189
|
+
prerelease: false
|
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
191
|
+
requirements:
|
|
192
|
+
- - ">="
|
|
193
|
+
- !ruby/object:Gem::Version
|
|
194
|
+
version: '0'
|
|
153
195
|
- !ruby/object:Gem::Dependency
|
|
154
196
|
name: pry
|
|
155
197
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -164,7 +206,21 @@ dependencies:
|
|
|
164
206
|
- - ">="
|
|
165
207
|
- !ruby/object:Gem::Version
|
|
166
208
|
version: '0'
|
|
167
|
-
|
|
209
|
+
- !ruby/object:Gem::Dependency
|
|
210
|
+
name: rubocop
|
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
|
212
|
+
requirements:
|
|
213
|
+
- - ">="
|
|
214
|
+
- !ruby/object:Gem::Version
|
|
215
|
+
version: '0'
|
|
216
|
+
type: :development
|
|
217
|
+
prerelease: false
|
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
219
|
+
requirements:
|
|
220
|
+
- - ">="
|
|
221
|
+
- !ruby/object:Gem::Version
|
|
222
|
+
version: '0'
|
|
223
|
+
description: Takes datafiles, Wordpress backups and converts them into Jekyll posts
|
|
168
224
|
email:
|
|
169
225
|
- f@sutty.nl
|
|
170
226
|
executables: []
|
|
@@ -175,6 +231,8 @@ extra_rdoc_files:
|
|
|
175
231
|
files:
|
|
176
232
|
- LICENSE.txt
|
|
177
233
|
- README.md
|
|
234
|
+
- lib/jekyll-wordpress-sql-migration.rb
|
|
235
|
+
- lib/jekyll-wordpress-xml-migration.rb
|
|
178
236
|
- lib/sutty-migration.rb
|
|
179
237
|
- lib/sutty_migration/core_extensions.rb
|
|
180
238
|
- lib/sutty_migration/data.rb
|
|
@@ -183,19 +241,18 @@ files:
|
|
|
183
241
|
- lib/sutty_migration/wordpress_xml.rb
|
|
184
242
|
- lib/sutty_migration/wordpress_xml/attachment.rb
|
|
185
243
|
- lib/sutty_migration/wordpress_xml/post.rb
|
|
186
|
-
homepage: https://
|
|
244
|
+
homepage: https://git.sutty.abyaya.la/jekyll/sutty-migration
|
|
187
245
|
licenses:
|
|
188
246
|
- GPL-3.0
|
|
189
247
|
metadata:
|
|
190
|
-
bug_tracker_uri: https://
|
|
191
|
-
homepage_uri: https://
|
|
192
|
-
source_code_uri: https://
|
|
193
|
-
changelog_uri: https://0xacab.org/sutty/jekyll/sutty-migration/-/blob/master/CHANGELOG.md
|
|
248
|
+
bug_tracker_uri: https://git.sutty.abyaya.la/jekyll/sutty-migration/issues
|
|
249
|
+
homepage_uri: https://git.sutty.abyaya.la/jekyll/sutty-migration
|
|
250
|
+
source_code_uri: https://git.sutty.abyaya.la/jekyll/sutty-migration
|
|
194
251
|
documentation_uri: https://rubydoc.info/gems/sutty-migration
|
|
195
252
|
post_install_message:
|
|
196
253
|
rdoc_options:
|
|
197
254
|
- "--title"
|
|
198
|
-
- sutty-migration -
|
|
255
|
+
- sutty-migration - CSV, Wordpress SQL and Wordpress XML migration
|
|
199
256
|
- "--main"
|
|
200
257
|
- README.md
|
|
201
258
|
- "--line-numbers"
|
|
@@ -207,15 +264,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
207
264
|
requirements:
|
|
208
265
|
- - ">="
|
|
209
266
|
- !ruby/object:Gem::Version
|
|
210
|
-
version:
|
|
267
|
+
version: '3.1'
|
|
211
268
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
212
269
|
requirements:
|
|
213
|
-
- - "
|
|
270
|
+
- - ">"
|
|
214
271
|
- !ruby/object:Gem::Version
|
|
215
|
-
version:
|
|
272
|
+
version: 1.3.1
|
|
216
273
|
requirements: []
|
|
217
274
|
rubygems_version: 3.3.27
|
|
218
275
|
signing_key:
|
|
219
276
|
specification_version: 4
|
|
220
|
-
summary:
|
|
277
|
+
summary: CSV, Wordpress SQL and Wordpress XML migration
|
|
221
278
|
test_files: []
|