diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index c5c98d6fd..87cb01661 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -22,10 +22,10 @@ class ProjectsController < ApplicationController menu_item :settings, :only => :settings menu_item :projects, :only => [:index, :new, :copy, :create] - before_action :find_project, :except => [ :index, :autocomplete, :list, :new, :create, :copy ] - before_action :authorize, :except => [ :index, :autocomplete, :list, :new, :create, :copy, :archive, :unarchive, :destroy] + before_action :find_project, :except => [ :index, :autocomplete, :list, :new, :create ] + before_action :authorize, :except => [ :index, :autocomplete, :list, :new, :create, :archive, :unarchive, :destroy] before_action :authorize_global, :only => [:new, :create] - before_action :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ] + before_action :require_admin, :only => [ :archive, :unarchive, :destroy ] accept_rss_auth :index accept_api_auth :index, :show, :create, :update, :destroy require_sudo_mode :destroy @@ -128,6 +128,7 @@ class ProjectsController < ApplicationController end def copy + @project = nil # Reset because source project was set in @project for authorize. @issue_custom_fields = IssueCustomField.sorted.to_a @trackers = Tracker.sorted.to_a @source_project = Project.find(params[:id]) diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb index de99eeabe..a8aca1291 100644 --- a/app/views/projects/show.html.erb +++ b/app/views/projects/show.html.erb @@ -5,6 +5,9 @@ <% if User.current.allowed_to?(:add_subprojects, @project) %> <%= link_to l(:label_subproject_new), new_project_path(:parent_id => @project), :class => 'icon icon-add' %> <% end %> + <% if User.current.allowed_to?(:copy_project, @project) %> + <%= link_to(l(:button_copy), copy_project_path(@project), :class => 'icon icon-copy') %> + <% end %> <% if User.current.allowed_to?(:close_project, @project) %> <% if @project.active? %> <%= link_to l(:button_close), close_project_path(@project), :data => {:confirm => l(:text_are_you_sure)}, :method => :post, :class => 'icon icon-lock' %> diff --git a/lib/redmine.rb b/lib/redmine.rb index 9ecca63c3..065a197cd 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -88,6 +88,7 @@ Redmine::AccessControl.map do |map| map.permission :manage_members, {:projects => :settings, :members => [:index, :show, :new, :create, :edit, :update, :destroy, :autocomplete]}, :require => :member map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member + map.permission :copy_project, {:projects => [:copy]}, :require => :member # Queries map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member map.permission :save_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin diff --git a/test/fixtures/roles.yml b/test/fixtures/roles.yml index 13433874e..527815b47 100644 --- a/test/fixtures/roles.yml +++ b/test/fixtures/roles.yml @@ -11,6 +11,7 @@ roles_001: - :edit_project - :close_project - :select_project_modules + - :copy_project - :manage_members - :manage_versions - :manage_categories diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index 6be670566..84fa8e1eb 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -1087,8 +1087,19 @@ class ProjectsControllerTest < Redmine::ControllerTest end end - def test_get_copy + def test_get_copy_by_admin_user @request.session[:user_id] = 1 # admin + orig = Project.find(1) # Login user is no member + get(:copy, :params => {:id => orig.id}) + assert_response :success + + assert_select 'textarea[name=?]', 'project[description]', :text => orig.description + assert_select 'input[name=?][value=?]', 'project[enabled_module_names][]', 'issue_tracking', 1 + end + + def test_get_copy_by_non_admin_user_with_copy_project_permission + @request.session[:user_id] = 3 + Role.find(2).add_permission! :copy_project orig = Project.find(1) get(:copy, :params => {:id => orig.id}) assert_response :success @@ -1097,6 +1108,14 @@ class ProjectsControllerTest < Redmine::ControllerTest assert_select 'input[name=?][value=?]', 'project[enabled_module_names][]', 'issue_tracking', 1 end + def test_get_copy_by_non_admin_user_without_copy_project_permission_should_respond_with_403 + @request.session[:user_id] = 3 + Role.find(2).remove_permission! :copy_project + orig = Project.find(1) + get(:copy, :params => {:id => orig.id}) + assert_response 403 + end + def test_get_copy_with_invalid_source_should_respond_with_404 @request.session[:user_id] = 1 get(:copy, :params => {:id => 99})