typo 3.99.3 → 3.99.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -39
- data/app/controllers/admin/feedback_controller.rb +71 -0
- data/app/controllers/articles_controller.rb +11 -0
- data/app/helpers/admin/feedback_helper.rb +9 -0
- data/app/helpers/application_helper.rb +5 -1
- data/app/models/article.rb +21 -1
- data/app/models/blog.rb +25 -1
- data/app/models/comment.rb +12 -2
- data/app/models/content.rb +63 -4
- data/app/models/ping.rb +3 -3
- data/app/models/text_filter.rb +1 -1
- data/app/models/trackback.rb +12 -1
- data/app/views/admin/content/_form.rhtml +2 -2
- data/app/views/admin/feedback/_item.rhtml +15 -0
- data/app/views/admin/feedback/list.rhtml +46 -0
- data/app/views/admin/general/index.rhtml +26 -0
- data/app/views/articles/_comment.rhtml +3 -0
- data/app/views/articles/read.rhtml +2 -2
- data/app/views/layouts/administration.rhtml +1 -0
- data/bin/typo +3 -23
- data/components/plugins/sidebars/archives_controller.rb +1 -1
- data/components/plugins/sidebars/xml_controller.rb +1 -1
- data/components/plugins/textfilters/flickr_controller.rb +1 -1
- data/components/plugins/textfilters/sparkline_controller.rb +3 -2
- data/components/plugins/textfilters/textile_controller.rb +6 -0
- data/config/mongrel.conf +2 -0
- data/db/converters/wordpress2.rb +291 -0
- data/db/migrate/022_superclass_trackbacks.rb +1 -0
- data/db/migrate/023_superclass_pages.rb +4 -3
- data/db/schema.rb +4 -4
- data/doc/Installer.txt +81 -6
- data/doc/typo-4.0-release-notes.txt +135 -0
- data/installer/rails-installer.rb +22 -3
- data/installer/rails-installer/commands.rb +27 -26
- data/installer/rails-installer/web-servers.rb +2 -0
- data/lib/sidebars/plugin.rb +10 -8
- data/lib/tasks/release.rake +1 -1
- data/lib/typo_version.rb +1 -1
- data/public/javascripts/dragdrop.js +252 -63
- data/public/javascripts/effects.js +15 -10
- data/public/javascripts/prototype.js +59 -38
- data/public/javascripts/typo.js +10 -0
- data/public/stylesheets/administration.css +111 -66
- data/test/functional/admin/feedback_controller_test.rb +24 -0
- data/test/mocks/test/http_mock.rb +2 -1
- data/test/unit/article_test.rb +6 -0
- data/test/unit/comment_test.rb +12 -9
- data/test/unit/ping_test.rb +1 -1
- data/test/unit/trackback_test.rb +3 -5
- data/themes/azure/stylesheets/azure.css +7 -0
- data/themes/scribbish/views/articles/_article.rhtml +1 -1
- data/themes/scribbish/views/articles/_comment_form.rhtml +1 -1
- data/vendor/akismet/Akismet.rb +36 -17
- data/vendor/plugins/expiring_action_cache/lib/actionparamcache.rb +3 -1
- metadata +11 -42
- data/installer/rails-installer/web-server.rb +0 -108
- data/tmp/cache/META/DATA/ACTION_PARAM/10.1.0.181/articles/index/.cache +0 -537
- data/tmp/cache/META/DATA/ACTION_PARAM/localhost/articles/index/.cache +0 -537
- data/tmp/cache/META/DATA/ACTION_PARAM/localhost/xml/feed/format=atom&type=feed.cache +0 -671
- data/tmp/cache/META/DATA/ACTION_PARAM/localhost/xml/feed/format=rss20&type=feed.cache +0 -401
- data/tmp/cache/META/META/ACTION_PARAM/10.1.0.181/articles/index/.cache +0 -2
- data/tmp/cache/META/META/ACTION_PARAM/localhost/articles/index/.cache +0 -2
- data/tmp/cache/META/META/ACTION_PARAM/localhost/xml/feed/format=atom&type=feed.cache +0 -2
- data/tmp/cache/META/META/ACTION_PARAM/localhost/xml/feed/format=rss20&type=feed.cache +0 -2
@@ -21,7 +21,7 @@
|
|
21
21
|
</select>
|
22
22
|
</p>
|
23
23
|
<p>
|
24
|
-
<label for="article_keywords">
|
24
|
+
<label for="article_keywords">Tags:</label><br/>
|
25
25
|
<%= text_field 'article', 'keywords' %>
|
26
26
|
</p>
|
27
27
|
<p>
|
@@ -35,7 +35,7 @@
|
|
35
35
|
<a href="#" onclick="Element.toggle('advanced'); return false;">Toggle Advanced Options (+/-)</a>
|
36
36
|
<div id="advanced" style="display:none;">
|
37
37
|
<label for="article_allow_comments">Allow comments: </label><%= check_box 'article', 'allow_comments' %><br />
|
38
|
-
<label for="article_allow_pings">Allow
|
38
|
+
<label for="article_allow_pings">Allow trackbacks: </label><%= check_box 'article', 'allow_pings' %><br />
|
39
39
|
<label for="article_published">Published:</label><%= check_box 'article', 'published' %><br />
|
40
40
|
<label for="article_text_filter">Textfilter: </label><%= select 'article', 'text_filter', text_filter_options %><br />
|
41
41
|
<label for="article_published_at">Publish at:</label><%= datetime_select 'article', 'published_at', :include_blank => true %><br />
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<tr class="feedbackbody">
|
2
|
+
<td class="field"><input class= "feedback_check" type="checkbox" name="feedback_check[<%= item.id %>]"/></td>
|
3
|
+
<td class="field"><%= item.published ? "Yes" : "<strong>No</strong>" %></td>
|
4
|
+
<td class="field"><%=h item.class %></td>
|
5
|
+
<td class="field"><%= link_to_article_edit item.article %></td>
|
6
|
+
<td class="field">
|
7
|
+
<%=h (item.author || '(unknown)').slice(0,40) %><br/>
|
8
|
+
<a href=<%=h item.url%>><%=h truncate(item.url.to_s)%></a><br/>
|
9
|
+
<%=h item.email.to_s.slice(0,40) %>
|
10
|
+
</td>
|
11
|
+
<td class="field"><%=h truncate(item.body,80)%></td>
|
12
|
+
<td class="field"><%=h item.ip %></td>
|
13
|
+
<td class="field"><%=h distance_of_time_in_words_to_now(item.created_at) %> ago</td>
|
14
|
+
<td class="field"><%= link_to image_tag('delete'), {:action => 'delete', :id => item.id, :search => params[:search], :page => params[:page] }, :confirm => "Are you sure?", :post => true %></td>
|
15
|
+
</tr>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<% @page_heading = "Comments and Trackbacks for #{ this_blog.settings['blog_name'] }" %>
|
2
|
+
|
3
|
+
<% content_for('tasks') do %>
|
4
|
+
<%= task_showmod 'Show only unpublished feedback' %>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
<div clas="search">
|
8
|
+
<form>
|
9
|
+
<label for="search">Feedback Search:</label><input type="text" id="search" name="search" value="<%=h params[:search] %>" size="15" />
|
10
|
+
</form>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<div class="list">
|
14
|
+
<%= form_tag({:action => 'bulkops'}, :method => :post) %>
|
15
|
+
<br/>
|
16
|
+
<%= submit_tag "Delete Checked Items" %>
|
17
|
+
<%= submit_tag "Publish Checked Items" %>
|
18
|
+
<%= submit_tag "Unpublish Checked Items" %>
|
19
|
+
|
20
|
+
<%= hidden_field_tag "search", params[:search]%>
|
21
|
+
<%= hidden_field_tag "page", params[:page]%>
|
22
|
+
|
23
|
+
<table>
|
24
|
+
<tr>
|
25
|
+
<th><input class="feedback_check" type="checkbox" name="checkall" id="checkall" onclick="check_all(this);"/></th>
|
26
|
+
<th>Pub</th>
|
27
|
+
<th>Type</th>
|
28
|
+
<th>Article</th>
|
29
|
+
<th>Author</th>
|
30
|
+
<th>Body</th>
|
31
|
+
<th>IP</th>
|
32
|
+
<th>Posted date</th>
|
33
|
+
<th>Delete</th>
|
34
|
+
</tr>
|
35
|
+
<%= render :partial => 'item', :collection => @feedback %>
|
36
|
+
</table>
|
37
|
+
<%= submit_tag "Delete Checked Items" %>
|
38
|
+
<%= submit_tag "Publish Checked Items" %>
|
39
|
+
<%= submit_tag "Unpublish Checked Items" %>
|
40
|
+
</form>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<%= link_to "Previous page", { :page => @pages.current.previous, :search => params[:search] } if @pages.current.previous %>
|
44
|
+
<%= pagination_links(@pages, :params => {:search => params[:search]}) %>
|
45
|
+
<%= link_to "Next page", { :page => @pages.current.next, :search => params[:search] } if @pages.current.next %>
|
46
|
+
|
@@ -78,6 +78,22 @@
|
|
78
78
|
|
79
79
|
<hr/>
|
80
80
|
|
81
|
+
<p>This setting allows you to disable trackbacks for every article in
|
82
|
+
your blog. It won't remove existing trackbacks, but it will prevent
|
83
|
+
any further attempt to add a trackback anywhere on your blog. You can
|
84
|
+
enable or disable trackbacks per-article using the article's extended
|
85
|
+
settings. See also the
|
86
|
+
<a href="#gensettings" onclick="new Effect.ScrollTo('gensettings'); return false">"Enable Trackbacks by default" setting</a> above.
|
87
|
+
</p>
|
88
|
+
<p>
|
89
|
+
<input name="setting[global_pings_disable]" id="global_pings_disable" type="checkbox" value="1" <%= 'checked="checked"' if this_blog.global_pings_disable%> />
|
90
|
+
<input name="setting[global_pings_disable]" type="hidden" value="0"/>
|
91
|
+
<label for="global_pings_disable">Disable trackbacks site-wide</label>
|
92
|
+
|
93
|
+
</p>
|
94
|
+
|
95
|
+
<hr />
|
96
|
+
|
81
97
|
<p>Should Typo send trackbacks to websites that you link to? This should be disabled
|
82
98
|
for private blogs, as it will leak non-public information to sites that you're discussing.
|
83
99
|
For public blogs, there's no real point in disabling this.</p>
|
@@ -141,6 +157,16 @@
|
|
141
157
|
<input name="setting[sp_global]" id="sp_global" type="checkbox" value="1" <%= 'checked="checked"' if this_blog.sp_global%> /><input name="setting[sp_global]" type="hidden" value="0"/>
|
142
158
|
<label for="sp_global">Enable spam protection</label>
|
143
159
|
</p>
|
160
|
+
<p>Typo can (optionally) use the <a href="http://akismet.com">Akismet</a> spam-filtering service. You need to register with
|
161
|
+
Akismet and receive an API key before you can use their service. If you have an Akismet key, enter it here.
|
162
|
+
</p>
|
163
|
+
<p>
|
164
|
+
<label for="sp_akismet_key">Akismet Key</label>
|
165
|
+
<input name="setting[sp_akismet_key]" id="sp_akismet_key" type="text" value="<%=h this_blog.sp_akismet_key %>"/>
|
166
|
+
</p>
|
167
|
+
<p>You can optionally disable non-Ajax comments. Typo will always use Ajax for comment submission if Javascript is enabled,
|
168
|
+
so non-Ajax comments are either from spammers or users without Javascript.
|
169
|
+
</p>
|
144
170
|
<p>
|
145
171
|
<input name="setting[sp_allow_non_ajax_comments]" id="sp_allow_non_ajax_comments" type="checkbox" value="1" <%= 'checked="checked"' if this_blog.sp_allow_non_ajax_comments%> />
|
146
172
|
<input name="setting[sp_allow_non_ajax_comments]" type="hidden" value="0" />
|
@@ -4,4 +4,7 @@
|
|
4
4
|
<%= gravatar_tag(comment.email) if this_blog.use_gravatar and comment.email %>
|
5
5
|
<cite><strong><%= link_to_unless(comment.url.blank?, h(comment.author), comment.url) %></strong> </cite> said <%= distance_of_time_in_words comment.article.published_at, comment.created_at %> later:<br />
|
6
6
|
<%= comment.full_html %>
|
7
|
+
<% unless comment.published -%>
|
8
|
+
<div class="spamwarning">This comment has been flagged for moderator approval. It won't appear on this blog until the author approves it.</div>
|
9
|
+
<% end -%>
|
7
10
|
</li>
|
@@ -29,7 +29,7 @@
|
|
29
29
|
|
30
30
|
<% if @article.allow_comments? or @article.comments.size > 0 -%>
|
31
31
|
<a name="comments"></a><h4 class="blueblk">Comments</h4>
|
32
|
-
<%
|
32
|
+
<% unless @article.comments_closed? -%>
|
33
33
|
<p class="postmetadata alt">
|
34
34
|
<small><a href="#respond">Leave a response</a></small>
|
35
35
|
</p>
|
@@ -63,7 +63,7 @@
|
|
63
63
|
</small>
|
64
64
|
</p>
|
65
65
|
|
66
|
-
<%
|
66
|
+
<% unless @article.comments_closed? -%>
|
67
67
|
<%= render :partial => 'comment_box' %>
|
68
68
|
<% else -%>
|
69
69
|
<p>Comments are disabled</p>
|
@@ -30,6 +30,7 @@
|
|
30
30
|
<ul id="tabs">
|
31
31
|
<%= tab "Articles", :controller=>"/admin/content", :action => 'index' %>
|
32
32
|
<%= tab "Pages", :controller=>"/admin/pages", :action => 'index' %>
|
33
|
+
<%= tab "Feedback", :controller=>"/admin/feedback", :action => 'index' %>
|
33
34
|
<%= tab "Categories", :controller=>"/admin/categories", :action => 'index' %>
|
34
35
|
<%= tab "Blacklist", :controller=>"/admin/blacklist", :action => 'index' %>
|
35
36
|
<%= tab "Sidebar", :controller=>"/admin/sidebar", :action => 'index' %>
|
data/bin/typo
CHANGED
@@ -7,26 +7,8 @@ class TypoInstaller < RailsInstaller
|
|
7
7
|
support_location 'the Typo mailing list'
|
8
8
|
rails_version '1.1.4'
|
9
9
|
|
10
|
-
def
|
11
|
-
stop
|
12
|
-
|
13
|
-
backup_database
|
14
|
-
pre_migrate_database
|
15
|
-
copy_files
|
16
|
-
freeze_rails
|
17
|
-
create_default_config_files
|
18
|
-
create_directories
|
19
|
-
create_initial_database
|
20
|
-
set_initial_port_number
|
21
|
-
expand_template_files
|
22
|
-
|
23
|
-
migrate
|
10
|
+
def install_post_hook
|
24
11
|
sweep_cache
|
25
|
-
save
|
26
|
-
|
27
|
-
run_rails_tests
|
28
|
-
|
29
|
-
start
|
30
12
|
end
|
31
13
|
|
32
14
|
# Sweep the cache
|
@@ -38,13 +20,11 @@ class TypoInstaller < RailsInstaller
|
|
38
20
|
end
|
39
21
|
|
40
22
|
class SweepCache < RailsInstaller::Command
|
23
|
+
help "Sweep Typo's cache"
|
24
|
+
|
41
25
|
def self.command(installer, *args)
|
42
26
|
installer.sweep_cache
|
43
27
|
end
|
44
|
-
|
45
|
-
def self.help(installer)
|
46
|
-
['',"Sweep Typo's cache"]
|
47
|
-
end
|
48
28
|
end
|
49
29
|
|
50
30
|
# Installer program
|
@@ -11,7 +11,7 @@ class Plugins::Sidebars::ArchivesController < Sidebars::ComponentPlugin
|
|
11
11
|
# across all three of our supported DBs. So, we resort to a bit of
|
12
12
|
# DB-specific code.
|
13
13
|
if Content.connection.kind_of? ActiveRecord::ConnectionAdapters::SQLiteAdapter
|
14
|
-
date_func = "strftime('%Y') as year, strftime('%m') as month"
|
14
|
+
date_func = "strftime('%Y', published_at) as year, strftime('%m', published_at) as month"
|
15
15
|
else
|
16
16
|
date_func = "extract(year from published_at) as year,extract(month from published_at) as month"
|
17
17
|
end
|
@@ -7,5 +7,5 @@ class Plugins::Sidebars::XmlController < Sidebars::ComponentPlugin
|
|
7
7
|
setting :trackbacks, false, :input_type => :checkbox
|
8
8
|
|
9
9
|
setting :format, 'rss20', :input_type => :radio,
|
10
|
-
:choices => [["rss20", "RSS 2.0"], ["atom10", "Atom 1.0"]
|
10
|
+
:choices => [["rss20", "RSS 2.0"], ["atom10", "Atom 1.0"]]
|
11
11
|
end
|
@@ -55,7 +55,7 @@ This macro takes a number of parameters:
|
|
55
55
|
imageurl = details['source']
|
56
56
|
imagelink = flickrimage.url
|
57
57
|
|
58
|
-
caption ||= sanitize(CGI.unescapeHTML(flickrimage.description))
|
58
|
+
caption ||= sanitize(CGI.unescapeHTML(flickrimage.description)) unless flickrimage.description.blank?
|
59
59
|
title ||= flickrimage.title
|
60
60
|
alt ||= title
|
61
61
|
|
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
|
3
3
|
begin
|
4
|
-
Kernel.require '
|
4
|
+
Kernel.require 'sparklines'
|
5
|
+
|
6
|
+
Sparklines # this will throw an exception if the require failed.
|
5
7
|
|
6
8
|
class Plugins::Textfilters::SparklineController < TextFilterPlugin::MacroPost
|
7
9
|
plugin_public_action :plot
|
@@ -81,7 +83,6 @@ For other options, see the [Ruby Sparkline][] website.
|
|
81
83
|
render :text => ''
|
82
84
|
end
|
83
85
|
end
|
84
|
-
|
85
86
|
rescue LoadError
|
86
87
|
# ignore load errors by not loading any of the library
|
87
88
|
end
|
@@ -2,6 +2,12 @@ class Plugins::Textfilters::TextileController < TextFilterPlugin::Markup
|
|
2
2
|
plugin_display_name "Textile"
|
3
3
|
plugin_description 'Textile markup language'
|
4
4
|
|
5
|
+
def self.help_text
|
6
|
+
%{
|
7
|
+
See [_why's Textile reference](http://hobix.com/textile/).
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
5
11
|
def self.filtertext(controller,content,text,params)
|
6
12
|
RedCloth.new(text).to_html(:textile)
|
7
13
|
end
|
data/config/mongrel.conf
ADDED
@@ -0,0 +1,291 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# WordPress 1.5x converter for typo by Patrick Lenz <patrick@lenz.sh>
|
4
|
+
# Updated to work with WordPress 2.0.x by Phillip Toland <toland@mac.com>
|
5
|
+
#
|
6
|
+
# See http://fiatdev.com/wp/2006/07/10/wordpress-to-typo-conversion-script/
|
7
|
+
# for more information.
|
8
|
+
#
|
9
|
+
# MAKE BACKUPS OF EVERYTHING BEFORE RUNNING THIS SCRIPT!
|
10
|
+
# THIS SCRIPT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
|
11
|
+
|
12
|
+
require File.dirname(__FILE__) + '/../../config/environment'
|
13
|
+
require 'application'
|
14
|
+
require 'optparse'
|
15
|
+
|
16
|
+
class WP2Migrate
|
17
|
+
attr_accessor :options
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
self.options = {}
|
21
|
+
self.parse_options
|
22
|
+
|
23
|
+
WP2Migrate.execute_without_timestamps do
|
24
|
+
self.create_blog
|
25
|
+
self.convert_users
|
26
|
+
self.convert_categories unless self.options[:tags]
|
27
|
+
self.convert_entries
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# Replaces <code></code> tags with <typo:code> and </typo:code>
|
33
|
+
# Also rewrites Markdown URLs with relative paths to a new base url
|
34
|
+
def replacements(str)
|
35
|
+
str.gsub!('<code', '<typo:code')
|
36
|
+
str.gsub!('</code>', '</typo:code>')
|
37
|
+
if self.options[:base_url]
|
38
|
+
str.gsub!(/\(\/([^\)]*)\)/,"(#{self.options[:base_url]}" +'\1)')
|
39
|
+
str.gsub!(/\[([^\]]*)\]: \/([^$])/, '[\1]:' +"#{self.options[:base_url]}"+'\2')
|
40
|
+
end
|
41
|
+
str
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_blog
|
45
|
+
puts 'Creating Blog...'
|
46
|
+
|
47
|
+
blog = Blog.new
|
48
|
+
|
49
|
+
ActiveRecord::Base.connection.select_all(%{
|
50
|
+
SELECT
|
51
|
+
(CASE option_name
|
52
|
+
WHEN 'blogname' THEN 'blog_name'
|
53
|
+
WHEN 'blogdescription' THEN 'blog_subtitle'
|
54
|
+
WHEN 'siteurl' THEN 'canonical_server_url'
|
55
|
+
WHEN 'default_comment_status' THEN 'default_allow_comments'
|
56
|
+
WHEN 'default_ping_status' THEN 'default_allow_pings'
|
57
|
+
END) AS name,
|
58
|
+
option_value AS value
|
59
|
+
FROM `#{self.options[:wp_db]}`.`#{self.options[:wp_prefix]}_options`
|
60
|
+
WHERE option_name IN ('blogname', 'default_comment_status', 'default_ping_status', 'siteurl', 'blogdescription')
|
61
|
+
}).each do |pref|
|
62
|
+
if pref['name'] =~ /^default_allow/
|
63
|
+
pref['value'] = (pref['value'] == "open" ? 1 : 0)
|
64
|
+
end
|
65
|
+
|
66
|
+
blog[pref['name']] = pref['value']
|
67
|
+
end
|
68
|
+
|
69
|
+
blog['send_outbound_pings'] = false
|
70
|
+
blog['text_filter'] = self.options[:text_filter]
|
71
|
+
blog['comment_text_filter'] = self.options[:text_filter]
|
72
|
+
|
73
|
+
blog.save
|
74
|
+
end
|
75
|
+
|
76
|
+
def convert_users
|
77
|
+
# The SQL statement below is meant to capture all of the WordPress users
|
78
|
+
# who have permission to post content and not the users who just signed up
|
79
|
+
# to post a comment. There is some confusion in WP 2.0 about the user levels
|
80
|
+
# and user roles. I am currently using the user levels to select the
|
81
|
+
# appropriate users even though that is the "old way". If that is a problem,
|
82
|
+
# replace the second part of the where clause with:
|
83
|
+
# AND (usermeta.meta_key = 'wp_capabilities' AND INSTR(meta_value, 'subscriber') = 0)
|
84
|
+
# which does (mostly) the same thing using the new roles system.
|
85
|
+
wp_users = ActiveRecord::Base.connection.select_all(%{
|
86
|
+
SELECT
|
87
|
+
users.ID AS id,
|
88
|
+
user_login AS login,
|
89
|
+
user_email AS email,
|
90
|
+
display_name AS name
|
91
|
+
FROM `#{self.options[:wp_db]}`.`#{self.options[:wp_prefix]}_users` AS users,
|
92
|
+
`#{self.options[:wp_db]}`.`#{self.options[:wp_prefix]}_usermeta` AS usermeta
|
93
|
+
WHERE users.ID = usermeta.user_id
|
94
|
+
AND (usermeta.meta_key = 'wp_user_level' AND (meta_value > 0 AND meta_value < 10))
|
95
|
+
})
|
96
|
+
|
97
|
+
puts "Converting #{wp_users.size} users..."
|
98
|
+
|
99
|
+
wp_users.each do |user|
|
100
|
+
u = User.new user
|
101
|
+
u.id = user['id']
|
102
|
+
u.password = u.password_confirmation = 'password'
|
103
|
+
u.save
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def convert_categories
|
108
|
+
wp_categories = ActiveRecord::Base.connection.select_all(%{
|
109
|
+
SELECT cat_name AS name
|
110
|
+
FROM `#{self.options[:wp_db]}`.`#{self.options[:wp_prefix]}_categories`
|
111
|
+
})
|
112
|
+
|
113
|
+
puts "Converting #{wp_categories.size} categories.."
|
114
|
+
|
115
|
+
wp_categories.each do |cat|
|
116
|
+
Category.create(cat) unless Category.find_by_name(cat['name'])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def convert_entries
|
121
|
+
wp_entries = ActiveRecord::Base.connection.select_all(%{
|
122
|
+
SELECT
|
123
|
+
`#{self.options[:wp_prefix]}_posts`.ID,
|
124
|
+
(CASE comment_status WHEN 'closed' THEN '0' ELSE '1' END) AS allow_comments,
|
125
|
+
(CASE ping_status WHEN 'closed' THEN '0' ELSE '1' END) AS allow_pings,
|
126
|
+
post_title AS title,
|
127
|
+
post_content AS body,
|
128
|
+
post_excerpt AS excerpt,
|
129
|
+
post_name AS permalink,
|
130
|
+
post_date AS created_at,
|
131
|
+
post_modified AS updated_at,
|
132
|
+
(CASE LENGTH(user_nicename) WHEN '0' THEN user_login ELSE user_nicename END) AS author,
|
133
|
+
(CASE post_status WHEN 'publish' THEN '1' ELSE '0' END) AS published,
|
134
|
+
post_author AS user_id,
|
135
|
+
post_status,
|
136
|
+
post_category
|
137
|
+
FROM `#{self.options[:wp_db]}`.`#{self.options[:wp_prefix]}_posts`, `#{self.options[:wp_db]}`.`#{self.options[:wp_prefix]}_users`
|
138
|
+
WHERE `#{self.options[:wp_prefix]}_users`.ID = `#{self.options[:wp_prefix]}_posts`.post_author
|
139
|
+
})
|
140
|
+
|
141
|
+
puts "Converting #{wp_entries.size} entries.."
|
142
|
+
|
143
|
+
wp_entries.each do |entry|
|
144
|
+
if entry['post_status'] == 'static'
|
145
|
+
a = Page.new
|
146
|
+
a.attributes = entry.reject { |k,v| k =~ /^(ID|post_category|body|post_status|permalink)/ }
|
147
|
+
a.name = entry['permalink']
|
148
|
+
a.created_at = entry['created_at']
|
149
|
+
a.updated_at = entry['updated_at']
|
150
|
+
else
|
151
|
+
a = Article.new
|
152
|
+
a.attributes = entry.reject { |k,v| k =~ /^(ID|post_category|body|post_status)/ }
|
153
|
+
a.created_at = entry['created_at']
|
154
|
+
a.created_at ||= DateTime.now
|
155
|
+
a.updated_at = entry['updated_at']
|
156
|
+
end
|
157
|
+
|
158
|
+
a.text_filter = TextFilter.find(:first, :conditions => [ 'name = ?', self.options[:text_filter] ] )
|
159
|
+
|
160
|
+
body = replacements(entry['body'])
|
161
|
+
more_index = body.index('<!--more-->')
|
162
|
+
if more_index
|
163
|
+
a.body = body[0...more_index]
|
164
|
+
a.extended = body[more_index + 11...body.length]
|
165
|
+
else
|
166
|
+
a.body = body
|
167
|
+
end
|
168
|
+
a.save
|
169
|
+
|
170
|
+
# Assign primary category
|
171
|
+
unless entry['post_category'].to_i.zero?
|
172
|
+
puts "Assign primary category for entry #{entry['ID']}"
|
173
|
+
|
174
|
+
ActiveRecord::Base.connection.select_all(%{
|
175
|
+
SELECT cat_name
|
176
|
+
FROM `#{self.options[:wp_db]}`.`#{self.options[:wp_prefix]}_categories`
|
177
|
+
WHERE cat_ID = '#{entry['post_category']}'
|
178
|
+
}).each do |c|
|
179
|
+
if self.options[:tags]
|
180
|
+
a.keywords = c['cat_name'].downcase || "";
|
181
|
+
else
|
182
|
+
a.categories.push_with_attributes(Category.find_by_name(c['cat_name']), :is_primary => 1) rescue nil
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end unless a.instance_of?(Page)
|
186
|
+
|
187
|
+
# Fetch category assignments
|
188
|
+
puts "Assign remaining categories for entry #{entry['ID']}"
|
189
|
+
ActiveRecord::Base.connection.select_all(%{
|
190
|
+
SELECT cat_name
|
191
|
+
FROM `#{self.options[:wp_db]}`.`#{self.options[:wp_prefix]}_categories`, `#{self.options[:wp_db]}`.`#{self.options[:wp_prefix]}_post2cat`
|
192
|
+
WHERE post_id = #{entry['ID']}
|
193
|
+
AND `#{self.options[:wp_prefix]}_post2cat`.category_id = `#{self.options[:wp_prefix]}_categories`.cat_ID
|
194
|
+
}).each do |c|
|
195
|
+
if self.options[:tags]
|
196
|
+
a.keywords ||= ""
|
197
|
+
a.keywords << " " << c['cat_name'].gsub(/\s/,'-').downcase
|
198
|
+
else
|
199
|
+
a.categories.push_with_attributes(Category.find_by_name(c['cat_name']), :is_primary => 0)
|
200
|
+
end
|
201
|
+
end unless a.instance_of?(Page)
|
202
|
+
|
203
|
+
a.save if self.options[:tags] # force tags to save
|
204
|
+
|
205
|
+
# Fetch comments
|
206
|
+
ActiveRecord::Base.connection.select_all(%{
|
207
|
+
SELECT
|
208
|
+
comment_author AS author,
|
209
|
+
comment_author_email AS email,
|
210
|
+
comment_author_url AS url,
|
211
|
+
comment_content AS body,
|
212
|
+
comment_date AS created_at,
|
213
|
+
comment_author_IP AS ip
|
214
|
+
FROM `#{self.options[:wp_db]}`.`#{self.options[:wp_prefix]}_comments`
|
215
|
+
WHERE comment_post_ID = #{entry['ID']}
|
216
|
+
AND comment_type != 'trackback'
|
217
|
+
AND comment_approved = '1'
|
218
|
+
}).each do |c|
|
219
|
+
a.comments.create(c)
|
220
|
+
end unless a.instance_of?(Page)
|
221
|
+
|
222
|
+
# Fetch trackbacks
|
223
|
+
ActiveRecord::Base.connection.select_all(%{
|
224
|
+
SELECT
|
225
|
+
comment_author AS blog_name,
|
226
|
+
comment_author_url AS url,
|
227
|
+
comment_content AS excerpt,
|
228
|
+
comment_date AS created_at,
|
229
|
+
comment_author_IP AS ip
|
230
|
+
FROM `#{self.options[:wp_db]}`.`#{self.options[:wp_prefix]}_comments`
|
231
|
+
WHERE comment_post_ID = #{entry['ID']}
|
232
|
+
AND comment_type = 'trackback'
|
233
|
+
AND comment_approved = '1'
|
234
|
+
}).each do |c|
|
235
|
+
c['title'] = c['excerpt'].match("<(strong)>(.+?)</\\1>")[2] rescue c['blog_name']
|
236
|
+
a.trackbacks.create(c)
|
237
|
+
end unless a.instance_of?(Page)
|
238
|
+
|
239
|
+
# Typo internally sets the published_at timestamp so we are going to go
|
240
|
+
# behind its back to set them to what they should be.
|
241
|
+
ActiveRecord::Base.connection.execute(%{
|
242
|
+
UPDATE contents SET published_at = created_at
|
243
|
+
WHERE published_at IS NOT NULL
|
244
|
+
})
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def parse_options
|
249
|
+
OptionParser.new do |opt|
|
250
|
+
opt.banner = "Usage: wordpress.rb [options]"
|
251
|
+
|
252
|
+
opt.on('--db DBNAME', String, 'WordPress database name.') { |d| self.options[:wp_db] = d }
|
253
|
+
opt.on('--prefix PREFIX', String, 'WordPress table prefix (defaults to \'wp\').') { |d| self.options[:wp_prefix] = d }
|
254
|
+
opt.on('--filter TEXTFILTER', String, 'Textfilter for imported articles (defaults to textile): eg \'markdown smartypants\'') { |d| self.options[:text_filter] = d }
|
255
|
+
opt.on('--tags', 'Convert categories to tags') { self.options[:tags] = true; }
|
256
|
+
opt.on('--base-url URL', String, 'Rewrite the base url for Markdown relative paths. (Trailing / required).' ) { |url| self.options[:base_url] = url }
|
257
|
+
opt.on_tail('-h', '--help', 'Show this message.') do
|
258
|
+
puts opt
|
259
|
+
exit
|
260
|
+
end
|
261
|
+
|
262
|
+
opt.parse!(ARGV)
|
263
|
+
end
|
264
|
+
|
265
|
+
unless self.options.include?(:wp_db)
|
266
|
+
puts "See wordpress.rb --help for help."
|
267
|
+
exit
|
268
|
+
end
|
269
|
+
|
270
|
+
unless self.options.include?(:wp_prefix)
|
271
|
+
self.options[:wp_prefix] = "wp"
|
272
|
+
end
|
273
|
+
|
274
|
+
unless self.options.include?(:text_filter)
|
275
|
+
self.options[:text_filter] = 'textile'
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# This method allows to execute a block while deactivating timestamp
|
280
|
+
# updating.
|
281
|
+
def self.execute_without_timestamps
|
282
|
+
old_state = ActiveRecord::Base.record_timestamps
|
283
|
+
ActiveRecord::Base.record_timestamps = false
|
284
|
+
|
285
|
+
yield
|
286
|
+
|
287
|
+
ActiveRecord::Base.record_timestamps = old_state
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
WP2Migrate.new
|