diff --git a/app/models/project.rb b/app/models/project.rb index 81ec37d106..31bd8f44ed 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -823,7 +823,6 @@ class Project < ActiveRecord::Base 'name', 'description', 'homepage', - 'is_public', 'identifier', 'custom_field_values', 'custom_fields', @@ -834,6 +833,22 @@ class Project < ActiveRecord::Base 'default_issue_query_id', 'default_assigned_to_id') + safe_attributes( + 'is_public', + :if => + lambda do |project, user| + if project.new_record? + if user.admin? + true + else + default_member_role&.has_permission?(:select_project_publicity) + end + else + user.allowed_to?(:select_project_publicity, project) + end + end + ) + safe_attributes( 'enabled_module_names', :if => diff --git a/app/views/projects/_form.html.erb b/app/views/projects/_form.html.erb index 7c988fb0e2..3cde415993 100644 --- a/app/views/projects/_form.html.erb +++ b/app/views/projects/_form.html.erb @@ -11,7 +11,7 @@ <% end %>

<%= f.text_field :homepage, :size => 60 %>

- <%= f.check_box :is_public %> + <%= f.check_box :is_public, :disabled => !@project.safe_attribute?(:is_public) %> <%= Setting.login_required? ? l(:text_project_is_public_non_member) : l(:text_project_is_public_anonymous) %>

diff --git a/config/locales/de.yml b/config/locales/de.yml index bcfbe9b7d7..f11a362c72 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -957,6 +957,7 @@ de: permission_rename_wiki_pages: Wiki-Seiten umbenennen permission_save_queries: Filter speichern permission_select_project_modules: Projektmodule auswählen + permission_select_project_publicity: Projekt als privat oder öffentlich markieren permission_set_issues_private: Tickets als privat oder öffentlich markieren permission_set_notes_private: Kommentar als privat markieren permission_set_own_issues_private: Eigene Tickets als privat oder öffentlich markieren diff --git a/config/locales/en.yml b/config/locales/en.yml index 335614dfbe..968bd4457b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -522,6 +522,7 @@ en: permission_edit_project: Edit project permission_close_project: Close / reopen the project permission_delete_project: Delete the project + permission_select_project_publicity: Set project public or private permission_select_project_modules: Select project modules permission_manage_members: Manage members permission_manage_project_activities: Manage project activities diff --git a/db/migrate/20221214173537_add_select_project_publicity_permission.rb b/db/migrate/20221214173537_add_select_project_publicity_permission.rb new file mode 100644 index 0000000000..9b3db0cdce --- /dev/null +++ b/db/migrate/20221214173537_add_select_project_publicity_permission.rb @@ -0,0 +1,15 @@ +class AddSelectProjectPublicityPermission < ActiveRecord::Migration[5.2] + def up + Role.find_each do |r| + r.add_permission!(:select_project_publicity) if r.permissions.include?(:edit_project) + end + end + + def down + Role.find_each do |r| + r.remove_permission!(:select_project_publicity) + end + end + + def run_in_request?; true end +end diff --git a/lib/redmine/preparation.rb b/lib/redmine/preparation.rb index 6e670c28e6..00f6752d55 100644 --- a/lib/redmine/preparation.rb +++ b/lib/redmine/preparation.rb @@ -39,6 +39,7 @@ module Redmine map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member map.permission :close_project, {:projects => [:close, :reopen]}, :require => :member, :read => true map.permission :delete_project, {:projects => :destroy}, :require => :member, :read => true + map.permission :select_project_publicity, {}, :require => :member map.permission :select_project_modules, {:projects => :modules}, :require => :member map.permission :view_members, {:members => [:index, :show]}, :public => true, :read => true map.permission :manage_members, {:projects => :settings, :members => [:index, :show, :new, :create, :edit, :update, :destroy, :autocomplete]}, :require => :member diff --git a/test/fixtures/roles.yml b/test/fixtures/roles.yml index 076d347ff7..df15c9c0fb 100644 --- a/test/fixtures/roles.yml +++ b/test/fixtures/roles.yml @@ -11,6 +11,7 @@ roles_001: - :edit_project - :close_project - :delete_project + - :select_project_publicity - :select_project_modules - :manage_members - :manage_versions @@ -85,6 +86,7 @@ roles_002: --- - :edit_project - :delete_project + - :select_project_publicity - :manage_members - :manage_versions - :manage_categories @@ -137,6 +139,7 @@ roles_003: permissions: | --- - :edit_project + - :select_project_publicity - :manage_members - :manage_versions - :manage_categories diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index 12da256c4a..14358fa623 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -407,6 +407,22 @@ class ProjectsControllerTest < Redmine::ControllerTest end end + def test_new_by_non_admin_should_enable_setting_public_if_default_role_is_allowed_to_set_public + Role.non_member.add_permission!(:add_project) + default_role = Role.generate!(permissions: [:add_project]) + user = User.generate! + @request.session[:user_id] = user.id + + with_settings new_project_user_role_id: default_role.id.to_s do + get :new + assert_select 'input[name=?][disabled=disabled]', 'project[is_public]' + + default_role.add_permission!(:select_project_publicity) + get :new + assert_select 'input[name=?]:not([disabled])', 'project[is_public]' + end + end + def test_new_should_not_display_invalid_search_link @request.session[:user_id] = 1 @@ -504,7 +520,6 @@ class ProjectsControllerTest < Redmine::ControllerTest :name => "blog", :description => "weblog", :identifier => "blog", - :is_public => 1, :custom_field_values => { '3' => 'Beta' }, @@ -518,7 +533,6 @@ class ProjectsControllerTest < Redmine::ControllerTest project = Project.find_by_name('blog') assert_kind_of Project, project assert_equal 'weblog', project.description - assert_equal true, project.is_public? assert_equal [1, 3], project.trackers.map(&:id).sort assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort @@ -527,6 +541,46 @@ class ProjectsControllerTest < Redmine::ControllerTest assert_equal 1, project.members.size end + test "#create by user without select_project_publicity permission should not create a new private project" do + Role.non_member.add_permission! :add_project + default_role = Project.default_member_role + default_role.remove_permission!(:select_project_publicity) + puts Setting.default_projects_public? + @request.session[:user_id] = 9 + + post( + :create, :params => { + :project => { + :name => "blog", + :identifier => "blog", + :enabled_module_names => ['issue_tracking', 'news', 'repository'], + :is_public => 0 + } + } + ) + + project = Project.find_by_name('blog') + assert_equal true, project.is_public? + end + + test "#create by non-admin user with add_project and select_project_publicity permission should create a new private project" do + @request.session[:user_id] = 2 + + post( + :create, :params => { + :project => { + :name => "blog", + :identifier => "blog", + :enabled_module_names => ['issue_tracking', 'news', 'repository'], + :is_public => 0 + } + } + ) + + project = Project.find_by_name('blog') + assert_equal false, project.is_public? + end + test "#create by non-admin user with add_project permission should fail with parent_id" do Role.non_member.add_permission! :add_project User.find(9).update! :language => 'en'