@@ -29,11 +29,11 class ContextMenusController < ApplicationController | |||
|
29 | 29 | |
|
30 | 30 | @allowed_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&) |
|
31 | 31 | |
|
32 | @can = {:edit => User.current.allowed_to?(:edit_issues, @projects), | |
|
32 | @can = {:edit => @issues.all?(&:attributes_editable?), | |
|
33 | 33 | :log_time => (@project && User.current.allowed_to?(:log_time, @project)), |
|
34 | 34 | :copy => User.current.allowed_to?(:copy_issues, @projects) && Issue.allowed_target_projects.any?, |
|
35 | 35 | :add_watchers => User.current.allowed_to?(:add_issue_watchers, @projects), |
|
36 |
:delete => |
|
|
36 | :delete => @issues.all?(&:deletable?) | |
|
37 | 37 | } |
|
38 | 38 | if @project |
|
39 | 39 | if @issue |
@@ -211,6 +211,10 class IssuesController < ApplicationController | |||
|
211 | 211 | unless User.current.allowed_to?(:copy_issues, @projects) |
|
212 | 212 | raise ::Unauthorized |
|
213 | 213 | end |
|
214 | else | |
|
215 | unless @issues.all?(&:attributes_editable?) | |
|
216 | raise ::Unauthorized | |
|
217 | end | |
|
214 | 218 | end |
|
215 | 219 | |
|
216 | 220 | @allowed_projects = Issue.allowed_target_projects |
@@ -263,6 +267,10 class IssuesController < ApplicationController | |||
|
263 | 267 | unless User.current.allowed_to?(:add_issues, target_projects) |
|
264 | 268 | raise ::Unauthorized |
|
265 | 269 | end |
|
270 | else | |
|
271 | unless @issues.all?(&:attributes_editable?) | |
|
272 | raise ::Unauthorized | |
|
273 | end | |
|
266 | 274 | end |
|
267 | 275 | |
|
268 | 276 | unsaved_issues = [] |
@@ -316,6 +324,7 class IssuesController < ApplicationController | |||
|
316 | 324 | end |
|
317 | 325 | |
|
318 | 326 | def destroy |
|
327 | raise Unauthorized unless @issues.all?(&:deletable?) | |
|
319 | 328 | @hours = TimeEntry.where(:issue_id => @issues.map(&:id)).sum(:hours).to_f |
|
320 | 329 | if @hours > 0 |
|
321 | 330 | case params[:todo] |
@@ -172,14 +172,24 class Issue < ActiveRecord::Base | |||
|
172 | 172 | end |
|
173 | 173 | end |
|
174 | 174 | |
|
175 |
# Returns true if user or current user is allowed to edit or add |
|
|
175 | # Returns true if user or current user is allowed to edit or add notes to the issue | |
|
176 | 176 | def editable?(user=User.current) |
|
177 |
attributes_editable?(user) || user |
|
|
177 | attributes_editable?(user) || notes_addable?(user) | |
|
178 | 178 | end |
|
179 | 179 | |
|
180 | 180 | # Returns true if user or current user is allowed to edit the issue |
|
181 | 181 | def attributes_editable?(user=User.current) |
|
182 | user.allowed_to?(:edit_issues, project) | |
|
182 | user_tracker_permission?(user, :edit_issues) | |
|
183 | end | |
|
184 | ||
|
185 | # Returns true if user or current user is allowed to add notes to the issue | |
|
186 | def notes_addable?(user=User.current) | |
|
187 | user_tracker_permission?(user, :add_issue_notes) | |
|
188 | end | |
|
189 | ||
|
190 | # Returns true if user or current user is allowed to delete the issue | |
|
191 | def deletable?(user=User.current) | |
|
192 | user_tracker_permission?(user, :delete_issues) | |
|
183 | 193 | end |
|
184 | 194 | |
|
185 | 195 | def initialize(attributes=nil, *args) |
@@ -429,10 +439,10 class Issue < ActiveRecord::Base | |||
|
429 | 439 | 'custom_fields', |
|
430 | 440 | 'lock_version', |
|
431 | 441 | 'notes', |
|
432 |
:if => lambda {|issue, user| issue.new_record? || |
|
|
442 | :if => lambda {|issue, user| issue.new_record? || issue.attributes_editable?(user) } | |
|
433 | 443 | |
|
434 | 444 | safe_attributes 'notes', |
|
435 | :if => lambda {|issue, user| user.allowed_to?(:add_issue_notes, issue.project)} | |
|
445 | :if => lambda {|issue, user| issue.notes_addable?(user)} | |
|
436 | 446 | |
|
437 | 447 | safe_attributes 'private_notes', |
|
438 | 448 | :if => lambda {|issue, user| !issue.new_record? && user.allowed_to?(:set_notes_private, issue.project)} |
@@ -447,7 +457,7 class Issue < ActiveRecord::Base | |||
|
447 | 457 | } |
|
448 | 458 | |
|
449 | 459 | safe_attributes 'parent_issue_id', |
|
450 |
:if => lambda {|issue, user| (issue.new_record? || |
|
|
460 | :if => lambda {|issue, user| (issue.new_record? || issue.attributes_editable?(user)) && | |
|
451 | 461 | user.allowed_to?(:manage_subtasks, issue.project)} |
|
452 | 462 | |
|
453 | 463 | def safe_attribute_names(user=nil) |
@@ -1406,6 +1416,11 class Issue < ActiveRecord::Base | |||
|
1406 | 1416 | |
|
1407 | 1417 | private |
|
1408 | 1418 | |
|
1419 | def user_tracker_permission?(user, permission) | |
|
1420 | roles = user.roles_for_project(project).select {|r| r.has_permission?(permission)} | |
|
1421 | roles.any? {|r| r.permissions_all_trackers?(permission) || r.permissions_tracker_ids?(permission, tracker_id)} | |
|
1422 | end | |
|
1423 | ||
|
1409 | 1424 | def after_project_change |
|
1410 | 1425 | # Update project_id on related time entries |
|
1411 | 1426 | TimeEntry.where({:issue_id => id}).update_all(["project_id = ?", project_id]) |
@@ -3,5 +3,5 | |||
|
3 | 3 | <%= link_to l(:button_log_time), new_issue_time_entry_path(@issue), :class => 'icon icon-time-add' if User.current.allowed_to?(:log_time, @project) %> |
|
4 | 4 | <%= watcher_link(@issue, User.current) %> |
|
5 | 5 | <%= link_to l(:button_copy), project_copy_issue_path(@project, @issue), :class => 'icon icon-copy' if User.current.allowed_to?(:copy_issues, @project) && Issue.allowed_target_projects.any? %> |
|
6 |
<%= link_to l(:button_delete), issue_path(@issue), :data => {:confirm => issues_destroy_confirmation_message(@issue)}, :method => :delete, :class => 'icon icon-del' if |
|
|
6 | <%= link_to l(:button_delete), issue_path(@issue), :data => {:confirm => issues_destroy_confirmation_message(@issue)}, :method => :delete, :class => 'icon icon-del' if @issue.deletable? %> | |
|
7 | 7 | </div> |
@@ -27,21 +27,22 | |||
|
27 | 27 | <% end %> |
|
28 | 28 | </fieldset> |
|
29 | 29 | <% end %> |
|
30 | ||
|
31 | <fieldset><legend><%= l(:field_notes) %></legend> | |
|
32 | <%= f.text_area :notes, :cols => 60, :rows => 10, :class => 'wiki-edit', :no_label => true %> | |
|
33 | <%= wikitoolbar_for 'issue_notes' %> | |
|
34 | ||
|
35 | <% if @issue.safe_attribute? 'private_notes' %> | |
|
36 | <%= f.check_box :private_notes, :no_label => true %> <label for="issue_private_notes"><%= l(:field_private_notes) %></label> | |
|
30 | <% if @issue.notes_addable? %> | |
|
31 | <fieldset><legend><%= l(:field_notes) %></legend> | |
|
32 | <%= f.text_area :notes, :cols => 60, :rows => 10, :class => 'wiki-edit', :no_label => true %> | |
|
33 | <%= wikitoolbar_for 'issue_notes' %> | |
|
34 | ||
|
35 | <% if @issue.safe_attribute? 'private_notes' %> | |
|
36 | <%= f.check_box :private_notes, :no_label => true %> <label for="issue_private_notes"><%= l(:field_private_notes) %></label> | |
|
37 | <% end %> | |
|
38 | ||
|
39 | <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %> | |
|
40 | </fieldset> | |
|
41 | ||
|
42 | <fieldset><legend><%= l(:label_attachment_plural) %></legend> | |
|
43 | <p><%= render :partial => 'attachments/form', :locals => {:container => @issue} %></p> | |
|
44 | </fieldset> | |
|
37 | 45 | <% end %> |
|
38 | ||
|
39 | <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %> | |
|
40 | </fieldset> | |
|
41 | ||
|
42 | <fieldset><legend><%= l(:label_attachment_plural) %></legend> | |
|
43 | <p><%= render :partial => 'attachments/form', :locals => {:container => @issue} %></p> | |
|
44 | </fieldset> | |
|
45 | 46 | </div> |
|
46 | 47 | |
|
47 | 48 | <%= f.hidden_field :lock_version %> |
@@ -1,4 +1,4 | |||
|
1 |
<% reply_links = |
|
|
1 | <% reply_links = issue.notes_addable? -%> | |
|
2 | 2 | <% for journal in journals %> |
|
3 | 3 | <div id="change-<%= journal.id %>" class="<%= journal.css_classes %>"> |
|
4 | 4 | <div id="note-<%= journal.indice %>"> |
@@ -77,7 +77,7 end %> | |||
|
77 | 77 | <% if @issue.description? %> |
|
78 | 78 | <div class="description"> |
|
79 | 79 | <div class="contextual"> |
|
80 |
<%= link_to l(:button_quote), quoted_issue_path(@issue), :remote => true, :method => 'post', :class => 'icon icon-comment' if |
|
|
80 | <%= link_to l(:button_quote), quoted_issue_path(@issue), :remote => true, :method => 'post', :class => 'icon icon-comment' if @issue.notes_addable? %> | |
|
81 | 81 | </div> |
|
82 | 82 | |
|
83 | 83 | <p><strong><%=l(:field_description)%></strong></p> |
@@ -64,7 +64,9 | |||
|
64 | 64 | |
|
65 | 65 | <div id="role-permissions-trackers"> |
|
66 | 66 | <h3><%= l(:label_issue_tracking) %></h3> |
|
67 | <% permissions = %w(view_issues add_issues) %> | |
|
67 | <% permissions = %w(view_issues add_issues edit_issues add_issue_notes delete_issues) %> | |
|
68 | ||
|
69 | <div class="autoscroll"> | |
|
68 | 70 | <table class="list"> |
|
69 | 71 | <thead> |
|
70 | 72 | <tr> |
@@ -87,7 +89,7 | |||
|
87 | 89 | <% end %> |
|
88 | 90 | </tr> |
|
89 | 91 | <% Tracker.sorted.all.each do |tracker| %> |
|
90 | <tr> | |
|
92 | <tr class="<%= cycle("odd", "even") %>"> | |
|
91 | 93 | <td class="name"><%= tracker.name %></td> |
|
92 | 94 | <% permissions.each do |permission| %> |
|
93 | 95 | <td><%= check_box_tag "role[permissions_tracker_ids][#{permission}][]", |
@@ -100,6 +102,7 | |||
|
100 | 102 | <% end %> |
|
101 | 103 | </tbody> |
|
102 | 104 | </table> |
|
105 | </div> | |
|
103 | 106 | |
|
104 | 107 | <% permissions.each do |permission| %> |
|
105 | 108 | <%= hidden_field_tag "role[permissions_tracker_ids][#{permission}][]", '' %> |
@@ -152,6 +152,7 table.list td.buttons img, div.buttons img {vertical-align:middle;} | |||
|
152 | 152 | table.list td.reorder {width:15%; white-space:nowrap; text-align:center; } |
|
153 | 153 | table.list table.progress td {padding-right:0px;} |
|
154 | 154 | table.list caption { text-align: left; padding: 0.5em 0.5em 0.5em 0; } |
|
155 | #role-permissions-trackers table.list th {white-space:normal;} | |
|
155 | 156 | |
|
156 | 157 | .table-list-cell {display: table-cell; vertical-align: top; padding:2px; } |
|
157 | 158 |
@@ -3872,6 +3872,30 class IssuesControllerTest < ActionController::TestCase | |||
|
3872 | 3872 | assert_redirected_to '/issues/11?issue_count=3&issue_position=2&next_issue_id=12&prev_issue_id=8' |
|
3873 | 3873 | end |
|
3874 | 3874 | |
|
3875 | def test_update_with_permission_on_tracker_should_be_allowed | |
|
3876 | role = Role.find(1) | |
|
3877 | role.set_permission_trackers :edit_issues, [1] | |
|
3878 | role.save! | |
|
3879 | issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :subject => 'Original subject') | |
|
3880 | ||
|
3881 | @request.session[:user_id] = 2 | |
|
3882 | put :update, :id => issue.id, :issue => {:subject => 'Changed subject'} | |
|
3883 | assert_response 302 | |
|
3884 | assert_equal 'Changed subject', issue.reload.subject | |
|
3885 | end | |
|
3886 | ||
|
3887 | def test_update_without_permission_on_tracker_should_be_denied | |
|
3888 | role = Role.find(1) | |
|
3889 | role.set_permission_trackers :edit_issues, [1] | |
|
3890 | role.save! | |
|
3891 | issue = Issue.generate!(:project_id => 1, :tracker_id => 2, :subject => 'Original subject') | |
|
3892 | ||
|
3893 | @request.session[:user_id] = 2 | |
|
3894 | put :update, :id => issue.id, :issue => {:subject => 'Changed subject'} | |
|
3895 | assert_response 302 | |
|
3896 | assert_equal 'Original subject', issue.reload.subject | |
|
3897 | end | |
|
3898 | ||
|
3875 | 3899 | def test_get_bulk_edit |
|
3876 | 3900 | @request.session[:user_id] = 2 |
|
3877 | 3901 | get :bulk_edit, :ids => [1, 3] |
@@ -4702,6 +4726,32 class IssuesControllerTest < ActionController::TestCase | |||
|
4702 | 4726 | assert_response 404 |
|
4703 | 4727 | end |
|
4704 | 4728 | |
|
4729 | def test_destroy_with_permission_on_tracker_should_be_allowed | |
|
4730 | role = Role.find(1) | |
|
4731 | role.set_permission_trackers :delete_issues, [1] | |
|
4732 | role.save! | |
|
4733 | issue = Issue.generate!(:project_id => 1, :tracker_id => 1) | |
|
4734 | ||
|
4735 | @request.session[:user_id] = 2 | |
|
4736 | assert_difference 'Issue.count', -1 do | |
|
4737 | delete :destroy, :id => issue.id | |
|
4738 | end | |
|
4739 | assert_response 302 | |
|
4740 | end | |
|
4741 | ||
|
4742 | def test_destroy_without_permission_on_tracker_should_be_denied | |
|
4743 | role = Role.find(1) | |
|
4744 | role.set_permission_trackers :delete_issues, [2] | |
|
4745 | role.save! | |
|
4746 | issue = Issue.generate!(:project_id => 1, :tracker_id => 1) | |
|
4747 | ||
|
4748 | @request.session[:user_id] = 2 | |
|
4749 | assert_no_difference 'Issue.count' do | |
|
4750 | delete :destroy, :id => issue.id | |
|
4751 | end | |
|
4752 | assert_response 403 | |
|
4753 | end | |
|
4754 | ||
|
4705 | 4755 | def test_default_search_scope |
|
4706 | 4756 | get :index |
|
4707 | 4757 |
General Comments 0
You need to be logged in to leave comments.
Login now