Commit 4f91184d39168c2105c77c1ad61fe0c5f2337789
Exists in
master
and in
29 other branches
Merge branch 'merge-requests/282'
Showing
37 changed files
with
1993 additions
and
681 deletions
Show diff stats
@@ -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 < ApplicationController | @@ -2,8 +2,6 @@ class ContentViewerController < 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 < ApplicationController | @@ -70,24 +68,8 @@ class ContentViewerController < 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 < ApplicationController | @@ -125,81 +107,8 @@ class ContentViewerController < 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 < ApplicationController | @@ -223,13 +132,9 @@ class ContentViewerController < 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? ? |
@@ -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 = {}) |
@@ -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 < ActiveRecord::Base | @@ -65,6 +65,7 @@ class Article < 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 < ActiveRecord::Base | @@ -327,6 +328,14 @@ class Article < 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 < ActiveRecord::Base | @@ -489,6 +498,14 @@ class Article < 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 < ActiveRecord::Base | @@ -34,7 +34,9 @@ class Comment < 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 < ActiveRecord::Base | @@ -248,4 +250,22 @@ class Comment < 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 < ActiveRecord::Base | @@ -74,7 +74,7 @@ class Task < 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 |
@@ -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? && ' ' || 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> |
@@ -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/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? && ' ' || 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 | - | ||
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 | - | ||
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 | - | ||
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 | - | ||
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 | - | ||
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
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> |
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 < Noosfero::Plugin | @@ -27,7 +27,7 @@ class RequireAuthToCommentPlugin < 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
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; |
@@ -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 < ActionController::TestCase | @@ -83,149 +83,6 @@ class ContentViewerControllerTest < 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 |
@@ -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 < ActiveSupport::TestCase | @@ -664,6 +664,30 @@ class ArticleTest < 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 |
@@ -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 < ActiveSupport::TestCase | @@ -550,6 +550,156 @@ class CommentTest < 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 = {}) |