community_engine 2.3.1 → 2.3.2

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.
Files changed (55) hide show
  1. checksums.yaml +6 -14
  2. data/.travis.yml +2 -2
  3. data/CHANGELOG +1 -0
  4. data/Gemfile +2 -0
  5. data/README.markdown +101 -57
  6. data/about.yml +1 -1
  7. data/app/controllers/authorizations_controller.rb +2 -1
  8. data/app/controllers/base_controller.rb +31 -43
  9. data/app/controllers/comments_controller.rb +11 -5
  10. data/app/controllers/messages_controller.rb +1 -1
  11. data/app/controllers/password_resets_controller.rb +4 -4
  12. data/app/controllers/sessions_controller.rb +3 -4
  13. data/app/helpers/base_helper.rb +47 -43
  14. data/app/models/album.rb +3 -3
  15. data/app/models/authorization.rb +14 -14
  16. data/app/models/clipping.rb +13 -13
  17. data/app/models/clipping_image.rb +4 -4
  18. data/app/models/comment.rb +18 -10
  19. data/app/models/event.rb +12 -12
  20. data/app/models/homepage_feature.rb +4 -4
  21. data/app/models/message.rb +2 -2
  22. data/app/models/metro_area.rb +1 -1
  23. data/app/models/photo.rb +4 -4
  24. data/app/models/poll.rb +1 -1
  25. data/app/models/post.rb +48 -48
  26. data/app/models/sb_post.rb +1 -1
  27. data/app/models/topic.rb +2 -2
  28. data/app/models/user.rb +3 -2
  29. data/app/views/admin/comments.html.haml +11 -8
  30. data/app/views/admin/users.html.haml +6 -4
  31. data/app/views/comments/_comment.html.haml +3 -0
  32. data/app/views/comments/approve.js.haml +1 -0
  33. data/app/views/layouts/application.html.haml +16 -16
  34. data/app/views/messages/_sent.html.haml +2 -2
  35. data/app/views/users/_search_options.html.haml +2 -2
  36. data/app/views/users/new.html.haml +4 -4
  37. data/community_engine.gemspec +4 -3
  38. data/config/locales/en.yml +10 -4
  39. data/config/routes.rb +35 -34
  40. data/db/migrate/090_add_comment_role.rb +15 -0
  41. data/lib/community_engine/authenticated_system.rb +11 -11
  42. data/lib/community_engine/engines_extensions.rb +14 -2
  43. data/lib/community_engine/version.rb +1 -1
  44. data/lib/tasks/community_engine_tasks.rake +3 -72
  45. data/test/functional/authorizations_controller_test.rb +60 -0
  46. data/test/functional/comments_controller_test.rb +44 -67
  47. data/test/functional/password_reset_controller_test.rb +14 -6
  48. data/test/functional/sessions_controller_test.rb +3 -3
  49. data/test/functional/topics_controller_test.rb +2 -2
  50. data/test/test_helper.rb +1 -8
  51. data/test/testapp/config/application.rb +4 -0
  52. data/test/testapp/db/schema.rb +4 -3
  53. data/test/unit/comment_test.rb +85 -28
  54. metadata +53 -49
  55. data/lib/community_engine/rails_asset_extensions.rb +0 -131
@@ -2,8 +2,8 @@ class ClippingImage < Asset
2
2
  include UrlUpload
3
3
 
4
4
  has_attached_file :asset, configatron.clipping.paperclip_options
5
- validates_attachment_presence :asset
6
- validates_attachment_content_type :asset, :content_type => configatron.clipping.validation_options.content_type
7
- validates_attachment_size :asset, :less_than => configatron.clipping.validation_options.max_size.to_i.megabytes
8
-
5
+ validates_attachment_presence :asset, :message => :photo_presence_error.l
6
+ validates_attachment_content_type :asset, :content_type => configatron.clipping.validation_options.content_type, :message => :photo_content_type_error.l
7
+ validates_attachment_size :asset, :less_than => configatron.clipping.validation_options.max_size.to_i.megabytes, :message => :photo_size_limit_error.l(:count => configatron.clipping.validation_options.max_size)
8
+
9
9
  end
@@ -13,6 +13,8 @@ class Comment < ActiveRecord::Base
13
13
  validates_length_of :comment, :maximum => 2000
14
14
 
15
15
  before_save :whitelist_attributes
16
+ after_create :send_notifications
17
+ after_save :send_notifications, :if => Proc.new{|record| record.role_change.eql?(['pending', 'published']) }
16
18
 
17
19
  validates_presence_of :user, :unless => Proc.new{|record| configatron.allow_anonymous_commenting }
18
20
  validates_presence_of :author_email, :unless => Proc.new{|record| record.user } #require email unless logged in
@@ -20,7 +22,7 @@ class Comment < ActiveRecord::Base
20
22
  validates_format_of :author_url, :with => /(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$)/ix, :unless => Proc.new{|record| record.user }
21
23
  validate :check_spam
22
24
 
23
- acts_as_activity :user, :if => Proc.new{|record| record.user } #don't record an activity if there's no user
25
+ acts_as_activity :user, :if => Proc.new{|record| record.user && !record.pending? } #don't record an activity if there's no user
24
26
 
25
27
  def self.find_photo_comments_for(user)
26
28
  Comment.find(:all, :conditions => ["recipient_id = ? AND commentable_type = ?", user.id, 'Photo'], :order => 'created_at DESC')
@@ -29,13 +31,14 @@ class Comment < ActiveRecord::Base
29
31
  def previous_commenters_to_notify
30
32
  # only send a notification on recent comments
31
33
  # limit the number of emails we'll send (or posting will be slooowww)
32
-
33
- User.find(:all,
34
- :conditions => ["users.id NOT IN (?) AND users.notify_comments = ?
35
- AND commentable_id = ? AND commentable_type = ?
36
- AND comments.notify_by_email = ?
37
- AND comments.created_at > ?", [user_id, recipient_id.to_i], true, commentable_id, commentable_type, true, 2.weeks.ago],
38
- :include => :comments_as_author, :limit => 20)
34
+ users = User.includes(:comments_as_author).limit(20)
35
+ users = users.where(:notify_comments => true).where('users.id NOT IN (?)', [user_id, recipient_id.to_i])
36
+ users = users.where(:comments => {
37
+ :commentable_id => commentable_id,
38
+ :commentable_type => commentable_type,
39
+ :notify_by_email => true
40
+ })
41
+ users = users.where('comments.created_at > ?', 2.weeks.ago)
39
42
  end
40
43
 
41
44
  def commentable_name
@@ -71,7 +74,7 @@ class Comment < ActiveRecord::Base
71
74
  end
72
75
 
73
76
  def should_notify_recipient?
74
- return unless recipient && recipient.email
77
+ return false unless recipient && recipient.email
75
78
  return false if recipient.eql?(user)
76
79
  return false unless recipient.notify_comments?
77
80
  true
@@ -92,6 +95,7 @@ class Comment < ActiveRecord::Base
92
95
 
93
96
  def send_notifications
94
97
  return if commentable.respond_to?(:send_comment_notifications?) && !commentable.send_comment_notifications?
98
+ return if pending?
95
99
  UserNotifier.comment_notice(self).deliver if should_notify_recipient?
96
100
  self.notify_previous_commenters
97
101
  self.notify_previous_anonymous_commenters if configatron.allow_anonymous_commenting
@@ -109,10 +113,14 @@ class Comment < ActiveRecord::Base
109
113
 
110
114
  def check_spam
111
115
  if !configatron.akismet_key.nil? && self.spam?
112
- self.errors.add(:base, :comment_spam_error.l)
116
+ self.role = 'pending'
113
117
  end
114
118
  end
115
119
 
120
+ def pending?
121
+ role.eql?('pending')
122
+ end
123
+
116
124
 
117
125
  protected
118
126
  def whitelist_attributes
@@ -12,23 +12,23 @@ class Event < ActiveRecord::Base
12
12
  has_many :attendees, :source=>:user, :through=>:rsvps
13
13
 
14
14
  attr_protected :user_id
15
-
15
+
16
16
  #Procs used to make sure time is calculated at runtime
17
- scope :upcoming, lambda {
17
+ scope :upcoming, lambda {
18
18
  where('end_time > ?' , Time.now).order('start_time ASC')
19
19
  }
20
- scope :past, lambda {
20
+ scope :past, lambda {
21
21
  where('end_time <= ?' , Time.now).order('start_time DESC')
22
- }
23
-
24
-
25
- acts_as_commentable
22
+ }
23
+
24
+
25
+ acts_as_moderated_commentable
26
26
 
27
27
  # Used by acts_as_commentable
28
28
  def owner
29
29
  self.user
30
- end
31
-
30
+ end
31
+
32
32
  def rsvped?(user)
33
33
  self.rsvps.find_by_user_id(user)
34
34
  end
@@ -52,10 +52,10 @@ class Event < ActiveRecord::Base
52
52
  def spans_days?
53
53
  (end_time - start_time) >= 86400
54
54
  end
55
-
55
+
56
56
  protected
57
57
  def start_time_cannot_be_before_end_time
58
58
  errors.add("start_time", " must be before end time") unless start_time && end_time && (start_time < end_time)
59
- end
60
-
59
+ end
60
+
61
61
  end
@@ -1,9 +1,9 @@
1
1
  class HomepageFeature < ActiveRecord::Base
2
2
  has_attached_file :image, configatron.feature.paperclip_options.to_hash
3
- validates_attachment_presence :image
4
- validates_attachment_content_type :image, :content_type => configatron.feature.validation_options.content_type
5
- validates_attachment_size :image, :less_than => configatron.feature.validation_options.max_size.to_i.megabytes
6
-
3
+ validates_attachment_presence :image, :message => :photo_presence_error.l
4
+ validates_attachment_content_type :image, :content_type => configatron.feature.validation_options.content_type, :message => :photo_content_type_error.l
5
+ validates_attachment_size :image, :less_than => configatron.feature.validation_options.max_size.to_i.megabytes, :message => :photo_size_limit_error.l(:count => configatron.feature.validation_options.max_size)
6
+
7
7
  attr_accessible :url, :title, :description, :image
8
8
 
9
9
  validates_presence_of :url
@@ -2,8 +2,8 @@ class Message < ActiveRecord::Base
2
2
  attr_accessor :to
3
3
  attr_accessor :reply_to
4
4
 
5
- attr_accessible :to, :subject, :body, :recipient, :sender, :recipient_id, :sender_id
6
-
5
+ attr_accessible :to, :subject, :body, :recipient, :sender, :recipient_id, :sender_id, :parent_id
6
+
7
7
  belongs_to :sender, :class_name => 'User', :foreign_key => 'sender_id', :inverse_of => :sent_messages
8
8
  belongs_to :recipient, :class_name => 'User', :foreign_key => 'recipient_id', :inverse_of => :received_messages
9
9
 
@@ -9,7 +9,7 @@ class MetroArea < ActiveRecord::Base
9
9
  validates_presence_of :country_id
10
10
  validates_presence_of :name
11
11
 
12
- acts_as_commentable
12
+ acts_as_moderated_commentable
13
13
 
14
14
  def to_s
15
15
  name
@@ -1,13 +1,13 @@
1
1
  class Photo < ActiveRecord::Base
2
2
  include UrlUpload
3
3
 
4
- acts_as_commentable
4
+ acts_as_moderated_commentable
5
5
  belongs_to :album
6
6
 
7
7
  has_attached_file :photo, configatron.photo.paperclip_options.to_hash
8
- validates_attachment_presence :photo, :unless => Proc.new{|record| record.photo_remote_url }
9
- validates_attachment_content_type :photo, :content_type => configatron.photo.validation_options.content_type
10
- validates_attachment_size :photo, :less_than => configatron.photo.validation_options.max_size.to_i.megabytes
8
+ validates_attachment_presence :photo, :unless => Proc.new{|record| record.photo_remote_url }, :message => :photo_presence_error.l
9
+ validates_attachment_content_type :photo, :content_type => configatron.photo.validation_options.content_type, :message => :photo_content_type_error.l
10
+ validates_attachment_size :photo, :less_than => configatron.photo.validation_options.max_size.to_i.megabytes, :message => :photo_size_limit_error.l(:count => configatron.photo.validation_options.max_size)
11
11
 
12
12
  attr_accessor :crop_x, :crop_y, :crop_w, :crop_h, :photo_remote_url
13
13
  after_update :reprocess_photo, :if => :cropping?
@@ -4,7 +4,7 @@ class Poll < ActiveRecord::Base
4
4
  validates_presence_of :post
5
5
  has_many :votes, :dependent => :destroy
6
6
  belongs_to :post
7
- attr_accessible :question
7
+ attr_accessible :question, :post_id, :post
8
8
 
9
9
  def voted?(user)
10
10
  !self.votes.find_by_user_id(user.id).nil?
@@ -1,44 +1,44 @@
1
1
  class Post < ActiveRecord::Base
2
2
  include Rakismet::Model
3
3
  rakismet_attrs :comment_type => 'post'
4
- attr_protected :akismet_attrs
5
-
6
-
7
- acts_as_commentable
4
+ attr_protected :akismet_attrs
5
+
6
+ acts_as_moderated_commentable
7
+
8
8
  acts_as_taggable
9
9
  acts_as_activity :user, :if => Proc.new{|r| r.is_live?}
10
10
  acts_as_publishable :live, :draft
11
-
11
+
12
12
  belongs_to :user
13
13
  belongs_to :category
14
14
  has_many :polls, :dependent => :destroy
15
15
  has_many :favorites, :as => :favoritable, :dependent => :destroy
16
-
16
+
17
17
  validates_presence_of :raw_post
18
18
  validates_presence_of :title
19
19
  validates_presence_of :user
20
20
  validates_presence_of :published_at, :if => Proc.new{|r| r.is_live? }
21
-
21
+
22
22
  before_save :transform_post, :if => Proc.new{|r| r.raw_post_changed? }
23
23
  before_validation :set_published_at
24
-
24
+
25
25
  after_save do |post|
26
26
  activity = Activity.find_by_item_type_and_item_id('Post', post.id)
27
27
  if post.is_live? && !activity
28
- post.create_activity_from_self
28
+ post.create_activity_from_self
29
29
  elsif post.is_draft? && activity
30
30
  activity.destroy
31
31
  end
32
32
  end
33
-
33
+
34
34
  attr_accessor :invalid_emails
35
35
 
36
36
  attr_accessible :category_id, :title, :raw_post, :published_as, :send_comment_notifications
37
-
37
+
38
38
  # Class Methods
39
39
  class << self
40
-
41
- # Scopes
40
+
41
+ # Scopes
42
42
  def by_featured_writers
43
43
  where("users.featured_writer = ?", true).includes(:user)
44
44
  end
@@ -46,21 +46,21 @@ class Post < ActiveRecord::Base
46
46
  order('posts.view_count DESC')
47
47
  end
48
48
  def since(days)
49
- where("posts.published_at > ?", days.ago)
49
+ where("posts.published_at > ?", days.ago)
50
50
  end
51
51
  def recent
52
- order("posts.published_at DESC")
52
+ order("posts.published_at DESC")
53
53
  end
54
-
54
+
55
55
  def find_related_to(post, options = {})
56
- options.reverse_merge!({:limit => 8,
57
- :order => 'published_at DESC',
56
+ options.reverse_merge!({:limit => 8,
57
+ :order => 'published_at DESC',
58
58
  :conditions => [ 'posts.id != ? AND published_as = ?', post.id, 'live' ]
59
59
  })
60
60
 
61
61
  limit(options[:limit]).order(options[:order]).where(options[:conditions]).tagged_with(post.tag_list, :any => true)
62
62
  end
63
-
63
+
64
64
  def find_recent(options = {:limit => 5})
65
65
  recent.find :all, options
66
66
  end
@@ -76,7 +76,7 @@ class Post < ActiveRecord::Base
76
76
  end
77
77
 
78
78
  def find_most_commented(limit = 10, since = 7.days.ago)
79
- Post.find(:all,
79
+ Post.find(:all,
80
80
  :select => 'posts.*, count(*) as comments_count',
81
81
  :joins => "LEFT JOIN comments ON comments.commentable_id = posts.id",
82
82
  :conditions => ['comments.commentable_type = ? AND posts.published_at > ?', 'Post', since],
@@ -84,24 +84,24 @@ class Post < ActiveRecord::Base
84
84
  :order => 'comments_count DESC',
85
85
  :limit => limit
86
86
  )
87
- end
88
-
87
+ end
88
+
89
89
  def new_from_bookmarklet(params)
90
90
  self.new(
91
91
  :title => "#{params[:title] || params[:uri]}",
92
92
  :raw_post => "<a href='#{params[:uri]}'>#{params[:uri]}</a>#{params[:selection] ? "<p>#{params[:selection]}</p>" : ''}"
93
93
  )
94
94
  end
95
-
95
+
96
96
  end
97
-
98
-
99
97
 
100
-
98
+
99
+
100
+
101
101
  def to_param
102
102
  id.to_s << "-" << (title ? title.parameterize : '' )
103
103
  end
104
-
104
+
105
105
  def display_title
106
106
  t = self.title
107
107
  if self.category
@@ -109,62 +109,62 @@ class Post < ActiveRecord::Base
109
109
  end
110
110
  t
111
111
  end
112
-
112
+
113
113
  def previous_post
114
114
  self.user.posts.order('published_at DESC').where('published_at < ? and published_as = ?', published_at, 'live').first
115
115
  end
116
116
  def next_post
117
- self.user.posts.except(:order).order('published_at DESC').where('published_at > ? and published_as = ?', published_at, 'live').first
117
+ self.user.posts.except(:order).order('published_at DESC').where('published_at > ? and published_as = ?', published_at, 'live').first
118
118
  end
119
-
119
+
120
120
  def first_image_in_body(size = nil, options = {})
121
121
  doc = Hpricot( post )
122
122
  image = doc.at("img")
123
123
  image ? image['src'] : nil
124
124
  end
125
-
125
+
126
126
  def tag_for_first_image_in_body
127
127
  image = first_image_in_body
128
128
  image.nil? ? '' : "<img src='#{image}' />"
129
129
  end
130
-
130
+
131
131
  ## transform the text and title into valid html
132
132
  def transform_post
133
133
  self.post = white_list(self.raw_post)
134
134
  self.title = white_list(self.title)
135
135
  end
136
-
136
+
137
137
  def set_published_at
138
138
  if self.is_live? && !self.published_at
139
139
  self.published_at = Time.now
140
140
  end
141
141
  end
142
-
142
+
143
143
  def owner
144
144
  self.user
145
145
  end
146
-
146
+
147
147
  def send_to(email_addresses = '', message = '', user = nil)
148
148
  self.invalid_emails = []
149
149
  emails = email_addresses.split(",").collect{|email| email.strip }.uniq
150
- emails.each do |email|
150
+ emails.each do |email|
151
151
  self.invalid_emails << email unless email =~ /[\w._%-]+@[\w.-]+.[a-zA-Z]{2,4}/
152
152
  end
153
153
  if email_addresses.blank? || !invalid_emails.empty?
154
154
  return false
155
- else
156
- emails = email_addresses.split(",").collect{|email| email.strip }.uniq
155
+ else
156
+ emails = email_addresses.split(",").collect{|email| email.strip }.uniq
157
157
  emails.each{|email|
158
158
  UserNotifier.post_recommendation((user ? user.login : 'Someone'), email, self, message, user).deliver
159
159
  }
160
- self.increment(:emailed_count).save
160
+ self.increment(:emailed_count).save
161
161
  end
162
162
  end
163
-
163
+
164
164
  def image_for_excerpt
165
- first_image_in_body || user.avatar_photo_url(:medium)
165
+ first_image_in_body || user.avatar_photo_url(:medium)
166
166
  end
167
-
167
+
168
168
  def create_poll(poll, choices)
169
169
  new_poll = self.polls.new(:question => poll[:question])
170
170
  choices.delete('')
@@ -173,7 +173,7 @@ class Post < ActiveRecord::Base
173
173
  new_poll.add_choices(choices)
174
174
  end
175
175
  end
176
-
176
+
177
177
  def update_poll(poll, choices)
178
178
  return unless self.poll
179
179
  self.poll.update_attributes(:question => poll[:question])
@@ -186,18 +186,18 @@ class Post < ActiveRecord::Base
186
186
  self.poll.destroy
187
187
  end
188
188
  end
189
-
189
+
190
190
  def poll
191
191
  !polls.empty? && polls.first
192
192
  end
193
-
193
+
194
194
  def has_been_favorited_by(user = nil, remote_ip = nil)
195
195
  f = Favorite.find_by_user_or_ip_address(self, user, remote_ip)
196
196
  return f
197
- end
198
-
197
+ end
198
+
199
199
  def published_at_display(format = :published_date)
200
200
  is_live? ? I18n.l(published_at, :format => format.to_sym) : 'Draft'
201
201
  end
202
-
202
+
203
203
  end
@@ -24,7 +24,7 @@ class SbPost < ActiveRecord::Base
24
24
 
25
25
  validates_presence_of :body, :topic
26
26
 
27
- attr_accessible :body, :author_email, :author_ip, :author_name, :author_url
27
+ attr_accessible :body, :author_email, :author_ip, :author_name, :author_url, :topic, :user_id
28
28
  after_create :monitor_topic
29
29
  after_create :notify_monitoring_users
30
30
 
@@ -17,8 +17,8 @@ class Topic < ActiveRecord::Base
17
17
  after_create :create_monitorship_for_owner
18
18
 
19
19
  accepts_nested_attributes_for :sb_posts
20
- attr_accessible :title, :sticky, :locked, :sb_posts_attributes, :forum_id
21
-
20
+ attr_accessible :title, :sticky, :locked, :sb_posts_attributes, :forum_id, :user_id
21
+
22
22
  scope :recently_replied, order('replied_at DESC')
23
23
 
24
24
  def notify_of_new_post(post)