Project

General

Profile

Feature #4687 » 4687-v4_for_v5.1.3.patch

Markus Boremski, 2024-08-27 16:11

View differences:

app/controllers/projects_controller.rb
23 23
  menu_item :projects, :only => [:index, :new, :copy, :create]
24 24

  
25 25
  before_action :find_project,
26
                :except => [:index, :autocomplete, :list, :new, :create, :copy, :bulk_destroy]
26
                :except => [:index, :autocomplete, :list, :new, :create, :bulk_destroy]
27 27
  before_action :authorize,
28
                :except => [:index, :autocomplete, :list, :new, :create, :copy,
28
                :except => [:index, :autocomplete, :list, :new, :create,
29 29
                            :archive, :unarchive,
30 30
                            :destroy, :bulk_destroy]
31 31
  before_action :authorize_global, :only => [:new, :create]
32
  before_action :require_admin, :only => [:copy, :archive, :unarchive, :bulk_destroy]
32
  before_action :require_admin, :only => [:archive, :unarchive, :bulk_destroy]
33 33
  accept_atom_auth :index
34 34
  accept_api_auth :index, :show, :create, :update, :destroy, :archive, :unarchive, :close, :reopen
35 35
  require_sudo_mode :destroy, :bulk_destroy
......
140 140
  end
141 141

  
142 142
  def copy
143
    @project = nil # Reset because source project was set in @project for authorize.
143 144
    @issue_custom_fields = IssueCustomField.sorted.to_a
144 145
    @trackers = Tracker.sorted.to_a
145 146
    @source_project = Project.find(params[:id])
app/models/role.rb
82 82
  validates_presence_of :name
83 83
  validates_uniqueness_of :name, :case_sensitive => true
84 84
  validates_length_of :name, :maximum => 255
85
  validate :check_the_prerequisites_for_copy_project_permission
86

  
85 87
  validates_inclusion_of(
86 88
    :issues_visibility,
87 89
    :in => ISSUES_VISIBILITY_OPTIONS.collect(&:first),
......
325 327
    role
326 328
  end
327 329
  private_class_method :find_or_create_system_role
330

  
331
  def check_the_prerequisites_for_copy_project_permission
332
    if self.permissions.include?(:copy_project) &&
333
        self.permissions.exclude?(:add_project) &&
334
        self.permissions.exclude?(:add_subprojects)
335
      errors.add(:base, l(:error_cannot_have_copy_project_permission))
336
    end
337
  end
328 338
end
app/views/projects/show.html.erb
5 5
  <% if User.current.allowed_to?(:add_subprojects, @project) %>
6 6
    <%= link_to l(:label_subproject_new), new_project_path(:parent_id => @project), :class => 'icon icon-add' %>
7 7
  <% end %>
8
  <% if User.current.allowed_to?(:copy_project, @project) %>
9
    <%= link_tol(:button_copy), copy_project_path(@project), :class => 'icon icon-copy' %>
10
  <% end %>
8 11
  <% if User.current.allowed_to?(:close_project, @project) %>
9 12
    <% if @project.active? %>
10 13
      <%= link_to l(:button_close), close_project_path(@project), :data => {:confirm => l(:text_project_close_confirmation, @project.to_s)}, :method => :post, :class => 'icon icon-lock' %>
app/views/roles/_form.html.erb
60 60
        <label class="floating">
61 61
        <%= check_box_tag 'role[permissions][]', permission.name, (@role.permissions.include? permission.name),
62 62
              :id => "role_permissions_#{permission.name}",
63
              :data => {:shows => ".#{permission.name}_shown"} %>
63
              :data => {:shows => ".#{permission.name}_shown" },
64
              :disabled => (true if permission.name == :copy_project && !@role.permissions.include?(:add_project) && !@role.permissions.include?(:add_subprojects)) %>
64 65
        <%= l_or_humanize(permission.name, :prefix => 'permission_') %>
65 66
        </label>
66 67
    <% end %>
config/locales/de.yml
906 906
  permission_add_issue_notes: Kommentare hinzufügen
907 907
  permission_add_issue_watchers: Beobachter hinzufügen
908 908
  permission_add_issues: Tickets hinzufügen
909
  permission_copy_project: Projekt kopieren
909 910
  permission_add_messages: Forenbeiträge hinzufügen
910 911
  permission_add_project: Projekt erstellen
911 912
  permission_add_subprojects: Unterprojekte erstellen
config/locales/en.yml
245 245
  error_attachment_not_found: "Attachment %{name} not found"
246 246
  error_invalid_authenticity_token: "Invalid form authenticity token."
247 247
  error_query_statement_invalid: "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
248
  error_cannot_have_copy_project_permission: "Can't have copy_project permission without add_project permission or add_subprojects permission."
248 249

  
249 250
  mail_subject_lost_password: "Your %{value} password"
250 251
  mail_body_lost_password: 'To change your password, click on the following link:'
lib/redmine/preparation.rb
45 45
        map.permission :manage_members, {:projects => :settings, :members => [:index, :show, :new, :create, :edit, :update, :destroy, :autocomplete]}, :require => :member
46 46
        map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member
47 47
        map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member
48
        map.permission :copy_project, {:projects => [:copy]}, :require => :member
48 49
        # Queries
49 50
        map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member
50 51
        map.permission :save_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin
public/javascripts/application.js
970 970
  event.preventDefault();
971 971
}
972 972

  
973
function toggleCopyProjectCheckboxInit() {
974
  $('input#role_permissions_add_project, input#role_permissions_add_subprojects').change(function () {
975
    if (['input#role_permissions_add_project', 'input#role_permissions_add_subprojects'].some(el => $(el).is(':checked'))) {
976
      $('input#role_permissions_copy_project').attr('disabled', false)
977
    } else {
978
      $('input#role_permissions_copy_project').attr('disabled', true)
979
    }
980
  });
981
}
982

  
973 983
function toggleDisabledOnChange() {
974 984
  var checked = $(this).is(':checked');
975 985
  $($(this).data('disables')).attr('disabled', checked);
......
1027 1037
$(document).ready(function(){
1028 1038
  $('#content').on('change', 'input[data-disables], input[data-enables], input[data-shows]', toggleDisabledOnChange);
1029 1039
  toggleDisabledInit();
1040
  toggleCopyProjectCheckboxInit();
1030 1041

  
1031 1042
  $('#content').on('click', '.toggle-multiselect', function() {
1032 1043
    toggleMultiSelect($(this).siblings('select'));
test/functional/projects_controller_test.rb
1390 1390
    end
1391 1391
  end
1392 1392

  
1393
  def test_get_copy
1393
  def test_get_copy_by_admin_user
1394 1394
    @request.session[:user_id] = 1 # admin
1395
    orig = Project.find(1) # Login user is no member
1396
    get(:copy, :params => {:id => orig.id})
1397
    assert_response :success
1398

  
1399
    assert_select 'textarea[name=?]', 'project[description]', :text => orig.description
1400
    assert_select 'input[name=?][value=?]', 'project[enabled_module_names][]', 'issue_tracking', 1
1401
  end
1402

  
1403
  def test_get_copy_by_non_admin_user_with_copy_project_permission
1404
    @request.session[:user_id] = 3
1405
    Role.find(2).add_permission!(:copy_project, :add_project)
1395 1406
    orig = Project.find(1)
1396 1407
    get(:copy, :params => {:id => orig.id})
1397 1408
    assert_response :success
......
1400 1411
    assert_select 'input[name=?][value=?]', 'project[enabled_module_names][]', 'issue_tracking', 1
1401 1412
  end
1402 1413

  
1414
  def test_get_copy_by_non_admin_user_without_copy_project_permission_should_respond_with_403
1415
    @request.session[:user_id] = 3
1416
    Role.find(2).remove_permission! :copy_project
1417
    orig = Project.find(1)
1418
    get(:copy, :params => {:id => orig.id})
1419
    assert_response 403
1420
  end
1421

  
1403 1422
  def test_get_copy_with_invalid_source_should_respond_with_404
1404 1423
    @request.session[:user_id] = 1
1405 1424
    get(:copy, :params => {:id => 99})
......
1446 1465
    assert_equal 0, project.members.count
1447 1466
  end
1448 1467

  
1468
  def test_post_copy_by_non_admin_user_with_copy_project_and_add_project_permission
1469
    @request.session[:user_id] = 3
1470
    Role.find(2).add_permission!(:copy_project, :add_project)
1471
    CustomField.delete_all
1472

  
1473
    assert_difference 'Project.count' do
1474
      post(
1475
        :copy,
1476
        :params => {
1477
          :id => 1,
1478
          :project => {
1479
            :name => 'Copy',
1480
            :identifier => 'unique-copy',
1481
            :tracker_ids => ['1', '2', '3', ''],
1482
            :enabled_module_names => %w(issue_tracking time_tracking)
1483
          },
1484
          :only => %w(issues versions)
1485
        }
1486
      )
1487
    end
1488
    project = Project.find('unique-copy')
1489
    source = Project.find(1)
1490
    assert_equal %w(issue_tracking time_tracking), project.enabled_module_names.sort
1491

  
1492
    assert_equal source.versions.count, project.versions.count, "All versions were not copied"
1493
    assert_equal source.issues.count, project.issues.count, "All issues were not copied"
1494
    assert_equal 0, project.members.count
1495
  end
1496

  
1497
  def test_post_copy_by_non_admin_user_with_copy_project_and_add_subprojects_permission
1498
    @request.session[:user_id] = 3
1499
    Role.find(2).add_permission!(:copy_project, :add_subprojects)
1500
    CustomField.delete_all
1501

  
1502
    assert_difference 'Project.count' do
1503
      post(
1504
        :copy,
1505
        :params => {
1506
          :id => 1,
1507
          :project => {
1508
            :name => 'Copy',
1509
            :identifier => 'unique-copy',
1510
            :tracker_ids => ['1', '2', '3', ''],
1511
            :enabled_module_names => %w(issue_tracking time_tracking),
1512
            :parent_id => 1
1513
          },
1514
          :only => %w(issues versions)
1515
        }
1516
      )
1517
    end
1518
    project = Project.find('unique-copy')
1519
    source = Project.find(1)
1520
    assert_equal %w(issue_tracking time_tracking), project.enabled_module_names.sort
1521
    assert_equal source, project.parent
1522

  
1523
    assert_equal source.versions.count, project.versions.count, "All versions were not copied"
1524
    assert_equal source.issues.count, project.issues.count, "All issues were not copied"
1525
    assert_equal 0, project.members.count
1526
  end
1527

  
1449 1528
  def test_post_copy_should_redirect_to_settings_when_successful
1450 1529
    @request.session[:user_id] = 1 # admin
1451 1530
    post(
test/unit/role_test.rb
22 22
class RoleTest < ActiveSupport::TestCase
23 23
  fixtures :roles, :workflows, :trackers, :users
24 24

  
25
  include Redmine::I18n
26

  
25 27
  def setup
26 28
    User.current = nil
27 29
  end
......
241 243
    assert_nil ActiveRecord::Base.connection.select_value("SELECT 1 FROM queries_roles WHERE role_id = #{role.id}")
242 244
    assert [1, 3], query.roles
243 245
  end
246

  
247
  def test_check_the_prerequisites_for_copy_project_permission
248
    role = Role.find(2)
249
    role.remove_permission!(:copy_project, :add_project, :add_subprojects)
250

  
251
    role.permissions = [:copy_project]
252
    assert_not role.valid?
253
    assert_equal l(:error_cannot_have_copy_project_permission), role.errors.messages[:base].first
254

  
255
    role.permissions = [:copy_project, :add_project]
256
    assert role.valid?
257

  
258
    role.permissions = [:copy_project, :add_subprojects]
259
    assert role.valid?
260
  end
244 261
end
(8-8/8)