typo 4.0.0 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/app/controllers/admin/comments_controller.rb +1 -1
  2. data/app/controllers/admin/content_controller.rb +1 -3
  3. data/app/controllers/admin/feedback_controller.rb +36 -31
  4. data/app/controllers/admin/sidebar_controller.rb +13 -2
  5. data/app/controllers/admin/users_controller.rb +2 -1
  6. data/app/controllers/articles_controller.rb +10 -19
  7. data/app/controllers/xml_controller.rb +2 -2
  8. data/app/helpers/admin/base_helper.rb +7 -3
  9. data/app/helpers/application_helper.rb +2 -2
  10. data/app/helpers/articles_helper.rb +5 -4
  11. data/app/models/article.rb +16 -10
  12. data/app/models/blog.rb +4 -10
  13. data/app/models/comment.rb +17 -36
  14. data/app/models/content.rb +31 -53
  15. data/app/models/content_state/base.rb +46 -3
  16. data/app/models/content_state/draft.rb +2 -9
  17. data/app/models/content_state/ham.rb +31 -0
  18. data/app/models/content_state/just_marked_as_ham.rb +10 -0
  19. data/app/models/content_state/just_marked_as_spam.rb +23 -0
  20. data/app/models/content_state/just_presumed_ham.rb +37 -0
  21. data/app/models/content_state/just_published.rb +15 -8
  22. data/app/models/content_state/new.rb +3 -10
  23. data/app/models/content_state/presumed_ham.rb +27 -0
  24. data/app/models/content_state/presumed_spam.rb +31 -0
  25. data/app/models/content_state/publication_pending.rb +7 -9
  26. data/app/models/content_state/published.rb +10 -9
  27. data/app/models/content_state/spam.rb +23 -0
  28. data/app/models/content_state/unclassified.rb +29 -0
  29. data/app/models/content_state/withdrawn.rb +28 -0
  30. data/app/models/email_notifier.rb +0 -1
  31. data/app/models/feedback.rb +151 -0
  32. data/app/models/trackback.rb +22 -29
  33. data/app/views/admin/feedback/_item.rhtml +5 -5
  34. data/app/views/admin/feedback/list.rhtml +13 -11
  35. data/app/views/admin/general/index.rhtml +8 -4
  36. data/app/views/admin/users/show.rhtml +7 -1
  37. data/app/views/articles/read.rhtml +2 -2
  38. data/bin/typo +7 -6
  39. data/components/plugins/sidebars/recent_comments_controller.rb +1 -1
  40. data/config/environment.rb +2 -0
  41. data/db/migrate/046_fixup_forthcoming_publications.rb +1 -1
  42. data/db/migrate/048_remove_count_caching.rb +31 -0
  43. data/db/migrate/049_move_feedback_to_new_state_machine.rb +33 -0
  44. data/db/schema.mysql-v3.sql +3 -4
  45. data/db/schema.mysql.sql +3 -4
  46. data/db/schema.postgresql.sql +3 -4
  47. data/db/schema.rb +12 -17
  48. data/db/schema.sqlite.sql +3 -4
  49. data/db/schema.sqlserver.sql +3 -4
  50. data/db/schema_version +1 -1
  51. data/doc/Installer.txt +4 -0
  52. data/lib/jabber_notify.rb +6 -2
  53. data/lib/sidebars/plugin.rb +2 -1
  54. data/lib/tasks/release.rake +5 -4
  55. data/lib/typo_version.rb +1 -1
  56. data/public/stylesheets/administration.css +22 -2
  57. data/test/fixtures/blogs.yml +2 -0
  58. data/test/fixtures/contents.yml +7 -7
  59. data/test/functional/admin/users_controller_test.rb +3 -0
  60. data/test/functional/articles_controller_test.rb +16 -1
  61. data/test/mocks/test/xmlrpc_mock.rb +5 -4
  62. data/test/unit/article_test.rb +16 -4
  63. data/test/unit/comment_test.rb +57 -35
  64. data/test/unit/content_state/factory_test.rb +7 -6
  65. data/test/unit/ping_test.rb +14 -0
  66. data/test/unit/trackback_test.rb +16 -15
  67. metadata +26 -26
  68. data/config/database.yml-pgsql +0 -17
  69. data/config/database.yml.sqlite +0 -14
  70. data/config/mail.yml +0 -8
  71. data/config/mongrel.conf +0 -2
  72. data/db/converters/mt-import.rb +0 -72
  73. data/db/development_structure.sql +0 -691
  74. data/installer/rails-installer.rb +0 -527
  75. data/installer/rails-installer/commands.rb +0 -118
  76. data/installer/rails-installer/web-servers.rb +0 -110
  77. data/log/development.log-1 +0 -991
  78. data/log/development.log-2 +0 -422
  79. data/log/development.log-3 +0 -429
  80. data/log/development.log-4 +0 -174
  81. data/svk-commitP6cVv.tmp +0 -1
  82. data/vendor/ruby-mp3info/lib/mp3info.rb +0 -720
@@ -20,7 +20,7 @@ class Admin::CommentsController < Admin::BaseController
20
20
 
21
21
  if request.post? and @comment.save
22
22
  # We should probably wave a spam filter over this, but for now, just mark it as published.
23
- @comment.published = true
23
+ @comment.mark_as_ham!
24
24
  flash[:notice] = 'Comment was successfully created.'
25
25
  redirect_to :action => 'show', :id => @comment.id
26
26
  end
@@ -78,9 +78,7 @@ class Admin::ContentController < Admin::BaseController
78
78
  def new_or_edit
79
79
  get_or_build_article
80
80
  params[:article] ||= {}
81
- # params[:article].reverse_merge!('allow_comments' => this_blog.default_allow_comments,
82
- # 'allow_pings' => this_blog.default_allow_pings,
83
- # 'published' => true)
81
+
84
82
  @article.attributes = (params[:article])
85
83
  setup_categories
86
84
  @selected = @article.categories.collect { |c| c.id }
@@ -1,67 +1,72 @@
1
+
2
+
1
3
  class Admin::FeedbackController < Admin::BaseController
4
+ model :comment, :trackback
5
+
2
6
  def index
3
- conditions = ["(contents.type = 'Comment' or contents.type = 'Trackback')"]
7
+ conditions = ['1=1', {}]
4
8
 
5
9
  if params[:search]
6
- search_sql = "%#{params[:search]}%"
7
- conditions.first << ' and (url like ? or author like ? or title like ? or ip like ? or email like ?)'
8
- 5.times { conditions.push search_sql }
10
+ conditions.first << ' and (url like :pattern or author like :pattern or title like :pattern or ip like :pattern or email like :pattern)'
11
+ conditions.last.merge!(:pattern => "%#{params[:search]}%")
9
12
  end
10
13
 
11
14
  if params[:published] == 'f'
12
- conditions.first << ' and (published = ?)'
13
- conditions.push false
15
+ conditions.first << ' and (published = :published)'
16
+ conditions.last.merge!(:published => false)
14
17
  end
15
18
 
16
- @pages, @feedback = paginate(:contents,
17
- :order => 'contents.created_at desc',
19
+ @pages, @feedback = paginate(:feedback,
20
+ :order => 'contents.created_at desc',
18
21
  :conditions => conditions,
19
22
  :per_page => 40)
20
-
23
+
21
24
  render_action 'list'
22
25
  end
23
-
26
+
24
27
  def delete
25
28
  if request.post?
26
- feedback = Content.find(params[:id])
27
- if feedback.kind_of? Comment or feedback.kind_of? Trackback
28
- feedback.destroy
29
+ begin
30
+ Feedback.destroy(params[:id])
29
31
  flash[:notice] = "Deleted"
30
- else
32
+ rescue ActiveRecord::RecordNotFound
31
33
  flash[:notice] = "Not found"
32
34
  end
33
35
  end
34
36
  redirect_to :action => 'index', :page => params[:page], :search => params[:search]
35
37
  end
36
-
38
+
37
39
  def bulkops
38
- STDERR.puts "Bulkops: #{params.inspect}"
39
-
40
40
  ids = (params[:feedback_check]||{}).keys.map(&:to_i)
41
-
41
+
42
42
  case params[:commit]
43
43
  when 'Delete Checked Items'
44
44
  count = 0
45
45
  ids.each do |id|
46
- count += Content.delete(id)
46
+ count += Feedback.delete(id) ## XXX Should this be #destroy?
47
47
  end
48
48
  flash[:notice] = "Deleted #{count} item(s)"
49
- when 'Publish Checked Items'
49
+
50
+ # Sweep cache
51
+ PageCache.sweep_all
52
+ expire_fragment(/.*/)
53
+ when 'Mark Checked Items as Ham'
54
+ ids.each do |id|
55
+ feedback = Feedback.find(id)
56
+ feedback.mark_as_ham!
57
+ end
58
+ flash[:notice]= "Marked #{ids.size} item(s) as Ham"
59
+ when 'Mark Checked Items as Spam'
50
60
  ids.each do |id|
51
- feedback = Content.find(id)
52
- feedback.attributes[:published] = true
53
- feedback.set_spam(false)
54
- feedback.save
61
+ feedback = Feedback.find(id)
62
+ feedback.mark_as_spam!
55
63
  end
56
- flash[:notice]= "Published #{ids.size} item(s)"
57
- when 'Unpublish Checked Items'
64
+ flash[:notice]= "Marked #{ids.size} item(s) as Spam"
65
+ when 'Confirm Classification of Checked Items'
58
66
  ids.each do |id|
59
- feedback = Content.find(id)
60
- feedback.withdraw!
61
- feedback.set_spam(true)
62
- feedback.save
67
+ Feedback.find(id).confirm_classification!
63
68
  end
64
- flash[:notice]= "Unpublished #{ids.size} item(s)"
69
+ flash[:notice] = "Confirmed classification of #{ids.size} item(s)"
65
70
  else
66
71
  flash[:notice] = "Not implemented"
67
72
  end
@@ -53,8 +53,19 @@ class Admin::SidebarController < Admin::BaseController
53
53
  params[:configure] ||= []
54
54
  Sidebar.update_all('active_position = null')
55
55
  flash[:sidebars].each do |id|
56
- Sidebar.find(id).update_attributes(:config => params[:configure][id.to_s],
57
- :active_position => position)
56
+ sidebar = Sidebar.find(id)
57
+
58
+ # If it's a checkbox and unchecked, convert the 0 to false
59
+ # This is ugly. Anyone have an improvement?
60
+ params[:configure][id.to_s].each do |k,v|
61
+ field = sidebar.sidebar_controller.fields.detect { |f| f.key == k }
62
+ if v == "0" && field.is_a?(Sidebars::Field::CheckBoxField)
63
+ params[:configure][id.to_s][k] = false
64
+ end
65
+ end
66
+
67
+ sidebar.update_attributes(:config => params[:configure][id.to_s],
68
+ :active_position => position)
58
69
  position += 1
59
70
  end
60
71
  Sidebar.delete_all('active_position is null')
@@ -10,7 +10,8 @@ class Admin::UsersController < Admin::BaseController
10
10
  end
11
11
 
12
12
  def show
13
- @user = User.find(params[:id])
13
+ @user = User.find(params[:id], :include => [ :articles ])
14
+ @articles = @user.articles
14
15
  end
15
16
 
16
17
  def new
@@ -27,12 +27,12 @@ class ArticlesController < ContentController
27
27
  @articles = Article.find( :all,
28
28
  :offset => @pages.current.offset,
29
29
  :limit => @pages.items_per_page,
30
- :order => "contents.published_at DESC",
30
+ :order => "contents.published_at DESC",
31
31
  :include => [:categories, :tags, :user, :blog],
32
32
  :conditions =>
33
33
  ['published = ? AND contents.published_at < ? AND blog_id = ?',
34
34
  true, Time.now, this_blog.id]
35
- )
35
+ )
36
36
  end
37
37
 
38
38
  def search
@@ -94,23 +94,14 @@ class ArticlesController < ContentController
94
94
 
95
95
  if request.post?
96
96
  begin
97
- params[:comment].merge!({:ip => request.remote_ip,
98
- :published => true })
99
97
  @article = this_blog.published_articles.find(params[:id])
100
- @comment = @article.comments.build(params[:comment])
101
- @comment.user = session[:user]
102
-
103
- spam_options = {
104
- :user_agent => request.env['HTTP_USER_AGENT'],
105
- :referrer => request.env['HTTP_REFERER'],
106
- :permalink => this_blog.article_url(@article, false)}
107
-
108
- if @comment.is_spam? spam_options
109
- STDERR.puts "Moderating comment as spam!"
110
- @comment.withdraw
111
- end
112
-
113
- @comment.save!
98
+ params[:comment].merge!({:ip => request.remote_ip,
99
+ :published => true,
100
+ :user => session[:user],
101
+ :user_agent => request.env['HTTP_USER_AGENT'],
102
+ :referrer => request.env['HTTP_REFERER'],
103
+ :permalink => this_blog.article_url(@article, false)})
104
+ @comment = @article.comments.create!(params[:comment])
114
105
  add_to_cookies(:author, @comment.author)
115
106
  add_to_cookies(:url, @comment.url)
116
107
 
@@ -165,7 +156,7 @@ class ArticlesController < ContentController
165
156
  render :nothing => true, :status => 404
166
157
  end
167
158
  end
168
-
159
+
169
160
  def markup_help
170
161
  render :text => TextFilter.find(params[:id]).commenthelp
171
162
  end
@@ -65,7 +65,7 @@ class XmlController < ContentController
65
65
 
66
66
  protected
67
67
 
68
- def fetch_items(association, order='created_at DESC', limit=nil)
68
+ def fetch_items(association, order='published_at DESC', limit=nil)
69
69
  if association.instance_of?(Symbol)
70
70
  association = this_blog.send(association)
71
71
  end
@@ -89,7 +89,7 @@ class XmlController < ContentController
89
89
 
90
90
  def prep_article
91
91
  article = this_blog.articles.find(params[:id])
92
- fetch_items(article.comments, 'created_at DESC', 25)
92
+ fetch_items(article.comments, 'published_at DESC', 25)
93
93
  @items.unshift(article)
94
94
  @feed_title << ": #{article.title}"
95
95
  @link = article_url(article, false)
@@ -1,5 +1,9 @@
1
1
  module Admin::BaseHelper
2
2
 
3
+ def state_class(item)
4
+ item.state.memento.underscore.sub(/.*\//, '')
5
+ end
6
+
3
7
  def render_flash
4
8
  output = []
5
9
 
@@ -14,10 +18,10 @@ module Admin::BaseHelper
14
18
  output = []
15
19
 
16
20
  for key,value in @tasks
17
- output << "<a href=\"#{value}\">#{key}</a>"
18
- end if @tasks
21
+ output << "<a href=\"#{value}\">#{key}</a>"
22
+ end if @tasks
19
23
 
20
- output.join("<br/>\n")
24
+ output.join("<br/>\n")
21
25
  end
22
26
 
23
27
  def current_user_notice
@@ -40,12 +40,12 @@ module ApplicationHelper
40
40
  end
41
41
 
42
42
  def comments_link(article)
43
- article_link(pluralize(article.comments.size, "comment"),
43
+ article_link(pluralize(article.published_comments.size, "comment"),
44
44
  article, 'comments')
45
45
  end
46
46
 
47
47
  def trackbacks_link(article)
48
- article_link(pluralize(article.trackbacks.size, "trackback"),
48
+ article_link(pluralize(article.published_trackbacks.size, "trackback"),
49
49
  article, 'trackbacks')
50
50
  end
51
51
 
@@ -42,7 +42,8 @@ module ArticlesHelper
42
42
 
43
43
  def page_title
44
44
  if @page_title
45
- @page_title
45
+ # this is where the page title prefix (string) should go
46
+ (this_blog.title_prefix ? "#{this_blog.blog_name || "Typo"} : " : '') + @page_title
46
47
  else
47
48
  this_blog.blog_name || "Typo"
48
49
  end
@@ -151,10 +152,10 @@ module ArticlesHelper
151
152
  end
152
153
 
153
154
  def ul_tag_for(grouping_class)
154
- case grouping_class
155
- when Tag
155
+ case
156
+ when grouping_class == Tag
156
157
  %{<ul id="taglist" class="tags">}
157
- when Category
158
+ when grouping_class == Category
158
159
  %{<ul class="categorylist">}
159
160
  else
160
161
  '<ul>'
@@ -37,9 +37,13 @@ class Article < Content
37
37
  urls
38
38
  end
39
39
 
40
- def really_send_pings(serverurl = blog.server_url, articleurl = location(nil, false))
40
+ def really_send_pings(serverurl = blog.server_url, articleurl = nil)
41
41
  return unless blog.send_outbound_pings
42
42
 
43
+ # Moved the default value for articleurl out of the method declaration to
44
+ # avoid unnecessary evaluations of the #location() method.
45
+ articleurl ||= location(nil, false)
46
+
43
47
  weblogupdatesping_urls = blog.ping_urls.gsub(/ +/,'').split(/[\n\r]+/)
44
48
  pingback_or_trackback_urls = self.html_urls
45
49
 
@@ -52,7 +56,7 @@ class Article < Content
52
56
 
53
57
  if weblogupdatesping_urls.include?(url)
54
58
  ping.send_weblogupdatesping(serverurl, articleurl)
55
- else pingback_or_trackback_urls.include?(url)
59
+ elsif pingback_or_trackback_urls.include?(url)
56
60
  ping.send_pingback_or_trackback(articleurl)
57
61
  end
58
62
  end
@@ -142,7 +146,7 @@ class Article < Content
142
146
  if user.notify_via_jabber?
143
147
  JabberNotify.send_message(user, "New post",
144
148
  "A new message was posted to #{blog.blog_name}",
145
- content.body_html)
149
+ body_html)
146
150
  end
147
151
  end
148
152
 
@@ -158,17 +162,20 @@ class Article < Content
158
162
  end
159
163
  end
160
164
 
165
+ def published_comments
166
+ comments.select {|c| c.published?}
167
+ end
168
+
169
+ def published_trackbacks
170
+ trackbacks.select {|c| c.published?}
171
+ end
172
+
161
173
  protected
162
174
 
163
175
  before_create :set_defaults, :create_guid, :add_notifications
164
176
  before_save :set_published_at
165
177
  after_save :keywords_to_tags
166
178
 
167
- def correct_counts
168
- self.comments_count = self.comments_count
169
- self.trackbacks_count = self.trackbacks_count
170
- end
171
-
172
179
  def set_published_at
173
180
  if self.published and self[:published_at].nil?
174
181
  self[:published_at] = self.created_at || Time.now
@@ -179,7 +186,6 @@ class Article < Content
179
186
  if self.attributes.include?("permalink") and self.permalink.blank?
180
187
  self.permalink = self.stripped_title
181
188
  end
182
- correct_counts
183
189
  if blog && self.allow_comments.nil?
184
190
  self.allow_comments = blog.default_allow_comments
185
191
  end
@@ -187,7 +193,7 @@ class Article < Content
187
193
  if blog && self.allow_pings.nil?
188
194
  self.allow_pings = blog.default_allow_pings
189
195
  end
190
-
196
+
191
197
  true
192
198
  end
193
199
 
@@ -2,9 +2,9 @@
2
2
  # This isn't enabled yet, but it will be soon...
3
3
  class BlogRequest
4
4
  include Reloadable
5
-
5
+
6
6
  attr_accessor :protocol, :host_with_port, :path, :symbolized_path_parameters, :relative_url_root
7
-
7
+
8
8
  def initialize(root)
9
9
  @protocol = @host_with_port = @path = ''
10
10
  @symbolized_path_parameters = {}
@@ -37,6 +37,7 @@ class Blog < ActiveRecord::Base
37
37
  # Description
38
38
  setting :blog_name, :string, 'My Shiny Weblog!'
39
39
  setting :blog_subtitle, :string, ''
40
+ setting :title_prefix, :boolean, false
40
41
  setting :geourl_location, :string, ''
41
42
  setting :canonical_server_url, :string, ''
42
43
 
@@ -86,13 +87,6 @@ class Blog < ActiveRecord::Base
86
87
  article_id = settings[:id]
87
88
  settings.delete(:id)
88
89
  trackback = published_articles.find(article_id).trackbacks.create!(settings)
89
-
90
- if trackback.is_spam?
91
- STDERR.puts "Moderating trackback as spam!"
92
- trackback.withdraw!
93
- end
94
-
95
- trackback
96
90
  end
97
91
 
98
92
 
@@ -161,7 +155,7 @@ class Blog < ActiveRecord::Base
161
155
  end
162
156
 
163
157
  def article_url(article, only_path = true, anchor = nil)
164
- url_for(:year => article.published_at.year,
158
+ url_for(:year => article.published_at.year,
165
159
  :month => sprintf("%.2d", article.published_at.month),
166
160
  :day => sprintf("%.2d", article.published_at.day),
167
161
  :title => article.permalink, :anchor => anchor,
@@ -2,22 +2,18 @@ require_dependency 'spam_protection'
2
2
  require 'sanitize'
3
3
  require 'timeout'
4
4
 
5
- class Comment < Content
6
- include TypoGuid
5
+ class Comment < Feedback
6
+ belongs_to :article
7
7
 
8
8
  content_fields :body
9
9
 
10
- belongs_to :article, :counter_cache => true
11
10
  belongs_to :user
12
11
 
13
12
  validates_presence_of :author, :body
14
- validates_age_of :article_id
15
- validate_on_create :check_article_is_open_to_comments
16
13
 
17
-
18
- def self.default_order
19
- 'created_at ASC'
20
- end
14
+ attr_accessor :user_agent
15
+ attr_accessor :referrer
16
+ attr_accessor :permalink
21
17
 
22
18
  def notify_user_via_email(controller, user)
23
19
  if user.notify_via_email?
@@ -37,17 +33,12 @@ class Comment < Content
37
33
  users
38
34
  end
39
35
 
40
- def location(anchor=:ignored, only_path=true)
41
- blog.url_for(article, "comment-#{id}", only_path)
42
- end
43
-
44
36
  protected
45
37
 
46
- def check_article_is_open_to_comments
47
- return unless article
48
- unless article.allow_comments?
49
- errors.add(:article, "Article is not open to comments")
50
- end
38
+ def article_allows_feedback?
39
+ return true if article.allow_comments?
40
+ errors.add(:article, "Article is not open to comments")
41
+ false
51
42
  end
52
43
 
53
44
  def body_html_postprocess(value, controller)
@@ -58,28 +49,18 @@ class Comment < Content
58
49
  'comment_text_filter'
59
50
  end
60
51
 
61
- before_create :create_guid
62
- before_save :correct_url, :make_nofollow
63
-
64
- def correct_url
65
- unless url.to_s.empty?
66
- unless url =~ /^http\:\/\//
67
- self.url = "http://#{url}"
68
- end
69
- end
70
- end
71
-
72
52
  def make_nofollow
73
53
  self.author = author.nofollowify
74
54
  self.body_html = body_html.to_s.nofollowify
75
55
  end
76
-
77
- def akismet_options
78
- {:user_ip => ip, :comment_type => 'comment', :comment_author => author, :comment_author_email => email,
79
- :comment_author_url => url, :comment_content => body}
56
+
57
+ def originator
58
+ author
80
59
  end
81
-
82
- def spam_fields
83
- [:body, :url, :ip]
60
+
61
+ def additional_akismet_options
62
+ { :user_agent => user_agent,
63
+ :referrer => referrer,
64
+ :permalink => permalink }
84
65
  end
85
66
  end