@@ -206,9 +206,11 class IssuesController < ApplicationController | |||||
206 | end |
|
206 | end | |
207 | end |
|
207 | end | |
208 |
|
208 | |||
209 | # Bulk edit a set of issues |
|
209 | # Bulk edit/copy a set of issues | |
210 | def bulk_edit |
|
210 | def bulk_edit | |
211 | @issues.sort! |
|
211 | @issues.sort! | |
|
212 | @copy = params[:copy].present? | |||
|
213 | @notes = params[:notes] | |||
212 |
|
214 | |||
213 | if User.current.allowed_to?(:move_issues, @projects) |
|
215 | if User.current.allowed_to?(:move_issues, @projects) | |
214 | @allowed_projects = Issue.allowed_target_projects_on_move |
|
216 | @allowed_projects = Issue.allowed_target_projects_on_move | |
@@ -226,18 +228,21 class IssuesController < ApplicationController | |||||
226 | @assignables = target_projects.map(&:assignable_users).inject{|memo,a| memo & a} |
|
228 | @assignables = target_projects.map(&:assignable_users).inject{|memo,a| memo & a} | |
227 | @trackers = target_projects.map(&:trackers).inject{|memo,t| memo & t} |
|
229 | @trackers = target_projects.map(&:trackers).inject{|memo,t| memo & t} | |
228 |
|
230 | |||
229 | @notes = params[:notes] |
|
|||
230 | render :layout => false if request.xhr? |
|
231 | render :layout => false if request.xhr? | |
231 | end |
|
232 | end | |
232 |
|
233 | |||
233 | def bulk_update |
|
234 | def bulk_update | |
234 | @issues.sort! |
|
235 | @issues.sort! | |
|
236 | @copy = params[:copy].present? | |||
235 | attributes = parse_params_for_bulk_issue_attributes(params) |
|
237 | attributes = parse_params_for_bulk_issue_attributes(params) | |
236 |
|
238 | |||
237 | unsaved_issue_ids = [] |
|
239 | unsaved_issue_ids = [] | |
238 | moved_issues = [] |
|
240 | moved_issues = [] | |
239 | @issues.each do |issue| |
|
241 | @issues.each do |issue| | |
240 | issue.reload |
|
242 | issue.reload | |
|
243 | if @copy | |||
|
244 | issue = Issue.new.copy_from(issue) | |||
|
245 | end | |||
241 | journal = issue.init_journal(User.current, params[:notes]) |
|
246 | journal = issue.init_journal(User.current, params[:notes]) | |
242 | issue.safe_attributes = attributes |
|
247 | issue.safe_attributes = attributes | |
243 | call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) |
|
248 | call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) |
@@ -110,7 +110,7 | |||||
110 | <li><%= context_menu_link l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue}, |
|
110 | <li><%= context_menu_link l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue}, | |
111 | :class => 'icon-duplicate', :disabled => !@can[:copy] %></li> |
|
111 | :class => 'icon-duplicate', :disabled => !@can[:copy] %></li> | |
112 | <% end %> |
|
112 | <% end %> | |
113 |
<li><%= context_menu_link l(:button_copy), |
|
113 | <li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :copy => '1'}, | |
114 | :class => 'icon-copy', :disabled => !@can[:move] %></li> |
|
114 | :class => 'icon-copy', :disabled => !@can[:move] %></li> | |
115 | <li><%= context_menu_link l(:button_delete), issues_path(:ids => @issues.collect(&:id), :back_url => @back), |
|
115 | <li><%= context_menu_link l(:button_delete), issues_path(:ids => @issues.collect(&:id), :back_url => @back), | |
116 | :method => :delete, :confirm => issues_destroy_confirmation_message(@issues), :class => 'icon-del', :disabled => !@can[:delete] %></li> |
|
116 | :method => :delete, :confirm => issues_destroy_confirmation_message(@issues), :class => 'icon-del', :disabled => !@can[:delete] %></li> |
@@ -3,6 +3,6 | |||||
3 | <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue}, :class => 'icon icon-time-add' %> |
|
3 | <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue}, :class => 'icon icon-time-add' %> | |
4 | <%= watcher_tag(@issue, User.current) %> |
|
4 | <%= watcher_tag(@issue, User.current) %> | |
5 | <%= link_to_if_authorized l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-duplicate' %> |
|
5 | <%= link_to_if_authorized l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-duplicate' %> | |
6 |
<%= link_to_if_authorized l(:button_copy), {:controller => 'issue |
|
6 | <%= link_to_if_authorized l(:button_copy), {:controller => 'issues', :action => 'bulk_edit', :id => @issue, :copy => '1'}, :class => 'icon icon-copy' %> | |
7 | <%= link_to l(:button_delete), issue_path(@issue), :confirm => issues_destroy_confirmation_message(@issue), :method => :delete, :class => 'icon icon-del' if User.current.allowed_to?(:delete_issues, @project) %> |
|
7 | <%= link_to l(:button_delete), issue_path(@issue), :confirm => issues_destroy_confirmation_message(@issue), :method => :delete, :class => 'icon icon-del' if User.current.allowed_to?(:delete_issues, @project) %> | |
8 | </div> |
|
8 | </div> |
@@ -1,4 +1,4 | |||||
1 | <h2><%= l(:label_bulk_edit_selected_issues) %></h2> |
|
1 | <h2><%= @copy ? l(:button_copy) : l(:label_bulk_edit_selected_issues) %></h2> | |
2 |
|
2 | |||
3 | <ul><%= @issues.collect {|i| |
|
3 | <ul><%= @issues.collect {|i| | |
4 | content_tag('li', |
|
4 | content_tag('li', | |
@@ -102,7 +102,11 | |||||
102 | </div> |
|
102 | </div> | |
103 |
|
103 | |||
104 | <p> |
|
104 | <p> | |
105 |
<% if @ |
|
105 | <% if @copy %> | |
|
106 | <%= hidden_field_tag 'copy', '1' %> | |||
|
107 | <%= submit_tag l(:button_copy) %> | |||
|
108 | <%= submit_tag l(:button_copy_and_follow), :name => 'follow' %> | |||
|
109 | <% elsif @target_project %> | |||
106 | <%= submit_tag l(:button_move) %> |
|
110 | <%= submit_tag l(:button_move) %> | |
107 | <%= submit_tag l(:button_move_and_follow), :name => 'follow' %> |
|
111 | <%= submit_tag l(:button_move_and_follow), :name => 'follow' %> | |
108 | <% else %> |
|
112 | <% else %> |
@@ -40,9 +40,6 ActionController::Routing::Routes.draw do |map| | |||||
40 | end |
|
40 | end | |
41 | end |
|
41 | end | |
42 |
|
42 | |||
43 | map.resources :issue_moves, :only => [:new, :create], |
|
|||
44 | :path_prefix => '/issues', :as => 'move' |
|
|||
45 |
|
||||
46 | # Misc issue routes. TODO: move into resources |
|
43 | # Misc issue routes. TODO: move into resources | |
47 | map.auto_complete_issues '/issues/auto_complete', :controller => 'auto_completes', |
|
44 | map.auto_complete_issues '/issues/auto_complete', :controller => 'auto_completes', | |
48 | :action => 'issues', :conditions => { :method => :get } |
|
45 | :action => 'issues', :conditions => { :method => :get } |
@@ -76,7 +76,7 Redmine::AccessControl.map do |map| | |||||
76 | map.permission :add_issue_notes, {:issues => [:edit, :update], :journals => [:new]} |
|
76 | map.permission :add_issue_notes, {:issues => [:edit, :update], :journals => [:new]} | |
77 | map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin |
|
77 | map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin | |
78 | map.permission :edit_own_issue_notes, {:journals => :edit}, :require => :loggedin |
|
78 | map.permission :edit_own_issue_notes, {:journals => :edit}, :require => :loggedin | |
79 |
map.permission :move_issues, {:issue |
|
79 | map.permission :move_issues, {:issues => [:bulk_edit, :bulk_update]}, :require => :loggedin | |
80 | map.permission :delete_issues, {:issues => :destroy}, :require => :member |
|
80 | map.permission :delete_issues, {:issues => :destroy}, :require => :member | |
81 | # Queries |
|
81 | # Queries | |
82 | map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member |
|
82 | map.permission :manage_public_queries, {:queries => [:new, :create, :edit, :update, :destroy]}, :require => :member |
@@ -47,7 +47,7 class ContextMenusControllerTest < ActionController::TestCase | |||||
47 | :attributes => { :href => '/projects/ecookbook/issues/1/copy', |
|
47 | :attributes => { :href => '/projects/ecookbook/issues/1/copy', | |
48 | :class => 'icon-duplicate' } |
|
48 | :class => 'icon-duplicate' } | |
49 | assert_tag :tag => 'a', :content => 'Copy', |
|
49 | assert_tag :tag => 'a', :content => 'Copy', | |
50 |
:attributes => { :href => '/issues/ |
|
50 | :attributes => { :href => '/issues/bulk_edit?copy=1&ids%5B%5D=1', | |
51 | :class => 'icon-copy' } |
|
51 | :class => 'icon-copy' } | |
52 | assert_no_tag :tag => 'a', :content => 'Move' |
|
52 | assert_no_tag :tag => 'a', :content => 'Move' | |
53 | assert_tag :tag => 'a', :content => 'Delete', |
|
53 | assert_tag :tag => 'a', :content => 'Delete', | |
@@ -86,7 +86,7 class ContextMenusControllerTest < ActionController::TestCase | |||||
86 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bassigned_to_id%5D=3", |
|
86 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bassigned_to_id%5D=3", | |
87 | :class => '' } |
|
87 | :class => '' } | |
88 | assert_tag :tag => 'a', :content => 'Copy', |
|
88 | assert_tag :tag => 'a', :content => 'Copy', | |
89 |
:attributes => { :href => "/issues/ |
|
89 | :attributes => { :href => "/issues/bulk_edit?copy=1&#{ids}", | |
90 | :class => 'icon-copy' } |
|
90 | :class => 'icon-copy' } | |
91 | assert_no_tag :tag => 'a', :content => 'Move' |
|
91 | assert_no_tag :tag => 'a', :content => 'Move' | |
92 | assert_tag :tag => 'a', :content => 'Delete', |
|
92 | assert_tag :tag => 'a', :content => 'Delete', |
@@ -2319,6 +2319,87 class IssuesControllerTest < ActionController::TestCase | |||||
2319 | assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier |
|
2319 | assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier | |
2320 | end |
|
2320 | end | |
2321 |
|
2321 | |||
|
2322 | def test_bulk_copy_to_another_project | |||
|
2323 | @request.session[:user_id] = 2 | |||
|
2324 | assert_difference 'Issue.count', 2 do | |||
|
2325 | assert_no_difference 'Project.find(1).issues.count' do | |||
|
2326 | post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1' | |||
|
2327 | end | |||
|
2328 | end | |||
|
2329 | assert_redirected_to '/projects/ecookbook/issues' | |||
|
2330 | end | |||
|
2331 | ||||
|
2332 | def test_bulk_copy_should_allow_not_changing_the issue_attributes | |||
|
2333 | @request.session[:user_id] = 2 | |||
|
2334 | issue_before_move = Issue.find(1) | |||
|
2335 | assert_difference 'Issue.count', 1 do | |||
|
2336 | assert_no_difference 'Project.find(1).issues.count' do | |||
|
2337 | post :bulk_update, :ids => [1], :copy => '1', | |||
|
2338 | :issue => { | |||
|
2339 | :project_id => '2', :tracker_id => '', :assigned_to_id => '', | |||
|
2340 | :status_id => '', :start_date => '', :due_date => '' | |||
|
2341 | } | |||
|
2342 | end | |||
|
2343 | end | |||
|
2344 | issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2}) | |||
|
2345 | assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id | |||
|
2346 | assert_equal issue_before_move.status_id, issue_after_move.status_id | |||
|
2347 | assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id | |||
|
2348 | end | |||
|
2349 | ||||
|
2350 | def test_bulk_copy_should_allow_changing_the_issue_attributes | |||
|
2351 | # Fixes random test failure with Mysql | |||
|
2352 | # where Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2}) | |||
|
2353 | # doesn't return the expected results | |||
|
2354 | Issue.delete_all("project_id=2") | |||
|
2355 | ||||
|
2356 | @request.session[:user_id] = 2 | |||
|
2357 | assert_difference 'Issue.count', 2 do | |||
|
2358 | assert_no_difference 'Project.find(1).issues.count' do | |||
|
2359 | post :bulk_update, :ids => [1, 2], :copy => '1', | |||
|
2360 | :issue => { | |||
|
2361 | :project_id => '2', :tracker_id => '', :assigned_to_id => '4', | |||
|
2362 | :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31' | |||
|
2363 | } | |||
|
2364 | end | |||
|
2365 | end | |||
|
2366 | ||||
|
2367 | copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2}) | |||
|
2368 | assert_equal 2, copied_issues.size | |||
|
2369 | copied_issues.each do |issue| | |||
|
2370 | assert_equal 2, issue.project_id, "Project is incorrect" | |||
|
2371 | assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect" | |||
|
2372 | assert_equal 3, issue.status_id, "Status is incorrect" | |||
|
2373 | assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect" | |||
|
2374 | assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect" | |||
|
2375 | end | |||
|
2376 | end | |||
|
2377 | ||||
|
2378 | def test_bulk_copy_should_allow_adding_a_note | |||
|
2379 | @request.session[:user_id] = 2 | |||
|
2380 | assert_difference 'Issue.count', 1 do | |||
|
2381 | post :bulk_update, :ids => [1], :copy => '1', | |||
|
2382 | :notes => 'Copying one issue', | |||
|
2383 | :issue => { | |||
|
2384 | :project_id => '', :tracker_id => '', :assigned_to_id => '4', | |||
|
2385 | :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31' | |||
|
2386 | } | |||
|
2387 | end | |||
|
2388 | ||||
|
2389 | issue = Issue.first(:order => 'id DESC') | |||
|
2390 | assert_equal 1, issue.journals.size | |||
|
2391 | journal = issue.journals.first | |||
|
2392 | assert_equal 0, journal.details.size | |||
|
2393 | assert_equal 'Copying one issue', journal.notes | |||
|
2394 | end | |||
|
2395 | ||||
|
2396 | def test_bulk_copy_to_another_project_should_follow_when_needed | |||
|
2397 | @request.session[:user_id] = 2 | |||
|
2398 | post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1' | |||
|
2399 | issue = Issue.first(:order => 'id DESC') | |||
|
2400 | assert_redirected_to :controller => 'issues', :action => 'show', :id => issue | |||
|
2401 | end | |||
|
2402 | ||||
2322 | def test_destroy_issue_with_no_time_entries |
|
2403 | def test_destroy_issue_with_no_time_entries | |
2323 | assert_nil TimeEntry.find_by_issue_id(2) |
|
2404 | assert_nil TimeEntry.find_by_issue_id(2) | |
2324 | @request.session[:user_id] = 2 |
|
2405 | @request.session[:user_id] = 2 |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now