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.
- checksums.yaml +6 -14
- data/.travis.yml +2 -2
- data/CHANGELOG +1 -0
- data/Gemfile +2 -0
- data/README.markdown +101 -57
- data/about.yml +1 -1
- data/app/controllers/authorizations_controller.rb +2 -1
- data/app/controllers/base_controller.rb +31 -43
- data/app/controllers/comments_controller.rb +11 -5
- data/app/controllers/messages_controller.rb +1 -1
- data/app/controllers/password_resets_controller.rb +4 -4
- data/app/controllers/sessions_controller.rb +3 -4
- data/app/helpers/base_helper.rb +47 -43
- data/app/models/album.rb +3 -3
- data/app/models/authorization.rb +14 -14
- data/app/models/clipping.rb +13 -13
- data/app/models/clipping_image.rb +4 -4
- data/app/models/comment.rb +18 -10
- data/app/models/event.rb +12 -12
- data/app/models/homepage_feature.rb +4 -4
- data/app/models/message.rb +2 -2
- data/app/models/metro_area.rb +1 -1
- data/app/models/photo.rb +4 -4
- data/app/models/poll.rb +1 -1
- data/app/models/post.rb +48 -48
- data/app/models/sb_post.rb +1 -1
- data/app/models/topic.rb +2 -2
- data/app/models/user.rb +3 -2
- data/app/views/admin/comments.html.haml +11 -8
- data/app/views/admin/users.html.haml +6 -4
- data/app/views/comments/_comment.html.haml +3 -0
- data/app/views/comments/approve.js.haml +1 -0
- data/app/views/layouts/application.html.haml +16 -16
- data/app/views/messages/_sent.html.haml +2 -2
- data/app/views/users/_search_options.html.haml +2 -2
- data/app/views/users/new.html.haml +4 -4
- data/community_engine.gemspec +4 -3
- data/config/locales/en.yml +10 -4
- data/config/routes.rb +35 -34
- data/db/migrate/090_add_comment_role.rb +15 -0
- data/lib/community_engine/authenticated_system.rb +11 -11
- data/lib/community_engine/engines_extensions.rb +14 -2
- data/lib/community_engine/version.rb +1 -1
- data/lib/tasks/community_engine_tasks.rake +3 -72
- data/test/functional/authorizations_controller_test.rb +60 -0
- data/test/functional/comments_controller_test.rb +44 -67
- data/test/functional/password_reset_controller_test.rb +14 -6
- data/test/functional/sessions_controller_test.rb +3 -3
- data/test/functional/topics_controller_test.rb +2 -2
- data/test/test_helper.rb +1 -8
- data/test/testapp/config/application.rb +4 -0
- data/test/testapp/db/schema.rb +4 -3
- data/test/unit/comment_test.rb +85 -28
- metadata +53 -49
- 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
|
data/app/models/comment.rb
CHANGED
@@ -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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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.
|
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
|
data/app/models/event.rb
CHANGED
@@ -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
|
-
|
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
|
data/app/models/message.rb
CHANGED
@@ -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
|
|
data/app/models/metro_area.rb
CHANGED
data/app/models/photo.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
class Photo < ActiveRecord::Base
|
2
2
|
include UrlUpload
|
3
3
|
|
4
|
-
|
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?
|
data/app/models/poll.rb
CHANGED
data/app/models/post.rb
CHANGED
@@ -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
|
-
|
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
|
data/app/models/sb_post.rb
CHANGED
@@ -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
|
|
data/app/models/topic.rb
CHANGED
@@ -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)
|