Project

General

Profile

Feature #4687 » 4687-v4-for_v4.2.9.patch

Markus Boremski, 2023-02-07 09:40

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_rss_auth :index
34 34
  accept_api_auth :index, :show, :create, :update, :destroy
35 35
  require_sudo_mode :destroy
......
139 139
  end
140 140

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

  
81 83
  validates_inclusion_of(
82 84
    :issues_visibility,
83 85
    :in => ISSUES_VISIBILITY_OPTIONS.collect(&:first),
......
320 322
    role
321 323
  end
322 324
  private_class_method :find_or_create_system_role
325

  
326
  def check_the_prerequisites_for_copy_project_permission
327
    if self.permissions.include?(:copy_project) &&
328
        self.permissions.exclude?(:add_project) &&
329
        self.permissions.exclude?(:add_subprojects)
330
      errors.add(:base, l(:error_cannot_have_copy_project_permission))
331
    end
332
  end
323 333
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/de.yml
905 905
  permission_add_issue_notes: Kommentare hinzufügen
906 906
  permission_add_issue_watchers: Beobachter hinzufügen
907 907
  permission_add_issues: Tickets hinzufügen
908
  permission_copy_project: Projekt kopieren
908 909
  permission_add_messages: Forenbeiträge hinzufügen
909 910
  permission_add_project: Projekt erstellen
910 911
  permission_add_subprojects: Unterprojekte erstellen
config/locales/en.yml
244 244
  error_attachment_not_found: "Attachment %{name} not found"
245 245
  error_invalid_authenticity_token: "Invalid form authenticity token."
246 246
  error_query_statement_invalid: "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
247
  error_cannot_have_copy_project_permission: "Can't have copy_project permission without add_project permission or add_subprojects permission."
247 248

  
248 249
  mail_subject_lost_password: "Your %{value} password"
249 250
  mail_body_lost_password: 'To change your password, click on the following link:'
......
511 512
  permission_add_project: Create project
512 513
  permission_add_subprojects: Create subprojects
513 514
  permission_edit_project: Edit project
515
  permission_copy_project: Copy project
514 516
  permission_close_project: Close / reopen the project
515 517
  permission_delete_project: Delete the project
516 518
  permission_select_project_modules: Select project modules
lib/redmine.rb
90 90
  map.permission :manage_members, {:projects => :settings, :members => [:index, :show, :new, :create, :edit, :update, :destroy, :autocomplete]}, :require => :member
91 91
  map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member
92 92
  map.permission :add_subprojects, {:projects => [:new, :create]}, :require => :member
93
  map.permission :copy_project, {:projects => [:copy]}, :require => :member
93 94
  # Queries
94 95
  map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member
95 96
  map.permission :save_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin
public/javascripts/application.js
967 967
  event.preventDefault();
968 968
}
969 969

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

  
970 980
function toggleDisabledOnChange() {
971 981
  var checked = $(this).is(':checked');
972 982
  $($(this).data('disables')).attr('disabled', checked);
......
1024 1034
$(document).ready(function(){
1025 1035
  $('#content').on('change', 'input[data-disables], input[data-enables], input[data-shows]', toggleDisabledOnChange);
1026 1036
  toggleDisabledInit();
1037
  toggleCopyProjectCheckboxInit();
1027 1038

  
1028 1039
  $('#content').on('click', '.toggle-multiselect', function() {
1029 1040
    toggleMultiSelect($(this).siblings('select'));
test/functional/projects_controller_test.rb
1234 1234
    end
1235 1235
  end
1236 1236

  
1237
  def test_get_copy
1237
  def test_get_copy_by_admin_user
1238 1238
    @request.session[:user_id] = 1 # admin
1239
    orig = Project.find(1) # Login user is no member
1240
    get(:copy, :params => {:id => orig.id})
1241
    assert_response :success
1242

  
1243
    assert_select 'textarea[name=?]', 'project[description]', :text => orig.description
1244
    assert_select 'input[name=?][value=?]', 'project[enabled_module_names][]', 'issue_tracking', 1
1245
  end
1246

  
1247
  def test_get_copy_by_non_admin_user_with_copy_project_permission
1248
    @request.session[:user_id] = 3
1249
    Role.find(2).add_permission!(:copy_project, :add_project)
1239 1250
    orig = Project.find(1)
1240 1251
    get(:copy, :params => {:id => orig.id})
1241 1252
    assert_response :success
......
1244 1255
    assert_select 'input[name=?][value=?]', 'project[enabled_module_names][]', 'issue_tracking', 1
1245 1256
  end
1246 1257

  
1258
  def test_get_copy_by_non_admin_user_without_copy_project_permission_should_respond_with_403
1259
    @request.session[:user_id] = 3
1260
    Role.find(2).remove_permission! :copy_project
1261
    orig = Project.find(1)
1262
    get(:copy, :params => {:id => orig.id})
1263
    assert_response 403
1264
  end
1265

  
1247 1266
  def test_get_copy_with_invalid_source_should_respond_with_404
1248 1267
    @request.session[:user_id] = 1
1249 1268
    get(:copy, :params => {:id => 99})
......
1290 1309
    assert_equal 0, project.members.count
1291 1310
  end
1292 1311

  
1312
  def test_post_copy_by_non_admin_user_with_copy_project_and_add_project_permission
1313
    @request.session[:user_id] = 3
1314
    Role.find(2).add_permission!(:copy_project, :add_project)
1315
    CustomField.delete_all
1316

  
1317
    assert_difference 'Project.count' do
1318
      post(
1319
        :copy,
1320
        :params => {
1321
          :id => 1,
1322
          :project => {
1323
            :name => 'Copy',
1324
            :identifier => 'unique-copy',
1325
            :tracker_ids => ['1', '2', '3', ''],
1326
            :enabled_module_names => %w(issue_tracking time_tracking)
1327
          },
1328
          :only => %w(issues versions)
1329
        }
1330
      )
1331
    end
1332
    project = Project.find('unique-copy')
1333
    source = Project.find(1)
1334
    assert_equal %w(issue_tracking time_tracking), project.enabled_module_names.sort
1335

  
1336
    assert_equal source.versions.count, project.versions.count, "All versions were not copied"
1337
    assert_equal source.issues.count, project.issues.count, "All issues were not copied"
1338
    assert_equal 0, project.members.count
1339
  end
1340

  
1341
  def test_post_copy_by_non_admin_user_with_copy_project_and_add_subprojects_permission
1342
    @request.session[:user_id] = 3
1343
    Role.find(2).add_permission!(:copy_project, :add_subprojects)
1344
    CustomField.delete_all
1345

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

  
1367
    assert_equal source.versions.count, project.versions.count, "All versions were not copied"
1368
    assert_equal source.issues.count, project.issues.count, "All issues were not copied"
1369
    assert_equal 0, project.members.count
1370
  end
1371

  
1293 1372
  def test_post_copy_should_redirect_to_settings_when_successful
1294 1373
    @request.session[:user_id] = 1 # admin
1295 1374
    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
......
228 230
      assert_equal Role::BUILTIN_NON_MEMBER, role.builtin
229 231
    end
230 232
  end
233

  
234
  def test_check_the_prerequisites_for_copy_project_permission
235
    role = Role.find(2)
236
    role.remove_permission!(:copy_project, :add_project, :add_subprojects)
237

  
238
    role.permissions = [:copy_project]
239
    assert_not role.valid?
240
    assert_equal l(:error_cannot_have_copy_project_permission), role.errors.messages[:base].first
241

  
242
    role.permissions = [:copy_project, :add_project]
243
    assert role.valid?
244

  
245
    role.permissions = [:copy_project, :add_subprojects]
246
    assert role.valid?
247
  end
231 248
end
(7-7/8)