Feature #4687 » 4687-v4-for_v4.2.9.patch
| 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 |