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 @@ @@ -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,8 +2,6 @@ class ContentViewerController &lt; ApplicationController
2 2
3 needs_profile 3 needs_profile
4 4
5 - before_filter :comment_author, :only => :edit_comment  
6 -  
7 helper ProfileHelper 5 helper ProfileHelper
8 helper TagsHelper 6 helper TagsHelper
9 7
@@ -70,24 +68,8 @@ class ContentViewerController &lt; ApplicationController @@ -70,24 +68,8 @@ class ContentViewerController &lt; ApplicationController
70 68
71 @form_div = params[:form] 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 if @page.has_posts? 74 if @page.has_posts?
93 posts = if params[:year] and params[:month] 75 posts = if params[:year] and params[:month]
@@ -125,81 +107,8 @@ class ContentViewerController &lt; ApplicationController @@ -125,81 +107,8 @@ class ContentViewerController &lt; ApplicationController
125 end 107 end
126 end 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 protected 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 def per_page 112 def per_page
204 12 113 12
205 end 114 end
@@ -223,13 +132,9 @@ class ContentViewerController &lt; ApplicationController @@ -223,13 +132,9 @@ class ContentViewerController &lt; ApplicationController
223 end 132 end
224 end 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 end 137 end
  138 + helper_method :pass_without_comment_captcha?
234 139
235 end 140 end
app/helpers/application_helper.rb
@@ -30,6 +30,8 @@ module ApplicationHelper @@ -30,6 +30,8 @@ module ApplicationHelper
30 30
31 include AccountHelper 31 include AccountHelper
32 32
  33 + include CommentHelper
  34 +
33 include BlogHelper 35 include BlogHelper
34 36
35 include ContentViewerHelper 37 include ContentViewerHelper
@@ -1385,12 +1387,12 @@ module ApplicationHelper @@ -1385,12 +1387,12 @@ module ApplicationHelper
1385 end 1387 end
1386 1388
1387 def expirable_button(content, action, text, url, options = {}) 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 expirable_content_reference content, action, text, url, options 1391 expirable_content_reference content, action, text, url, options
1390 end 1392 end
1391 1393
1392 def expirable_comment_link(content, action, text, url, options = {}) 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 expirable_content_reference content, action, text, url, options 1396 expirable_content_reference content, action, text, url, options
1395 end 1397 end
1396 1398
app/helpers/article_helper.rb
@@ -35,7 +35,13 @@ module ArticleHelper @@ -35,7 +35,13 @@ module ArticleHelper
35 'div', 35 'div',
36 check_box(:article, :notify_comments) + 36 check_box(:article, :notify_comments) +
37 content_tag('label', _('I want to receive a notification of each comment written by e-mail'), :for => 'article_notify_comments') + 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 (article.can_display_hits? ? 47 (article.can_display_hits? ?
app/helpers/comment_helper.rb 0 → 100644
@@ -0,0 +1,70 @@ @@ -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,11 +5,9 @@ module ContentViewerHelper
5 5
6 def number_of_comments(article) 6 def number_of_comments(article)
7 n = article.comments.without_spam.count 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 end 11 end
14 12
15 def article_title(article, args = {}) 13 def article_title(article, args = {})
app/models/approve_comment.rb 0 → 100644
@@ -0,0 +1,104 @@ @@ -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,6 +65,7 @@ class Article &lt; ActiveRecord::Base
65 settings_items :display_hits, :type => :boolean, :default => true 65 settings_items :display_hits, :type => :boolean, :default => true
66 settings_items :author_name, :type => :string, :default => "" 66 settings_items :author_name, :type => :string, :default => ""
67 settings_items :allow_members_to_edit, :type => :boolean, :default => false 67 settings_items :allow_members_to_edit, :type => :boolean, :default => false
  68 + settings_items :moderate_comments, :type => :boolean, :default => false
68 settings_items :followers, :type => Array, :default => [] 69 settings_items :followers, :type => Array, :default => []
69 70
70 belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id' 71 belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id'
@@ -327,6 +328,14 @@ class Article &lt; ActiveRecord::Base @@ -327,6 +328,14 @@ class Article &lt; ActiveRecord::Base
327 @view_url ||= image? ? url.merge(:view => true) : url 328 @view_url ||= image? ? url.merge(:view => true) : url
328 end 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 def allow_children? 339 def allow_children?
331 true 340 true
332 end 341 end
@@ -489,6 +498,14 @@ class Article &lt; ActiveRecord::Base @@ -489,6 +498,14 @@ class Article &lt; ActiveRecord::Base
489 allow_post_content?(user) || user && allow_members_to_edit && user.is_member_of?(profile) 498 allow_post_content?(user) || user && allow_members_to_edit && user.is_member_of?(profile)
490 end 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 def accept_category?(cat) 509 def accept_category?(cat)
493 !cat.is_a?(ProductCategory) 510 !cat.is_a?(ProductCategory)
494 end 511 end
app/models/comment.rb
@@ -34,7 +34,9 @@ class Comment &lt; ActiveRecord::Base @@ -34,7 +34,9 @@ class Comment &lt; ActiveRecord::Base
34 34
35 xss_terminate :only => [ :body, :title, :name ], :on => 'validation' 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 def action_tracker_target 41 def action_tracker_target
40 self.article.profile 42 self.article.profile
@@ -248,4 +250,22 @@ class Comment &lt; ActiveRecord::Base @@ -248,4 +250,22 @@ class Comment &lt; ActiveRecord::Base
248 plugins.dispatch(:comment_marked_as_ham, self) 250 plugins.dispatch(:comment_marked_as_ham, self)
249 end 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 end 271 end
app/models/task.rb
@@ -74,7 +74,7 @@ class Task &lt; ActiveRecord::Base @@ -74,7 +74,7 @@ class Task &lt; ActiveRecord::Base
74 end 74 end
75 75
76 def self.all_types 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 end 78 end
79 79
80 # this method finished the task. It calls #perform, which must be overriden 80 # this method finished the task. It calls #perform, which must be overriden
app/views/comment/_comment.rhtml 0 → 100644
@@ -0,0 +1,84 @@ @@ -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 @@ @@ -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,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,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,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,6 +8,12 @@
8 8
9 <%= render :partial => 'confirm_unfollow' %> 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 <div id="article-toolbar"></div> 17 <div id="article-toolbar"></div>
12 18
13 <script type="text/javascript"> 19 <script type="text/javascript">
@@ -89,15 +95,15 @@ @@ -89,15 +95,15 @@
89 <% end %> 95 <% end %>
90 96
91 <% if @page.accept_comments? && @comments_count > 1 %> 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 <% end %> 99 <% end %>
94 100
95 <ul class="article-comments-list"> 101 <ul class="article-comments-list">
96 - <%= render :partial => 'comment', :collection => @comments %> 102 + <%= render :partial => 'comment/comment', :collection => @comments %>
97 </ul> 103 </ul>
98 104
99 <% if @page.accept_comments? %> 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 <% end %> 107 <% end %>
102 </div><!-- end class="comments" --> 108 </div><!-- end class="comments" -->
103 109
app/views/layouts/application-ng.rhtml
@@ -17,6 +17,10 @@ @@ -17,6 +17,10 @@
17 content.respond_to?(:call) ? content.call : content 17 content.respond_to?(:call) ? content.call : content
18 end.join("\n") 18 end.join("\n")
19 %> 19 %>
  20 +
  21 + <script type='text/javascript'>
  22 + DEFAULT_LOADING_MESSAGE = <%="'#{ _('loading...') }'" %>;
  23 + </script>
20 </head> 24 </head>
21 <body class="<%= body_classes %>"> 25 <body class="<%= body_classes %>">
22 <a href="#content" id="link-go-content"><span><%= _("Go to the content") %></span></a> 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,7 +8,7 @@
8 <div id='article'> 8 <div id='article'>
9 <div class="comments" id="comments_list"> 9 <div class="comments" id="comments_list">
10 <ul class="article-comments-list"> 10 <ul class="article-comments-list">
11 - <%= render :partial => 'content_viewer/comment', :collection => @spam %> 11 + <%= render :partial => 'comment/comment', :collection => @spam %>
12 </ul> 12 </ul>
13 </div> 13 </div>
14 </div> 14 </div>
app/views/tasks/_approve_comment_accept_details.rhtml 0 → 100644
@@ -0,0 +1,7 @@ @@ -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,6 +76,9 @@ ActionController::Routing::Routes.draw do |map|
76 # profile search 76 # profile search
77 map.profile_search 'profile/:profile/search', :controller => 'profile_search', :action => 'index', :profile => /#{Noosfero.identifier_format}/ 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 # public profile information 82 # public profile information
80 map.profile 'profile/:profile/:action/:id', :controller => 'profile', :action => 'index', :id => /[^\/]*/, :profile => /#{Noosfero.identifier_format}/ 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,7 +125,6 @@ ActionController::Routing::Routes.draw do |map|
122 # cache stuff - hack 125 # cache stuff - hack
123 map.cache 'public/:action/:id', :controller => 'public' 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 # match requests for profiles that don't have a custom domain 129 # match requests for profiles that don't have a custom domain
128 map.homepage ':profile/*page', :controller => 'content_viewer', :action => 'view_page', :profile => /#{Noosfero.identifier_format}/, :conditions => { :if => lambda { |env| !Domain.hosting_profile_at(env[:host]) } } 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,6 +320,30 @@ class Noosfero::Plugin
320 def comment_marked_as_ham(comment) 320 def comment_marked_as_ham(comment)
321 end 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 # -> Adds fields to the signup form 347 # -> Adds fields to the signup form
324 # returns = lambda block that creates html code 348 # returns = lambda block that creates html code
325 def signup_extra_contents 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,7 +27,7 @@ class RequireAuthToCommentPlugin &lt; Noosfero::Plugin
27 end 27 end
28 28
29 def js_files 29 def js_files
30 - ['hide_comment_form.js'] 30 + ['hide_comment_form.js', 'jquery.livequery.min.js']
31 end 31 end
32 32
33 def body_beginning 33 def body_beginning
plugins/require_auth_to_comment/public/hide_comment_form.js
1 (function($) { 1 (function($) {
2 $(window).bind('userDataLoaded', function(event, data) { 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 })(jQuery); 15 })(jQuery);
plugins/require_auth_to_comment/public/jquery.livequery.min.js 0 → 100644
@@ -0,0 +1,9 @@ @@ -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 \ No newline at end of file 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 display: none; 2 display: none;
3 } 3 }
public/designs/themes/base/style.css
@@ -1016,7 +1016,7 @@ hr.pre-posts, hr.sep-posts { @@ -1016,7 +1016,7 @@ hr.pre-posts, hr.sep-posts {
1016 1016
1017 .post_comment_box.opened { 1017 .post_comment_box.opened {
1018 border: none; 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,4 +100,24 @@ jQuery(function($) {
100 clicked.parent().find(".send-an-email").fadeOut(); 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,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 function show_warning(field, message) { 103 function show_warning(field, message) {
83 new Effect.Highlight(field, {duration:3}); 104 new Effect.Highlight(field, {duration:3});
84 $(message).show(); 105 $(message).show();
@@ -146,8 +167,9 @@ function loading_done(element_id) { @@ -146,8 +167,9 @@ function loading_done(element_id) {
146 jQuery(element_id).removeClass('small-loading-dark'); 167 jQuery(element_id).removeClass('small-loading-dark');
147 } 168 }
148 function open_loading(message) { 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 jQuery('#overlay_loading').show(); 171 jQuery('#overlay_loading').show();
  172 + jQuery('#overlay_loading_modal').center();
151 jQuery('#overlay_loading_modal').fadeIn('slow'); 173 jQuery('#overlay_loading_modal').fadeIn('slow');
152 } 174 }
153 function close_loading() { 175 function close_loading() {
@@ -292,11 +314,17 @@ function toggleSubmenu(trigger, title, link_list) { @@ -292,11 +314,17 @@ function toggleSubmenu(trigger, title, link_list) {
292 content.append('<h4>' + title + '</h4>'); 314 content.append('<h4>' + title + '</h4>');
293 jQuery.each(link_list, function(index, link_hash) { 315 jQuery.each(link_list, function(index, link_hash) {
294 for (label in link_hash) { 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 content.append(list); 330 content.append(list);
@@ -320,9 +348,9 @@ function hideAllSubmenus() { @@ -320,9 +348,9 @@ function hideAllSubmenus() {
320 // Hide visible ballons when clicked outside them 348 // Hide visible ballons when clicked outside them
321 jQuery(document).ready(function() { 349 jQuery(document).ready(function() {
322 jQuery('body').live('click', function() { hideAllSubmenus(); }); 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 function input_javascript_ordering_stuff() { 356 function input_javascript_ordering_stuff() {
@@ -688,22 +716,51 @@ jQuery(function($) { @@ -688,22 +716,51 @@ jQuery(function($) {
688 }); 716 });
689 717
690 function add_comment_reply_form(button, comment_id) { 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 var f = container.find('.comment_form'); 721 var f = container.find('.comment_form');
693 if (f.length == 0) { 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 if (container.hasClass('closed')) { 730 if (container.hasClass('closed')) {
700 container.removeClass('closed'); 731 container.removeClass('closed');
701 container.addClass('opened'); 732 container.addClass('opened');
702 container.find('.comment_form input[type=text]:visible:first').focus(); 733 container.find('.comment_form input[type=text]:visible:first').focus();
703 } 734 }
  735 + jQuery('.display-comment-form').hide();
704 return f; 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 function remove_comment(button, url, msg) { 764 function remove_comment(button, url, msg) {
708 var $ = jQuery; 765 var $ = jQuery;
709 var $button = $(button); 766 var $button = $(button);
@@ -716,17 +773,27 @@ function remove_comment(button, url, msg) { @@ -716,17 +773,27 @@ function remove_comment(button, url, msg) {
716 if (data.ok) { 773 if (data.ok) {
717 var $comment = $button.closest('.article-comment'); 774 var $comment = $button.closest('.article-comment');
718 var $replies = $comment.find('.comment-replies .article-comment'); 775 var $replies = $comment.find('.comment-replies .article-comment');
719 - $comment.slideUp(); 776 +
  777 + var $comments_div = $button.closest('.comments');
  778 +
720 var comments_removed = 1; 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 jQuery('.display-comment-form').click(function(){ 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 jQuery('.display-comment-form').hide(); 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 return false; 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 if(div.hasClass('opened')) { 21 if(div.hasClass('opened')) {
17 div.removeClass('opened'); 22 div.removeClass('opened');
18 div.addClass('closed'); 23 div.addClass('closed');
@@ -21,3 +26,67 @@ function toggleBox(div_selector){ @@ -21,3 +26,67 @@ function toggleBox(div_selector){
21 div.addClass('opened'); 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,11 +528,15 @@ code input {
528 background: transparent url(../images/loading-small-dark.gif) no-repeat 10% center; 528 background: transparent url(../images/loading-small-dark.gif) no-repeat 10% center;
529 } 529 }
530 #overlay_loading { 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 #overlay_loading_modal { 538 #overlay_loading_modal {
535 - z-index: 101; 539 + z-index: 10001;
536 width: 160px; 540 width: 160px;
537 height: 120px; 541 height: 120px;
538 border: 1px solid #000; 542 border: 1px solid #000;
@@ -998,6 +1002,9 @@ code input { @@ -998,6 +1002,9 @@ code input {
998 1002
999 .comments { 1003 .comments {
1000 } 1004 }
  1005 +span.comment-count.hide{
  1006 + display: none;
  1007 +}
1001 #content .no-comments-yet { 1008 #content .no-comments-yet {
1002 text-align: center; 1009 text-align: center;
1003 font-size: 80%; 1010 font-size: 80%;
@@ -1018,7 +1025,7 @@ code input { @@ -1018,7 +1025,7 @@ code input {
1018 margin-bottom: 10px; 1025 margin-bottom: 10px;
1019 padding: 4px; 1026 padding: 4px;
1020 } 1027 }
1021 -#article .article-comment h4 { 1028 +#article .article-comment .comment-details h4 {
1022 font-size: 13px; 1029 font-size: 13px;
1023 margin: 0px; 1030 margin: 0px;
1024 display: inline; 1031 display: inline;
@@ -1291,6 +1298,10 @@ a.comment-picture { @@ -1291,6 +1298,10 @@ a.comment-picture {
1291 -webkit-border-radius: 4px; 1298 -webkit-border-radius: 4px;
1292 border-radius: 4px; 1299 border-radius: 4px;
1293 } 1300 }
  1301 +.post_comment_box.opened h4 {
  1302 + border: none;
  1303 + cursor: default;
  1304 +}
1294 .post_comment_box.opened { 1305 .post_comment_box.opened {
1295 border: 1px solid #888; 1306 border: 1px solid #888;
1296 background: #eee; 1307 background: #eee;
@@ -1341,6 +1352,11 @@ a.comment-picture { @@ -1341,6 +1352,11 @@ a.comment-picture {
1341 .post_comment_box.comment_reply #comment_title { 1352 .post_comment_box.comment_reply #comment_title {
1342 width: 100%; 1353 width: 100%;
1343 } 1354 }
  1355 +
  1356 +#page-comment-form-template {
  1357 + display:none;
  1358 +}
  1359 +
1344 #page-comment-form .post_comment_box { 1360 #page-comment-form .post_comment_box {
1345 text-align: left; 1361 text-align: left;
1346 padding-left: 0; 1362 padding-left: 0;
@@ -1656,13 +1672,15 @@ a.button.disabled, input.disabled { @@ -1656,13 +1672,15 @@ a.button.disabled, input.disabled {
1656 * Block options editor floating window * 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 #cboxLoadedContent { 1677 #cboxLoadedContent {
1660 background: #FFF; 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 .block-config-options { 1686 .block-config-options {
@@ -1786,13 +1804,32 @@ a.button.disabled, input.disabled { @@ -1786,13 +1804,32 @@ a.button.disabled, input.disabled {
1786 #content .profile-list-block ul { 1804 #content .profile-list-block ul {
1787 width: 200px; 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 padding: 0px; 1828 padding: 0px;
1792 list-style: none; 1829 list-style: none;
1793 position: relative; 1830 position: relative;
1794 } 1831 }
1795 -.common-profile-list-block .vcard a { 1832 +.common-profile-list-block .vcard a, .comment-actions .vcard a {
1796 display: block; 1833 display: block;
1797 height: 112px; 1834 height: 112px;
1798 max-height: 112px; 1835 max-height: 112px;
@@ -1803,7 +1840,7 @@ a.button.disabled, input.disabled { @@ -1803,7 +1840,7 @@ a.button.disabled, input.disabled {
1803 text-align: center; 1840 text-align: center;
1804 overflow: hidden; 1841 overflow: hidden;
1805 font-size: 11px; 1842 font-size: 11px;
1806 - text-decoration: none; 1843 + text-decoration: none !important;
1807 color: #000; 1844 color: #000;
1808 position: relative; 1845 position: relative;
1809 cursor: pointer; /* work arround bug for MSIE */ 1846 cursor: pointer; /* work arround bug for MSIE */
@@ -4559,11 +4596,17 @@ h1#agenda-title { @@ -4559,11 +4596,17 @@ h1#agenda-title {
4559 } 4596 }
4560 /* Profile balloon */ 4597 /* Profile balloon */
4561 4598
4562 -.common-profile-list-block .vcard { 4599 +.common-profile-list-block .vcard, .comment-actions .vcard {
4563 position: relative !important; 4600 position: relative !important;
4564 float: left; 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 display: none; 4610 display: none;
4568 width: 16px; 4611 width: 16px;
4569 height: 16px; 4612 height: 16px;
@@ -4577,7 +4620,7 @@ h1#agenda-title { @@ -4577,7 +4620,7 @@ h1#agenda-title {
4577 -moz-border-radius: 5px; 4620 -moz-border-radius: 5px;
4578 -webkit-border-radius: 5px; 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 background: #fff url(/images/top-arrow.png) center center no-repeat; 4624 background: #fff url(/images/top-arrow.png) center center no-repeat;
4582 border: 1px solid #ccc; 4625 border: 1px solid #ccc;
4583 } 4626 }
@@ -4596,6 +4639,10 @@ h1#agenda-title { @@ -4596,6 +4639,10 @@ h1#agenda-title {
4596 padding: 0; 4639 padding: 0;
4597 text-align: left; 4640 text-align: left;
4598 } 4641 }
  4642 +.comment-details .menu-submenu {
  4643 + bottom: 0px;
  4644 + right: -134px;
  4645 +}
4599 .box-2 .menu-submenu, .box-3 .menu-submenu { 4646 .box-2 .menu-submenu, .box-3 .menu-submenu {
4600 bottom: 78px; 4647 bottom: 78px;
4601 right: -44px; 4648 right: -44px;
@@ -4630,7 +4677,7 @@ h1#agenda-title { @@ -4630,7 +4677,7 @@ h1#agenda-title {
4630 .msie7 #search-results .menu-submenu-trigger { 4677 .msie7 #search-results .menu-submenu-trigger {
4631 width: 20px !important; 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 float: none; 4681 float: none;
4635 display: block; 4682 display: block;
4636 height: auto; 4683 height: auto;
test/functional/comment_controller_test.rb 0 → 100644
@@ -0,0 +1,562 @@ @@ -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,149 +83,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
83 assert_equal feed.data, @response.body 83 assert_equal feed.data, @response.body
84 end 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 should "display current article's tags" do 86 should "display current article's tags" do
230 page = profile.articles.create!(:name => 'myarticle', :body => 'test article', :tag_list => 'tag1, tag2') 87 page = profile.articles.create!(:name => 'myarticle', :body => 'test article', :tag_list => 'tag1, tag2')
231 88
@@ -429,14 +286,6 @@ end @@ -429,14 +286,6 @@ end
429 assert_template 'view_page' 286 assert_template 'view_page'
430 end 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 should 'list comments if article has them, even if new comments are not allowed' do 289 should 'list comments if article has them, even if new comments are not allowed' do
441 page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text', :accept_comments => false) 290 page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text', :accept_comments => false)
442 page.comments.create!(:author => profile, :title => 'list my comment', :body => 'foo bar baz') 291 page.comments.create!(:author => profile, :title => 'list my comment', :body => 'foo bar baz')
@@ -719,15 +568,6 @@ end @@ -719,15 +568,6 @@ end
719 assert_tag :tag => 'a', :content => 'Upload files', :attributes => {:href => /parent_id=#{folder.id}/} 568 assert_tag :tag => 'a', :content => 'Upload files', :attributes => {:href => /parent_id=#{folder.id}/}
720 end 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 should 'render slideshow template' do 571 should 'render slideshow template' do
732 f = Folder.create!(:name => 'gallery', :profile => profile) 572 f = Folder.create!(:name => 'gallery', :profile => profile)
733 get :view_page, :profile => profile.identifier, :page => f.explode_path, :slideshow => true 573 get :view_page, :profile => profile.identifier, :page => f.explode_path, :slideshow => true
@@ -869,17 +709,6 @@ end @@ -869,17 +709,6 @@ end
869 assert_tag :tag => 'a', :content => 'New article' 709 assert_tag :tag => 'a', :content => 'New article'
870 end 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 should 'display message if user was removed' do 712 should 'display message if user was removed' do
884 article = profile.articles.create(:name => 'comment test') 713 article = profile.articles.create(:name => 'comment test')
885 to_be_removed = create_user('removed_user').person 714 to_be_removed = create_user('removed_user').person
@@ -891,13 +720,6 @@ end @@ -891,13 +720,6 @@ end
891 assert_tag :tag => 'span', :content => '(removed user)', :attributes => {:class => 'comment-user-status icon-user-removed'} 720 assert_tag :tag => 'span', :content => '(removed user)', :attributes => {:class => 'comment-user-status icon-user-removed'}
892 end 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 should 'show only first paragraph of blog posts if visualization_format is short' do 723 should 'show only first paragraph of blog posts if visualization_format is short' do
902 login_as(profile.identifier) 724 login_as(profile.identifier)
903 725
@@ -1188,22 +1010,14 @@ end @@ -1188,22 +1010,14 @@ end
1188 assert_equal [es_article], assigns(:posts) 1010 assert_equal [es_article], assigns(:posts)
1189 end 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 should 'display reply to comment button if authenticated' do 1013 should 'display reply to comment button if authenticated' do
1199 profile = create_user('testuser').person 1014 profile = create_user('testuser').person
1200 article = profile.articles.build(:name => 'test') 1015 article = profile.articles.build(:name => 'test')
1201 article.save! 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 login_as 'testuser' 1018 login_as 'testuser'
1205 get :view_page, :profile => 'testuser', :page => [ 'test' ] 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 end 1021 end
1208 1022
1209 should 'display reply to comment button if not authenticated' do 1023 should 'display reply to comment button if not authenticated' do
@@ -1213,7 +1027,7 @@ end @@ -1213,7 +1027,7 @@ end
1213 comment = article.comments.build(:author => profile, :title => 'a comment', :body => 'lalala') 1027 comment = article.comments.build(:author => profile, :title => 'a comment', :body => 'lalala')
1214 comment.save! 1028 comment.save!
1215 get :view_page, :profile => 'testuser', :page => [ 'test' ] 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 end 1031 end
1218 1032
1219 should 'display replies if comment has replies' do 1033 should 'display replies if comment has replies' do
@@ -1238,34 +1052,6 @@ end @@ -1238,34 +1052,6 @@ end
1238 assert_no_tag :tag => 'ul', :attributes => { :class => 'comment-replies' } 1052 assert_no_tag :tag => 'ul', :attributes => { :class => 'comment-replies' }
1239 end 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 should 'add an zero width space every 4 caracters of comment urls' do 1055 should 'add an zero width space every 4 caracters of comment urls' do
1270 url = 'www.an.url.to.be.splited.com' 1056 url = 'www.an.url.to.be.splited.com'
1271 a = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'textile', :language => 'en') 1057 a = fast_create(TextileArticle, :profile_id => @profile.id, :path => 'textile', :language => 'en')
@@ -1346,60 +1132,6 @@ end @@ -1346,60 +1132,6 @@ end
1346 assert_no_tag :tag => 'body', :attributes => { :class => /profile-homepage/ } 1132 assert_no_tag :tag => 'body', :attributes => { :class => /profile-homepage/ }
1347 end 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 should 'not display article actions button if any plugins says so' do 1135 should 'not display article actions button if any plugins says so' do
1404 class Plugin1 < Noosfero::Plugin 1136 class Plugin1 < Noosfero::Plugin
1405 def content_remove_edit(content); true; end 1137 def content_remove_edit(content); true; end
@@ -1453,53 +1185,4 @@ end @@ -1453,53 +1185,4 @@ end
1453 assert_equal 1, assigns(:comments_count) 1185 assert_equal 1, assigns(:comments_count)
1454 end 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 end 1188 end
test/unit/approve_comment_test.rb 0 → 100644
@@ -0,0 +1,217 @@ @@ -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,6 +664,30 @@ class ArticleTest &lt; ActiveSupport::TestCase
664 assert a.notify_comments? 664 assert a.notify_comments?
665 end 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 should 'hold hits count' do 691 should 'hold hits count' do
668 a = fast_create(Article, :name => 'Test article', :profile_id => profile.id) 692 a = fast_create(Article, :name => 'Test article', :profile_id => profile.id)
669 a.hits = 10 693 a.hits = 10
test/unit/comment_helper_test.rb 0 → 100644
@@ -0,0 +1,139 @@ @@ -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,6 +550,156 @@ class CommentTest &lt; ActiveSupport::TestCase
550 SpammerLogger.clean_log 550 SpammerLogger.clean_log
551 end 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 private 703 private
554 704
555 def create_comment(args = {}) 705 def create_comment(args = {})