When copy is allowed, target projects are those on which the user has the :add_issues permission. git-svn-id: http://svn.redmine.org/redmine/trunk@13985 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/3.0.0
@can = {:edit => User.current.allowed_to?(:edit_issues, @projects), | @can = {:edit => User.current.allowed_to?(:edit_issues, @projects), | ||||
:log_time => (@project && User.current.allowed_to?(:log_time, @project)), | :log_time => (@project && User.current.allowed_to?(:log_time, @project)), | ||||
:copy => User.current.allowed_to?(:add_issues, @projects), | |||||
:copy => User.current.allowed_to?(:copy_issues, @projects) && Issue.allowed_target_projects.any?, | |||||
:delete => User.current.allowed_to?(:delete_issues, @projects) | :delete => User.current.allowed_to?(:delete_issues, @projects) | ||||
} | } | ||||
if @project | if @project |
end | end | ||||
def create | def create | ||||
unless User.current.allowed_to?(:add_issues, @issue.project) | |||||
raise ::Unauthorized | |||||
end | |||||
call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue }) | call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue }) | ||||
@issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads])) | @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads])) | ||||
if @issue.save | if @issue.save | ||||
@copy = params[:copy].present? | @copy = params[:copy].present? | ||||
@notes = params[:notes] | @notes = params[:notes] | ||||
if @copy | |||||
unless User.current.allowed_to?(:copy_issues, @projects) | |||||
raise ::Unauthorized | |||||
end | |||||
end | |||||
@allowed_projects = Issue.allowed_target_projects | @allowed_projects = Issue.allowed_target_projects | ||||
if params[:issue] | if params[:issue] | ||||
@target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s} | @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s} | ||||
@copy = params[:copy].present? | @copy = params[:copy].present? | ||||
attributes = parse_params_for_bulk_issue_attributes(params) | attributes = parse_params_for_bulk_issue_attributes(params) | ||||
if @copy | |||||
unless User.current.allowed_to?(:copy_issues, @projects) | |||||
raise ::Unauthorized | |||||
end | |||||
target_projects = @projects | |||||
if attributes['project_id'].present? | |||||
target_projects = Project.where(:id => attributes['project_id']).to_a | |||||
end | |||||
unless User.current.allowed_to?(:add_issues, target_projects) | |||||
raise ::Unauthorized | |||||
end | |||||
end | |||||
unsaved_issues = [] | unsaved_issues = [] | ||||
saved_issues = [] | saved_issues = [] | ||||
begin | begin | ||||
@issue.init_journal(User.current) | @issue.init_journal(User.current) | ||||
@copy_from = Issue.visible.find(params[:copy_from]) | @copy_from = Issue.visible.find(params[:copy_from]) | ||||
unless User.current.allowed_to?(:copy_issues, @copy_from.project) | |||||
raise ::Unauthorized | |||||
end | |||||
@link_copy = link_copy?(params[:link_copy]) || request.get? | @link_copy = link_copy?(params[:link_copy]) || request.get? | ||||
@copy_attachments = params[:copy_attachments].present? || request.get? | @copy_attachments = params[:copy_attachments].present? || request.get? | ||||
@copy_subtasks = params[:copy_subtasks].present? || request.get? | @copy_subtasks = params[:copy_subtasks].present? || request.get? |
def project_tree_options_for_select(projects, options = {}) | def project_tree_options_for_select(projects, options = {}) | ||||
s = ''.html_safe | s = ''.html_safe | ||||
if options[:include_blank] | |||||
s << content_tag('option', ' '.html_safe, :value => '') | |||||
if blank_text = options[:include_blank] | |||||
if blank_text == true | |||||
blank_text = ' '.html_safe | |||||
end | |||||
s << content_tag('option', blank_text, :value => '') | |||||
end | end | ||||
project_tree(projects) do |project, level| | project_tree(projects) do |project, level| | ||||
name_prefix = (level > 0 ? ' ' * 2 * level + '» ' : '').html_safe | name_prefix = (level > 0 ? ' ' * 2 * level + '» ' : '').html_safe |
names = super | names = super | ||||
names -= disabled_core_fields | names -= disabled_core_fields | ||||
names -= read_only_attribute_names(user) | names -= read_only_attribute_names(user) | ||||
if new_record? && copy? | |||||
names |= %w(project_id) | |||||
end | |||||
names | names | ||||
end | end | ||||
<%= 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? %> | <%= 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? %> | ||||
<%= 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) %> | <%= 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) %> | ||||
<%= watcher_link(@issue, User.current) %> | <%= watcher_link(@issue, User.current) %> | ||||
<%= link_to l(:button_copy), project_copy_issue_path(@project, @issue), :class => 'icon icon-copy' if User.current.allowed_to?(:add_issues, @project) %> | |||||
<%= 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? %> | |||||
<%= 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) %> | <%= 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) %> | ||||
</div> | </div> |
<p> | <p> | ||||
<label for="issue_project_id"><%= l(:field_project) %></label> | <label for="issue_project_id"><%= l(:field_project) %></label> | ||||
<%= select_tag('issue[project_id]', | <%= select_tag('issue[project_id]', | ||||
content_tag('option', l(:label_no_change_option), :value => '') + | |||||
project_tree_options_for_select(@allowed_projects, :selected => @target_project), | |||||
project_tree_options_for_select(@allowed_projects, | |||||
:include_blank => ((!@copy || (@projects & @allowed_projects == @projects)) ? l(:label_no_change_option) : false), | |||||
:selected => @target_project), | |||||
:onchange => "updateBulkEditFrom('#{escape_javascript url_for(:action => 'bulk_edit', :format => 'js')}')") %> | :onchange => "updateBulkEditFrom('#{escape_javascript url_for(:action => 'bulk_edit', :format => 'js')}')") %> | ||||
</p> | </p> | ||||
<% end %> | <% end %> |
permission_view_issues: View Issues | permission_view_issues: View Issues | ||||
permission_add_issues: Add issues | permission_add_issues: Add issues | ||||
permission_edit_issues: Edit issues | permission_edit_issues: Edit issues | ||||
permission_copy_issues: Copy issues | |||||
permission_manage_issue_relations: Manage issue relations | permission_manage_issue_relations: Manage issue relations | ||||
permission_set_issues_private: Set issues public or private | permission_set_issues_private: Set issues public or private | ||||
permission_set_own_issues_private: Set own issues public or private | permission_set_own_issues_private: Set own issues public or private |
permission_view_issues: Voir les demandes | permission_view_issues: Voir les demandes | ||||
permission_add_issues: Créer des demandes | permission_add_issues: Créer des demandes | ||||
permission_edit_issues: Modifier les demandes | permission_edit_issues: Modifier les demandes | ||||
permission_copy_issues: Copier les demandes | |||||
permission_manage_issue_relations: Gérer les relations | permission_manage_issue_relations: Gérer les relations | ||||
permission_set_issues_private: Rendre les demandes publiques ou privées | permission_set_issues_private: Rendre les demandes publiques ou privées | ||||
permission_set_own_issues_private: Rendre ses propres demandes publiques ou privées | permission_set_own_issues_private: Rendre ses propres demandes publiques ou privées |
:read => true | :read => true | ||||
map.permission :add_issues, {:issues => [:new, :create, :update_form], :attachments => :upload} | map.permission :add_issues, {:issues => [:new, :create, :update_form], :attachments => :upload} | ||||
map.permission :edit_issues, {:issues => [:edit, :update, :bulk_edit, :bulk_update, :update_form], :journals => [:new], :attachments => :upload} | map.permission :edit_issues, {:issues => [:edit, :update, :bulk_edit, :bulk_update, :update_form], :journals => [:new], :attachments => :upload} | ||||
map.permission :copy_issues, {:issues => [:new, :create, :bulk_edit, :bulk_update, :update_form], :attachments => :upload} | |||||
map.permission :manage_issue_relations, {:issue_relations => [:index, :show, :create, :destroy]} | map.permission :manage_issue_relations, {:issue_relations => [:index, :show, :create, :destroy]} | ||||
map.permission :manage_subtasks, {} | map.permission :manage_subtasks, {} | ||||
map.permission :set_issues_private, {} | map.permission :set_issues_private, {} |
- :view_issues | - :view_issues | ||||
- :add_issues | - :add_issues | ||||
- :edit_issues | - :edit_issues | ||||
- :copy_issues | |||||
- :manage_issue_relations | - :manage_issue_relations | ||||
- :manage_subtasks | - :manage_subtasks | ||||
- :add_issue_notes | - :add_issue_notes | ||||
- :view_issues | - :view_issues | ||||
- :add_issues | - :add_issues | ||||
- :edit_issues | - :edit_issues | ||||
- :copy_issues | |||||
- :manage_issue_relations | - :manage_issue_relations | ||||
- :manage_subtasks | - :manage_subtasks | ||||
- :add_issue_notes | - :add_issue_notes |
assert_select '#main-menu a.new-issue[href="/projects/ecookbook/issues/new"]' | assert_select '#main-menu a.new-issue[href="/projects/ecookbook/issues/new"]' | ||||
end | end | ||||
def test_new_as_copy_without_add_issues_permission_should_not_propose_current_project_as_target | |||||
user = setup_user_with_copy_but_not_add_permission | |||||
@request.session[:user_id] = user.id | |||||
get :new, :project_id => 1, :copy_from => 1 | |||||
assert_response :success | |||||
assert_template 'new' | |||||
assert_select 'select[name=?]', 'issue[project_id]' do | |||||
assert_select 'option[value="1"]', 0 | |||||
assert_select 'option[value="2"]', :text => 'OnlineStore' | |||||
end | |||||
end | |||||
def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox | def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox | ||||
@request.session[:user_id] = 2 | @request.session[:user_id] = 2 | ||||
issue = Issue.find(3) | issue = Issue.find(3) | ||||
assert_not_nil issues | assert_not_nil issues | ||||
assert_equal [1, 2, 3], issues.map(&:id).sort | assert_equal [1, 2, 3], issues.map(&:id).sort | ||||
assert_select 'select[name=?]', 'issue[project_id]' do | |||||
assert_select 'option[value=""]' | |||||
end | |||||
assert_select 'input[name=copy_attachments]' | assert_select 'input[name=copy_attachments]' | ||||
end | end | ||||
def test_get_bulk_copy_without_add_issues_permission_should_not_propose_current_project_as_target | |||||
user = setup_user_with_copy_but_not_add_permission | |||||
@request.session[:user_id] = user.id | |||||
get :bulk_edit, :ids => [1, 2, 3], :copy => '1' | |||||
assert_response :success | |||||
assert_template 'bulk_edit' | |||||
assert_select 'select[name=?]', 'issue[project_id]' do | |||||
assert_select 'option[value=""]', 0 | |||||
assert_select 'option[value="2"]' | |||||
end | |||||
end | |||||
def test_bulk_copy_to_another_project | def test_bulk_copy_to_another_project | ||||
@request.session[:user_id] = 2 | @request.session[:user_id] = 2 | ||||
assert_difference 'Issue.count', 2 do | assert_difference 'Issue.count', 2 do | ||||
end | end | ||||
end | end | ||||
def test_bulk_copy_without_add_issues_permission_should_be_allowed_on_project_with_permission | |||||
user = setup_user_with_copy_but_not_add_permission | |||||
@request.session[:user_id] = user.id | |||||
assert_difference 'Issue.count', 3 do | |||||
post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '2'}, :copy => '1' | |||||
assert_response 302 | |||||
end | |||||
end | |||||
def test_bulk_copy_on_same_project_without_add_issues_permission_should_be_denied | |||||
user = setup_user_with_copy_but_not_add_permission | |||||
@request.session[:user_id] = user.id | |||||
post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => ''}, :copy => '1' | |||||
assert_response 403 | |||||
end | |||||
def test_bulk_copy_on_different_project_without_add_issues_permission_should_be_denied | |||||
user = setup_user_with_copy_but_not_add_permission | |||||
@request.session[:user_id] = user.id | |||||
post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '1'}, :copy => '1' | |||||
assert_response 403 | |||||
end | |||||
def test_bulk_copy_should_allow_not_changing_the_issue_attributes | def test_bulk_copy_should_allow_not_changing_the_issue_attributes | ||||
@request.session[:user_id] = 2 | @request.session[:user_id] = 2 | ||||
issues = [ | issues = [ | ||||
assert_select 'input[name=issues][value="1"][type=hidden]' | assert_select 'input[name=issues][value="1"][type=hidden]' | ||||
end | end | ||||
end | end | ||||
def setup_user_with_copy_but_not_add_permission | |||||
Role.all.each {|r| r.remove_permission! :add_issues} | |||||
Role.find_by_name('Manager').add_permission! :add_issues | |||||
user = User.generate! | |||||
User.add_to_project(user, Project.find(1), Role.find_by_name('Developer')) | |||||
User.add_to_project(user, Project.find(2), Role.find_by_name('Manager')) | |||||
user | |||||
end | |||||
end | end |