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
@@ -1,6 +1,6 @@
1
1
  class CommentsController < BaseController
2
2
  before_filter :login_required, :except => [:index, :unsubscribe]
3
- before_filter :admin_or_moderator_required, :only => [:delete_selected, :edit, :update]
3
+ before_filter :admin_or_moderator_required, :only => [:delete_selected, :edit, :update, :approve, :disapprove]
4
4
 
5
5
  if configatron.allow_anonymous_commenting
6
6
  skip_before_filter :verify_authenticity_token, :only => [:create] #because the auth token might be cached anyway
@@ -28,6 +28,15 @@ class CommentsController < BaseController
28
28
  end
29
29
  end
30
30
 
31
+ def approve
32
+ @comment = Comment.find(params[:id])
33
+ @comment.ham! if !configatron.akismet_key.nil?
34
+ @comment.role = 'published'
35
+ @comment.save!
36
+ respond_to do |format|
37
+ format.js
38
+ end
39
+ end
31
40
 
32
41
  def index
33
42
  commentable_type = get_commentable_type(params[:commentable_type])
@@ -91,17 +100,14 @@ class CommentsController < BaseController
91
100
  commentable_type = get_commentable_type(params[:commentable_type])
92
101
  @commentable = commentable_type.singularize.constantize.find(params[:commentable_id])
93
102
 
94
- @comment = Comment.new(params[:comment])
103
+ @comment = @commentable.comments.new(params[:comment])
95
104
 
96
- @comment.commentable = @commentable
97
105
  @comment.recipient = @commentable.owner
98
106
  @comment.user_id = current_user.id if current_user
99
107
  @comment.author_ip = request.remote_ip #save the ip address for everyone, just because
100
108
 
101
109
  respond_to do |format|
102
110
  if (logged_in? || verify_recaptcha(@comment)) && @comment.save
103
- @comment.send_notifications
104
-
105
111
  flash.now[:notice] = :comment_was_successfully_created.l
106
112
  format.html { redirect_to commentable_url(@comment) }
107
113
  format.js
@@ -23,7 +23,7 @@ class MessagesController < BaseController
23
23
  end
24
24
 
25
25
  def show
26
- @message = Message.read(params[:id], current_user)
26
+ @message = Message.read(params[:id], @user)
27
27
  @message_thread = MessageThread.for(@message, (admin? ? @message.recipient : current_user ))
28
28
  @reply = Message.new_reply(@user, @message_thread, params)
29
29
  end
@@ -7,15 +7,15 @@ class PasswordResetsController < BaseController
7
7
  end
8
8
 
9
9
  def create
10
- @user = User.find_by_email(params[:email])
10
+ @user = User.where("lower(email) = ?", params[:email].downcase).first
11
11
  if @user
12
12
  @user.deliver_password_reset_instructions!
13
13
 
14
- flash[:info] = :your_password_reset_instructions_have_been_emailed_to_you.l
14
+ flash[:notice] = :your_password_reset_instructions_have_been_emailed_to_you.l
15
15
 
16
16
  redirect_to login_path
17
17
  else
18
- flash[:error] = :sorry_we_dont_recognize_that_email_address.l
18
+ flash[:error] = :sorry_we_dont_recognize_that_email_address.l
19
19
 
20
20
  render :action => :new
21
21
  end
@@ -49,4 +49,4 @@ class PasswordResetsController < BaseController
49
49
  end
50
50
  end
51
51
 
52
- end
52
+ end
@@ -1,11 +1,11 @@
1
- # This controller handles the login/logout function of the site.
1
+ # This controller handles the login/logout function of the site.
2
2
  class SessionsController < BaseController
3
3
 
4
4
  skip_before_filter :store_location, :only => [:new, :create]
5
5
 
6
6
  def index
7
7
  redirect_to :action => "new"
8
- end
8
+ end
9
9
 
10
10
  def new
11
11
  redirect_to user_path(current_user) and return if current_user
@@ -16,9 +16,8 @@ class SessionsController < BaseController
16
16
  @user_session = UserSession.new(:login => params[:email], :password => params[:password], :remember_me => params[:remember_me])
17
17
 
18
18
  if @user_session.save
19
+ self.current_user = @user_session.record #if current_user has been called before this, it will ne nil, so we have to make to reset it
19
20
 
20
- current_user = @user_session.record #if current_user has been called before this, it will ne nil, so we have to make to reset it
21
-
22
21
  flash[:notice] = :thanks_youre_now_logged_in.l
23
22
  redirect_back_or_default(dashboard_user_path(current_user))
24
23
  else
@@ -3,19 +3,23 @@ require 'digest/md5'
3
3
  # Methods added to this helper will be available to all templates in the application.
4
4
  module BaseHelper
5
5
  include ActsAsTaggableOn::TagsHelper
6
-
6
+
7
7
  def commentable_url(comment)
8
- if comment.commentable_type != "User"
9
- polymorphic_url([comment.recipient, comment.commentable])+"#comment_#{comment.id}"
10
- else
11
- user_url(comment.recipient)+"#comment_#{comment.id}"
8
+ if comment.recipient && comment.commentable
9
+ if comment.commentable_type != "User"
10
+ polymorphic_url([comment.recipient, comment.commentable])+"#comment_#{comment.id}"
11
+ elsif comment
12
+ user_url(comment.recipient)+"#comment_#{comment.id}"
13
+ end
14
+ elsif comment.commentable
15
+ polymorphic_url(comment.commentable)+"#comment_#{comment.id}"
12
16
  end
13
17
  end
14
-
18
+
15
19
  def forum_page?
16
20
  %w(forums topics sb_posts).include?(controller.controller_name)
17
21
  end
18
-
22
+
19
23
  def is_current_user_and_featured?(u)
20
24
  u && u.eql?(current_user) && u.featured_writer?
21
25
  end
@@ -28,20 +32,20 @@ module BaseHelper
28
32
  options.collect {|key,val| str << " #{key}=\"#{val}\"" }
29
33
  str << '><div class="box_top"></div>'
30
34
  str << "\n"
31
-
35
+
32
36
  concat(str.html_safe)
33
37
  yield(content)
34
38
  concat('<br class="clear" /><div class="box_bottom"></div></div>'.html_safe)
35
39
  end
36
-
40
+
37
41
  def block_to_partial(partial_name, html_options = {}, &block)
38
42
  concat(render(:partial => partial_name, :locals => {:body => capture(&block), :html_options => html_options}))
39
43
  end
40
44
 
41
45
  def box(html_options = {}, &block)
42
46
  block_to_partial('shared/box', html_options, &block)
43
- end
44
-
47
+ end
48
+
45
49
  def city_cloud(cities, classes)
46
50
  max, min = 0, 0
47
51
  cities.each { |c|
@@ -62,7 +66,7 @@ module BaseHelper
62
66
  string = words[0..(length-1)].join(' ') + (words.length > length ? end_string : '')
63
67
  string.html_safe
64
68
  end
65
-
69
+
66
70
  def truncate_words_with_highlight(text, phrase)
67
71
  t = excerpt(text, phrase)
68
72
  highlight truncate_words(t, 18), phrase
@@ -75,13 +79,13 @@ module BaseHelper
75
79
  if paragraph
76
80
  paragraph.to_html + end_string
77
81
  else
78
- truncate_words(text, 150, end_string)
82
+ truncate_words(text, 150, end_string)
79
83
  end
80
84
  end
81
85
 
82
86
  def page_title
83
- divider = " | ".html_safe
84
-
87
+ divider = " | ".html_safe
88
+
85
89
  app_base = configatron.community_name
86
90
  tagline = " #{divider} #{configatron.community_tagline}"
87
91
  title = app_base
@@ -100,10 +104,10 @@ module BaseHelper
100
104
  @canonical_url = user_post_url(@post.user, @post)
101
105
  end
102
106
  when 'users'
103
- if @user && !@user.new_record? && @user.login
107
+ if @user && !@user.new_record? && @user.login
104
108
  title = @user.login
105
109
  title += divider + app_base + tagline
106
- @canonical_url = user_url(@user)
110
+ @canonical_url = user_url(@user)
107
111
  else
108
112
  title = :showing_users.l+divider + app_base + tagline
109
113
  end
@@ -124,7 +128,7 @@ module BaseHelper
124
128
  title = :posts_photos_and_bookmarks.l(:name => @tags.map(&:name).join(', '))
125
129
  end
126
130
  title += " (#{:related_tags.l}: #{@related_tags.join(', ')})" if @related_tags
127
- title += divider + app_base
131
+ title += divider + app_base
128
132
  @canonical_url = tag_url(URI.escape(URI.escape(@tags_raw), /[\/.?#]/)) if @tags_raw
129
133
  else
130
134
  title = "Showing tags #{divider} #{app_base} #{tagline}"
@@ -133,15 +137,15 @@ module BaseHelper
133
137
  if @category and @category.name
134
138
  title = :posts_photos_and_bookmarks.l(:name => @category.name) + divider + app_base + tagline
135
139
  else
136
- title = :showing_categories.l + divider + app_base + tagline
140
+ title = :showing_categories.l + divider + app_base + tagline
137
141
  end
138
142
  when 'sessions'
139
- title = :login.l + divider + app_base + tagline
143
+ title = :login.l + divider + app_base + tagline
140
144
  end
141
145
 
142
146
  if @page_title
143
147
  title = @page_title + divider + app_base + tagline
144
- elsif title == app_base
148
+ elsif title == app_base
145
149
  title = :showing.l + ' ' + controller.controller_name + divider + app_base + tagline
146
150
  end
147
151
 
@@ -152,11 +156,11 @@ module BaseHelper
152
156
  html = "<span class='friend_request' id='friend_request_#{user.id}'>"
153
157
  html += link_to_remote :request_friendship.l,
154
158
  { :update => "friend_request_#{user.id}",
155
- :loading => "$$('span#friend_request_#{user.id} span.spinner')[0].show(); $$('span#friend_request_#{user.id} a.add_friend_btn')[0].hide()",
159
+ :loading => "$$('span#friend_request_#{user.id} span.spinner')[0].show(); $$('span#friend_request_#{user.id} a.add_friend_btn')[0].hide()",
156
160
  :complete => visual_effect(:highlight, "friend_request_#{user.id}", :duration => 1),
157
161
  500 => "alert('#{escape_javascript(:sorry_there_was_an_error_requesting_friendship.l)}')",
158
- :url => hash_for_user_friendships_url(:user_id => current_user.id, :friend_id => user.id),
159
- :method => :post
162
+ :url => hash_for_user_friendships_url(:user_id => current_user.id, :friend_id => user.id),
163
+ :method => :post
160
164
  }, {:class => "add_friend button"}
161
165
  html += "<span style='display:none;' class='spinner'>"
162
166
  html += image_tag('spinner.gif')
@@ -167,34 +171,34 @@ module BaseHelper
167
171
  def topnav_tab(name, options)
168
172
  classes = [options.delete(:class)]
169
173
  classes << 'current' if options[:section] && (options.delete(:section).to_a.include?(@section))
170
-
174
+
171
175
  string = "<li class='#{classes.join(' ')}'>" + link_to( content_tag(:span, name), options.delete(:url), options) + "</li>"
172
176
  string.html_safe
173
177
  end
174
-
178
+
175
179
  def more_comments_links(commentable)
176
180
  html = link_to "&raquo; ".html_safe + :all_comments.l, commentable_comments_url(commentable.class.to_s.tableize, commentable.to_param)
177
181
  html += "<br />".html_safe
178
182
  html += link_to "&raquo; ".html_safe + :comments_rss.l, commentable_comments_url(commentable.class.to_s.tableize, commentable.to_param, :format => :rss)
179
183
  html.html_safe
180
184
  end
181
-
185
+
182
186
  def show_footer_content?
183
187
  return true #you can override this in your app
184
188
  end
185
-
189
+
186
190
  def clippings_link
187
- "javascript:(function() {d=document, w=window, e=w.getSelection, k=d.getSelection, x=d.selection, s=(e?e():(k)?k():(x?x.createRange().text:0)), e=encodeURIComponent, document.location='#{home_url}new_clipping?uri='+e(document.location)+'&title='+e(document.title)+'&selection='+e(s);} )();"
191
+ "javascript:(function() {d=document, w=window, e=w.getSelection, k=d.getSelection, x=d.selection, s=(e?e():(k)?k():(x?x.createRange().text:0)), e=encodeURIComponent, document.location='#{home_url}new_clipping?uri='+e(document.location)+'&title='+e(document.title)+'&selection='+e(s);} )();"
188
192
  end
189
-
193
+
190
194
  def paginating_links(paginator, options = {}, html_options = {})
191
195
  paginate paginator
192
- end
193
-
196
+ end
197
+
194
198
  def last_active
195
199
  session[:last_active] ||= Time.now.utc
196
200
  end
197
-
201
+
198
202
  def ajax_spinner_for(id, spinner="spinner.gif")
199
203
  "<img src='/assets/#{spinner}' style='display:none; vertical-align:middle;' id='#{id.to_s}_spinner'> ".html_safe
200
204
  end
@@ -228,7 +232,7 @@ module BaseHelper
228
232
  from_time = from_time.to_time if from_time.respond_to?(:to_time)
229
233
  to_time = to_time.to_time if to_time.respond_to?(:to_time)
230
234
  distance_in_minutes = (((to_time - from_time).abs)/60).round
231
-
235
+
232
236
  case distance_in_minutes
233
237
  when 0 then :a_few_seconds_ago.l
234
238
  when 1..59 then :minutes_ago.l(:count => distance_in_minutes)
@@ -247,33 +251,33 @@ module BaseHelper
247
251
  display = I18n.l(date.to_date, :format => :date_ago)
248
252
  end
249
253
  end
250
-
254
+
251
255
  def profile_completeness(user)
252
256
  segments = [
253
257
  {:val => 2, :action => link_to(:upload_a_profile_photo.l, edit_user_path(user, :anchor => 'profile_details')), :test => !user.avatar.nil? },
254
- {:val => 1, :action => link_to(:tell_us_about_yourself.l, edit_user_path(user, :anchor => 'user_description')), :test => !user.description.blank?},
255
- {:val => 2, :action => link_to(:select_your_city.l, edit_user_path(user, :anchor => 'location_chooser')), :test => !user.metro_area.nil? },
256
- {:val => 1, :action => link_to(:tag_yourself.l, edit_user_path(user, :anchor => "user_tags")), :test => user.tags.any?},
258
+ {:val => 1, :action => link_to(:tell_us_about_yourself.l, edit_user_path(user, :anchor => 'user_description')), :test => !user.description.blank?},
259
+ {:val => 2, :action => link_to(:select_your_city.l, edit_user_path(user, :anchor => 'location_chooser')), :test => !user.metro_area.nil? },
260
+ {:val => 1, :action => link_to(:tag_yourself.l, edit_user_path(user, :anchor => "user_tags")), :test => user.tags.any?},
257
261
  {:val => 1, :action => link_to(:invite_some_friends.l, new_invitation_path), :test => user.invitations.any?}
258
262
  ]
259
-
263
+
260
264
  completed_score = segments.select{|s| s[:test].eql?(true)}.sum{|s| s[:val]}
261
265
  incomplete = segments.select{|s| !s[:test] }
262
-
266
+
263
267
  total = segments.sum{|s| s[:val] }
264
268
  score = (completed_score.to_f/total.to_f)*100
265
269
 
266
270
  {:score => score, :incomplete => incomplete, :total => total}
267
271
  end
268
-
272
+
269
273
 
270
274
  def possesive(user)
271
- user.gender ? (user.male? ? :his.l : :her.l) : :their.l
275
+ user.gender ? (user.male? ? :his.l : :her.l) : :their.l
272
276
  end
273
277
 
274
278
  def tiny_mce_init_if_needed
275
279
  if !@uses_tiny_mce.blank?
276
- tinymce_js = tinymce_javascript(@tiny_mce_configuration)
280
+ tinymce_js = tinymce_javascript(@tiny_mce_configuration)
277
281
  javascript_tag(tinymce_js)
278
282
  end
279
283
  end
@@ -3,10 +3,10 @@ class Album < ActiveRecord::Base
3
3
  belongs_to :user
4
4
  validates_presence_of :user_id
5
5
  acts_as_activity :user
6
- acts_as_commentable
7
- validates_presence_of :title
6
+ acts_as_moderated_commentable
7
+ validates_presence_of :title
8
8
 
9
- attr_accessible :title, :description
9
+ attr_accessible :title, :description, :user_id
10
10
 
11
11
  def owner
12
12
  self.user
@@ -14,36 +14,36 @@ class Authorization < ActiveRecord::Base
14
14
  create_from_hash(hash, existing_user)
15
15
  end
16
16
  end
17
-
17
+
18
18
  def self.create_from_hash(hash, existing_user = nil)
19
19
  create do |authorization|
20
20
  authorization.assign_account_info(hash)
21
21
  authorization.find_or_create_or_associate_user(existing_user)
22
- end
22
+ end
23
23
  end
24
-
25
- def self.find_from_hash(hash)
24
+
25
+ def self.find_from_hash(hash)
26
26
  find_by_provider_and_uid(hash['provider'], hash['uid'])
27
27
  end
28
28
 
29
29
  def find_or_create_or_associate_user(existing_user = nil)
30
30
  if existing_user
31
31
  self.user = existing_user
32
- elsif self.user
32
+ elsif self.user
33
33
  self.user
34
34
  else
35
- self.user = User.find_or_create_from_authorization(self)
35
+ self.user = User.find_or_create_from_authorization(self)
36
36
  end
37
37
  end
38
-
39
-
38
+
39
+
40
40
  def allow_destroy?
41
- if user.authorizations.count.eql?(1)
42
- errors.add(:base, "You must have at least one authorization provider.")
43
- raise ActiveRecord::Rollback
41
+ if user.authorizations.count.eql?(1)
42
+ errors.add(:base, "You must have at least one authorization provider.")
43
+ raise ActiveRecord::Rollback
44
44
  end
45
45
  end
46
-
46
+
47
47
  def assign_account_info(auth_hash)
48
48
  self.uid = auth_hash['uid']
49
49
  self.provider = auth_hash['provider']
@@ -55,6 +55,6 @@ class Authorization < ActiveRecord::Base
55
55
  self.access_token = auth_hash['credentials']['token']
56
56
  self.access_token_secret = auth_hash['credentials']['secret']
57
57
  end
58
- end
58
+ end
59
59
 
60
- end
60
+ end
@@ -1,6 +1,6 @@
1
1
  class Clipping < ActiveRecord::Base
2
2
 
3
- acts_as_commentable
3
+ acts_as_moderated_commentable
4
4
  belongs_to :user
5
5
  validates_presence_of :user
6
6
  validates_presence_of :url
@@ -10,20 +10,20 @@ class Clipping < ActiveRecord::Base
10
10
  validates_associated :image
11
11
  validates_presence_of :image
12
12
  after_save :save_image
13
-
13
+
14
14
  has_one :image, :as => :attachable, :class_name => "ClippingImage", :dependent => :destroy
15
15
  has_many :favorites, :as => :favoritable, :dependent => :destroy
16
-
16
+
17
17
  acts_as_taggable
18
18
  acts_as_activity :user
19
19
 
20
20
  attr_accessible :user, :url, :description, :image_url
21
-
22
- scope :recent, :order => 'clippings.created_at DESC'
23
-
24
-
21
+
22
+ scope :recent, :order => 'clippings.created_at DESC'
23
+
24
+
25
25
  def self.find_related_to(clipping, options = {})
26
- options.reverse_merge!({:limit => 8,
26
+ options.reverse_merge!({:limit => 8,
27
27
  :order => 'created_at DESC',
28
28
  :conditions => [ 'clippings.id != ?', clipping.id ]
29
29
  })
@@ -42,17 +42,17 @@ class Clipping < ActiveRecord::Base
42
42
  self.user.clippings.order('created_at DESC').where('created_at < ?', self.created_at).first
43
43
  end
44
44
  def next_clipping
45
- self.user.clippings.where('created_at > ?', self.created_at).order('created_at ASC').first
45
+ self.user.clippings.where('created_at > ?', self.created_at).order('created_at ASC').first
46
46
  end
47
47
 
48
48
  def owner
49
49
  self.user
50
50
  end
51
-
51
+
52
52
  def image_uri(size = '')
53
53
  image && image.asset.url(size) || image_url
54
54
  end
55
-
55
+
56
56
  def title_for_rss
57
57
  description.empty? ? created_at.to_formatted_s(:long) : description
58
58
  end
@@ -61,7 +61,7 @@ class Clipping < ActiveRecord::Base
61
61
  f = Favorite.find_by_user_or_ip_address(self, user, remote_ip)
62
62
  return f
63
63
  end
64
-
64
+
65
65
  def add_image
66
66
  begin
67
67
  clipping_image = ClippingImage.new
@@ -72,7 +72,7 @@ class Clipping < ActiveRecord::Base
72
72
  nil
73
73
  end
74
74
  end
75
-
75
+
76
76
  def save_image
77
77
  if valid? && image
78
78
  image.attachable = self