community_engine 2.3.1 → 2.3.2

Sign up to get free protection for your applications and to get access to all the features.
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