##// END OF EJS Templates
Adds a News#commentable? method to easily specific additional rules....
Jean-Philippe Lang -
r8734:8ddcc4caf51c
parent child
Show More
@@ -1,36 +1,54
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
1 class CommentsController < ApplicationController
18 class CommentsController < ApplicationController
2 default_search_scope :news
19 default_search_scope :news
3 model_object News
20 model_object News
4 before_filter :find_model_object
21 before_filter :find_model_object
5 before_filter :find_project_from_association
22 before_filter :find_project_from_association
6 before_filter :authorize
23 before_filter :authorize
7
24
8 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
25 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
9 def create
26 def create
27 raise Unauthorized unless @news.commentable?
28
10 @comment = Comment.new(params[:comment])
29 @comment = Comment.new(params[:comment])
11 @comment.author = User.current
30 @comment.author = User.current
12 if @news.comments << @comment
31 if @news.comments << @comment
13 flash[:notice] = l(:label_comment_added)
32 flash[:notice] = l(:label_comment_added)
14 end
33 end
15
34
16 redirect_to :controller => 'news', :action => 'show', :id => @news
35 redirect_to :controller => 'news', :action => 'show', :id => @news
17 end
36 end
18
37
19 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
38 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
20 def destroy
39 def destroy
21 @news.comments.find(params[:comment_id]).destroy
40 @news.comments.find(params[:comment_id]).destroy
22 redirect_to :controller => 'news', :action => 'show', :id => @news
41 redirect_to :controller => 'news', :action => 'show', :id => @news
23 end
42 end
24
43
25 private
44 private
26
45
27 # ApplicationController's find_model_object sets it based on the controller
46 # ApplicationController's find_model_object sets it based on the controller
28 # name so it needs to be overriden and set to @news instead
47 # name so it needs to be overriden and set to @news instead
29 def find_model_object
48 def find_model_object
30 super
49 super
31 @news = @object
50 @news = @object
32 @comment = nil
51 @comment = nil
33 @news
52 @news
34 end
53 end
35
36 end
54 end
@@ -1,58 +1,63
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class News < ActiveRecord::Base
18 class News < ActiveRecord::Base
19 belongs_to :project
19 belongs_to :project
20 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
20 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
21 has_many :comments, :as => :commented, :dependent => :delete_all, :order => "created_on"
21 has_many :comments, :as => :commented, :dependent => :delete_all, :order => "created_on"
22
22
23 validates_presence_of :title, :description
23 validates_presence_of :title, :description
24 validates_length_of :title, :maximum => 60
24 validates_length_of :title, :maximum => 60
25 validates_length_of :summary, :maximum => 255
25 validates_length_of :summary, :maximum => 255
26
26
27 acts_as_attachable :delete_permission => :manage_news
27 acts_as_attachable :delete_permission => :manage_news
28 acts_as_searchable :columns => ['title', 'summary', "#{table_name}.description"], :include => :project
28 acts_as_searchable :columns => ['title', 'summary', "#{table_name}.description"], :include => :project
29 acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
29 acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
30 acts_as_activity_provider :find_options => {:include => [:project, :author]},
30 acts_as_activity_provider :find_options => {:include => [:project, :author]},
31 :author_key => :author_id
31 :author_key => :author_id
32 acts_as_watchable
32 acts_as_watchable
33
33
34 after_create :add_author_as_watcher
34 after_create :add_author_as_watcher
35
35
36 named_scope :visible, lambda {|*args| {
36 named_scope :visible, lambda {|*args| {
37 :include => :project,
37 :include => :project,
38 :conditions => Project.allowed_to_condition(args.shift || User.current, :view_news, *args)
38 :conditions => Project.allowed_to_condition(args.shift || User.current, :view_news, *args)
39 }}
39 }}
40
40
41 def visible?(user=User.current)
41 def visible?(user=User.current)
42 !user.nil? && user.allowed_to?(:view_news, project)
42 !user.nil? && user.allowed_to?(:view_news, project)
43 end
43 end
44
44
45 # Returns true if the news can be commented by user
46 def commentable?(user=User.current)
47 user.allowed_to?(:comment_news, project)
48 end
49
45 # returns latest news for projects visible by user
50 # returns latest news for projects visible by user
46 def self.latest(user = User.current, count = 5)
51 def self.latest(user = User.current, count = 5)
47 find(:all, :limit => count,
52 find(:all, :limit => count,
48 :conditions => Project.allowed_to_condition(user, :view_news),
53 :conditions => Project.allowed_to_condition(user, :view_news),
49 :include => [ :author, :project ],
54 :include => [ :author, :project ],
50 :order => "#{News.table_name}.created_on DESC")
55 :order => "#{News.table_name}.created_on DESC")
51 end
56 end
52
57
53 private
58 private
54
59
55 def add_author_as_watcher
60 def add_author_as_watcher
56 Watcher.create(:watchable => self, :user => author)
61 Watcher.create(:watchable => self, :user => author)
57 end
62 end
58 end
63 end
@@ -1,71 +1,71
1 <div class="contextual">
1 <div class="contextual">
2 <%= watcher_tag(@news, User.current) %>
2 <%= watcher_tag(@news, User.current) %>
3 <%= link_to(l(:button_edit),
3 <%= link_to(l(:button_edit),
4 edit_news_path(@news),
4 edit_news_path(@news),
5 :class => 'icon icon-edit',
5 :class => 'icon icon-edit',
6 :accesskey => accesskey(:edit),
6 :accesskey => accesskey(:edit),
7 :onclick => 'Element.show("edit-news"); return false;') if User.current.allowed_to?(:manage_news, @project) %>
7 :onclick => 'Element.show("edit-news"); return false;') if User.current.allowed_to?(:manage_news, @project) %>
8 <%= link_to(l(:button_delete),
8 <%= link_to(l(:button_delete),
9 news_path(@news),
9 news_path(@news),
10 :confirm => l(:text_are_you_sure),
10 :confirm => l(:text_are_you_sure),
11 :method => :delete,
11 :method => :delete,
12 :class => 'icon icon-del') if User.current.allowed_to?(:manage_news, @project) %>
12 :class => 'icon icon-del') if User.current.allowed_to?(:manage_news, @project) %>
13 </div>
13 </div>
14
14
15 <h2><%= avatar(@news.author, :size => "24") %><%=h @news.title %></h2>
15 <h2><%= avatar(@news.author, :size => "24") %><%=h @news.title %></h2>
16
16
17 <% if authorize_for('news', 'edit') %>
17 <% if authorize_for('news', 'edit') %>
18 <div id="edit-news" style="display:none;">
18 <div id="edit-news" style="display:none;">
19 <% labelled_form_for :news, @news, :url => news_path(@news),
19 <% labelled_form_for :news, @news, :url => news_path(@news),
20 :html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %>
20 :html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %>
21 <%= render :partial => 'form', :locals => { :f => f } %>
21 <%= render :partial => 'form', :locals => { :f => f } %>
22 <%= submit_tag l(:button_save) %>
22 <%= submit_tag l(:button_save) %>
23 <%= link_to_remote l(:label_preview),
23 <%= link_to_remote l(:label_preview),
24 { :url => preview_news_path(:project_id => @project),
24 { :url => preview_news_path(:project_id => @project),
25 :method => 'get',
25 :method => 'get',
26 :update => 'preview',
26 :update => 'preview',
27 :with => "Form.serialize('news-form')"
27 :with => "Form.serialize('news-form')"
28 }, :accesskey => accesskey(:preview) %> |
28 }, :accesskey => accesskey(:preview) %> |
29 <%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("edit-news"); return false;' %>
29 <%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("edit-news"); return false;' %>
30 <% end %>
30 <% end %>
31 <div id="preview" class="wiki"></div>
31 <div id="preview" class="wiki"></div>
32 </div>
32 </div>
33 <% end %>
33 <% end %>
34
34
35 <p><% unless @news.summary.blank? %><em><%=h @news.summary %></em><br /><% end %>
35 <p><% unless @news.summary.blank? %><em><%=h @news.summary %></em><br /><% end %>
36 <span class="author"><%= authoring @news.created_on, @news.author %></span></p>
36 <span class="author"><%= authoring @news.created_on, @news.author %></span></p>
37 <div class="wiki">
37 <div class="wiki">
38 <%= textilizable(@news, :description) %>
38 <%= textilizable(@news, :description) %>
39 </div>
39 </div>
40 <%= link_to_attachments @news %>
40 <%= link_to_attachments @news %>
41 <br />
41 <br />
42
42
43 <div id="comments" style="margin-bottom:16px;">
43 <div id="comments" style="margin-bottom:16px;">
44 <h3 class="comments"><%= l(:label_comment_plural) %></h3>
44 <h3 class="comments"><%= l(:label_comment_plural) %></h3>
45 <% @comments.each do |comment| %>
45 <% @comments.each do |comment| %>
46 <% next if comment.new_record? %>
46 <% next if comment.new_record? %>
47 <div class="contextual">
47 <div class="contextual">
48 <%= link_to_if_authorized image_tag('delete.png'), {:controller => 'comments', :action => 'destroy', :id => @news, :comment_id => comment},
48 <%= link_to_if_authorized image_tag('delete.png'), {:controller => 'comments', :action => 'destroy', :id => @news, :comment_id => comment},
49 :confirm => l(:text_are_you_sure), :method => :delete, :title => l(:button_delete) %>
49 :confirm => l(:text_are_you_sure), :method => :delete, :title => l(:button_delete) %>
50 </div>
50 </div>
51 <h4><%= avatar(comment.author, :size => "24") %><%= authoring comment.created_on, comment.author %></h4>
51 <h4><%= avatar(comment.author, :size => "24") %><%= authoring comment.created_on, comment.author %></h4>
52 <%= textilizable(comment.comments) %>
52 <%= textilizable(comment.comments) %>
53 <% end if @comments.any? %>
53 <% end if @comments.any? %>
54 </div>
54 </div>
55
55
56 <% if authorize_for 'comments', 'create' %>
56 <% if @news.commentable? %>
57 <p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comments" %></p>
57 <p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comments" %></p>
58 <% form_tag({:controller => 'comments', :action => 'create', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %>
58 <% form_tag({:controller => 'comments', :action => 'create', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %>
59 <div class="box">
59 <div class="box">
60 <%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit' %>
60 <%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit' %>
61 <%= wikitoolbar_for 'comment_comments' %>
61 <%= wikitoolbar_for 'comment_comments' %>
62 </div>
62 </div>
63 <p><%= submit_tag l(:button_add) %></p>
63 <p><%= submit_tag l(:button_add) %></p>
64 <% end %>
64 <% end %>
65 <% end %>
65 <% end %>
66
66
67 <% html_title @news.title -%>
67 <% html_title @news.title -%>
68
68
69 <% content_for :header_tags do %>
69 <% content_for :header_tags do %>
70 <%= stylesheet_link_tag 'scm' %>
70 <%= stylesheet_link_tag 'scm' %>
71 <% end %>
71 <% end %>
@@ -1,55 +1,64
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class CommentsControllerTest < ActionController::TestCase
20 class CommentsControllerTest < ActionController::TestCase
21 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :news, :comments
21 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :news, :comments
22
22
23 def setup
23 def setup
24 User.current = nil
24 User.current = nil
25 end
25 end
26
26
27 def test_add_comment
27 def test_add_comment
28 @request.session[:user_id] = 2
28 @request.session[:user_id] = 2
29 post :create, :id => 1, :comment => { :comments => 'This is a test comment' }
29 post :create, :id => 1, :comment => { :comments => 'This is a test comment' }
30 assert_redirected_to '/news/1'
30 assert_redirected_to '/news/1'
31
31
32 comment = News.find(1).comments.find(:first, :order => 'created_on DESC')
32 comment = News.find(1).comments.find(:first, :order => 'created_on DESC')
33 assert_not_nil comment
33 assert_not_nil comment
34 assert_equal 'This is a test comment', comment.comments
34 assert_equal 'This is a test comment', comment.comments
35 assert_equal User.find(2), comment.author
35 assert_equal User.find(2), comment.author
36 end
36 end
37
37
38 def test_empty_comment_should_not_be_added
38 def test_empty_comment_should_not_be_added
39 @request.session[:user_id] = 2
39 @request.session[:user_id] = 2
40 assert_no_difference 'Comment.count' do
40 assert_no_difference 'Comment.count' do
41 post :create, :id => 1, :comment => { :comments => '' }
41 post :create, :id => 1, :comment => { :comments => '' }
42 assert_response :redirect
42 assert_response :redirect
43 assert_redirected_to '/news/1'
43 assert_redirected_to '/news/1'
44 end
44 end
45 end
45 end
46
46
47 def test_create_should_be_denied_if_news_is_not_commentable
48 News.any_instance.stubs(:commentable?).returns(false)
49 @request.session[:user_id] = 2
50 assert_no_difference 'Comment.count' do
51 post :create, :id => 1, :comment => { :comments => 'This is a test comment' }
52 assert_response 403
53 end
54 end
55
47 def test_destroy_comment
56 def test_destroy_comment
48 comments_count = News.find(1).comments.size
57 comments_count = News.find(1).comments.size
49 @request.session[:user_id] = 2
58 @request.session[:user_id] = 2
50 delete :destroy, :id => 1, :comment_id => 2
59 delete :destroy, :id => 1, :comment_id => 2
51 assert_redirected_to '/news/1'
60 assert_redirected_to '/news/1'
52 assert_nil Comment.find_by_id(2)
61 assert_nil Comment.find_by_id(2)
53 assert_equal comments_count - 1, News.find(1).comments.size
62 assert_equal comments_count - 1, News.find(1).comments.size
54 end
63 end
55 end
64 end
General Comments 0
You need to be logged in to leave comments. Login now