Commit 4f91184d39168c2105c77c1ad61fe0c5f2337789

Authored by Rodrigo Souto
2 parents 38d06635 590e13de

Merge branch 'merge-requests/282'

app/controllers/public/comment_controller.rb 0 → 100644
... ... @@ -0,0 +1,164 @@
  1 +class CommentController < ApplicationController
  2 +
  3 + needs_profile
  4 +
  5 + before_filter :can_update?, :only => [:edit, :update]
  6 +
  7 + def create
  8 + begin
  9 + @page = profile.articles.find(params[:id])
  10 + rescue
  11 + @page = nil
  12 + end
  13 +
  14 + # page not found, give error
  15 + if @page.nil?
  16 + respond_to do |format|
  17 + format.js do
  18 + render :json => { :msg => _('Page not found.')}
  19 + end
  20 + end
  21 + return
  22 + end
  23 +
  24 + unless @page.accept_comments?
  25 + respond_to do |format|
  26 + format.js do
  27 + render :json => { :msg => _('Comment not allowed in this article')}
  28 + end
  29 + end
  30 + return
  31 + end
  32 +
  33 + @comment = Comment.new(params[:comment])
  34 + @comment.author = user if logged_in?
  35 + @comment.article = @page
  36 + @comment.ip_address = request.remote_ip
  37 + @comment.user_agent = request.user_agent
  38 + @comment.referrer = request.referrer
  39 + @plugins.dispatch(:filter_comment, @comment)
  40 +
  41 + if @comment.rejected?
  42 + respond_to do |format|
  43 + format.js do
  44 + render :json => { :msg => _('Comment was rejected')}
  45 + end
  46 + end
  47 + return
  48 + end
  49 +
  50 + if !@comment.valid? || (not pass_without_comment_captcha? and not verify_recaptcha(:model => @comment, :message => _('Please type the words correctly')))
  51 + respond_to do |format|
  52 + format.js do
  53 + render :json => {
  54 + :render_target => 'form',
  55 + :html => render_to_string(:partial => 'comment_form', :object => @comment, :locals => {:comment => @comment, :display_link => true, :show_form => true})
  56 + }
  57 + end
  58 + end
  59 + return
  60 + end
  61 +
  62 + if @comment.need_moderation?
  63 + @comment.created_at = Time.now
  64 + ApproveComment.create!(:requestor => @comment.author, :target => profile, :comment_attributes => @comment.attributes.to_json)
  65 +
  66 + respond_to do |format|
  67 + format.js do
  68 + render :json => { :render_target => nil, :msg => _('Your comment is waiting for approval.') }
  69 + end
  70 + end
  71 + return
  72 + end
  73 +
  74 + @comment.save
  75 +
  76 + respond_to do |format|
  77 + format.js do
  78 + comment_to_render = @comment.comment_root
  79 + render :json => {
  80 + :render_target => comment_to_render.anchor,
  81 + :html => render_to_string(:partial => 'comment', :locals => {:comment => comment_to_render, :display_link => true}),
  82 + :msg => _('Comment successfully created.')
  83 + }
  84 + end
  85 + end
  86 + end
  87 +
  88 + def destroy
  89 + comment = profile.comments_received.find(params[:id])
  90 +
  91 + if comment && comment.can_be_destroyed_by?(user) && comment.destroy
  92 + render :text => {'ok' => true}.to_json, :content_type => 'application/json'
  93 + else
  94 + session[:notice] = _("The comment was not removed.")
  95 + render :text => {'ok' => false}.to_json, :content_type => 'application/json'
  96 + end
  97 + end
  98 +
  99 + def mark_as_spam
  100 + comment = profile.comments_received.find(params[:id])
  101 + if comment.can_be_marked_as_spam_by?(user)
  102 + comment.spam!
  103 + render :text => {'ok' => true}.to_json, :content_type => 'application/json'
  104 + else
  105 + session[:notice] = _("You couldn't mark this comment as spam.")
  106 + render :text => {'ok' => false}.to_json, :content_type => 'application/json'
  107 + end
  108 + end
  109 +
  110 + def edit
  111 + render :partial => "comment_form", :locals => {:comment => @comment, :display_link => params[:reply_of_id].present?, :edition_mode => true, :show_form => true}
  112 + end
  113 +
  114 + def update
  115 + if @comment.update_attributes(params[:comment])
  116 + respond_to do |format|
  117 + format.js do
  118 + comment_to_render = @comment.comment_root
  119 + render :json => {
  120 + :ok => true,
  121 + :render_target => comment_to_render.anchor,
  122 + :html => render_to_string(:partial => 'comment', :locals => {:comment => comment_to_render})
  123 + }
  124 + end
  125 + end
  126 + else
  127 + respond_to do |format|
  128 + format.js do
  129 + render :json => {
  130 + :ok => false,
  131 + :render_target => 'form',
  132 + :html => render_to_string(:partial => 'comment_form', :object => @comment, :locals => {:comment => @comment, :display_link => false, :edition_mode => true, :show_form => true})
  133 + }
  134 + end
  135 + end
  136 + end
  137 + end
  138 +
  139 + def check_actions
  140 + comment = profile.comments_received.find(params[:id])
  141 + ids = @plugins.dispatch(:check_comment_actions, comment).collect do |action|
  142 + action.kind_of?(Proc) ? self.instance_eval(&action) : action
  143 + end.flatten.compact
  144 + render :json => {:ids => ids}
  145 + end
  146 +
  147 + protected
  148 +
  149 + def pass_without_comment_captcha?
  150 + logged_in? && !environment.enabled?('captcha_for_logged_users')
  151 + end
  152 + helper_method :pass_without_comment_captcha?
  153 +
  154 + def can_update?
  155 + begin
  156 + @comment = profile.comments_received.find(params[:id])
  157 + raise ActiveRecord::RecordNotFound unless @comment.can_be_updated_by?(user) # Not reveal that the comment exists
  158 + rescue ActiveRecord::RecordNotFound
  159 + render_not_found
  160 + return
  161 + end
  162 + end
  163 +
  164 +end
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -2,8 +2,6 @@ class ContentViewerController &lt; ApplicationController
2 2  
3 3 needs_profile
4 4  
5   - before_filter :comment_author, :only => :edit_comment
6   -
7 5 helper ProfileHelper
8 6 helper TagsHelper
9 7  
... ... @@ -70,24 +68,8 @@ class ContentViewerController &lt; ApplicationController
70 68  
71 69 @form_div = params[:form]
72 70  
73   - if params[:comment] && params[:confirm] == 'true'
74   - @comment = Comment.new(params[:comment])
75   - if request.post? && @page.accept_comments?
76   - add_comment
77   - end
78   - else
79   - @comment = Comment.new
80   - end
81   -
82   - if request.post?
83   - if params[:remove_comment]
84   - remove_comment
85   - return
86   - elsif params[:mark_comment_as_spam]
87   - mark_comment_as_spam
88   - return
89   - end
90   - end
  71 + #FIXME see a better way to do this. It's not need to pass this variable anymore
  72 + @comment = Comment.new
91 73  
92 74 if @page.has_posts?
93 75 posts = if params[:year] and params[:month]
... ... @@ -125,81 +107,8 @@ class ContentViewerController &lt; ApplicationController
125 107 end
126 108 end
127 109  
128   - def edit_comment
129   - path = params[:page].join('/')
130   - @page = profile.articles.find_by_path(path)
131   - @form_div = 'opened'
132   - @comment = @page.comments.find_by_id(params[:id])
133   - if @comment
134   - if request.post?
135   - begin
136   - @comment.update_attributes(params[:comment])
137   - session[:notice] = _('Comment succesfully updated')
138   - redirect_to :action => 'view_page', :profile => profile.identifier, :page => @comment.article.explode_path
139   - rescue
140   - session[:notice] = _('Comment could not be updated')
141   - end
142   - end
143   - else
144   - redirect_to @page.view_url
145   - session[:notice] = _('Could not find the comment in the article')
146   - end
147   - end
148   -
149 110 protected
150 111  
151   - def add_comment
152   - @comment.author = user if logged_in?
153   - @comment.article = @page
154   - @comment.ip_address = request.remote_ip
155   - @comment.user_agent = request.user_agent
156   - @comment.referrer = request.referrer
157   - plugins_filter_comment(@comment)
158   - return if @comment.rejected?
159   - if (pass_without_comment_captcha? || verify_recaptcha(:model => @comment, :message => _('Please type the words correctly'))) && @comment.save
160   - @page.touch
161   - @comment = nil # clear the comment form
162   - redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view]
163   - else
164   - @form_div = 'opened' if params[:comment][:reply_of_id].blank?
165   - end
166   - end
167   -
168   - def plugins_filter_comment(comment)
169   - @plugins.each do |plugin|
170   - plugin.filter_comment(comment)
171   - end
172   - end
173   -
174   - def pass_without_comment_captcha?
175   - logged_in? && !environment.enabled?('captcha_for_logged_users')
176   - end
177   - helper_method :pass_without_comment_captcha?
178   -
179   - def remove_comment
180   - @comment = @page.comments.find(params[:remove_comment])
181   - if (user == @comment.author || user == @page.profile || user.has_permission?(:moderate_comments, @page.profile))
182   - @comment.destroy
183   - end
184   - finish_comment_handling
185   - end
186   -
187   - def mark_comment_as_spam
188   - @comment = @page.comments.find(params[:mark_comment_as_spam])
189   - if logged_in? && (user == @page.profile || user.has_permission?(:moderate_comments, @page.profile))
190   - @comment.spam!
191   - end
192   - finish_comment_handling
193   - end
194   -
195   - def finish_comment_handling
196   - if request.xhr?
197   - render :text => {'ok' => true}.to_json, :content_type => 'application/json'
198   - else
199   - redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view]
200   - end
201   - end
202   -
203 112 def per_page
204 113 12
205 114 end
... ... @@ -223,13 +132,9 @@ class ContentViewerController &lt; ApplicationController
223 132 end
224 133 end
225 134  
226   - def comment_author
227   - comment = Comment.find_by_id(params[:id])
228   - if comment
229   - render_access_denied if comment.author.blank? || comment.author != user
230   - else
231   - render_not_found
232   - end
  135 + def pass_without_comment_captcha?
  136 + logged_in? && !environment.enabled?('captcha_for_logged_users')
233 137 end
  138 + helper_method :pass_without_comment_captcha?
234 139  
235 140 end
... ...
app/helpers/application_helper.rb
... ... @@ -30,6 +30,8 @@ module ApplicationHelper
30 30  
31 31 include AccountHelper
32 32  
  33 + include CommentHelper
  34 +
33 35 include BlogHelper
34 36  
35 37 include ContentViewerHelper
... ... @@ -1385,12 +1387,12 @@ module ApplicationHelper
1385 1387 end
1386 1388  
1387 1389 def expirable_button(content, action, text, url, options = {})
1388   - options[:class] = "button with-text icon-#{action.to_s}"
  1390 + options[:class] = ["button with-text icon-#{action.to_s}", options[:class]].compact.join(' ')
1389 1391 expirable_content_reference content, action, text, url, options
1390 1392 end
1391 1393  
1392 1394 def expirable_comment_link(content, action, text, url, options = {})
1393   - options[:class] = "comment-footer comment-footer-link comment-footer-hide"
  1395 + options[:class] = ["comment-footer comment-footer-link comment-footer-hide", options[:class]].compact.join(' ')
1394 1396 expirable_content_reference content, action, text, url, options
1395 1397 end
1396 1398  
... ...
app/helpers/article_helper.rb
... ... @@ -35,7 +35,13 @@ module ArticleHelper
35 35 'div',
36 36 check_box(:article, :notify_comments) +
37 37 content_tag('label', _('I want to receive a notification of each comment written by e-mail'), :for => 'article_notify_comments') +
38   - observe_field(:article_accept_comments, :function => "$('article_notify_comments').disabled = ! $('article_accept_comments').checked")
  38 + observe_field(:article_accept_comments, :function => "$('article_notify_comments').disabled = ! $('article_accept_comments').checked;$('article_moderate_comments').disabled = ! $('article_accept_comments').checked")
  39 + ) +
  40 +
  41 + content_tag(
  42 + 'div',
  43 + check_box(:article, :moderate_comments) +
  44 + content_tag('label', _('I want to approve comments on this article'), :for => 'article_moderate_comments')
39 45 ) +
40 46  
41 47 (article.can_display_hits? ?
... ...
app/helpers/comment_helper.rb 0 → 100644
... ... @@ -0,0 +1,70 @@
  1 +module CommentHelper
  2 +
  3 + def article_title(article, args = {})
  4 + title = article.title
  5 + title = article.display_title if article.kind_of?(UploadedFile) && article.image?
  6 + title = content_tag('h1', h(title), :class => 'title')
  7 + if article.belongs_to_blog?
  8 + unless args[:no_link]
  9 + title = content_tag('h1', link_to(article.name, article.url), :class => 'title')
  10 + end
  11 + comments = ''
  12 + unless args[:no_comments] || !article.accept_comments
  13 + comments = (" - %s") % link_to_comments(article)
  14 + end
  15 + title << content_tag('span',
  16 + content_tag('span', show_date(article.published_at), :class => 'date') +
  17 + content_tag('span', [_(", by %s") % link_to(article.author_name, article.author_url)], :class => 'author') +
  18 + content_tag('span', comments, :class => 'comments'),
  19 + :class => 'created-at'
  20 + )
  21 + end
  22 + title
  23 + end
  24 +
  25 + def comment_actions(comment)
  26 + url = url_for(:profile => profile.identifier, :controller => :comment, :action => :check_actions, :id => comment.id)
  27 + links = links_for_comment_actions(comment)
  28 + content_tag(:li, link_to(content_tag(:span, _('Contents menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger comment-trigger', :url => url), :class=> 'vcard') unless links.empty?
  29 + end
  30 +
  31 + private
  32 +
  33 + def links_for_comment_actions(comment)
  34 + actions = [link_for_report_abuse(comment), link_for_spam(comment), link_for_edit(comment), link_for_remove(comment)]
  35 + @plugins.dispatch(:comment_actions, comment).collect do |action|
  36 + actions << (action.kind_of?(Proc) ? self.instance_eval(&action) : action)
  37 + end
  38 + actions.flatten.compact
  39 + end
  40 +
  41 + def link_for_report_abuse(comment)
  42 + if comment.author
  43 + report_abuse_link = report_abuse(comment.author, :comment_link, comment)
  44 + {:link => report_abuse_link} if report_abuse_link
  45 + end
  46 + end
  47 +
  48 + def link_for_spam(comment)
  49 + if comment.can_be_marked_as_spam_by?(user)
  50 + if comment.spam?
  51 + {:link => link_to_function(_('Mark as NOT SPAM'), 'remove_comment(this, %s); return false;' % url_for(:profile => profile.identifier, :mark_comment_as_ham => comment.id).to_json, :class => 'comment-footer comment-footer-link comment-footer-hide')}
  52 + else
  53 + {:link => link_to_function(_('Mark as SPAM'), 'remove_comment(this, %s, %s); return false;' % [url_for(:profile => profile.identifier, :controller => 'comment', :action => :mark_as_spam, :id => comment.id).to_json, _('Are you sure you want to mark this comment as SPAM?').to_json], :class => 'comment-footer comment-footer-link comment-footer-hide')}
  54 + end
  55 + end
  56 + end
  57 +
  58 + def link_for_edit(comment)
  59 + if comment.can_be_updated_by?(user)
  60 + {:link => expirable_comment_link(comment, :edit, _('Edit'), url_for(:profile => profile.identifier, :controller => :comment, :action => :edit, :id => comment.id),:class => 'colorbox')}
  61 + end
  62 + end
  63 +
  64 + def link_for_remove(comment)
  65 + if comment.can_be_destroyed_by?(user)
  66 + {:link => link_to_function(_('Remove'), 'remove_comment(this, %s, %s); return false ;' % [url_for(:profile => profile.identifier, :controller => 'comment', :action => :destroy, :id => comment.id).to_json, _('Are you sure you want to remove this comment and all its replies?').to_json], :class => 'comment-footer comment-footer-link comment-footer-hide remove-children')}
  67 + end
  68 + end
  69 +
  70 +end
... ...
app/helpers/content_viewer_helper.rb
... ... @@ -5,11 +5,9 @@ module ContentViewerHelper
5 5  
6 6 def number_of_comments(article)
7 7 n = article.comments.without_spam.count
8   - if n == 0
9   - _('No comments yet')
10   - else
11   - n_('One comment', '<span class="comment-count">%{comments}</span> comments', n) % { :comments => n }
12   - end
  8 + base_str = "<span class='comment-count hide'>#{n}</span>"
  9 + amount_str = n == 0 ? _('no comments yet') : (n == 1 ? _('One comment') : _('%s comments') % n)
  10 + base_str + "<span class='comment-count-write-out'>#{amount_str}</span>"
13 11 end
14 12  
15 13 def article_title(article, args = {})
... ...
app/models/approve_comment.rb 0 → 100644
... ... @@ -0,0 +1,104 @@
  1 +class ApproveComment < Task
  2 + validates_presence_of :target_id
  3 +
  4 + settings_items :comment_attributes, :closing_statment
  5 +
  6 + validates_presence_of :comment_attributes
  7 +
  8 + def comment
  9 + @comment ||= Comment.new(JSON.parse(self.comment_attributes)) unless self.comment_attributes.nil?
  10 + end
  11 +
  12 + def requestor_name
  13 + requestor ? requestor.name : (comment.name || _('Anonymous'))
  14 + end
  15 +
  16 + def article
  17 + Article.find_by_id comment.source_id unless self.comment.nil?
  18 + end
  19 +
  20 + def article_name
  21 + article ? article.name : _("Article removed.")
  22 + end
  23 +
  24 + def perform
  25 + comment.save!
  26 + end
  27 +
  28 + def title
  29 + _("New comment to article")
  30 + end
  31 +
  32 + def icon
  33 + result = {:type => :defined_image, :src => '/images/icons-app/article-minor.png'}
  34 + result.merge!({:url => article.url}) if article
  35 + result
  36 + end
  37 +
  38 + def linked_subject
  39 + {:text => article_name, :url => article.url} if article
  40 + end
  41 +
  42 + def information
  43 + if article
  44 + if requestor
  45 + {:message => _('%{requestor} commented on the the article: %{linked_subject}.')}
  46 + else
  47 + { :message => _('%{requestor} commented on the the article: %{linked_subject}.'),
  48 + :variables => {:requestor => requestor_name} }
  49 + end
  50 + else
  51 + {:message => _("The article was removed.")}
  52 + end
  53 + end
  54 +
  55 + def accept_details
  56 + true
  57 + end
  58 +
  59 + def reject_details
  60 + true
  61 + end
  62 +
  63 + def default_decision
  64 + if article
  65 + 'skip'
  66 + else
  67 + 'reject'
  68 + end
  69 + end
  70 +
  71 + def accept_disabled?
  72 + article.blank?
  73 + end
  74 +
  75 + def target_notification_description
  76 + if article
  77 + _('%{requestor} wants to comment the article: %{article}.') % {:requestor => requestor_name, :article => article.name}
  78 + else
  79 + _('%{requestor} wanted to comment the article but it was removed.') % {:requestor => requestor_name}
  80 + end
  81 + end
  82 +
  83 + def target_notification_message
  84 + target_notification_description + "\n\n" +
  85 + _('You need to login on %{system} in order to approve or reject this comment.') % { :system => target.environment.name }
  86 + end
  87 +
  88 + def task_finished_message
  89 + if !closing_statment.blank?
  90 + _("Your comment to the article \"%{article}\" was approved. Here is the comment left by the admin who approved your comment:\n\n%{comment} ") % {:article => article_name, :comment => closing_statment}
  91 + else
  92 + _('Your request for comment the article "%{article}" was approved.') % {:article => article_name}
  93 + end
  94 + end
  95 +
  96 + def task_cancelled_message
  97 + message = _('Your request for commenting the article "%{article}" was rejected.') % {:article => article_name}
  98 + if !reject_explanation.blank?
  99 + message += " " + _("Here is the reject explanation left by the administrator who rejected your comment: \n\n%{reject_explanation}") % {:reject_explanation => reject_explanation}
  100 + end
  101 + message
  102 + end
  103 +
  104 +end
... ...
app/models/article.rb
... ... @@ -65,6 +65,7 @@ class Article &lt; ActiveRecord::Base
65 65 settings_items :display_hits, :type => :boolean, :default => true
66 66 settings_items :author_name, :type => :string, :default => ""
67 67 settings_items :allow_members_to_edit, :type => :boolean, :default => false
  68 + settings_items :moderate_comments, :type => :boolean, :default => false
68 69 settings_items :followers, :type => Array, :default => []
69 70  
70 71 belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id'
... ... @@ -327,6 +328,14 @@ class Article &lt; ActiveRecord::Base
327 328 @view_url ||= image? ? url.merge(:view => true) : url
328 329 end
329 330  
  331 + def comment_url_structure(comment, action = :edit)
  332 + if comment.new_record?
  333 + profile.url.merge(:page => path.split("/"), :controller => :comment, :action => :create)
  334 + else
  335 + profile.url.merge(:page => path.split("/"), :controller => :comment, :action => action || :edit, :id => comment.id)
  336 + end
  337 + end
  338 +
330 339 def allow_children?
331 340 true
332 341 end
... ... @@ -489,6 +498,14 @@ class Article &lt; ActiveRecord::Base
489 498 allow_post_content?(user) || user && allow_members_to_edit && user.is_member_of?(profile)
490 499 end
491 500  
  501 + def moderate_comments?
  502 + moderate_comments == true
  503 + end
  504 +
  505 + def comments_updated
  506 + solr_save
  507 + end
  508 +
492 509 def accept_category?(cat)
493 510 !cat.is_a?(ProductCategory)
494 511 end
... ...
app/models/comment.rb
... ... @@ -34,7 +34,9 @@ class Comment &lt; ActiveRecord::Base
34 34  
35 35 xss_terminate :only => [ :body, :title, :name ], :on => 'validation'
36 36  
37   - delegate :environment, :to => :source
  37 + def comment_root
  38 + (reply_of && reply_of.comment_root) || self
  39 + end
38 40  
39 41 def action_tracker_target
40 42 self.article.profile
... ... @@ -248,4 +250,22 @@ class Comment &lt; ActiveRecord::Base
248 250 plugins.dispatch(:comment_marked_as_ham, self)
249 251 end
250 252  
  253 + def need_moderation?
  254 + article.moderate_comments? && (author.nil? || article.author != author)
  255 + end
  256 +
  257 + def can_be_destroyed_by?(user)
  258 + return if user.nil?
  259 + user == author || user == profile || user.has_permission?(:moderate_comments, profile)
  260 + end
  261 +
  262 + def can_be_marked_as_spam_by?(user)
  263 + return if user.nil?
  264 + user == profile || user.has_permission?(:moderate_comments, profile)
  265 + end
  266 +
  267 + def can_be_updated_by?(user)
  268 + user.present? && user == author
  269 + end
  270 +
251 271 end
... ...
app/models/task.rb
... ... @@ -74,7 +74,7 @@ class Task &lt; ActiveRecord::Base
74 74 end
75 75  
76 76 def self.all_types
77   - %w[Invitation EnterpriseActivation AddMember Ticket SuggestArticle AddFriend CreateCommunity AbuseComplaint ApproveArticle CreateEnterprise ChangePassword EmailActivation InviteFriend InviteMember]
  77 + %w[Invitation EnterpriseActivation AddMember Ticket SuggestArticle AddFriend CreateCommunity AbuseComplaint ApproveComment ApproveArticle CreateEnterprise ChangePassword EmailActivation InviteFriend InviteMember]
78 78 end
79 79  
80 80 # this method finished the task. It calls #perform, which must be overriden
... ...
app/views/comment/_comment.rhtml 0 → 100644
... ... @@ -0,0 +1,84 @@
  1 +<li id="<%= comment.anchor %>" class="article-comment">
  2 + <div class="article-comment-inner">
  3 +
  4 + <div class="comment-content comment-logged-<%= comment.author ? 'in' : 'out' %> <%= 'comment-from-owner' if ( comment.author && (profile == comment.author) ) %>">
  5 +
  6 + <% if comment.author %>
  7 + <%= link_to image_tag(profile_icon(comment.author, :minor)) +
  8 + content_tag('span', comment.author_name, :class => 'comment-info'),
  9 + comment.author.url,
  10 + :class => 'comment-picture',
  11 + :title => comment.author_name
  12 + %>
  13 + <% else %>
  14 + <% url_image, status_class = comment.author_id ?
  15 + [comment.removed_user_image, 'icon-user-removed'] :
  16 + [str_gravatar_url_for( comment.email, :size => 50, :d=>404 ), 'icon-user-unknown'] %>
  17 +
  18 + <%= link_to(
  19 + image_tag(url_image, :onerror=>'gravatarCommentFailback(this)',
  20 + 'data-gravatar'=>str_gravatar_url_for(comment.email, :size=>50)) +
  21 + content_tag('span', comment.author_name, :class => 'comment-info') +
  22 + content_tag('span', comment.message,
  23 + :class => 'comment-user-status ' + status_class),
  24 + gravatar_profile_url(comment.email),
  25 + :target => '_blank',
  26 + :class => 'comment-picture',
  27 + :title => '%s %s' % [comment.author_name, comment.message]
  28 + )%>
  29 + <% end %>
  30 +
  31 + <% comment_balloon do %>
  32 +
  33 + <div class="comment-details">
  34 + <div class="comment-header">
  35 + <ul>
  36 + <div class="comment-actions">
  37 + <%= comment_actions(comment) %>
  38 + </div>
  39 + </ul>
  40 + <% unless comment.spam? %>
  41 + <%= link_to_function '',
  42 + "var f = add_comment_reply_form(this, %s); f.find('comment_title, textarea').val(''); return false" % comment.id,
  43 + :class => 'comment-footer comment-footer-link comment-footer-hide comment-actions-reply button',
  44 + :id => 'comment-reply-to-' + comment.id.to_s
  45 + %>
  46 + <% end %>
  47 + </div>
  48 +
  49 + <div class="comment-created-at">
  50 + <%= show_time(comment.created_at) %>
  51 + </div>
  52 + <h4><%= comment.title.blank? && '&nbsp;' || comment.title %></h4>
  53 + <div class="comment-text">
  54 + <p/>
  55 + <%= txt2html comment.body %>
  56 + </div>
  57 + </div>
  58 +
  59 + <div class="comment_reply post_comment_box closed" id="comment_reply_to_<%= comment.id %>">
  60 + <% if @comment && @comment.errors.any? && @comment.reply_of_id.to_i == comment.id %>
  61 + <%= error_messages_for :comment %>
  62 + <script type="text/javascript">
  63 + jQuery(function() {
  64 + document.location.href = '#<%= comment.anchor %>';
  65 + add_comment_reply_form('#comment-reply-to-<%= comment.id %>', <%= comment.id %>);
  66 + });
  67 + </script>
  68 + <% end %>
  69 + </div>
  70 +
  71 + <% end %>
  72 +
  73 + </div>
  74 +
  75 + <% unless comment.replies.blank? || comment.spam? %>
  76 + <ul class="comment-replies">
  77 + <% comment.replies.each do |reply| %>
  78 + <%= render :partial => 'comment/comment', :locals => { :comment => reply } %>
  79 + <% end %>
  80 + </ul>
  81 + <% end %>
  82 +
  83 + </div>
  84 +</li>
... ...
app/views/comment/_comment_form.rhtml 0 → 100644
... ... @@ -0,0 +1,96 @@
  1 +<% edition_mode = (defined? edition_mode) ? edition_mode : false %>
  2 +<div class="<%= edition_mode ? '' : 'page-comment-form' %>">
  3 +
  4 +<% focus_on = logged_in? ? 'title' : 'name' %>
  5 +
  6 +
  7 +<% if !edition_mode && !pass_without_comment_captcha? %>
  8 + <div id="recaptcha-container" style="display: none">
  9 + <h3><%= _('Please type the two words below') %></h3>
  10 + <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) %>
  11 + <% button_bar do %>
  12 + <%= button_to_function :add, _('Confirm'), "return false", :id => "confirm-captcha" %>
  13 + <%= button_to_function :cancel, _('Cancel'), "jQuery.colorbox.close()" %>
  14 + <% end %>
  15 + </div>
  16 +
  17 + <script type="text/javascript">
  18 + jQuery(document).bind('cbox_cleanup', function() {
  19 + jQuery('#recaptcha-container').hide();
  20 + });
  21 + </script>
  22 +<% end %>
  23 +
  24 +<script type="text/javascript">
  25 +function check_captcha(button, confirm_action) {
  26 + <% if edition_mode %>
  27 + return true;
  28 + <% elsif pass_without_comment_captcha? %>
  29 + button.form.confirm.value = 'true';
  30 + button.disabled = true;
  31 + return true;
  32 + <% else %>
  33 + jQuery('#recaptcha-container').show();
  34 + jQuery.colorbox({ inline : true, href : '#recaptcha-container', maxWidth : '600px', maxHeight : '300px' });
  35 + jQuery('#confirm-captcha').unbind('click');
  36 + jQuery('#confirm-captcha').bind('click', function() {
  37 + jQuery.colorbox.close();
  38 + button.form.recaptcha_response_field.value = jQuery('#recaptcha_response_field').val();
  39 + button.form.recaptcha_challenge_field.value = jQuery('#recaptcha_challenge_field').val();
  40 + button.form.confirm.value = 'true';
  41 + button.disabled = false;
  42 + confirm_action(button);
  43 + });
  44 + return false;
  45 + <% end %>
  46 +}
  47 +</script>
  48 +
  49 +<% if @comment && @comment.errors.any? %>
  50 + <%= error_messages_for :comment %>
  51 +<% end %>
  52 +
  53 +<div class="post_comment_box <%= ((defined? show_form) && show_form) ? 'opened' : 'closed' %>">
  54 +
  55 + <%= link_to(_('Post a comment'), '#', :class => 'display-comment-form') if display_link && @comment.reply_of_id.blank? %>
  56 +<% remote_form_for(:comment, comment, :url => {:profile => profile.identifier, :controller => 'comment', :action => (edition_mode ? 'update' : 'create'), :id => (edition_mode ? comment.id : @page.id)}, :html => { :class => 'comment_form' } ) do |f| %>
  57 +
  58 + <%= required_fields_message %>
  59 +
  60 + <% unless logged_in? %>
  61 +
  62 + <%= required labelled_form_field(_('Name'), f.text_field(:name)) %>
  63 + <%= required labelled_form_field(_('e-mail'), f.text_field(:email)) %>
  64 + <p>
  65 + <%= _('If you are a registered user, you can login and be automatically recognized.') %>
  66 + </p>
  67 +
  68 + <% end %>
  69 +
  70 + <% if !edition_mode && !pass_without_comment_captcha? %>
  71 + <%= hidden_field_tag(:recaptcha_response_field, nil, :id => nil) %>
  72 + <%= hidden_field_tag(:recaptcha_challenge_field, nil, :id => nil) %>
  73 + <% end %>
  74 +
  75 + <%= labelled_form_field(_('Title'), f.text_field(:title)) %>
  76 + <%= required labelled_form_field(_('Enter your comment'), f.text_area(:body, :rows => 5)) %>
  77 +
  78 + <%= hidden_field_tag(:confirm, 'false') %>
  79 + <%= hidden_field_tag(:view, params[:view])%>
  80 + <%= f.hidden_field(:reply_of_id) %>
  81 +
  82 + <% button_bar do %>
  83 + <%= submit_button('add', _('Post comment'), :onclick => "if(check_captcha(this)) { save_comment(this) } else { check_captcha(this, save_comment)};return false;") %>
  84 + <% if !edition_mode %>
  85 + <%= button :cancel, _('Cancel'), '', :id => 'cancel-comment' %>
  86 + <% else %>
  87 + <%= button :cancel, _('Cancel'), '#', :onclick => "jQuery.colorbox.close();" %>
  88 + <% end %>
  89 + <% end %>
  90 +<% end %>
  91 +
  92 +
  93 +</div><!-- end class="post_comment_box" -->
  94 +</div><!-- end class="page-comment-form" -->
  95 +
  96 +<%= javascript_include_tag 'comment_form'%>
... ...
app/views/comment/edit.rhtml 0 → 100644
app/views/content_viewer/_comment.rhtml
... ... @@ -1,99 +0,0 @@
1   -<li id="<%= comment.anchor %>" class="article-comment">
2   - <div class="article-comment-inner">
3   -
4   - <div class="comment-content comment-logged-<%= comment.author ? 'in' : 'out' %> <%= 'comment-from-owner' if ( comment.author && (profile == comment.author) ) %>">
5   -
6   - <% if comment.author %>
7   - <%= link_to image_tag(profile_icon(comment.author, :minor)) +
8   - content_tag('span', comment.author_name, :class => 'comment-info'),
9   - comment.author.url,
10   - :class => 'comment-picture',
11   - :title => comment.author_name
12   - %>
13   - <% else %>
14   - <% url_image, status_class = comment.author_id ?
15   - [comment.removed_user_image, 'icon-user-removed'] :
16   - [str_gravatar_url_for( comment.email, :size => 50, :d=>404 ), 'icon-user-unknown'] %>
17   -
18   - <%= link_to(
19   - image_tag(url_image, :onerror=>'gravatarCommentFailback(this)',
20   - 'data-gravatar'=>str_gravatar_url_for(comment.email, :size=>50)) +
21   - content_tag('span', comment.author_name, :class => 'comment-info') +
22   - content_tag('span', comment.message,
23   - :class => 'comment-user-status ' + status_class),
24   - gravatar_profile_url(comment.email),
25   - :target => '_blank',
26   - :class => 'comment-picture',
27   - :title => '%s %s' % [comment.author_name, comment.message]
28   - )%>
29   - <% end %>
30   -
31   - <% comment_balloon do %>
32   -
33   - <div class="comment-details">
34   - <div class="comment-created-at">
35   - <%= show_time(comment.created_at) %>
36   - </div>
37   - <h4><%= comment.title.blank? && '&nbsp;' || comment.title %></h4>
38   - <div class="comment-text">
39   - <p/>
40   - <%= txt2html comment.body %>
41   - </div>
42   - </div>
43   -
44   - <div class="comment_reply post_comment_box closed">
45   - <% if @comment && @comment.errors.any? && @comment.reply_of_id.to_i == comment.id %>
46   - <%= error_messages_for :comment %>
47   - <script type="text/javascript">
48   - jQuery(function() {
49   - document.location.href = '#<%= comment.anchor %>';
50   - add_comment_reply_form('#comment-reply-to-<%= comment.id %>', <%= comment.id %>);
51   - });
52   - </script>
53   - <% end %>
54   - <%= report_abuse(comment.author, :comment_link, comment) if comment.author %>
55   -
56   - <% if comment.spam? %>
57   - &nbsp;
58   - <%= link_to_function(_('Mark as NOT SPAM'), 'remove_comment(this, %s); return false;' % url_for(:mark_comment_as_ham => comment.id).to_json, :class => 'comment-footer comment-footer-link comment-footer-hide') %>
59   - <% else %>
60   - <% if (logged_in? && (user == profile || user.has_permission?(:moderate_comments, profile))) %>
61   - &nbsp;
62   - <%= link_to_function(_('Mark as SPAM'), 'remove_comment(this, %s, %s); return false;' % [url_for(:mark_comment_as_spam => comment.id).to_json, _('Are you sure you want to mark this comment as SPAM?').to_json], :class => 'comment-footer comment-footer-link comment-footer-hide') %>
63   - <% end %>
64   - <% end %>
65   -
66   - <% if comment.author && comment.author == user %>
67   - &nbsp;
68   - <%= expirable_comment_link comment, :edit, _('Edit'), {:action => 'edit_comment', :id => comment.id, :profile => profile.identifier} %>
69   - <% end %>
70   -
71   - <% if logged_in? && (user == profile || user == comment.author || user.has_permission?(:moderate_comments, profile)) %>
72   - &nbsp;
73   - <%= link_to_function(_('Remove'), 'remove_comment(this, %s, %s); return false ;' % [url_for(:profile => params[:profile], :remove_comment => comment.id, :view => params[:view]).to_json, _('Are you sure you want to remove this comment and all its replies?').to_json], :class => 'comment-footer comment-footer-link comment-footer-hide remove-children') %>
74   - <% end %>
75   -
76   - <% unless comment.spam? %>
77   - &nbsp;
78   - <%= link_to_function _('Reply'),
79   - "var f = add_comment_reply_form(this, %s); f.find('comment_title, textarea').val(''); return false" % comment.id,
80   - :class => 'comment-footer comment-footer-link comment-footer-hide',
81   - :id => 'comment-reply-to-' + comment.id.to_s
82   - %>
83   - <% end %>
84   - </div>
85   -
86   - <% end %>
87   -
88   - </div>
89   -
90   - <% unless comment.replies.blank? || comment.spam? %>
91   - <ul class="comment-replies">
92   - <% comment.replies.each do |reply| %>
93   - <%= render :partial => 'comment', :locals => { :comment => reply } %>
94   - <% end %>
95   - </ul>
96   - <% end %>
97   -
98   - </div>
99   -</li>
app/views/content_viewer/_comment_form.rhtml
... ... @@ -1,88 +0,0 @@
1   -<script type="text/javascript">
2   -function submit_comment_form(button) {
3   - <% if pass_without_comment_captcha? %>
4   - button.form.confirm.value = 'true';
5   - button.disabled = true;
6   - button.form.submit();
7   - return true;
8   - <% else %>
9   - jQuery('#recaptcha-container').show();
10   - jQuery.colorbox({ inline : true, href : '#recaptcha-container', maxWidth : '600px', maxHeight : '300px' });
11   - jQuery('#confirm-captcha').unbind('click');
12   - jQuery('#confirm-captcha').bind('click', function() {
13   - jQuery.colorbox.close();
14   - button.form.recaptcha_response_field.value = jQuery('#recaptcha_response_field').val();
15   - button.form.recaptcha_challenge_field.value = jQuery('#recaptcha_challenge_field').val();
16   - button.form.confirm.value = 'true';
17   - button.disabled = true;
18   - button.form.submit();
19   - });
20   - <% end %>
21   -}
22   -</script>
23   -
24   -<% if @comment && @comment.errors.any? && @comment.reply_of_id.blank? %>
25   - <%= error_messages_for :comment %>
26   - <script type="text/javascript">jQuery(function() { document.location.href = '#page-comment-form'; });</script>
27   -<% end %>
28   -
29   -<% @form_div ||= 'closed' %>
30   -
31   -<div class="post_comment_box <%= @form_div %>">
32   -
33   - <%= link_to(_('Post a comment'), '#', :class => 'display-comment-form') if display_link %>
34   -
35   -<% unless pass_without_comment_captcha? %>
36   - <div id="recaptcha-container" style="display: none">
37   - <h3><%= _('Please type the two words below') %></h3>
38   - <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) %>
39   - <% button_bar do %>
40   - <%= button_to_function :add, _('Confirm'), "return false", :id => "confirm-captcha" %>
41   - <%= button_to_function :cancel, _('Cancel'), "jQuery.colorbox.close()" %>
42   - <% end %>
43   - </div>
44   -
45   - <script type="text/javascript">
46   - jQuery(document).bind('cbox_cleanup', function() {
47   - jQuery('#recaptcha-container').hide();
48   - });
49   - </script>
50   -<% end %>
51   -
52   -<% form_tag( url, { :class => 'comment_form' } ) do %>
53   - <%= required_fields_message %>
54   -
55   - <% unless logged_in? %>
56   -
57   - <%= required labelled_form_field(_('Name'), text_field(:comment, :name)) %>
58   - <%= required labelled_form_field(_('e-mail'), text_field(:comment, :email)) %>
59   - <p>
60   - <%= _('If you are a registered user, you can login and be automatically recognized.') %>
61   - </p>
62   -
63   - <% end %>
64   -
65   - <% unless pass_without_comment_captcha? %>
66   - <%= hidden_field_tag(:recaptcha_response_field, nil, :id => nil) %>
67   - <%= hidden_field_tag(:recaptcha_challenge_field, nil, :id => nil) %>
68   - <% end %>
69   -
70   - <%= labelled_form_field(_('Title'), text_field(:comment, :title)) %>
71   - <%= required labelled_form_field(_('Enter your comment'), text_area(:comment, :body, :rows => 5)) %>
72   -
73   - <%= hidden_field_tag(:confirm, 'false') %>
74   - <%= hidden_field_tag(:view, params[:view])%>
75   -
76   - <% button_bar do %>
77   - <%= submit_button('add', _('Post comment'), :onclick => "submit_comment_form(this); return false") %>
78   - <% if cancel_triggers_hide %>
79   - <%= button :cancel, _('Cancel'), '', :id => 'cancel-comment' %>
80   - <% else %>
81   - <%= button('cancel', _('Cancel'), {:action => 'view_page', :profile => profile.identifier, :page => @comment.article.explode_path})%>
82   - <% end %>
83   - <% end %>
84   -<% end %>
85   -
86   -</div><!-- end class="post_comment_box" -->
87   -
88   -<%= javascript_include_tag 'comment_form'%>
app/views/content_viewer/edit_comment.html.erb
... ... @@ -1,3 +0,0 @@
1   -<h1><%= _('Edit comment') %></h1>
2   -
3   -<%= render :partial => 'comment_form', :locals => {:url => {:action => 'edit_comment', :profile => profile.identifier}, :display_link => false, :cancel_triggers_hide => false} %>
app/views/content_viewer/view_page.rhtml
... ... @@ -8,6 +8,12 @@
8 8  
9 9 <%= render :partial => 'confirm_unfollow' %>
10 10  
  11 +<script type="text/javascript">
  12 + window.ONE_COMMENT = "<%= _('One comment') %>";
  13 + window.COMMENT_PLURAL = "<%= _('comments') %>";
  14 + window.NO_COMMENT_YET = "<%= _('No comments yet') %>";
  15 +</script>
  16 +
11 17 <div id="article-toolbar"></div>
12 18  
13 19 <script type="text/javascript">
... ... @@ -89,15 +95,15 @@
89 95 <% end %>
90 96  
91 97 <% if @page.accept_comments? && @comments_count > 1 %>
92   - <%= link_to(_('Post a comment'), '#', :class => 'display-comment-form', :id => 'top-post-comment-button') %>
  98 + <%= link_to(_('Post a comment'), '#', :class => 'display-comment-form', :id => 'top-post-comment-button', :onclick => "jQuery('#page-comment-form .display-comment-form').first().click();") %>
93 99 <% end %>
94 100  
95 101 <ul class="article-comments-list">
96   - <%= render :partial => 'comment', :collection => @comments %>
  102 + <%= render :partial => 'comment/comment', :collection => @comments %>
97 103 </ul>
98 104  
99 105 <% if @page.accept_comments? %>
100   - <div id="page-comment-form"><%= render :partial => 'comment_form', :locals => {:url => url_for(@page.view_url.merge({:only_path => true})), :display_link => true, :cancel_triggers_hide => true}%></div>
  106 + <div id='page-comment-form' class='page-comment-form'><%= render :partial => 'comment/comment_form', :locals =>{:comment => Comment.new, :url => {:controller => :comment, :action => :create}, :display_link => true, :cancel_triggers_hide => true}%></div>
101 107 <% end %>
102 108 </div><!-- end class="comments" -->
103 109  
... ...
app/views/layouts/application-ng.rhtml
... ... @@ -17,6 +17,10 @@
17 17 content.respond_to?(:call) ? content.call : content
18 18 end.join("\n")
19 19 %>
  20 +
  21 + <script type='text/javascript'>
  22 + DEFAULT_LOADING_MESSAGE = <%="'#{ _('loading...') }'" %>;
  23 + </script>
20 24 </head>
21 25 <body class="<%= body_classes %>">
22 26 <a href="#content" id="link-go-content"><span><%= _("Go to the content") %></span></a>
... ...
app/views/spam/index.rhtml
... ... @@ -8,7 +8,7 @@
8 8 <div id='article'>
9 9 <div class="comments" id="comments_list">
10 10 <ul class="article-comments-list">
11   - <%= render :partial => 'content_viewer/comment', :collection => @spam %>
  11 + <%= render :partial => 'comment/comment', :collection => @spam %>
12 12 </ul>
13 13 </div>
14 14 </div>
... ...
app/views/tasks/_approve_comment_accept_details.rhtml 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +<p>
  2 + <b><%= _('Title: ') %></b>
  3 + <%= task.comment.title %>
  4 +</p>
  5 +<p>
  6 + <%= task.comment.body %>
  7 +</p>
... ...
config/routes.rb
... ... @@ -76,6 +76,9 @@ ActionController::Routing::Routes.draw do |map|
76 76 # profile search
77 77 map.profile_search 'profile/:profile/search', :controller => 'profile_search', :action => 'index', :profile => /#{Noosfero.identifier_format}/
78 78  
  79 + # comments
  80 + map.comment 'profile/:profile/comment/:action/:id', :controller => 'comment', :profile => /#{Noosfero.identifier_format}/
  81 +
79 82 # public profile information
80 83 map.profile 'profile/:profile/:action/:id', :controller => 'profile', :action => 'index', :id => /[^\/]*/, :profile => /#{Noosfero.identifier_format}/
81 84  
... ... @@ -122,7 +125,6 @@ ActionController::Routing::Routes.draw do |map|
122 125 # cache stuff - hack
123 126 map.cache 'public/:action/:id', :controller => 'public'
124 127  
125   - map.connect ':profile/edit_comment/:id/*page', :controller => 'content_viewer', :action => 'edit_comment', :profile => /#{Noosfero.identifier_format}/
126 128  
127 129 # match requests for profiles that don't have a custom domain
128 130 map.homepage ':profile/*page', :controller => 'content_viewer', :action => 'view_page', :profile => /#{Noosfero.identifier_format}/, :conditions => { :if => lambda { |env| !Domain.hosting_profile_at(env[:host]) } }
... ...
lib/noosfero/plugin.rb
... ... @@ -320,6 +320,30 @@ class Noosfero::Plugin
320 320 def comment_marked_as_ham(comment)
321 321 end
322 322  
  323 + # Adds extra actions for comments
  324 + # returns = list of hashes or lambda block that creates a list of hashes
  325 + # example:
  326 + #
  327 + # def comment_actions(comment)
  328 + # [{:link => link_to_function(...)}]
  329 + # end
  330 + #
  331 + def comment_actions(comment)
  332 + nil
  333 + end
  334 +
  335 + # This method is called when the user click on comment actions menu.
  336 + # returns = list or lambda block that return ids of enabled menu items for comments
  337 + # example:
  338 + #
  339 + # def check_comment_actions(comment)
  340 + # ['#action1', '#action2']
  341 + # end
  342 + #
  343 + def check_comment_actions(comment)
  344 + []
  345 + end
  346 +
323 347 # -> Adds fields to the signup form
324 348 # returns = lambda block that creates html code
325 349 def signup_extra_contents
... ...
plugins/require_auth_to_comment/lib/require_auth_to_comment_plugin.rb
... ... @@ -27,7 +27,7 @@ class RequireAuthToCommentPlugin &lt; Noosfero::Plugin
27 27 end
28 28  
29 29 def js_files
30   - ['hide_comment_form.js']
  30 + ['hide_comment_form.js', 'jquery.livequery.min.js']
31 31 end
32 32  
33 33 def body_beginning
... ...
plugins/require_auth_to_comment/public/hide_comment_form.js
1 1 (function($) {
2 2 $(window).bind('userDataLoaded', function(event, data) {
3   - if (data.login || $('meta[name="profile.allow_unauthenticated_comments"]').length > 0) {
4   - $('.post-comment-button').show();
5   - $('#page-comment-form').show();
6   - $('.comment-footer').show();
  3 + if (data.login || $('meta[name=profile.allow_unauthenticated_comments]').length > 0) {
  4 + $('.post-comment-button').livequery(function() {
  5 + $(this).show();
  6 + });
  7 + $('.page-comment-form').livequery(function() {
  8 + $(this).show();
  9 + });
  10 + $('.comment-footer').livequery(function() {
  11 + $(this).show();
  12 + });
7 13 }
8 14 });
9 15 })(jQuery);
... ...
plugins/require_auth_to_comment/public/jquery.livequery.min.js 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +/* Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
  2 + * Dual licensed under the MIT (MIT_LICENSE.txt)
  3 + * and GPL Version 2 (GPL_LICENSE.txt) licenses.
  4 + *
  5 + * Version: 1.1.1
  6 + * Requires jQuery 1.3+
  7 + * Docs: http://docs.jquery.com/Plugins/livequery
  8 + */
  9 +(function(a){a.extend(a.fn,{livequery:function(e,d,c){var b=this,f;if(a.isFunction(e)){c=d,d=e,e=undefined}a.each(a.livequery.queries,function(g,h){if(b.selector==h.selector&&b.context==h.context&&e==h.type&&(!d||d.$lqguid==h.fn.$lqguid)&&(!c||c.$lqguid==h.fn2.$lqguid)){return(f=h)&&false}});f=f||new a.livequery(this.selector,this.context,e,d,c);f.stopped=false;f.run();return this},expire:function(e,d,c){var b=this;if(a.isFunction(e)){c=d,d=e,e=undefined}a.each(a.livequery.queries,function(f,g){if(b.selector==g.selector&&b.context==g.context&&(!e||e==g.type)&&(!d||d.$lqguid==g.fn.$lqguid)&&(!c||c.$lqguid==g.fn2.$lqguid)&&!this.stopped){a.livequery.stop(g.id)}});return this}});a.livequery=function(b,d,f,e,c){this.selector=b;this.context=d;this.type=f;this.fn=e;this.fn2=c;this.elements=[];this.stopped=false;this.id=a.livequery.queries.push(this)-1;e.$lqguid=e.$lqguid||a.livequery.guid++;if(c){c.$lqguid=c.$lqguid||a.livequery.guid++}return this};a.livequery.prototype={stop:function(){var b=this;if(this.type){this.elements.unbind(this.type,this.fn)}else{if(this.fn2){this.elements.each(function(c,d){b.fn2.apply(d)})}}this.elements=[];this.stopped=true},run:function(){if(this.stopped){return}var d=this;var e=this.elements,c=a(this.selector,this.context),b=c.not(e);this.elements=c;if(this.type){b.bind(this.type,this.fn);if(e.length>0){a.each(e,function(f,g){if(a.inArray(g,c)<0){a.event.remove(g,d.type,d.fn)}})}}else{b.each(function(){d.fn.apply(this)});if(this.fn2&&e.length>0){a.each(e,function(f,g){if(a.inArray(g,c)<0){d.fn2.apply(g)}})}}}};a.extend(a.livequery,{guid:0,queries:[],queue:[],running:false,timeout:null,checkQueue:function(){if(a.livequery.running&&a.livequery.queue.length){var b=a.livequery.queue.length;while(b--){a.livequery.queries[a.livequery.queue.shift()].run()}}},pause:function(){a.livequery.running=false},play:function(){a.livequery.running=true;a.livequery.run()},registerPlugin:function(){a.each(arguments,function(c,d){if(!a.fn[d]){return}var b=a.fn[d];a.fn[d]=function(){var e=b.apply(this,arguments);a.livequery.run();return e}})},run:function(b){if(b!=undefined){if(a.inArray(b,a.livequery.queue)<0){a.livequery.queue.push(b)}}else{a.each(a.livequery.queries,function(c){if(a.inArray(c,a.livequery.queue)<0){a.livequery.queue.push(c)}})}if(a.livequery.timeout){clearTimeout(a.livequery.timeout)}a.livequery.timeout=setTimeout(a.livequery.checkQueue,20)},stop:function(b){if(b!=undefined){a.livequery.queries[b].stop()}else{a.each(a.livequery.queries,function(c){a.livequery.queries[c].stop()})}}});a.livequery.registerPlugin("append","prepend","after","before","wrap","attr","removeAttr","addClass","removeClass","toggleClass","empty","remove","html");a(function(){a.livequery.play()})})(jQuery);
0 10 \ No newline at end of file
... ...
plugins/require_auth_to_comment/public/style.css
1   -.post-comment-button, #page-comment-form, .comment-footer {
  1 +.post-comment-button, .page-comment-form, .comment-footer {
2 2 display: none;
3 3 }
... ...
public/designs/themes/base/style.css
... ... @@ -1016,7 +1016,7 @@ hr.pre-posts, hr.sep-posts {
1016 1016  
1017 1017 .post_comment_box.opened {
1018 1018 border: none;
1019   - background: #FFF;
  1019 + background: transparent;
1020 1020 }
1021 1021  
1022 1022  
... ...
public/javascripts/add-and-join.js
... ... @@ -100,4 +100,24 @@ jQuery(function($) {
100 100 clicked.parent().find(".send-an-email").fadeOut();
101 101 })
102 102 })
  103 +
  104 + $(".comment-trigger").live('click', function(){
  105 + clicked = $(this);
  106 + url = clicked.attr("url");
  107 + $.get(url, function(data){
  108 + ids = [];
  109 + if(data && data.ids) {
  110 + for(var i=0; i<data.ids.length; i++) {
  111 + clicked.parent().find(data.ids[i]).fadeIn();
  112 + ids.push(data.ids[i]);
  113 + }
  114 + }
  115 + clicked.parent().find('.comment-action-extra').each(function() {
  116 + if($.inArray('#'+$(this).attr('id'), ids))
  117 + $(this).fadeOut();
  118 + });
  119 + })
  120 + return false;
  121 + })
  122 +
103 123 });
... ...
public/javascripts/application.js
... ... @@ -79,6 +79,27 @@ function updateUrlField(name_field, id) {
79 79 }
80 80 }
81 81  
  82 +
  83 +
  84 +jQuery.fn.centerInForm = function () {
  85 + var $ = jQuery;
  86 + var form = $(this).parent('form');
  87 + this.css("position", "absolute");
  88 + this.css("top", (form.height() - this.height())/ 2 + form.scrollTop() + "px");
  89 + this.css("left", (form.width() - this.width()) / 2 + form.scrollLeft() + "px");
  90 + this.css("width", form.width() + "px");
  91 + this.css("height", form.height() + "px");
  92 + return this;
  93 +}
  94 +
  95 +jQuery.fn.center = function () {
  96 + var $ = jQuery;
  97 + this.css("position", "absolute");
  98 + this.css("top", ($(window).height() - this.height())/ 2 + $(window).scrollTop() + "px");
  99 + this.css("left", ($(window).width() - this.width()) / 2 + $(window).scrollLeft() + "px");
  100 + return this;
  101 +}
  102 +
82 103 function show_warning(field, message) {
83 104 new Effect.Highlight(field, {duration:3});
84 105 $(message).show();
... ... @@ -146,8 +167,9 @@ function loading_done(element_id) {
146 167 jQuery(element_id).removeClass('small-loading-dark');
147 168 }
148 169 function open_loading(message) {
149   - jQuery('body').append("<div id='overlay_loading' class='ui-widget-overlay' style='display: none'/><div id='overlay_loading_modal' style='display: none'><p>"+message+"</p><img src='/images/loading-dark.gif'/></div>");
  170 + jQuery('body').prepend("<div id='overlay_loading' class='ui-widget-overlay' style='display: none'/><div id='overlay_loading_modal' style='display: none'><p>"+message+"</p><img src='/images/loading-dark.gif'/></div>");
150 171 jQuery('#overlay_loading').show();
  172 + jQuery('#overlay_loading_modal').center();
151 173 jQuery('#overlay_loading_modal').fadeIn('slow');
152 174 }
153 175 function close_loading() {
... ... @@ -292,11 +314,17 @@ function toggleSubmenu(trigger, title, link_list) {
292 314 content.append('<h4>' + title + '</h4>');
293 315 jQuery.each(link_list, function(index, link_hash) {
294 316 for (label in link_hash) {
295   - options = "";
296   - jQuery.each(link_hash[label], function(option, value){
297   - options += option +'="'+ value + '" ';
298   - })
299   - list.append('<li><a '+ options +'>' + label + '</a></li>');
  317 + if(link_hash[label]!=null) {
  318 + if(label=='link' && jQuery.type(link_hash[label])=="string") {
  319 + list.append('<li>' + link_hash[label] + '</li>');
  320 + } else {
  321 + options = "";
  322 + jQuery.each(link_hash[label], function(option, value){
  323 + options += option +'="'+ value + '" ';
  324 + })
  325 + list.append('<li><a '+ options +'>' + label + '</a></li>');
  326 + }
  327 + }
300 328 }
301 329 });
302 330 content.append(list);
... ... @@ -320,9 +348,9 @@ function hideAllSubmenus() {
320 348 // Hide visible ballons when clicked outside them
321 349 jQuery(document).ready(function() {
322 350 jQuery('body').live('click', function() { hideAllSubmenus(); });
323   - jQuery('.menu-submenu-trigger').click(function(e) { e.stopPropagation(); });
324   - jQuery('.simplemenu-trigger').click(function(e) { e.stopPropagation(); });
325   - jQuery('#chat-online-users').click(function(e) { e.stopPropagation(); });
  351 + jQuery('.menu-submenu-trigger').live('click', function(e) { e.stopPropagation(); });
  352 + jQuery('.simplemenu-trigger').live('click', function(e) { e.stopPropagation(); });
  353 + jQuery('#chat-online-users').live('click', function(e) { e.stopPropagation(); });
326 354 });
327 355  
328 356 function input_javascript_ordering_stuff() {
... ... @@ -688,22 +716,51 @@ jQuery(function($) {
688 716 });
689 717  
690 718 function add_comment_reply_form(button, comment_id) {
691   - var container = jQuery(button).parents('.comment_reply');
  719 + //var container = jQuery(button).parents('.comment_reply');
  720 + var container = jQuery('#comment_reply_to_'+comment_id);
692 721 var f = container.find('.comment_form');
693 722 if (f.length == 0) {
694   - f = jQuery('#page-comment-form .comment_form').clone();
695   - f.find('.fieldWithErrors').map(function() { jQuery(this).replaceWith(jQuery(this).contents()); });
696   - f.prepend('<input type="hidden" name="comment[reply_of_id]" value="' + comment_id + '" />');
697   - container.append(f);
  723 + comments_div = jQuery(button).parents('.comments');
  724 + f = comments_div.find('.comment_form').first().clone();
  725 + f.find('.errorExplanation').remove();
  726 + f.append('<input type="hidden" name="comment[reply_of_id]" value="' + comment_id + '" />');
  727 + container.append('<div class="page-comment-form"></div>');
  728 + container.find('.page-comment-form').append(f);
698 729 }
699 730 if (container.hasClass('closed')) {
700 731 container.removeClass('closed');
701 732 container.addClass('opened');
702 733 container.find('.comment_form input[type=text]:visible:first').focus();
703 734 }
  735 + jQuery('.display-comment-form').hide();
704 736 return f;
705 737 }
706 738  
  739 +function update_comment_count(element, new_count) {
  740 + var $ = jQuery;
  741 + var content = '';
  742 + var parent_element = element.parent();
  743 +
  744 + write_out = parent_element.find('.comment-count-write-out');
  745 +
  746 + element.html(new_count);
  747 +
  748 + if(new_count == 0) {
  749 + content = NO_COMMENT_YET;
  750 + parent_element.addClass("no-comments-yet");
  751 + } else if(new_count == 1) {
  752 + parent_element.removeClass("no-comments-yet");
  753 + content = ONE_COMMENT;
  754 + } else {
  755 + content = new_count + ' ' + COMMENT_PLURAL;
  756 + }
  757 +
  758 + if(write_out){
  759 + write_out.html(content);
  760 + }
  761 +
  762 +}
  763 +
707 764 function remove_comment(button, url, msg) {
708 765 var $ = jQuery;
709 766 var $button = $(button);
... ... @@ -716,17 +773,27 @@ function remove_comment(button, url, msg) {
716 773 if (data.ok) {
717 774 var $comment = $button.closest('.article-comment');
718 775 var $replies = $comment.find('.comment-replies .article-comment');
719   - $comment.slideUp();
  776 +
  777 + var $comments_div = $button.closest('.comments');
  778 +
720 779 var comments_removed = 1;
721   - if ($button.hasClass('remove-children')) {
722   - comments_removed = 1 + $replies.size();
723   - } else {
724   - $replies.appendTo('.article-comments-list');
725   - }
726   - $('.comment-count').each(function() {
727   - var count = parseInt($(this).html());
728   - $(this).html(count - comments_removed);
  780 + $comment.slideUp(400, function() {
  781 + if ($button.hasClass('remove-children')) {
  782 + comments_removed = 1 + $replies.size();
  783 + } else {
  784 + $replies.appendTo('.article-comments-list');
  785 + }
  786 +
  787 + $comments_div.find('.comment-count').add('#article-header .comment-count').each(function() {
  788 + var count = parseInt($(this).html());
  789 + update_comment_count($(this), count - comments_removed);
  790 + });
  791 + $(this).remove();
729 792 });
  793 +
  794 + }else{
  795 + $button.removeClass('comment-button-loading');
  796 + return;
730 797 }
731 798 });
732 799 }
... ...
public/javascripts/comment_form.js
  1 +jQuery('.display-comment-form').unbind();
1 2 jQuery('.display-comment-form').click(function(){
2   - toggleBox('.post_comment_box');
  3 + var $button = jQuery(this);
  4 + toggleBox($button.parents('.post_comment_box'));
3 5 jQuery('.display-comment-form').hide();
4   - jQuery('form.comment_form input').first().focus();
  6 + $button.closest('.page-comment-form').find('input').first().focus();
5 7 return false;
6 8 });
7 9  
8   -jQuery('#cancel-comment').click(function(){
9   - toggleBox('.post_comment_box');
10   - jQuery('.display-comment-form').show();
11   - return false
12   -})
  10 +jQuery('#cancel-comment').die();
  11 +jQuery('#cancel-comment').live("click", function(){
  12 + var $button = jQuery(this);
  13 + toggleBox($button.parents('.post_comment_box'));
  14 + show_display_comment_button();
  15 + var page_comment_form = $button.parents('.page-comment-form');
  16 + page_comment_form.find('.errorExplanation').remove();
  17 + return false;
  18 +});
13 19  
14   -function toggleBox(div_selector){
15   - div = jQuery(div_selector);
  20 +function toggleBox(div){
16 21 if(div.hasClass('opened')) {
17 22 div.removeClass('opened');
18 23 div.addClass('closed');
... ... @@ -21,3 +26,67 @@ function toggleBox(div_selector){
21 26 div.addClass('opened');
22 27 }
23 28 }
  29 +
  30 +function save_comment(button) {
  31 + var $ = jQuery;
  32 + open_loading(DEFAULT_LOADING_MESSAGE);
  33 + var $button = $(button);
  34 + var form = $button.parents("form");
  35 + var post_comment_box = $button.parents('.post_comment_box');
  36 + var comment_div = $button.parents('.comments');
  37 + var page_comment_form = $button.parents('.page-comment-form');
  38 + $button.addClass('comment-button-loading');
  39 + $.post(form.attr("action"), form.serialize(), function(data) {
  40 +
  41 + if(data.render_target == null) {
  42 + //Comment for approval
  43 + form.find("input[type='text']").add('textarea').each(function() {
  44 + this.value = '';
  45 + });
  46 + page_comment_form.find('.errorExplanation').remove();
  47 + } else if(data.render_target == 'form') {
  48 + //Comment with errors
  49 + $.scrollTo(page_comment_form);
  50 + page_comment_form.html(data.html);
  51 + $('.display-comment-form').hide();
  52 + } else if($('#' + data.render_target).size() > 0) {
  53 + //Comment of reply
  54 + $('#'+ data.render_target).replaceWith(data.html);
  55 + $('#' + data.render_target).effect("highlight", {}, 3000);
  56 + $.colorbox.close();
  57 + } else {
  58 + //New comment of article
  59 + comment_div.find('.article-comments-list').append(data.html);
  60 +
  61 + form.find("input[type='text']").add('textarea').each(function() {
  62 + this.value = '';
  63 + });
  64 +
  65 + page_comment_form.find('.errorExplanation').remove();
  66 + $.colorbox.close();
  67 + }
  68 +
  69 + comment_div.find('.comment-count').add('#article-header .comment-count').each(function() {
  70 + var count = parseInt($(this).html());
  71 + update_comment_count($(this), count + 1);
  72 + });
  73 +
  74 + if(jQuery('#recaptcha_response_field').val()){
  75 + Recaptcha.reload();
  76 + }
  77 +
  78 + if(data.msg != null) {
  79 + display_notice(data.msg);
  80 + }
  81 + close_loading();
  82 + toggleBox($button.closest('.post_comment_box'));
  83 + show_display_comment_button();
  84 + $button.removeClass('comment-button-loading');
  85 + $button.enable();
  86 + }, 'json');
  87 +}
  88 +
  89 +function show_display_comment_button() {
  90 + if(jQuery('.post_comment_box.opened').length==0)
  91 + jQuery('.display-comment-form').show();
  92 +}
... ...
public/stylesheets/application.css
... ... @@ -528,11 +528,15 @@ code input {
528 528 background: transparent url(../images/loading-small-dark.gif) no-repeat 10% center;
529 529 }
530 530 #overlay_loading {
531   - z-index: 100;
532   - cursor: progress;
  531 + z-index: 10000;
  532 + top: 0;
  533 + left: 0;
  534 + position: fixed;
  535 + width: 100%;
  536 + height: 100%;
533 537 }
534 538 #overlay_loading_modal {
535   - z-index: 101;
  539 + z-index: 10001;
536 540 width: 160px;
537 541 height: 120px;
538 542 border: 1px solid #000;
... ... @@ -998,6 +1002,9 @@ code input {
998 1002  
999 1003 .comments {
1000 1004 }
  1005 +span.comment-count.hide{
  1006 + display: none;
  1007 +}
1001 1008 #content .no-comments-yet {
1002 1009 text-align: center;
1003 1010 font-size: 80%;
... ... @@ -1018,7 +1025,7 @@ code input {
1018 1025 margin-bottom: 10px;
1019 1026 padding: 4px;
1020 1027 }
1021   -#article .article-comment h4 {
  1028 +#article .article-comment .comment-details h4 {
1022 1029 font-size: 13px;
1023 1030 margin: 0px;
1024 1031 display: inline;
... ... @@ -1291,6 +1298,10 @@ a.comment-picture {
1291 1298 -webkit-border-radius: 4px;
1292 1299 border-radius: 4px;
1293 1300 }
  1301 +.post_comment_box.opened h4 {
  1302 + border: none;
  1303 + cursor: default;
  1304 +}
1294 1305 .post_comment_box.opened {
1295 1306 border: 1px solid #888;
1296 1307 background: #eee;
... ... @@ -1341,6 +1352,11 @@ a.comment-picture {
1341 1352 .post_comment_box.comment_reply #comment_title {
1342 1353 width: 100%;
1343 1354 }
  1355 +
  1356 +#page-comment-form-template {
  1357 + display:none;
  1358 +}
  1359 +
1344 1360 #page-comment-form .post_comment_box {
1345 1361 text-align: left;
1346 1362 padding-left: 0;
... ... @@ -1656,13 +1672,15 @@ a.button.disabled, input.disabled {
1656 1672 * Block options editor floating window *
1657 1673 ****************************************/
1658 1674  
  1675 +/* FIXME This changes broke colorboxes all over the place.
  1676 + * Therefore I'm canceling them until they are properly treateda. */
1659 1677 #cboxLoadedContent {
1660 1678 background: #FFF;
1661   - box-shadow: 0 0 15px #888 inset;
1662   - border-radius: 5px;
1663   - border: 1px solid #777;
1664   - border-left: none;
1665   - border-right: none;
  1679 +/* box-shadow: 0 0 15px #888 inset;
  1680 + border-radius: 5px;
  1681 + border: 1px solid #777;
  1682 + border-left: none;
  1683 + border-right: none; */
1666 1684 }
1667 1685  
1668 1686 .block-config-options {
... ... @@ -1786,13 +1804,32 @@ a.button.disabled, input.disabled {
1786 1804 #content .profile-list-block ul {
1787 1805 width: 200px;
1788 1806 }
1789   -#content .common-profile-list-block li {
1790   - margin: 0px;
  1807 +#content .comment-header .comment-actions-reply {
  1808 + float: right;
  1809 + background-image: url(/designs/icons/tango/Tango/16x16/actions/go-jump.png);
  1810 + height: 12px;
  1811 +}
  1812 +#content .comment-header ul {
  1813 + float: right;
  1814 + margin: 1px 0px 14px 0px;
  1815 +}
  1816 +#content .comment-actions .menu-submenu-header, #content .comment-actions .menu-submenu-footer, #content .comment-actions .menu-submenu h4 {
  1817 + display: none;
  1818 +}
  1819 +#content .comment-actions .menu-submenu ul {
  1820 + border: 1px solid #888a85;
  1821 + background-color: #efefef;
  1822 + padding-right: 2px;
  1823 + height: auto;
  1824 + display: block;
  1825 +}
  1826 +#content .common-profile-list-block li, #content .comment-actions li {
  1827 + margin: 0px !important;
1791 1828 padding: 0px;
1792 1829 list-style: none;
1793 1830 position: relative;
1794 1831 }
1795   -.common-profile-list-block .vcard a {
  1832 +.common-profile-list-block .vcard a, .comment-actions .vcard a {
1796 1833 display: block;
1797 1834 height: 112px;
1798 1835 max-height: 112px;
... ... @@ -1803,7 +1840,7 @@ a.button.disabled, input.disabled {
1803 1840 text-align: center;
1804 1841 overflow: hidden;
1805 1842 font-size: 11px;
1806   - text-decoration: none;
  1843 + text-decoration: none !important;
1807 1844 color: #000;
1808 1845 position: relative;
1809 1846 cursor: pointer; /* work arround bug for MSIE */
... ... @@ -4559,11 +4596,17 @@ h1#agenda-title {
4559 4596 }
4560 4597 /* Profile balloon */
4561 4598  
4562   -.common-profile-list-block .vcard {
  4599 +.common-profile-list-block .vcard, .comment-actions .vcard {
4563 4600 position: relative !important;
4564 4601 float: left;
4565 4602 }
4566   -.common-profile-list-block .vcard .menu-submenu-trigger, .menu-submenu-trigger {
  4603 +#content .comment-actions .vcard {
  4604 + padding-right: 20px;
  4605 +}
  4606 +#content .comment-actions .vcard .menu-submenu-trigger {
  4607 + display: block;
  4608 +}
  4609 +.common-profile-list-block .vcard .menu-submenu-trigger, .menu-submenu-trigger, .comment-actions .vcard .menu-submenu-trigger {
4567 4610 display: none;
4568 4611 width: 16px;
4569 4612 height: 16px;
... ... @@ -4577,7 +4620,7 @@ h1#agenda-title {
4577 4620 -moz-border-radius: 5px;
4578 4621 -webkit-border-radius: 5px;
4579 4622 }
4580   -.common-profile-list-block .vcard .menu-submenu-trigger:hover, .menu-submenu-trigger:hover {
  4623 +.common-profile-list-block .vcard .menu-submenu-trigger:hover, .menu-submenu-trigger:hover, .comment-actions .vcard .menu-submenu-trigger:hover {
4581 4624 background: #fff url(/images/top-arrow.png) center center no-repeat;
4582 4625 border: 1px solid #ccc;
4583 4626 }
... ... @@ -4596,6 +4639,10 @@ h1#agenda-title {
4596 4639 padding: 0;
4597 4640 text-align: left;
4598 4641 }
  4642 +.comment-details .menu-submenu {
  4643 + bottom: 0px;
  4644 + right: -134px;
  4645 +}
4599 4646 .box-2 .menu-submenu, .box-3 .menu-submenu {
4600 4647 bottom: 78px;
4601 4648 right: -44px;
... ... @@ -4630,7 +4677,7 @@ h1#agenda-title {
4630 4677 .msie7 #search-results .menu-submenu-trigger {
4631 4678 width: 20px !important;
4632 4679 }
4633   -.common-profile-list-block .vcard .menu-submenu a {
  4680 +.common-profile-list-block .vcard .menu-submenu a, .comment-actions .vcard .menu-submenu a {
4634 4681 float: none;
4635 4682 display: block;
4636 4683 height: auto;
... ...
test/functional/comment_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,562 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +require 'comment_controller'
  3 +
  4 +# Re-raise errors caught by the controller.
  5 +class CommentController; def rescue_action(e) raise e end; end
  6 +
  7 +class CommentControllerTest < ActionController::TestCase
  8 +
  9 + def setup
  10 + @controller = CommentController.new
  11 + @request = ActionController::TestRequest.new
  12 + @response = ActionController::TestResponse.new
  13 +
  14 + @profile = create_user('testinguser').person
  15 + @environment = @profile.environment
  16 + end
  17 + attr_reader :profile, :environment
  18 +
  19 + should "not be able to remove other people's comments if not moderator or admin" do
  20 + create_user('normaluser')
  21 + profile = create_user('testuser').person
  22 + article = profile.articles.build(:name => 'test')
  23 + article.save!
  24 +
  25 + commenter = create_user('otheruser').person
  26 + comment = fast_create(Comment, :source_id => article, :title => 'a comment', :body => 'lalala')
  27 +
  28 + login_as 'normaluser' # normaluser cannot remove other people's comments
  29 + assert_no_difference Comment, :count do
  30 + post :destroy, :profile => profile.identifier, :id => comment.id
  31 + end
  32 + end
  33 +
  34 + should "not be able to remove other people's comments if not moderator or admin and return json if is an ajax request" do
  35 + create_user('normaluser')
  36 + profile = create_user('testuser').person
  37 + article = profile.articles.build(:name => 'test')
  38 + article.save!
  39 +
  40 + commenter = create_user('otheruser').person
  41 + comment = fast_create(Comment, :source_id => article, :author_id => commenter, :title => 'a comment', :body => 'lalala')
  42 +
  43 + login_as 'normaluser' # normaluser cannot remove other people's comments
  44 + assert_no_difference Comment, :count do
  45 + xhr :post, :destroy, :profile => profile.identifier, :id => comment.id
  46 + assert_response :success
  47 + end
  48 + assert_match /\{\"ok\":false\}/, @response.body
  49 + end
  50 +
  51 + should 'be able to remove comments on their articles' do
  52 + profile = create_user('testuser').person
  53 + article = profile.articles.build(:name => 'test')
  54 + article.save!
  55 +
  56 + commenter = create_user('otheruser').person
  57 + comment = fast_create(Comment, :source_id => article, :author_id => commenter, :title => 'a comment', :body => 'lalala')
  58 +
  59 + login_as 'testuser' # testuser must be able to remove comments in his articles
  60 + assert_difference Comment, :count, -1 do
  61 + xhr :post, :destroy, :profile => profile.identifier, :id => comment.id
  62 + assert_response :success
  63 + end
  64 + assert_match /\{\"ok\":true\}/, @response.body
  65 + end
  66 +
  67 + should 'be able to remove comments of their images' do
  68 + profile = create_user('testuser').person
  69 +
  70 + image = UploadedFile.create!(:profile => profile, :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  71 + image.save!
  72 +
  73 + commenter = create_user('otheruser').person
  74 + comment = fast_create(Comment, :source_id => image, :author_id => commenter, :title => 'a comment', :body => 'lalala')
  75 +
  76 + login_as 'testuser' # testuser must be able to remove comments in his articles
  77 + assert_difference Comment, :count, -1 do
  78 + xhr :post, :destroy, :profile => profile.identifier, :id => comment.id
  79 + assert_response :success
  80 + end
  81 + end
  82 +
  83 + should 'be able to remove comments if is moderator' do
  84 + commenter = create_user('commenter_user').person
  85 + community = Community.create!(:name => 'Community test', :identifier => 'community-test')
  86 + article = community.articles.create!(:name => 'test', :profile => community)
  87 + comment = fast_create(Comment, :source_id => article, :author_id => commenter, :title => 'a comment', :body => 'lalala')
  88 + community.add_moderator(profile)
  89 + login_as profile.identifier
  90 + assert_difference Comment, :count, -1 do
  91 + xhr :post, :destroy, :profile => community.identifier, :id => comment.id
  92 + assert_response :success
  93 + end
  94 + assert_match /\{\"ok\":true\}/, @response.body
  95 + end
  96 +
  97 + should 'be able to remove comment' do
  98 + profile = create_user('testuser').person
  99 + article = profile.articles.build(:name => 'test')
  100 + article.save!
  101 + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala')
  102 +
  103 + login_as 'testuser'
  104 + assert_difference Comment, :count, -1 do
  105 + xhr :post, :destroy, :profile => profile.identifier, :id => comment.id
  106 + assert_response :success
  107 + end
  108 + end
  109 +
  110 + should 'display not found page if a user should try to make a cross comment' do
  111 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  112 +
  113 + other_person = create_user('otheruser').person
  114 + other_page = other_person.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  115 +
  116 + assert_no_difference Comment, :count do
  117 + xhr :post, :create, :profile => profile.identifier, :id => other_page.id, :comment => { :title => 'crap!', :body => 'I think that this article is crap' }
  118 + end
  119 + assert_match /not found/, @response.body
  120 + end
  121 +
  122 + should 'not be able to post comment if article do not accept it' do
  123 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text', :accept_comments => false)
  124 +
  125 + assert_no_difference Comment, :count do
  126 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => { :title => 'crap!', :body => 'I think that this article is crap' }
  127 + end
  128 + assert_match /Comment not allowed in this article/, @response.body
  129 + end
  130 +
  131 + should "the author's comment be the logged user" do
  132 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  133 +
  134 + login_as profile.identifier
  135 +
  136 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => { :title => 'crap!', :body => 'I think that this article is crap' }
  137 + assert_equal profile, assigns(:comment).author
  138 + end
  139 +
  140 + should "the articles's comment be the article passed as parameter" do
  141 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  142 +
  143 + login_as profile.identifier
  144 +
  145 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => { :title => 'crap!', :body => 'I think that this article is crap' }
  146 + assert_equal page, assigns(:comment).article
  147 + end
  148 +
  149 + should 'show comment form opened on error' do
  150 + login_as profile.identifier
  151 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  152 + xhr :post, :create, :profile => @profile.identifier, :id => page.id, :comment => { :title => '', :body => '' }, :confirm => 'true'
  153 + response = JSON.parse @response.body
  154 + assert_match /<div class=\"post_comment_box opened\"/, response["html"]
  155 + end
  156 +
  157 + should 'show validation error when body comment is missing' do
  158 + login_as @profile.identifier
  159 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  160 + xhr :post, :create, :profile => @profile.identifier, :id => page.id, :comment => { :title => '', :body => '' }, :confirm => 'true'
  161 + response = JSON.parse @response.body
  162 + assert_match /errorExplanation/, response["html"]
  163 + end
  164 +
  165 + should 'not save a comment if a plugin rejects it' do
  166 + class TestFilterPlugin < Noosfero::Plugin
  167 + def filter_comment(c)
  168 + c.reject!
  169 + end
  170 + end
  171 + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([TestFilterPlugin.new])
  172 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  173 + assert_no_difference Comment, :count do
  174 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => { :title => 'title', :body => 'body', :name => "Spammer", :email => 'damn@spammer.com' }, :confirm => 'true'
  175 + end
  176 + end
  177 +
  178 + should 'display a message if a plugin reject the comment' do
  179 + class TestFilterPlugin < Noosfero::Plugin
  180 + def filter_comment(c)
  181 + c.reject!
  182 + end
  183 + end
  184 + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([TestFilterPlugin.new])
  185 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  186 + assert_no_difference Comment, :count do
  187 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => { :title => 'title', :body => 'body', :name => "Spammer", :email => 'damn@spammer.com' }, :confirm => 'true'
  188 + end
  189 +
  190 + assert_match /rejected/, @response.body
  191 + end
  192 +
  193 + should 'store IP address, user agent and referrer for comments' do
  194 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  195 + @request.stubs(:remote_ip).returns('33.44.55.66')
  196 + @request.stubs(:referrer).returns('http://example.com')
  197 + @request.stubs(:user_agent).returns('MyBrowser')
  198 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => { :title => 'title', :body => 'body', :name => "Spammer", :email => 'damn@spammer.com' }, :confirm => 'true'
  199 + comment = Comment.last
  200 + assert_equal '33.44.55.66', comment.ip_address
  201 + assert_equal 'MyBrowser', comment.user_agent
  202 + assert_equal 'http://example.com', comment.referrer
  203 + end
  204 +
  205 + should 'invalid comment display the comment form open' do
  206 + article = profile.articles.build(:name => 'test')
  207 + article.save!
  208 + login_as('testinguser')
  209 +
  210 + assert_no_difference Comment, :count do
  211 + xhr :post, :create, :profile => profile.identifier, :id =>article.id, :comment => {:body => ""}, :confirm => 'true'
  212 + end
  213 + assert_match /post_comment_box opened/, @response.body
  214 + end
  215 +
  216 + should 'invalid captcha display the comment form open' do
  217 + article = profile.articles.build(:name => 'test')
  218 + article.save!
  219 + login_as('testinguser')
  220 + @controller.stubs(:verify_recaptcha).returns(false)
  221 +
  222 + environment.enable('captcha_for_logged_users')
  223 + environment.save!
  224 +
  225 + xhr :post, :create, :profile => profile.identifier, :id =>article.id, :comment => {:body => "Some comment...", :author => profile}, :confirm => 'true'
  226 + assert_match /post_comment_box opened/, @response.body
  227 + end
  228 +
  229 + should 'ask for captcha if environment defines even with logged user' do
  230 + article = profile.articles.build(:name => 'test')
  231 + article.save!
  232 + login_as('testinguser')
  233 + @controller.stubs(:verify_recaptcha).returns(false)
  234 +
  235 + assert_difference Comment, :count, 1 do
  236 + xhr :post, :create, :profile => profile.identifier, :id => article.id, :comment => {:body => "Some comment...", :author => profile}, :confirm => 'true'
  237 + end
  238 +
  239 + environment.enable('captcha_for_logged_users')
  240 + environment.save!
  241 +
  242 + assert_no_difference Comment, :count do
  243 + xhr :post, :create, :profile => profile.identifier, :id =>article.id, :comment => {:body => "Some comment...", :author => profile}, :confirm => 'true'
  244 + end
  245 + assert_not_nil assigns(:comment)
  246 + end
  247 +
  248 + should 'ask for captcha if user not logged' do
  249 + article = profile.articles.build(:name => 'test')
  250 + article.save!
  251 +
  252 + @controller.stubs(:verify_recaptcha).returns(false)
  253 + assert_no_difference Comment, :count do
  254 + xhr :post, :create, :profile => profile.identifier, :id => article.id, :comment => {:body => "Some comment...", :author => profile}, :confirm => 'true'
  255 + end
  256 +
  257 + @controller.stubs(:verify_recaptcha).returns(true)
  258 + assert_difference Comment, :count, 1 do
  259 + xhr :post, :create, :profile => profile.identifier, :id => article.id, :comment => {:body => "Some comment...", :author => profile}, :confirm => 'true'
  260 + end
  261 + end
  262 +
  263 + should 'create ApproveComment task when adding a comment in a moderated article' do
  264 + login_as @profile.identifier
  265 + community = Community.create!(:name => 'testcomm')
  266 + page = community.articles.create!(:name => 'myarticle', :moderate_comments => true)
  267 +
  268 + commenter = create_user('otheruser').person
  269 + assert_difference ApproveComment, :count, 1 do
  270 + xhr :post, :create, :profile => community.identifier, :id => page.id, :comment => {:body => 'Some comment...', :author => commenter}, :confirm => 'true'
  271 + end
  272 + end
  273 +
  274 + should 'not create ApproveComment task when the comment author is the same of article author' do
  275 + login_as @profile.identifier
  276 + community = Community.create!(:name => 'testcomm')
  277 + page = community.articles.create!(:name => 'myarticle', :moderate_comments => true, :last_changed_by => @profile)
  278 + community.add_moderator(@profile)
  279 +
  280 + assert_no_difference ApproveComment, :count do
  281 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => {:body => 'Some comment...'}, :confirm => 'true'
  282 + end
  283 + end
  284 +
  285 + should 'create ApproveComment task with the comment author as requestor' do
  286 + community = Community.create!(:name => 'testcomm')
  287 + page = community.articles.create!(:name => 'myarticle', :moderate_comments => true)
  288 +
  289 + commenter = create_user('otheruser').person
  290 + assert_difference ApproveComment, :count, 1 do
  291 + xhr :post, :create, :profile => community.identifier, :id => page.id, :comment => {:body => 'Some comment...', :author => commenter}, :confirm => 'true'
  292 + end
  293 + task = Task.last
  294 + assert_equal commenter, task.requestor
  295 +
  296 + end
  297 +
  298 + should "create ApproveComment task with the articles's owner profile as the target" do
  299 + login_as @profile.identifier
  300 + community = Community.create!(:name => 'testcomm')
  301 + page = community.articles.create!(:name => 'myarticle', :moderate_comments => true)
  302 +
  303 + commenter = create_user('otheruser').person
  304 + assert_difference ApproveComment, :count, 1 do
  305 + xhr :post, :create, :profile => community.identifier, :id => page.id, :comment => {:body => 'Some comment...', :author => commenter}, :confirm => 'true'
  306 + end
  307 + task = Task.last
  308 + assert_equal community, task.target
  309 + end
  310 +
  311 + should "create ApproveComment task with the comment created_at attribute defined to now" do
  312 + login_as @profile.identifier
  313 + community = Community.create!(:name => 'testcomm')
  314 + page = community.articles.create!(:name => 'myarticle', :moderate_comments => true)
  315 +
  316 + now = Time.now
  317 + Time.stubs(:now).returns(now)
  318 + xhr :post, :create, :profile => community.identifier, :id => page.id, :comment => {:body => 'Some comment...'}, :confirm => 'true'
  319 + task = Task.last
  320 + assert_equal now.to_s, task.comment.created_at.to_s
  321 + end
  322 +
  323 + should "render_target be nil in article with moderation" do
  324 + page = profile.articles.create!(:name => 'myarticle', :moderate_comments => true)
  325 +
  326 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => {:body => 'Some comment...', :name => 'some name', :email => 'some@test.com.br'}, :confirm => 'true'
  327 + assert_nil ActiveSupport::JSON.decode(@response.body)['render_target']
  328 + end
  329 +
  330 + should "display message 'waitting for approval' of comments in article with moderation" do
  331 + page = profile.articles.create!(:name => 'myarticle', :moderate_comments => true)
  332 +
  333 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => {:body => 'Some comment...', :name => 'some name', :email => 'some@test.com.br'}, :confirm => 'true'
  334 + assert_match /waiting for approval/, @response.body
  335 + end
  336 +
  337 + should "render_target be the comment anchor if everithing is fine" do
  338 + login_as profile.identifier
  339 + page = profile.articles.create!(:name => 'myarticle')
  340 +
  341 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => {:body => 'Some comment...'}, :confirm => 'true'
  342 + assert_match /#{Comment.last.id}/, ActiveSupport::JSON.decode(@response.body)['render_target']
  343 + end
  344 +
  345 + should "display message 'successfully created' if the comment was saved with success" do
  346 + login_as profile.identifier
  347 + page = profile.articles.create!(:name => 'myarticle')
  348 +
  349 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => {:body => 'Some comment...'}, :confirm => 'true'
  350 + assert_match /successfully created/, @response.body
  351 + end
  352 +
  353 + should "render partial comment if everithing is fine" do
  354 + login_as profile.identifier
  355 + page = profile.articles.create!(:name => 'myarticle')
  356 +
  357 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => {:body => 'Some comment...'}, :confirm => 'true'
  358 + assert_match /id="#{Comment.last.anchor}" class="article-comment"/, ActiveSupport::JSON.decode(@response.body)['html']
  359 + end
  360 +
  361 + should "render the root comment when a reply is made" do
  362 + login_as profile.identifier
  363 + page = profile.articles.create!(:name => 'myarticle')
  364 +
  365 + comment = fast_create(Comment, :body => 'some content', :source_id => page.id, :source_type => 'Article')
  366 +
  367 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => {:body => 'Some comment...', :reply_of_id => comment.id}, :confirm => 'true'
  368 + assert_match /id="#{comment.anchor}" class="article-comment"/, ActiveSupport::JSON.decode(@response.body)['html']
  369 + end
  370 +
  371 + should 'filter html content from body' do
  372 + login_as @profile.identifier
  373 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  374 +
  375 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => { :title => 'html comment', :body => "this is a <strong id='html_test_comment'>html comment</strong>"}
  376 +
  377 + assert Comment.last.body.match(/this is a html comment/)
  378 + assert_no_tag :tag => 'strong', :attributes => { :id => 'html_test_comment' }
  379 + end
  380 +
  381 + should 'filter html content from title' do
  382 + login_as @profile.identifier
  383 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  384 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => { :title => "html <strong id='html_test_comment'>comment</strong>", :body => "this is a comment"}
  385 + assert Comment.last.title.match(/html comment/)
  386 + assert_no_tag :tag => 'strong', :attributes => { :id => 'html_test_comment' }
  387 + end
  388 +
  389 + should 'touch article after adding a comment' do
  390 + yesterday = Time.now.yesterday
  391 + Article.record_timestamps = false
  392 + page = profile.articles.create(:name => 'myarticle', :body => 'the body of the text', :created_at => yesterday, :updated_at => yesterday)
  393 + Article.record_timestamps = true
  394 +
  395 + login_as('ze')
  396 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => { :title => 'crap!', :body => 'I think that this article is crap' }, :confirm => 'true'
  397 + assert_not_equal yesterday, page.reload.updated_at
  398 + end
  399 +
  400 + should 'be able to mark comments as spam' do
  401 + login_as profile.identifier
  402 + article = fast_create(Article, :profile_id => profile.id)
  403 + spam = fast_create(Comment, :name => 'foo', :email => 'foo@example.com', :source_id => article.id, :source_type => 'Article')
  404 +
  405 + xhr :post, :mark_as_spam, :profile => profile.identifier, :id => spam.id
  406 +
  407 + spam.reload
  408 + assert spam.spam?
  409 + end
  410 +
  411 + should "not be able to mark as spam other people's comments if not moderator or admin" do
  412 + create_user('normaluser')
  413 + profile = create_user('testuser').person
  414 + article = profile.articles.build(:name => 'test')
  415 + article.save!
  416 +
  417 + commenter = create_user('otheruser').person
  418 + comment = fast_create(Comment, :source_id => article, :title => 'a comment', :body => 'lalala')
  419 +
  420 + login_as 'normaluser' # normaluser cannot remove other people's comments
  421 + xhr :post, :mark_as_spam, :profile => profile.identifier, :id => comment.id
  422 + comment.reload
  423 + assert !comment.spam?
  424 + end
  425 +
  426 + should "not be able to mark as spam other people's comments if not moderator or admin and return json if is an ajax request" do
  427 + create_user('normaluser')
  428 + profile = create_user('testuser').person
  429 + article = profile.articles.build(:name => 'test')
  430 + article.save!
  431 +
  432 + commenter = create_user('otheruser').person
  433 + comment = fast_create(Comment, :source_id => article, :author_id => commenter, :title => 'a comment', :body => 'lalala')
  434 +
  435 + login_as 'normaluser' # normaluser cannot remove other people's comments
  436 +
  437 + xhr :post, :mark_as_spam, :profile => profile.identifier, :id => comment.id
  438 + assert_response :success
  439 + comment.reload
  440 + assert !comment.spam?
  441 + assert_match /\{\"ok\":false\}/, @response.body
  442 + end
  443 +
  444 + should 'be able to mark as spam comments on their articles' do
  445 + profile = create_user('testuser').person
  446 + article = profile.articles.build(:name => 'test')
  447 + article.save!
  448 +
  449 + commenter = create_user('otheruser').person
  450 + comment = fast_create(Comment, :source_id => article, :author_id => commenter, :title => 'a comment', :body => 'lalala')
  451 +
  452 + login_as 'testuser' # testuser must be able to remove comments in his articles
  453 +
  454 + xhr :post, :mark_as_spam, :profile => profile.identifier, :id => comment.id
  455 + assert_response :success
  456 + comment.reload
  457 + assert comment.spam?
  458 +
  459 + assert_match /\{\"ok\":true\}/, @response.body
  460 + end
  461 +
  462 + should 'be able to mark comments as spam if is moderator' do
  463 + commenter = create_user('commenter_user').person
  464 + community = Community.create!(:name => 'Community test', :identifier => 'community-test')
  465 + article = community.articles.create!(:name => 'test', :profile => community)
  466 + comment = fast_create(Comment, :source_id => article, :author_id => commenter, :title => 'a comment', :body => 'lalala')
  467 + community.add_moderator(profile)
  468 + login_as profile.identifier
  469 +
  470 + xhr :post, :mark_as_spam, :profile => community.identifier, :id => comment.id
  471 + assert_response :success
  472 + comment.reload
  473 + assert comment.spam?
  474 + assert_match /\{\"ok\":true\}/, @response.body
  475 + end
  476 +
  477 + should 'edit comment from a page' do
  478 + login_as profile.identifier
  479 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  480 + comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article', :author_id => profile.id)
  481 +
  482 + get :edit, :id => comment.id, :profile => profile.identifier, :comment => { :body => 'Comment edited' }
  483 + assert_tag :tag => 'textarea', :attributes => {:id => 'comment_body'}, :content => 'Original comment'
  484 + end
  485 +
  486 + should 'not crash on edit comment if comment does not exist' do
  487 + login_as profile.identifier
  488 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  489 +
  490 + get :edit, :id => 1000, :profile => profile.identifier, :comment => { :body => 'Comment edited' }
  491 + assert_response 404
  492 + end
  493 +
  494 + should 'not be able to edit comment not logged' do
  495 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  496 + comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article')
  497 +
  498 + get :edit, :id => comment.id, :profile => profile.identifier, :comment => { :body => 'Comment edited' }
  499 + assert_response 404
  500 + end
  501 +
  502 + should 'not be able to edit comment if does not have the permission to' do
  503 + user = create_user('any_guy').person
  504 + login_as user.identifier
  505 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  506 + comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article')
  507 +
  508 + get :edit, :id => comment.id, :profile => profile.identifier, :comment => { :body => 'Comment edited' }
  509 + assert_response 404
  510 + end
  511 +
  512 + should 'be able to update a comment' do
  513 + login_as profile.identifier
  514 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text', :accept_comments => false)
  515 + comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article', :author_id => profile)
  516 +
  517 + xhr :post, :update, :id => comment.id, :profile => profile.identifier, :comment => { :body => 'Comment edited' }
  518 + assert JSON.parse(@response.body)["ok"], "attribute ok expected to be true"
  519 + assert_equal 'Comment edited', Comment.find(comment.id).body
  520 + end
  521 +
  522 + should 'not crash on update comment if comment does not exist' do
  523 + login_as profile.identifier
  524 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  525 +
  526 + xhr :post, :update, :id => 1000, :profile => profile.identifier, :comment => { :body => 'Comment edited' }
  527 + assert_response 404
  528 + end
  529 +
  530 + should 'not be able to update comment not logged' do
  531 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  532 + comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article')
  533 +
  534 + xhr :post, :update, :id => comment.id, :profile => profile.identifier, :comment => { :body => 'Comment edited' }
  535 + assert_response 404
  536 + end
  537 +
  538 + should 'not be able to update comment if does not have the permission to' do
  539 + user = create_user('any_guy').person
  540 + login_as user.identifier
  541 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  542 + comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article')
  543 +
  544 + xhr :post, :update, :id => comment.id, :profile => profile.identifier, :comment => { :body => 'Comment edited' }
  545 + assert_response 404
  546 + end
  547 +
  548 + should 'returns ids of menu items that has to be displayed' do
  549 + class TestActionPlugin < Noosfero::Plugin
  550 + def check_comment_actions(c)
  551 + ['action1', 'action2']
  552 + end
  553 + end
  554 + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([TestActionPlugin.new])
  555 + login_as profile.identifier
  556 + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
  557 + comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article')
  558 + xhr :post, :check_actions, :profile => profile.identifier, :id => comment.id
  559 + assert_match /\{\"ids\":\[\"action1\",\"action2\"\]\}/, @response.body
  560 + end
  561 +
  562 +end
... ...
test/functional/content_viewer_controller_test.rb
... ... @@ -83,149 +83,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
83 83 assert_equal feed.data, @response.body
84 84 end
85 85  
86   - should 'display remove comment button' do
87   - profile = create_user('testuser').person
88   - article = profile.articles.build(:name => 'test')
89   - article.save!
90   - comment = article.comments.build(:author => profile, :title => 'a comment', :body => 'lalala')
91   - comment.save!
92   -
93   - login_as 'testuser'
94   - get :view_page, :profile => 'testuser', :page => [ 'test' ]
95   - assert_tag :tag => 'a', :attributes => { :onclick => %r(/testuser/test\?remove_comment=#{comment.id}.quot) }
96   - end
97   -
98   - should 'display remove comment button with param view when image' do
99   - profile = create_user('testuser').person
100   -
101   - image = UploadedFile.create!(:profile => profile, :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
102   - image.save!
103   -
104   - comment = image.comments.build(:author => profile, :title => 'a comment', :body => 'lalala')
105   - comment.save!
106   -
107   - login_as 'testuser'
108   - get :view_page, :profile => 'testuser', :page => [ image.filename ], :view => true
109   - assert_tag :tag => 'a', :attributes => { :onclick => %r(/testuser/#{image.filename}\?remove_comment=#{comment.id}.*amp;view=true.quot) }
110   -end
111   -
112   -
113   - should 'not add unneeded params for remove comment button' do
114   - profile = create_user('testuser').person
115   - article = profile.articles.build(:name => 'test')
116   - article.save!
117   - comment = article.comments.build(:author => profile, :title => 'a comment', :body => 'lalala')
118   - comment.save!
119   -
120   - login_as 'testuser'
121   - get :view_page, :profile => 'testuser', :page => [ 'test' ], :random_param => 'bli'
122   - assert_tag :tag => 'a', :attributes => { :onclick => %r(/testuser/test\?remove_comment=#{comment.id.to_s}.quot) }
123   - end
124   -
125   - should 'be able to remove comment' do
126   - profile = create_user('testuser').person
127   - article = profile.articles.build(:name => 'test')
128   - article.save!
129   - comment = article.comments.build(:author => profile, :title => 'a comment', :body => 'lalala')
130   - comment.save!
131   -
132   - login_as 'testuser'
133   - assert_difference Comment, :count, -1 do
134   - post :view_page, :profile => profile.identifier, :page => [ 'test' ], :remove_comment => comment.id
135   - assert_redirected_to :controller => 'content_viewer', :profile => 'testuser', :action => 'view_page', :page => [ 'test' ]
136   - end
137   - end
138   -
139   - should "not be able to remove other people's comments if not moderator or admin" do
140   - create_user('normaluser')
141   - profile = create_user('testuser').person
142   - article = profile.articles.build(:name => 'test')
143   - article.save!
144   -
145   - commenter = create_user('otheruser').person
146   - comment = article.comments.build(:author => commenter, :title => 'a comment', :body => 'lalala')
147   - comment.save!
148   -
149   - login_as 'normaluser' # normaluser cannot remove other people's comments
150   - assert_no_difference Comment, :count do
151   - post :view_page, :profile => profile.identifier, :page => [ 'test' ], :remove_comment => comment.id
152   - assert_response :redirect
153   - end
154   - end
155   -
156   - should 'be able to remove comments on their articles' do
157   - profile = create_user('testuser').person
158   - article = profile.articles.build(:name => 'test')
159   - article.save!
160   -
161   - commenter = create_user('otheruser').person
162   - comment = article.comments.build(:author => commenter, :title => 'a comment', :body => 'lalala')
163   - comment.save!
164   -
165   - login_as 'testuser' # testuser must be able to remove comments in his articles
166   - assert_difference Comment, :count, -1 do
167   - post :view_page, :profile => profile.identifier, :page => [ 'test' ], :remove_comment => comment.id
168   - assert_response :redirect
169   - end
170   - end
171   -
172   - should 'be able to remove comments of their images' do
173   - profile = create_user('testuser').person
174   -
175   - image = UploadedFile.create!(:profile => profile, :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
176   - image.save!
177   -
178   - commenter = create_user('otheruser').person
179   - comment = image.comments.build(:author => commenter, :title => 'a comment', :body => 'lalala')
180   - comment.save!
181   -
182   - login_as 'testuser' # testuser must be able to remove comments in his articles
183   - assert_difference Comment, :count, -1 do
184   - post :view_page, :profile => profile.identifier, :page => [ image.filename ], :remove_comment => comment.id, :view => true
185   -
186   - assert_response :redirect
187   - assert_redirected_to :controller => 'content_viewer', :action => 'view_page', :profile => profile.identifier, :page => image.explode_path, :view => true
188   - end
189   - end
190   -
191   - should 'be able to remove comments if is moderator' do
192   - commenter = create_user('commenter_user').person
193   - community = Community.create!(:name => 'Community test', :identifier => 'community-test')
194   - article = community.articles.create!(:name => 'test')
195   - comment = article.comments.create!(:author => commenter, :title => 'a comment', :body => 'lalala')
196   - community.add_moderator(profile)
197   - login_as profile.identifier
198   - assert_difference Comment, :count, -1 do
199   - post :view_page, :profile => community.identifier, :page => [ 'test' ], :remove_comment => comment.id
200   - assert_response :redirect
201   - end
202   - end
203   -
204   - should 'filter html content from body' do
205   - login_as @profile.identifier
206   - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
207   - post :view_page, :profile => @profile.identifier, :page => [ 'myarticle' ],
208   - :comment => { :title => 'html comment', :body => "this is a <strong id='html_test_comment'>html comment</strong>" }
209   - assert_no_tag :tag => 'strong', :attributes => { :id => 'html_test_comment' }
210   - end
211   -
212   - should 'filter html content from title' do
213   - login_as @profile.identifier
214   - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
215   - post :view_page, :profile => @profile.identifier, :page => [ 'myarticle' ],
216   - :comment => { :title => "html <strong id='html_test_comment'>comment</strong>", :body => "this is a comment" }
217   - assert_no_tag :tag => 'strong', :attributes => { :id => 'html_test_comment' }
218   - end
219   -
220   - should "point to article's url in comment form" do
221   - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
222   - Article.any_instance.stubs(:url).returns({:host => 'www.mysite.com', :controller => 'content_viewer', :action => 'view_page', :profile => 'person', :page => ['article']})
223   -
224   - get :view_page, :profile => profile.identifier, :page => [ 'myarticle' ]
225   -
226   - assert_tag :tag => 'form', :attributes => { :class => /^comment_form/, :action => '/person/article' }
227   - end
228   -
229 86 should "display current article's tags" do
230 87 page = profile.articles.create!(:name => 'myarticle', :body => 'test article', :tag_list => 'tag1, tag2')
231 88  
... ... @@ -429,14 +286,6 @@ end
429 286 assert_template 'view_page'
430 287 end
431 288  
432   - should 'not be able to post comment if article do not accept it' do
433   - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text', :accept_comments => false)
434   -
435   - assert_no_difference Comment, :count do
436   - post :view_page, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :title => 'crap!', :body => 'I think that this article is crap', :name => 'Anonymous coward', :email => 'coward@anonymous.com' }
437   - end
438   - end
439   -
440 289 should 'list comments if article has them, even if new comments are not allowed' do
441 290 page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text', :accept_comments => false)
442 291 page.comments.create!(:author => profile, :title => 'list my comment', :body => 'foo bar baz')
... ... @@ -719,15 +568,6 @@ end
719 568 assert_tag :tag => 'a', :content => 'Upload files', :attributes => {:href => /parent_id=#{folder.id}/}
720 569 end
721 570  
722   - should 'post comment in a image' do
723   - login_as(profile.identifier)
724   - image = UploadedFile.create!(:profile => profile, :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
725   - comment_count = image.comments.count
726   - post :view_page, :profile => profile.identifier, :page => image.explode_path, :view => true
727   - assert_equal comment_count, image.reload.comments.count
728   - assert_template 'view_page'
729   - end
730   -
731 571 should 'render slideshow template' do
732 572 f = Folder.create!(:name => 'gallery', :profile => profile)
733 573 get :view_page, :profile => profile.identifier, :page => f.explode_path, :slideshow => true
... ... @@ -869,17 +709,6 @@ end
869 709 assert_tag :tag => 'a', :content => 'New article'
870 710 end
871 711  
872   - should 'touch article after adding a comment' do
873   - yesterday = Time.now.yesterday
874   - Article.record_timestamps = false
875   - page = profile.articles.create(:name => 'myarticle', :body => 'the body of the text', :created_at => yesterday, :updated_at => yesterday)
876   - Article.record_timestamps = true
877   -
878   - login_as('ze')
879   - post :view_page, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :title => 'crap!', :body => 'I think that this article is crap' }
880   - assert_not_equal yesterday, assigns(:page).updated_at
881   - end
882   -
883 712 should 'display message if user was removed' do
884 713 article = profile.articles.create(:name => 'comment test')
885 714 to_be_removed = create_user('removed_user').person
... ... @@ -891,13 +720,6 @@ end
891 720 assert_tag :tag => 'span', :content => '(removed user)', :attributes => {:class => 'comment-user-status icon-user-removed'}
892 721 end
893 722  
894   - should 'show comment form opened on error' do
895   - login_as @profile.identifier
896   - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
897   - post :view_page, :profile => @profile.identifier, :page => [ 'myarticle' ], :comment => { :title => '', :body => '' }, :confirm => 'true'
898   - assert_tag :tag => 'div', :attributes => { :class => 'post_comment_box opened' }
899   - end
900   -
901 723 should 'show only first paragraph of blog posts if visualization_format is short' do
902 724 login_as(profile.identifier)
903 725  
... ... @@ -1188,22 +1010,14 @@ end
1188 1010 assert_equal [es_article], assigns(:posts)
1189 1011 end
1190 1012  
1191   - should 'be redirect after posting a comment' do
1192   - login_as @profile.identifier
1193   - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
1194   - post :view_page, :profile => @profile.identifier, :page => [ 'myarticle' ], :comment => { :title => 'title', :body => 'body' }, :confirm => 'true'
1195   - assert_redirected_to :controller => 'content_viewer', :action => 'view_page', :profile => @profile.identifier, :page => page.explode_path
1196   - end
1197   -
1198 1013 should 'display reply to comment button if authenticated' do
1199 1014 profile = create_user('testuser').person
1200 1015 article = profile.articles.build(:name => 'test')
1201 1016 article.save!
1202   - comment = article.comments.build(:author => profile, :title => 'a comment', :body => 'lalala')
1203   - comment.save!
  1017 + comment = Comment.create!(:author => profile, :title => 'a comment', :body => 'lalala', :article => article)
1204 1018 login_as 'testuser'
1205 1019 get :view_page, :profile => 'testuser', :page => [ 'test' ]
1206   - assert_tag :tag => 'a', :attributes => { :class => /comment-footer-link/ }, :content => 'Reply'
  1020 + assert_tag :tag => 'a', :attributes => { :class => /comment-actions-reply/ }
1207 1021 end
1208 1022  
1209 1023 should 'display reply to comment button if not authenticated' do
... ... @@ -1213,7 +1027,7 @@ end
1213 1027 comment = article.comments.build(:author => profile, :title => 'a comment', :body => 'lalala')
1214 1028 comment.save!
1215 1029 get :view_page, :profile => 'testuser', :page => [ 'test' ]
1216   - assert_tag :tag => 'a', :attributes => { :class => /comment-footer-link/ }, :content => 'Reply'
  1030 + assert_tag :tag => 'a', :attributes => { :class => /comment-actions-reply/ }
1217 1031 end
1218 1032  
1219 1033 should 'display replies if comment has replies' do
... ... @@ -1238,34 +1052,6 @@ end
1238 1052 assert_no_tag :tag => 'ul', :attributes => { :class => 'comment-replies' }
1239 1053 end
1240 1054  
1241   - should 'show reply error' do
1242   - profile = create_user('testuser').person
1243   - article = profile.articles.build(:name => 'test')
1244   - article.save!
1245   - comment = article.comments.build(:author => profile, :title => 'root', :body => 'root')
1246   - comment.save!
1247   - login_as 'testuser'
1248   - post :view_page, :profile => profile.identifier, :page => ['test'], :comment => { :title => '', :body => '', :reply_of_id => comment.id }, :confirm => 'true'
1249   - assert_tag :tag => 'div', :attributes => { :class => /comment_reply/ }, :descendant => {:tag => 'div', :attributes => {:class => 'errorExplanation'} }
1250   - assert_no_tag :tag => 'div', :attributes => { :id => 'page-comment-form' }, :descendant => {:tag => 'div', :attributes => {:class => 'errorExplanation'} }
1251   - assert_tag :tag => 'div', :attributes => { :id => 'page-comment-form' }, :descendant => { :tag => 'div', :attributes => { :class => /post_comment_box closed/ } }
1252   - end
1253   -
1254   - should 'show comment error' do
1255   - profile = create_user('testuser').person
1256   - article = profile.articles.build(:name => 'test')
1257   - article.save!
1258   - comment1 = article.comments.build(:author => profile, :title => 'root', :body => 'root')
1259   - comment1.save!
1260   - comment2 = article.comments.build(:author => profile, :title => 'root', :body => 'root', :reply_of_id => comment1.id)
1261   - comment2.save!
1262   - login_as 'testuser'
1263   - post :view_page, :profile => profile.identifier, :page => ['test'], :comment => { :title => '', :body => '' }, :confirm => 'true'
1264   - assert_no_tag :tag => 'div', :attributes => { :class => /comment_reply/ }, :descendant => {:tag => 'div', :attributes => {:class => 'errorExplanation'} }
1265   - assert_tag :tag => 'div', :attributes => { :id => 'page-comment-form' }, :descendant => {:tag => 'div', :attributes => {:class => 'errorExplanation'} }
1266   - assert_tag :tag => 'div', :attributes => { :id => 'page-comment-form' }, :descendant => { :tag => 'div', :attributes => { :class => /post_comment_box opened/ } }
1267   - end
1268   -
1269 1055 should 'add an zero width space every 4 caracters of comment urls' do
1270 1056 url = 'www.an.url.to.be.splited.com'
1271 1057 a = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'textile', :language => 'en')
... ... @@ -1346,60 +1132,6 @@ end
1346 1132 assert_no_tag :tag => 'body', :attributes => { :class => /profile-homepage/ }
1347 1133 end
1348 1134  
1349   - should 'ask for captcha if user not logged' do
1350   - article = profile.articles.build(:name => 'test')
1351   - article.save!
1352   -
1353   - @controller.stubs(:verify_recaptcha).returns(false)
1354   - post :view_page, :profile => profile.identifier, :page => ['test'], :comment => {:body => "Some comment...", :author => profile}, :confirm => 'true'
1355   - assert_not_nil assigns(:comment)
1356   -
1357   - @controller.stubs(:verify_recaptcha).returns(true)
1358   - post :view_page, :profile => profile.identifier, :page => ['test'], :comment => {:body => "Some comment...", :author => profile}, :confirm => 'true'
1359   - assert_nil assigns(:comment)
1360   - end
1361   -
1362   - should 'ask for captcha if environment defines even with logged user' do
1363   - article = profile.articles.build(:name => 'test')
1364   - article.save!
1365   - login_as('testinguser')
1366   - @controller.stubs(:verify_recaptcha).returns(false)
1367   -
1368   - post :view_page, :profile => profile.identifier, :page => ['test'], :comment => {:body => "Some comment...", :author => profile}, :confirm => 'true'
1369   - assert_nil assigns(:comment)
1370   -
1371   - environment.enable('captcha_for_logged_users')
1372   - environment.save!
1373   -
1374   - post :view_page, :profile => profile.identifier, :page => ['test'], :comment => {:body => "Some comment...", :author => profile}, :confirm => 'true'
1375   - assert_not_nil assigns(:comment)
1376   - end
1377   -
1378   - should 'store IP address, user agent and referrer for comments' do
1379   - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
1380   - @request.stubs(:remote_ip).returns('33.44.55.66')
1381   - @request.stubs(:referrer).returns('http://example.com')
1382   - @request.stubs(:user_agent).returns('MyBrowser')
1383   - post :view_page, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :title => 'title', :body => 'body', :name => "Spammer", :email => 'damn@spammer.com' }, :confirm => 'true'
1384   - comment = Comment.last
1385   - assert_equal '33.44.55.66', comment.ip_address
1386   - assert_equal 'MyBrowser', comment.user_agent
1387   - assert_equal 'http://example.com', comment.referrer
1388   - end
1389   -
1390   - should 'not save a comment if a plugin rejects it' do
1391   - class TestFilterPlugin < Noosfero::Plugin
1392   - def filter_comment(c)
1393   - c.reject!
1394   - end
1395   - end
1396   - Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([TestFilterPlugin.new])
1397   - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
1398   - assert_no_difference Comment, :count do
1399   - post :view_page, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :title => 'title', :body => 'body', :name => "Spammer", :email => 'damn@spammer.com' }, :confirm => 'true'
1400   - end
1401   - end
1402   -
1403 1135 should 'not display article actions button if any plugins says so' do
1404 1136 class Plugin1 < Noosfero::Plugin
1405 1137 def content_remove_edit(content); true; end
... ... @@ -1453,53 +1185,4 @@ end
1453 1185 assert_equal 1, assigns(:comments_count)
1454 1186 end
1455 1187  
1456   - should 'be able to mark comments as spam' do
1457   - login_as profile.identifier
1458   - article = fast_create(Article, :profile_id => profile.id)
1459   - spam = fast_create(Comment, :name => 'foo', :email => 'foo@example.com', :source_id => article.id, :source_type => 'Article')
1460   -
1461   - post 'view_page', :profile => profile.identifier, :page => article.path.split('/'), :mark_comment_as_spam => spam.id
1462   -
1463   - spam.reload
1464   - assert spam.spam?
1465   - end
1466   -
1467   - should 'be able to edit a comment' do
1468   - login_as profile.identifier
1469   - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text', :accept_comments => false)
1470   - comment = fast_create(Comment, :body => 'Original comment', :author_id => profile.id, :source_id => page.id, :source_type => 'Article')
1471   -
1472   - post :edit_comment, :id => comment.id, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :body => 'Comment edited' }
1473   - assert_equal 'Comment edited', Comment.find(comment.id).body
1474   - end
1475   -
1476   - should 'edit comment from a page' do
1477   - login_as profile.identifier
1478   - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
1479   - comment = fast_create(Comment, :body => 'Original comment', :author_id => profile.id, :source_id => page.id, :source_type => 'Article')
1480   -
1481   - get :edit_comment, :id => comment.id, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :body => 'Comment edited' }
1482   - assert_tag :tag => 'h1', :content => 'Edit comment'
1483   - end
1484   -
1485   - should 'not edit comment from other page' do
1486   - login_as profile.identifier
1487   - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
1488   - comment = fast_create(Comment, :body => 'Original comment', :author_id => profile.id, :source_id => page.id, :source_type => 'Article')
1489   -
1490   - other_page = profile.articles.create!(:name => 'my other article', :body => 'the body of the text')
1491   - comment_on_other_page = fast_create(Comment, :body => 'Comment on other article', :author_id => profile.id, :source_id => other_page.id, :source_type => 'Article')
1492   -
1493   - get :edit_comment, :id => comment_on_other_page.id, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :body => 'Comment edited' }
1494   - assert_redirected_to page.url
1495   - end
1496   -
1497   - should 'not crash on edit comment if comment does not exist' do
1498   - login_as profile.identifier
1499   - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
1500   -
1501   - get :edit_comment, :id => 1000, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :body => 'Comment edited' }
1502   - assert_response 404
1503   - end
1504   -
1505 1188 end
... ...
test/unit/approve_comment_test.rb 0 → 100644
... ... @@ -0,0 +1,217 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class ApproveCommentTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + ActionMailer::Base.delivery_method = :test
  7 + ActionMailer::Base.perform_deliveries = true
  8 + ActionMailer::Base.deliveries = []
  9 + @profile = create_user('test_user', :email => "someone@anyhost.com").person
  10 + @article = fast_create(TextileArticle, :profile_id => @profile.id, :name => 'test name', :abstract => 'Lead of article', :body => 'This is my article')
  11 + @community = create(Community, :contact_email => "someone@anyhost.com")
  12 + @comment = @article.comments.build(:title => 'any comment', :body => "any text", :author => create_user('someperson').person)
  13 + end
  14 +
  15 + attr_reader :profile, :article, :community
  16 +
  17 + should 'be a task' do
  18 + ok { ApproveComment.new.kind_of?(Task) }
  19 + end
  20 +
  21 + should 'comment method deserialize comment attributes' do
  22 + a = ApproveComment.new(:comment_attributes => @comment.attributes.to_json)
  23 + assert_equal @comment.attributes, a.comment.attributes
  24 + end
  25 +
  26 + should 'article method returns comment article' do
  27 + @comment.article = @article
  28 + a = ApproveComment.new(:comment_attributes => @comment.attributes.to_json)
  29 + assert_equal @article, @comment.article
  30 + end
  31 +
  32 + should 'article method returns nil if comment.article if nil' do
  33 + @comment.article = nil
  34 + a = ApproveComment.new(:comment_attributes => @comment.attributes.to_json)
  35 + assert_nil @comment.article
  36 + end
  37 +
  38 + should 'not raise in comment action if comment_attributes if nil' do
  39 + a = ApproveComment.new(:comment_attributes => nil)
  40 + assert_nothing_raised do
  41 + a.comment
  42 + end
  43 + end
  44 +
  45 + should 'have article_name reference comment article' do
  46 + approve_comment = ApproveComment.create!(:target => @community, :comment_attributes => @comment.attributes.to_json, :requestor => @profile)
  47 +
  48 + assert_equal @article.name, approve_comment.article_name
  49 + end
  50 +
  51 + should 'article_name be article removed if there is no article associated to comment' do
  52 + @comment.article = nil
  53 + approve_comment = ApproveComment.new(:comment_attributes => @comment.attributes.to_json)
  54 +
  55 + assert_equal "Article removed.", approve_comment.article_name
  56 + end
  57 +
  58 + should 'have linked_subject reference comment article' do
  59 + approve_comment = ApproveComment.new(:comment_attributes => @comment.attributes.to_json)
  60 +
  61 + expected = {:text => @article.name, :url => @article.url}
  62 + assert_equal expected, approve_comment.linked_subject
  63 + end
  64 +
  65 + should 'have linked_subject ne nil if there is no article associated to comment' do
  66 + @comment.article = nil
  67 + approve_comment = ApproveComment.new(:comment_attributes => @comment.attributes.to_json)
  68 +
  69 + assert_nil approve_comment.linked_subject
  70 + end
  71 +
  72 + should 'create comment when finishing task' do
  73 + approve_comment = ApproveComment.create!(:target => @community, :comment_attributes => @comment.attributes.to_json, :requestor => @profile)
  74 + assert_difference @article.comments, :count, 1 do
  75 + approve_comment.finish
  76 + end
  77 + end
  78 +
  79 + should 'create comment with the created_at atribute passed as parameter when finishing task' do
  80 + now = Time.now - 10
  81 + @comment.created_at = now
  82 + approve_comment = ApproveComment.create!(:target => @community, :comment_attributes => @comment.attributes.to_json, :requestor => @profile)
  83 + assert_difference @article.comments, :count, 1 do
  84 + approve_comment.finish
  85 + end
  86 + comment = Comment.last
  87 + assert_equal now.to_s, comment.created_at.to_s
  88 + end
  89 +
  90 + should 'require target (profile which the article is going to be commented)' do
  91 + task = ApproveComment.new
  92 + task.valid?
  93 +
  94 + ok('must not validate with empty target') { task.errors.invalid?(:target_id) }
  95 +
  96 + task.target = Person.new
  97 + task.valid?
  98 + ok('must validate when target is given') { task.errors.invalid?(:target_id)}
  99 + end
  100 +
  101 + should 'send e-mails' do
  102 + TaskMailer.expects(:deliver_target_notification).at_least_once
  103 +
  104 + task = ApproveComment.create!(:target => @community, :comment_attributes => @comment.attributes.to_json, :requestor => @profile)
  105 +
  106 + end
  107 +
  108 + should 'override target notification message method from Task' do
  109 + task = ApproveComment.new(:target => @community, :comment_attributes => @comment.attributes.to_json, :requestor => @profile)
  110 + assert_nothing_raised NotImplementedError do
  111 + task.target_notification_message
  112 + end
  113 + end
  114 +
  115 + should 'deliver target notification message' do
  116 + task = ApproveComment.new(:target => @community, :comment_attributes => @comment.attributes.to_json, :requestor => @profile)
  117 +
  118 + email = TaskMailer.deliver_target_notification(task, task.target_notification_message)
  119 + assert_match(/\[#{task.environment.name}\] #{task.requestor.name} wants to comment the article: #{task.article_name}/, email.subject)
  120 + end
  121 +
  122 + should 'alert when reference article is removed' do
  123 + a = ApproveComment.create!(:target => @community, :comment_attributes => @comment.attributes.to_json, :requestor => @profile)
  124 +
  125 + @article.destroy
  126 + a.reload
  127 + assert_equal "The article was removed.", a.information[:message]
  128 + end
  129 +
  130 + should 'display anonymous name if the requestor is nil' do
  131 + a = ApproveComment.create!(:target => @community, :comment_attributes => @comment.attributes.to_json, :requestor => nil)
  132 +
  133 + assert_match /nonymous/, a.information[:message]
  134 + end
  135 +
  136 + should 'accept_details be true' do
  137 + a = ApproveComment.new
  138 + assert a.accept_details
  139 + end
  140 +
  141 + should 'reject_details be true' do
  142 + a = ApproveComment.new
  143 + assert a.reject_details
  144 + end
  145 +
  146 + should 'default decision be skip if there is an article associated to task' do
  147 + a = ApproveComment.new(:comment_attributes => @comment.attributes.to_json)
  148 + assert 'skip', a.default_decision
  149 + end
  150 +
  151 + should 'default decision be reject if there is no article associated to task' do
  152 + a = ApproveComment.new()
  153 + assert 'reject', a.default_decision
  154 + end
  155 +
  156 + should 'accept_disabled be true if there is no article associated to task' do
  157 + a = ApproveComment.new
  158 + assert a.accept_disabled?
  159 + end
  160 +
  161 + should 'accept_disabled be false if there is an article associated to task' do
  162 + a = ApproveComment.new(:comment_attributes => @comment.attributes.to_json)
  163 + assert !a.accept_disabled?
  164 + end
  165 +
  166 + should 'have target notification description' do
  167 + task = ApproveComment.create!(:target => @community, :comment_attributes => @comment.attributes.to_json, :requestor => @profile)
  168 +
  169 + assert_match(/#{task.requestor.name} wants to comment the article: #{article.name}/, task.target_notification_description)
  170 + end
  171 +
  172 + should 'have an target notification description for comments on removed articles' do
  173 + task = ApproveComment.create!(:target => @community, :comment_attributes => @comment.attributes.to_json, :requestor => @profile)
  174 +
  175 + @article.destroy
  176 + assert_match(/#{task.requestor.name} wanted to comment the article but it was removed/, task.target_notification_description)
  177 + end
  178 +
  179 + should 'have a default finished messsage after approval' do
  180 + task = ApproveComment.create!(:target => @community, :comment_attributes => @comment.attributes.to_json, :requestor => @profile)
  181 + assert_match(/Your request for comment the article "#{task.article.title}" was approved/, task.task_finished_message)
  182 + end
  183 +
  184 + should 'have a personalized finished messsage after approval' do
  185 + task = ApproveComment.create!(:target => @community, :comment_attributes => @comment.attributes.to_json, :requestor => @profile)
  186 + task.stubs(:closing_statment).returns('somenthing')
  187 +
  188 + assert_match(/Your .*#{task.article.title}.*Here is the comment.*\n\n#{task.closing_statment}/, task.task_finished_message)
  189 + end
  190 +
  191 + should 'return reject message even without reject explanation' do
  192 + task = ApproveComment.new
  193 + assert_not_nil task.task_cancelled_message
  194 + end
  195 +
  196 + should 'show the name of the article in the reject message' do
  197 + task = ApproveComment.new(:comment_attributes => @comment.attributes.to_json)
  198 + assert_match /Your request for commenting the article .*#{@article.name}.* was rejected/, task.task_cancelled_message
  199 + end
  200 +
  201 + should 'return reject message with reject explanation' do
  202 + task = ApproveComment.new
  203 + task.reject_explanation= "Some reject explanation"
  204 + assert_match(/Your request for commenting .* Here is the reject explanation .*\n\n#{task.reject_explanation}/, task.task_cancelled_message)
  205 + end
  206 +
  207 + should 'requestor name be the name of the requestor' do
  208 + a = fast_create(ApproveComment, :target_id => community, :requestor_id => profile)
  209 + assert_equal profile.name, a.requestor_name
  210 + end
  211 +
  212 + should 'requestor name be Anonymous if there is no requestor' do
  213 + a = fast_create(ApproveComment, :target_id => community)
  214 + assert_equal 'Anonymous', a.requestor_name
  215 + end
  216 +
  217 +end
... ...
test/unit/article_test.rb
... ... @@ -664,6 +664,30 @@ class ArticleTest &lt; ActiveSupport::TestCase
664 664 assert a.notify_comments?
665 665 end
666 666  
  667 + should 'has moderate comments false by default' do
  668 + a = Article.create!(:name => 'my article', :body => 'my text', :profile_id => profile.id)
  669 + a.reload
  670 + assert a.moderate_comments == false
  671 + end
  672 +
  673 + should 'save a article with moderate comments as true' do
  674 + a = Article.create!(:name => 'my article', :body => 'my text', :profile_id => profile.id, :moderate_comments => true)
  675 + a.reload
  676 + assert a.moderate_comments
  677 + end
  678 +
  679 + should 'moderate_comments? return true if moderate_comments variable is true' do
  680 + a = Article.new
  681 + a.moderate_comments= true
  682 + assert a.moderate_comments?
  683 + end
  684 +
  685 + should 'moderate_comments? return false if moderate_comments variable is false' do
  686 + a = Article.new
  687 + a.moderate_comments= false
  688 + assert !a.moderate_comments?
  689 + end
  690 +
667 691 should 'hold hits count' do
668 692 a = fast_create(Article, :name => 'Test article', :profile_id => profile.id)
669 693 a.hits = 10
... ...
test/unit/comment_helper_test.rb 0 → 100644
... ... @@ -0,0 +1,139 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class CommentHelperTest < ActiveSupport::TestCase
  4 +
  5 + include CommentHelper
  6 + include ActionView::Helpers::TagHelper
  7 + include NoosferoTestHelper
  8 +
  9 + def setup
  10 + @user = create_user('usertest').person
  11 + @profile = @user
  12 + self.stubs(:logged_in?).returns(true)
  13 + self.stubs(:report_abuse).returns('<a href="#">link</a>')
  14 + self.stubs(:expirable_comment_link).returns('<a href="#">link</a>')
  15 + @plugins.stubs(:dispatch).returns([])
  16 + end
  17 +
  18 + attr_reader :user, :profile
  19 +
  20 + should 'show menu if it has links for actions' do
  21 + article = Article.new(:profile => profile)
  22 + comment = Comment.new(:article => article)
  23 + menu = comment_actions(comment)
  24 + assert menu
  25 + end
  26 +
  27 + should 'do not show menu if it has no actions' do
  28 + comment = Comment.new
  29 + self.stubs(:links_for_comment_actions).returns([])
  30 + menu = comment_actions(comment)
  31 + assert !menu
  32 + end
  33 +
  34 + should 'do not show menu if it has nil actions only' do
  35 + comment = Comment.new
  36 + self.stubs(:link_for_report_abuse).returns(nil)
  37 + self.stubs(:link_for_spam).returns(nil)
  38 + self.stubs(:link_for_edit).returns(nil)
  39 + self.stubs(:link_for_remove).returns(nil)
  40 + menu = comment_actions(comment)
  41 + assert !menu
  42 + end
  43 +
  44 + should 'include actions of plugins in menu' do
  45 + article = Article.new(:profile => profile)
  46 + comment = Comment.new(:article => article)
  47 + plugin_action = {:link => 'plugin_action'}
  48 + @plugins.stubs(:dispatch).returns([plugin_action])
  49 + links = links_for_comment_actions(comment)
  50 + assert_includes links, plugin_action
  51 + end
  52 +
  53 + should 'include lambda actions of plugins in menu' do
  54 + article = Article.new(:profile => profile)
  55 + comment = Comment.new(:article => article)
  56 + plugin_action = lambda{[{:link => 'plugin_action'}, {:link => 'plugin_action2'}]}
  57 + @plugins.stubs(:dispatch).returns([plugin_action])
  58 + links = links_for_comment_actions(comment)
  59 + assert_includes links, {:link => 'plugin_action'}
  60 + assert_includes links, {:link => 'plugin_action2'}
  61 + end
  62 +
  63 + should 'return link for report abuse action when comment has a author' do
  64 + comment = Comment.new
  65 + comment.author = user
  66 + link = link_for_report_abuse(comment)
  67 + assert link
  68 + end
  69 +
  70 + should 'do not return link for report abuse action when comment has no author' do
  71 + comment = Comment.new
  72 + link = link_for_report_abuse(comment)
  73 + assert !link
  74 + end
  75 +
  76 + should 'return link for mark comment as spam' do
  77 + comment = Comment.new
  78 + comment.stubs(:can_be_marked_as_spam_by?).with(user).returns(true)
  79 + link = link_for_spam(comment)
  80 + assert_match /Mark as SPAM/, link[:link]
  81 + end
  82 +
  83 + should 'not return link for mark comment as spam if user does not have the permissions' do
  84 + comment = Comment.new
  85 + comment.stubs(:can_be_marked_as_spam_by?).with(user).returns(false)
  86 + link = link_for_spam(comment)
  87 + assert_nil link
  88 + end
  89 +
  90 + should 'return link for mark comment as not spam' do
  91 + comment = Comment.new
  92 + comment.spam = true
  93 + comment.stubs(:can_be_marked_as_spam_by?).with(user).returns(true)
  94 + link = link_for_spam(comment)
  95 + assert_match /Mark as NOT SPAM/, link[:link]
  96 + end
  97 +
  98 + should 'not return link for mark comment as not spam if user does not have the permissions' do
  99 + comment = Comment.new
  100 + comment.spam = true
  101 + comment.stubs(:can_be_marked_as_spam_by?).with(user).returns(false)
  102 + link = link_for_spam(comment)
  103 + assert_nil link
  104 + end
  105 +
  106 + should 'do not return link for edit comment' do
  107 + comment = Comment.new
  108 + comment.stubs(:can_be_updated_by?).with(user).returns(false)
  109 + link = link_for_edit(comment)
  110 + assert_nil link
  111 + end
  112 +
  113 + should 'return link for edit comment' do
  114 + comment = Comment.new
  115 + comment.stubs(:can_be_updated_by?).with(user).returns(true)
  116 + link = link_for_edit(comment)
  117 + assert link
  118 + end
  119 +
  120 + should 'do not return link for remove comment' do
  121 + comment = Comment.new
  122 + comment.stubs(:can_be_destroyed_by?).with(user).returns(false)
  123 + link = link_for_remove(comment)
  124 + assert_nil link
  125 + end
  126 +
  127 + should 'return link for remove comment' do
  128 + comment = Comment.new
  129 + comment.stubs(:can_be_destroyed_by?).with(user).returns(true)
  130 + link = link_for_remove(comment)
  131 + assert link
  132 + end
  133 +
  134 + def link_to_function(content, url, options = {})
  135 + link_to(content, url, options)
  136 + end
  137 +
  138 +end
  139 +
... ...
test/unit/comment_test.rb
... ... @@ -550,6 +550,156 @@ class CommentTest &lt; ActiveSupport::TestCase
550 550 SpammerLogger.clean_log
551 551 end
552 552  
  553 + should 'not need moderation if article is not moderated' do
  554 + article = Article.new
  555 + comment = Comment.new(:article => article)
  556 +
  557 + assert !comment.need_moderation?
  558 + end
  559 +
  560 + should 'not need moderation if the comment author is the article author' do
  561 + author = Person.new
  562 + article = Article.new
  563 +
  564 + article.stubs(:author).returns(author)
  565 + article.moderate_comments = true
  566 +
  567 + comment = Comment.new(:article => article)
  568 + comment.stubs(:author).returns(author)
  569 +
  570 + assert !comment.need_moderation?
  571 + end
  572 +
  573 + should 'need moderation if article is moderated and the comment has no author' do
  574 + article = Article.new
  575 + article.stubs(:moderate_comments?).returns(true)
  576 +
  577 + comment = Comment.new(:article => article)
  578 +
  579 + assert comment.need_moderation?
  580 + end
  581 +
  582 + should 'need moderation if article is moderated and the comment author is different from article author' do
  583 + article_author = Person.new
  584 + comment_author = Person.new
  585 +
  586 + article = Article.new
  587 + article.stubs(:author).returns(article_author)
  588 + article.stubs(:moderate_comments?).returns(true)
  589 +
  590 + comment = Comment.new(:article => article)
  591 + comment.stubs(:author).returns(comment_author)
  592 +
  593 + assert comment.need_moderation?
  594 + end
  595 +
  596 + should 'not be able to destroy comment without user' do
  597 + comment = Comment.new
  598 +
  599 + assert !comment.can_be_destroyed_by?(nil)
  600 + end
  601 +
  602 + should 'not be able to destroy comment' do
  603 + user = Person.new
  604 + profile = Profile.new
  605 + article = Article.new(:profile => profile)
  606 + comment = Comment.new(:article => article)
  607 + user.expects(:has_permission?).with(:moderate_comments, profile).returns(false)
  608 +
  609 + assert !comment.can_be_destroyed_by?(user)
  610 + end
  611 +
  612 + should 'be able to destroy comment if is the author' do
  613 + user = Person.new
  614 + comment = Comment.new(:author => user)
  615 +
  616 + assert comment.can_be_destroyed_by?(user)
  617 + end
  618 +
  619 + should 'be able to destroy comment if is the profile' do
  620 + user = Person.new
  621 + article = Article.new(:profile => user)
  622 + comment = Comment.new(:article => article)
  623 +
  624 + assert comment.can_be_destroyed_by?(user)
  625 + end
  626 +
  627 + should 'be able to destroy comment if can moderate_comments on the profile' do
  628 + user = Person.new
  629 + profile = Profile.new
  630 + article = Article.new(:profile => profile)
  631 + comment = Comment.new(:article => article)
  632 +
  633 + user.expects(:has_permission?).with(:moderate_comments, profile).returns(true)
  634 +
  635 + assert comment.can_be_destroyed_by?(user)
  636 + end
  637 +
  638 + should 'not be able to mark comment as spam without user' do
  639 + comment = Comment.new
  640 +
  641 + assert !comment.can_be_marked_as_spam_by?(nil)
  642 + end
  643 +
  644 + should 'not be able to mark comment as spam' do
  645 + user = Person.new
  646 + profile = Profile.new
  647 + article = Article.new(:profile => profile)
  648 + comment = Comment.new(:article => article)
  649 + user.expects(:has_permission?).with(:moderate_comments, profile).returns(false)
  650 +
  651 + assert !comment.can_be_marked_as_spam_by?(user)
  652 + end
  653 +
  654 + should 'be able to mark comment as spam if is the profile' do
  655 + user = Person.new
  656 + article = Article.new(:profile => user)
  657 + comment = Comment.new(:article => article)
  658 +
  659 + assert comment.can_be_marked_as_spam_by?(user)
  660 + end
  661 +
  662 + should 'be able to mark comment as spam if can moderate_comments on the profile' do
  663 + user = Person.new
  664 + profile = Profile.new
  665 + article = Article.new(:profile => profile)
  666 + comment = Comment.new(:article => article)
  667 +
  668 + user.expects(:has_permission?).with(:moderate_comments, profile).returns(true)
  669 +
  670 + assert comment.can_be_marked_as_spam_by?(user)
  671 + end
  672 +
  673 + should 'not be able to update comment without user' do
  674 + comment = Comment.new
  675 +
  676 + assert !comment.can_be_updated_by?(nil)
  677 + end
  678 +
  679 + should 'not be able to update comment' do
  680 + user = Person.new
  681 + comment = Comment.new
  682 +
  683 + assert !comment.can_be_updated_by?(user)
  684 + end
  685 +
  686 + should 'be able to update comment if is the author' do
  687 + user = Person.new
  688 + comment = Comment.new(:author => user)
  689 +
  690 + assert comment.can_be_updated_by?(user)
  691 + end
  692 +
  693 + should 'get comment root' do
  694 + c1 = Comment.new
  695 + c2 = Comment.new(:reply_of => c1)
  696 + c3 = Comment.new(:reply_of => c2)
  697 +
  698 + assert_equal c1, c3.comment_root
  699 + assert_equal c1, c2.comment_root
  700 + assert_equal c1, c1.comment_root
  701 + end
  702 +
553 703 private
554 704  
555 705 def create_comment(args = {})
... ...