@@ -31,7 +31,7 class ContextMenusController < ApplicationController | |||||
31 |
|
31 | |||
32 | @can = {:edit => User.current.allowed_to?(:edit_issues, @projects), |
|
32 | @can = {:edit => User.current.allowed_to?(:edit_issues, @projects), | |
33 | :log_time => (@project && User.current.allowed_to?(:log_time, @project)), |
|
33 | :log_time => (@project && User.current.allowed_to?(:log_time, @project)), | |
34 |
:copy => User.current.allowed_to?(: |
|
34 | :copy => User.current.allowed_to?(:copy_issues, @projects) && Issue.allowed_target_projects.any?, | |
35 | :delete => User.current.allowed_to?(:delete_issues, @projects) |
|
35 | :delete => User.current.allowed_to?(:delete_issues, @projects) | |
36 | } |
|
36 | } | |
37 | if @project |
|
37 | if @project |
@@ -143,6 +143,9 class IssuesController < ApplicationController | |||||
143 | end |
|
143 | end | |
144 |
|
144 | |||
145 | def create |
|
145 | def create | |
|
146 | unless User.current.allowed_to?(:add_issues, @issue.project) | |||
|
147 | raise ::Unauthorized | |||
|
148 | end | |||
146 | call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue }) |
|
149 | call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue }) | |
147 | @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads])) |
|
150 | @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads])) | |
148 | if @issue.save |
|
151 | if @issue.save | |
@@ -219,6 +222,12 class IssuesController < ApplicationController | |||||
219 | @copy = params[:copy].present? |
|
222 | @copy = params[:copy].present? | |
220 | @notes = params[:notes] |
|
223 | @notes = params[:notes] | |
221 |
|
224 | |||
|
225 | if @copy | |||
|
226 | unless User.current.allowed_to?(:copy_issues, @projects) | |||
|
227 | raise ::Unauthorized | |||
|
228 | end | |||
|
229 | end | |||
|
230 | ||||
222 | @allowed_projects = Issue.allowed_target_projects |
|
231 | @allowed_projects = Issue.allowed_target_projects | |
223 | if params[:issue] |
|
232 | if params[:issue] | |
224 | @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s} |
|
233 | @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s} | |
@@ -255,6 +264,19 class IssuesController < ApplicationController | |||||
255 | @copy = params[:copy].present? |
|
264 | @copy = params[:copy].present? | |
256 | attributes = parse_params_for_bulk_issue_attributes(params) |
|
265 | attributes = parse_params_for_bulk_issue_attributes(params) | |
257 |
|
266 | |||
|
267 | if @copy | |||
|
268 | unless User.current.allowed_to?(:copy_issues, @projects) | |||
|
269 | raise ::Unauthorized | |||
|
270 | end | |||
|
271 | target_projects = @projects | |||
|
272 | if attributes['project_id'].present? | |||
|
273 | target_projects = Project.where(:id => attributes['project_id']).to_a | |||
|
274 | end | |||
|
275 | unless User.current.allowed_to?(:add_issues, target_projects) | |||
|
276 | raise ::Unauthorized | |||
|
277 | end | |||
|
278 | end | |||
|
279 | ||||
258 | unsaved_issues = [] |
|
280 | unsaved_issues = [] | |
259 | saved_issues = [] |
|
281 | saved_issues = [] | |
260 |
|
282 | |||
@@ -407,6 +429,9 class IssuesController < ApplicationController | |||||
407 | begin |
|
429 | begin | |
408 | @issue.init_journal(User.current) |
|
430 | @issue.init_journal(User.current) | |
409 | @copy_from = Issue.visible.find(params[:copy_from]) |
|
431 | @copy_from = Issue.visible.find(params[:copy_from]) | |
|
432 | unless User.current.allowed_to?(:copy_issues, @copy_from.project) | |||
|
433 | raise ::Unauthorized | |||
|
434 | end | |||
410 | @link_copy = link_copy?(params[:link_copy]) || request.get? |
|
435 | @link_copy = link_copy?(params[:link_copy]) || request.get? | |
411 | @copy_attachments = params[:copy_attachments].present? || request.get? |
|
436 | @copy_attachments = params[:copy_attachments].present? || request.get? | |
412 | @copy_subtasks = params[:copy_subtasks].present? || request.get? |
|
437 | @copy_subtasks = params[:copy_subtasks].present? || request.get? |
@@ -343,8 +343,11 module ApplicationHelper | |||||
343 |
|
343 | |||
344 | def project_tree_options_for_select(projects, options = {}) |
|
344 | def project_tree_options_for_select(projects, options = {}) | |
345 | s = ''.html_safe |
|
345 | s = ''.html_safe | |
346 | if options[:include_blank] |
|
346 | if blank_text = options[:include_blank] | |
347 | s << content_tag('option', ' '.html_safe, :value => '') |
|
347 | if blank_text == true | |
|
348 | blank_text = ' '.html_safe | |||
|
349 | end | |||
|
350 | s << content_tag('option', blank_text, :value => '') | |||
348 | end |
|
351 | end | |
349 | project_tree(projects) do |project, level| |
|
352 | project_tree(projects) do |project, level| | |
350 | name_prefix = (level > 0 ? ' ' * 2 * level + '» ' : '').html_safe |
|
353 | name_prefix = (level > 0 ? ' ' * 2 * level + '» ' : '').html_safe |
@@ -424,6 +424,9 class Issue < ActiveRecord::Base | |||||
424 | names = super |
|
424 | names = super | |
425 | names -= disabled_core_fields |
|
425 | names -= disabled_core_fields | |
426 | names -= read_only_attribute_names(user) |
|
426 | names -= read_only_attribute_names(user) | |
|
427 | if new_record? && copy? | |||
|
428 | names |= %w(project_id) | |||
|
429 | end | |||
427 | names |
|
430 | names | |
428 | end |
|
431 | end | |
429 |
|
432 |
@@ -2,6 +2,6 | |||||
2 | <%= link_to l(:button_edit), edit_issue_path(@issue), :onclick => 'showAndScrollTo("update", "issue_notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit) if @issue.editable? %> |
|
2 | <%= link_to l(:button_edit), edit_issue_path(@issue), :onclick => 'showAndScrollTo("update", "issue_notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit) if @issue.editable? %> | |
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) %> |
|
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 | <%= watcher_link(@issue, User.current) %> |
|
4 | <%= watcher_link(@issue, User.current) %> | |
5 |
<%= link_to l(:button_copy), project_copy_issue_path(@project, @issue), :class => 'icon icon-copy' if User.current.allowed_to?(: |
|
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 User.current.allowed_to?(:delete_issues, @project) %> |
|
6 | <%= link_to l(:button_delete), issue_path(@issue), :data => {:confirm => issues_destroy_confirmation_message(@issue)}, :method => :delete, :class => 'icon icon-del' if User.current.allowed_to?(:delete_issues, @project) %> | |
7 | </div> |
|
7 | </div> |
@@ -33,8 +33,9 | |||||
33 | <p> |
|
33 | <p> | |
34 | <label for="issue_project_id"><%= l(:field_project) %></label> |
|
34 | <label for="issue_project_id"><%= l(:field_project) %></label> | |
35 | <%= select_tag('issue[project_id]', |
|
35 | <%= select_tag('issue[project_id]', | |
36 | content_tag('option', l(:label_no_change_option), :value => '') + |
|
36 | project_tree_options_for_select(@allowed_projects, | |
37 | project_tree_options_for_select(@allowed_projects, :selected => @target_project), |
|
37 | :include_blank => ((!@copy || (@projects & @allowed_projects == @projects)) ? l(:label_no_change_option) : false), | |
|
38 | :selected => @target_project), | |||
38 | :onchange => "updateBulkEditFrom('#{escape_javascript url_for(:action => 'bulk_edit', :format => 'js')}')") %> |
|
39 | :onchange => "updateBulkEditFrom('#{escape_javascript url_for(:action => 'bulk_edit', :format => 'js')}')") %> | |
39 | </p> |
|
40 | </p> | |
40 | <% end %> |
|
41 | <% end %> |
@@ -429,6 +429,7 en: | |||||
429 | permission_view_issues: View Issues |
|
429 | permission_view_issues: View Issues | |
430 | permission_add_issues: Add issues |
|
430 | permission_add_issues: Add issues | |
431 | permission_edit_issues: Edit issues |
|
431 | permission_edit_issues: Edit issues | |
|
432 | permission_copy_issues: Copy issues | |||
432 | permission_manage_issue_relations: Manage issue relations |
|
433 | permission_manage_issue_relations: Manage issue relations | |
433 | permission_set_issues_private: Set issues public or private |
|
434 | permission_set_issues_private: Set issues public or private | |
434 | permission_set_own_issues_private: Set own issues public or private |
|
435 | permission_set_own_issues_private: Set own issues public or private |
@@ -449,6 +449,7 fr: | |||||
449 | permission_view_issues: Voir les demandes |
|
449 | permission_view_issues: Voir les demandes | |
450 | permission_add_issues: CrΓ©er des demandes |
|
450 | permission_add_issues: CrΓ©er des demandes | |
451 | permission_edit_issues: Modifier les demandes |
|
451 | permission_edit_issues: Modifier les demandes | |
|
452 | permission_copy_issues: Copier les demandes | |||
452 | permission_manage_issue_relations: GΓ©rer les relations |
|
453 | permission_manage_issue_relations: GΓ©rer les relations | |
453 | permission_set_issues_private: Rendre les demandes publiques ou privΓ©es |
|
454 | permission_set_issues_private: Rendre les demandes publiques ou privΓ©es | |
454 | permission_set_own_issues_private: Rendre ses propres demandes publiques ou privΓ©es |
|
455 | permission_set_own_issues_private: Rendre ses propres demandes publiques ou privΓ©es |
@@ -100,6 +100,7 Redmine::AccessControl.map do |map| | |||||
100 | :read => true |
|
100 | :read => true | |
101 | map.permission :add_issues, {:issues => [:new, :create, :update_form], :attachments => :upload} |
|
101 | map.permission :add_issues, {:issues => [:new, :create, :update_form], :attachments => :upload} | |
102 | map.permission :edit_issues, {:issues => [:edit, :update, :bulk_edit, :bulk_update, :update_form], :journals => [:new], :attachments => :upload} |
|
102 | map.permission :edit_issues, {:issues => [:edit, :update, :bulk_edit, :bulk_update, :update_form], :journals => [:new], :attachments => :upload} | |
|
103 | map.permission :copy_issues, {:issues => [:new, :create, :bulk_edit, :bulk_update, :update_form], :attachments => :upload} | |||
103 | map.permission :manage_issue_relations, {:issue_relations => [:index, :show, :create, :destroy]} |
|
104 | map.permission :manage_issue_relations, {:issue_relations => [:index, :show, :create, :destroy]} | |
104 | map.permission :manage_subtasks, {} |
|
105 | map.permission :manage_subtasks, {} | |
105 | map.permission :set_issues_private, {} |
|
106 | map.permission :set_issues_private, {} |
@@ -17,6 +17,7 roles_001: | |||||
17 | - :view_issues |
|
17 | - :view_issues | |
18 | - :add_issues |
|
18 | - :add_issues | |
19 | - :edit_issues |
|
19 | - :edit_issues | |
|
20 | - :copy_issues | |||
20 | - :manage_issue_relations |
|
21 | - :manage_issue_relations | |
21 | - :manage_subtasks |
|
22 | - :manage_subtasks | |
22 | - :add_issue_notes |
|
23 | - :add_issue_notes | |
@@ -77,6 +78,7 roles_002: | |||||
77 | - :view_issues |
|
78 | - :view_issues | |
78 | - :add_issues |
|
79 | - :add_issues | |
79 | - :edit_issues |
|
80 | - :edit_issues | |
|
81 | - :copy_issues | |||
80 | - :manage_issue_relations |
|
82 | - :manage_issue_relations | |
81 | - :manage_subtasks |
|
83 | - :manage_subtasks | |
82 | - :add_issue_notes |
|
84 | - :add_issue_notes |
@@ -2473,6 +2473,20 class IssuesControllerTest < ActionController::TestCase | |||||
2473 | assert_select '#main-menu a.new-issue[href="/projects/ecookbook/issues/new"]' |
|
2473 | assert_select '#main-menu a.new-issue[href="/projects/ecookbook/issues/new"]' | |
2474 | end |
|
2474 | end | |
2475 |
|
2475 | |||
|
2476 | def test_new_as_copy_without_add_issues_permission_should_not_propose_current_project_as_target | |||
|
2477 | user = setup_user_with_copy_but_not_add_permission | |||
|
2478 | ||||
|
2479 | @request.session[:user_id] = user.id | |||
|
2480 | get :new, :project_id => 1, :copy_from => 1 | |||
|
2481 | ||||
|
2482 | assert_response :success | |||
|
2483 | assert_template 'new' | |||
|
2484 | assert_select 'select[name=?]', 'issue[project_id]' do | |||
|
2485 | assert_select 'option[value="1"]', 0 | |||
|
2486 | assert_select 'option[value="2"]', :text => 'OnlineStore' | |||
|
2487 | end | |||
|
2488 | end | |||
|
2489 | ||||
2476 | def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox |
|
2490 | def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox | |
2477 | @request.session[:user_id] = 2 |
|
2491 | @request.session[:user_id] = 2 | |
2478 | issue = Issue.find(3) |
|
2492 | issue = Issue.find(3) | |
@@ -3770,9 +3784,26 class IssuesControllerTest < ActionController::TestCase | |||||
3770 | assert_not_nil issues |
|
3784 | assert_not_nil issues | |
3771 | assert_equal [1, 2, 3], issues.map(&:id).sort |
|
3785 | assert_equal [1, 2, 3], issues.map(&:id).sort | |
3772 |
|
3786 | |||
|
3787 | assert_select 'select[name=?]', 'issue[project_id]' do | |||
|
3788 | assert_select 'option[value=""]' | |||
|
3789 | end | |||
3773 | assert_select 'input[name=copy_attachments]' |
|
3790 | assert_select 'input[name=copy_attachments]' | |
3774 | end |
|
3791 | end | |
3775 |
|
3792 | |||
|
3793 | def test_get_bulk_copy_without_add_issues_permission_should_not_propose_current_project_as_target | |||
|
3794 | user = setup_user_with_copy_but_not_add_permission | |||
|
3795 | @request.session[:user_id] = user.id | |||
|
3796 | ||||
|
3797 | get :bulk_edit, :ids => [1, 2, 3], :copy => '1' | |||
|
3798 | assert_response :success | |||
|
3799 | assert_template 'bulk_edit' | |||
|
3800 | ||||
|
3801 | assert_select 'select[name=?]', 'issue[project_id]' do | |||
|
3802 | assert_select 'option[value=""]', 0 | |||
|
3803 | assert_select 'option[value="2"]' | |||
|
3804 | end | |||
|
3805 | end | |||
|
3806 | ||||
3776 | def test_bulk_copy_to_another_project |
|
3807 | def test_bulk_copy_to_another_project | |
3777 | @request.session[:user_id] = 2 |
|
3808 | @request.session[:user_id] = 2 | |
3778 | assert_difference 'Issue.count', 2 do |
|
3809 | assert_difference 'Issue.count', 2 do | |
@@ -3788,6 +3819,32 class IssuesControllerTest < ActionController::TestCase | |||||
3788 | end |
|
3819 | end | |
3789 | end |
|
3820 | end | |
3790 |
|
3821 | |||
|
3822 | def test_bulk_copy_without_add_issues_permission_should_be_allowed_on_project_with_permission | |||
|
3823 | user = setup_user_with_copy_but_not_add_permission | |||
|
3824 | @request.session[:user_id] = user.id | |||
|
3825 | ||||
|
3826 | assert_difference 'Issue.count', 3 do | |||
|
3827 | post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '2'}, :copy => '1' | |||
|
3828 | assert_response 302 | |||
|
3829 | end | |||
|
3830 | end | |||
|
3831 | ||||
|
3832 | def test_bulk_copy_on_same_project_without_add_issues_permission_should_be_denied | |||
|
3833 | user = setup_user_with_copy_but_not_add_permission | |||
|
3834 | @request.session[:user_id] = user.id | |||
|
3835 | ||||
|
3836 | post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => ''}, :copy => '1' | |||
|
3837 | assert_response 403 | |||
|
3838 | end | |||
|
3839 | ||||
|
3840 | def test_bulk_copy_on_different_project_without_add_issues_permission_should_be_denied | |||
|
3841 | user = setup_user_with_copy_but_not_add_permission | |||
|
3842 | @request.session[:user_id] = user.id | |||
|
3843 | ||||
|
3844 | post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '1'}, :copy => '1' | |||
|
3845 | assert_response 403 | |||
|
3846 | end | |||
|
3847 | ||||
3791 | def test_bulk_copy_should_allow_not_changing_the_issue_attributes |
|
3848 | def test_bulk_copy_should_allow_not_changing_the_issue_attributes | |
3792 | @request.session[:user_id] = 2 |
|
3849 | @request.session[:user_id] = 2 | |
3793 | issues = [ |
|
3850 | issues = [ | |
@@ -4079,4 +4136,13 class IssuesControllerTest < ActionController::TestCase | |||||
4079 | assert_select 'input[name=issues][value="1"][type=hidden]' |
|
4136 | assert_select 'input[name=issues][value="1"][type=hidden]' | |
4080 | end |
|
4137 | end | |
4081 | end |
|
4138 | end | |
|
4139 | ||||
|
4140 | def setup_user_with_copy_but_not_add_permission | |||
|
4141 | Role.all.each {|r| r.remove_permission! :add_issues} | |||
|
4142 | Role.find_by_name('Manager').add_permission! :add_issues | |||
|
4143 | user = User.generate! | |||
|
4144 | User.add_to_project(user, Project.find(1), Role.find_by_name('Developer')) | |||
|
4145 | User.add_to_project(user, Project.find(2), Role.find_by_name('Manager')) | |||
|
4146 | user | |||
|
4147 | end | |||
4082 |
|
|
4148 | end |
General Comments 0
You need to be logged in to leave comments.
Login now