Project

General

Profile

Feature #4687 » 4687-v4.patch

Mizuki ISHIKAWA, 2022-04-14 03:54

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]
26
                :except => [:index, :autocomplete, :list, :new, :create]
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]
31 31
  before_action :authorize_global, :only => [:new, :create]
32
  before_action :require_admin, :only => [:copy, :archive, :unarchive]
32
  before_action :require_admin, :only => [:archive, :unarchive]
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
......
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
80 80
  validates_presence_of :name
81 81
  validates_uniqueness_of :name, :case_sensitive => true
82 82
  validates_length_of :name, :maximum => 255
83
  validate :check_the_prerequisites_for_copy_project_permission
84

  
83 85
  validates_inclusion_of(
84 86
    :issues_visibility,
85 87
    :in => ISSUES_VISIBILITY_OPTIONS.collect(&:first),
......
318 320
    role
319 321
  end
320 322
  private_class_method :find_or_create_system_role
323

  
324
  def check_the_prerequisites_for_copy_project_permission
325
    if self.permissions.include?(:copy_project) &&
326
        self.permissions.exclude?(:add_project) &&
327
        self.permissions.exclude?(:add_subprojects)
328
      errors.add(:base, l(:error_cannot_have_copy_project_permission))
329
    end
330
  end
321 331
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_to(l(: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_are_you_sure)}, :method => :post, :class => 'icon icon-lock' %>
app/views/roles/_form.html.erb
56 56
        <label class="floating">
57 57
        <%= check_box_tag 'role[permissions][]', permission.name, (@role.permissions.include? permission.name),
58 58
              :id => "role_permissions_#{permission.name}",
59
              :data => {:shows => ".#{permission.name}_shown"} %>
59
              :data => {:shows => ".#{permission.name}_shown" },
60
              :disabled => (true if permission.name == :copy_project && !@role.permissions.include?(:add_project) && !@role.permissions.include?(:add_subprojects)) %>
60 61
        <%= l_or_humanize(permission.name, :prefix => 'permission_') %>
61 62
        </label>
62 63
    <% end %>
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
44 44
        map.permission :manage_members, {:projects => :settings, :members => [:index, :show, :new, :create, :edit, :update, :destroy, :autocomplete]}, :require => :member
45 45
        map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member
46 46
        map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member
47
        map.permission :copy_project, {:projects => [:copy]}, :require => :member
47 48
        # Queries
48 49
        map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member
49 50
        map.permission :save_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin
public/javascripts/application.js
972 972
  event.preventDefault();
973 973
}
974 974

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

  
975 985
function toggleDisabledOnChange() {
976 986
  var checked = $(this).is(':checked');
977 987
  $($(this).data('disables')).attr('disabled', checked);
......
1029 1039
$(document).ready(function(){
1030 1040
  $('#content').on('change', 'input[data-disables], input[data-enables], input[data-shows]', toggleDisabledOnChange);
1031 1041
  toggleDisabledInit();
1042
  toggleCopyProjectCheckboxInit();
1032 1043

  
1033 1044
  $('#content').on('click', '.toggle-multiselect', function() {
1034 1045
    toggleMultiSelect($(this).siblings('select'));
test/functional/projects_controller_test.rb
1259 1259
    end
1260 1260
  end
1261 1261

  
1262
  def test_get_copy
1262
  def test_get_copy_by_admin_user
1263 1263
    @request.session[:user_id] = 1 # admin
1264
    orig = Project.find(1) # Login user is no member
1265
    get(:copy, :params => {:id => orig.id})
1266
    assert_response :success
1267

  
1268
    assert_select 'textarea[name=?]', 'project[description]', :text => orig.description
1269
    assert_select 'input[name=?][value=?]', 'project[enabled_module_names][]', 'issue_tracking', 1
1270
  end
1271

  
1272
  def test_get_copy_by_non_admin_user_with_copy_project_permission
1273
    @request.session[:user_id] = 3
1274
    Role.find(2).add_permission!(:copy_project, :add_project)
1264 1275
    orig = Project.find(1)
1265 1276
    get(:copy, :params => {:id => orig.id})
1266 1277
    assert_response :success
......
1269 1280
    assert_select 'input[name=?][value=?]', 'project[enabled_module_names][]', 'issue_tracking', 1
1270 1281
  end
1271 1282

  
1283
  def test_get_copy_by_non_admin_user_without_copy_project_permission_should_respond_with_403
1284
    @request.session[:user_id] = 3
1285
    Role.find(2).remove_permission! :copy_project
1286
    orig = Project.find(1)
1287
    get(:copy, :params => {:id => orig.id})
1288
    assert_response 403
1289
  end
1290

  
1272 1291
  def test_get_copy_with_invalid_source_should_respond_with_404
1273 1292
    @request.session[:user_id] = 1
1274 1293
    get(:copy, :params => {:id => 99})
......
1315 1334
    assert_equal 0, project.members.count
1316 1335
  end
1317 1336

  
1337
  def test_post_copy_by_non_admin_user_with_copy_project_and_add_project_permission
1338
    @request.session[:user_id] = 3
1339
    Role.find(2).add_permission!(:copy_project, :add_project)
1340
    CustomField.delete_all
1341

  
1342
    assert_difference 'Project.count' do
1343
      post(
1344
        :copy,
1345
        :params => {
1346
          :id => 1,
1347
          :project => {
1348
            :name => 'Copy',
1349
            :identifier => 'unique-copy',
1350
            :tracker_ids => ['1', '2', '3', ''],
1351
            :enabled_module_names => %w(issue_tracking time_tracking)
1352
          },
1353
          :only => %w(issues versions)
1354
        }
1355
      )
1356
    end
1357
    project = Project.find('unique-copy')
1358
    source = Project.find(1)
1359
    assert_equal %w(issue_tracking time_tracking), project.enabled_module_names.sort
1360

  
1361
    assert_equal source.versions.count, project.versions.count, "All versions were not copied"
1362
    assert_equal source.issues.count, project.issues.count, "All issues were not copied"
1363
    assert_equal 0, project.members.count
1364
  end
1365

  
1366
  def test_post_copy_by_non_admin_user_with_copy_project_and_add_subprojects_permission
1367
    @request.session[:user_id] = 3
1368
    Role.find(2).add_permission!(:copy_project, :add_subprojects)
1369
    CustomField.delete_all
1370

  
1371
    assert_difference 'Project.count' do
1372
      post(
1373
        :copy,
1374
        :params => {
1375
          :id => 1,
1376
          :project => {
1377
            :name => 'Copy',
1378
            :identifier => 'unique-copy',
1379
            :tracker_ids => ['1', '2', '3', ''],
1380
            :enabled_module_names => %w(issue_tracking time_tracking),
1381
            :parent_id => 1
1382
          },
1383
          :only => %w(issues versions)
1384
        }
1385
      )
1386
    end
1387
    project = Project.find('unique-copy')
1388
    source = Project.find(1)
1389
    assert_equal %w(issue_tracking time_tracking), project.enabled_module_names.sort
1390
    assert_equal source, project.parent
1391

  
1392
    assert_equal source.versions.count, project.versions.count, "All versions were not copied"
1393
    assert_equal source.issues.count, project.issues.count, "All issues were not copied"
1394
    assert_equal 0, project.members.count
1395
  end
1396

  
1318 1397
  def test_post_copy_should_redirect_to_settings_when_successful
1319 1398
    @request.session[:user_id] = 1 # admin
1320 1399
    post(
test/unit/role_test.rb
22 22
class RoleTest < ActiveSupport::TestCase
23 23
  fixtures :roles, :workflows, :trackers
24 24

  
25
  include Redmine::I18n
26

  
25 27
  def setup
26 28
    User.current = nil
27 29
  end
......
165 167
    assert_nil ActiveRecord::Base.connection.select_value("SELECT 1 FROM queries_roles WHERE role_id = #{role.id}")
166 168
    assert [1, 3], query.roles
167 169
  end
170

  
171
  def test_check_the_prerequisites_for_copy_project_permission
172
    role = Role.find(2)
173
    role.remove_permission!(:copy_project, :add_project, :add_subprojects)
174

  
175
    role.permissions = [:copy_project]
176
    assert_not role.valid?
177
    assert_equal l(:error_cannot_have_copy_project_permission), role.errors.messages[:base].first
178

  
179
    role.permissions = [:copy_project, :add_project]
180
    assert role.valid?
181

  
182
    role.permissions = [:copy_project, :add_subprojects]
183
    assert role.valid?
184
  end
168 185
end
(6-6/8)