@projects = @issues.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1
- @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
+ @can = {:edit => User.current.allowed_to?(:edit_issues, @projects),
:log_time => (@project && User.current.allowed_to?(:log_time, @project)),
- :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
+ :update => (User.current.allowed_to?(:edit_issues, @projects) || (User.current.allowed_to?(:change_status, @projects) && !@allowed_statuses.blank?)),
:move => (@project && User.current.allowed_to?(:move_issues, @project)),
:copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
:delete => User.current.allowed_to?(:delete_issues, @projects)
@assignables = @project.assignable_users
@assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
@trackers = @project.trackers
+ else
+ #when multiple projects, we only keep the intersection of each set
+ @assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
+ @trackers = @projects.map(&:trackers).inject{|memo,t| memo & t}
end
@priorities = IssuePriority.all.reverse
before_filter :find_issue, :only => [:show, :edit, :update]
before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :move, :perform_move, :destroy]
- before_filter :check_project_uniqueness, :only => [:bulk_edit, :bulk_update, :move, :perform_move]
+ before_filter :check_project_uniqueness, :only => [:move, :perform_move]
before_filter :find_project, :only => [:new, :create]
before_filter :authorize, :except => [:index]
before_filter :find_optional_project, :only => [:index]
# Bulk edit a set of issues
def bulk_edit
@issues.sort!
- @available_statuses = Workflow.available_statuses(@project)
- @custom_fields = @project.all_issue_custom_fields
+ @available_statuses = @projects.map{|p|Workflow.available_statuses(p)}.inject{|memo,w|memo & w}
+ @custom_fields = @projects.map{|p|p.all_issue_custom_fields}.inject{|memo,c|memo & c}
+ @assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
+ @trackers = @projects.map(&:trackers).inject{|memo,t| memo & t}
end
def bulk_update
</li>
<% end %>
- <% if @projects.size == 1 %>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_priority) %></a>
<ul>
<% end -%>
</ul>
</li>
- <% end %>
+ <% #TODO: allow editing versions when multiple projects %>
<% unless @project.nil? || @project.shared_versions.open.empty? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
</li>
<% end -%>
- <% if Issue.use_field_for_done_ratio? && @projects.size == 1 %>
+ <% if Issue.use_field_for_done_ratio? %>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
<ul>
<div class="splitcontentleft">
<p>
<label><%= l(:field_tracker) %></label>
- <%= select_tag('issue[tracker_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@project.trackers, :id, :name)) %>
+ <%= select_tag('issue[tracker_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@trackers, :id, :name)) %>
</p>
<% if @available_statuses.any? %>
<p>
<label><%= l(:field_assigned_to) %></label>
<%= select_tag('issue[assigned_to_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_nobody), :value => 'none') +
- options_from_collection_for_select(@project.assignable_users, :id, :name)) %>
+ options_from_collection_for_select(@assignables, :id, :name)) %>
</p>
+<% if @project %>
<p>
<label><%= l(:field_category) %></label>
<%= select_tag('issue[category_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_none), :value => 'none') +
options_from_collection_for_select(@project.issue_categories, :id, :name)) %>
</p>
+<% end %>
+<% #TODO: allow editing versions when multiple projects %>
+<% if @project %>
<p>
<label><%= l(:field_fixed_version) %></label>
<%= select_tag('issue[fixed_version_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_none), :value => 'none') +
version_options_for_select(@project.shared_versions.open)) %>
</p>
+<% end %>
<% @custom_fields.each do |custom_field| %>
<p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('issue', custom_field) %></p>
assert_response :success
assert_template 'context_menu'
ids = "ids%5B%5D=1&ids%5B%5D=2&ids%5B%5D=6"
+ assert_tag :tag => 'a', :content => 'Edit',
+ :attributes => { :href => "/issues/bulk_edit?#{ids}",
+ :class => 'icon-edit' }
+ assert_tag :tag => 'a', :content => 'Closed',
+ :attributes => { :href => "/issues/bulk_edit?#{ids}&issue%5Bstatus_id%5D=5",
+ :class => '' }
+ assert_tag :tag => 'a', :content => 'Immediate',
+ :attributes => { :href => "/issues/bulk_edit?#{ids}&issue%5Bpriority_id%5D=8",
+ :class => '' }
+ assert_tag :tag => 'a', :content => 'John Smith',
+ :attributes => { :href => "/issues/bulk_edit?#{ids}&issue%5Bassigned_to_id%5D=2",
+ :class => '' }
assert_tag :tag => 'a', :content => 'Delete',
:attributes => { :href => "/issues/destroy?#{ids}",
:class => 'icon-del' }
assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
end
+ def test_get_bulk_edit_on_different_projects
+ @request.session[:user_id] = 2
+ get :bulk_edit, :ids => [1, 2, 6]
+ assert_response :success
+ assert_template 'bulk_edit'
+
+ # Project specific custom field, date type
+ field = CustomField.find(9)
+ assert !field.is_for_all?
+ assert !field.project_ids.include?(Issue.find(6).project_id)
+ assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
+ end
+
def test_bulk_update
@request.session[:user_id] = 2
# update issues priority
assert_equal 1, journal.details.size
end
+ def test_bulk_update_on_different_projects
+ @request.session[:user_id] = 2
+ # update issues priority
+ post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
+ :issue => {:priority_id => 7,
+ :assigned_to_id => '',
+ :custom_field_values => {'2' => ''}}
+
+ assert_response 302
+ # check that the issues were updated
+ assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
+
+ issue = Issue.find(1)
+ journal = issue.journals.find(:first, :order => 'created_on DESC')
+ assert_equal '125', issue.custom_value_for(2).value
+ assert_equal 'Bulk editing', journal.notes
+ assert_equal 1, journal.details.size
+ end
+
+ def test_bulk_update_on_different_projects_without_rights
+ @request.session[:user_id] = 3
+ user = User.find(3)
+ action = { :controller => "issues", :action => "bulk_update" }
+ assert user.allowed_to?(action, Issue.find(1).project)
+ assert ! user.allowed_to?(action, Issue.find(6).project)
+ post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
+ :issue => {:priority_id => 7,
+ :assigned_to_id => '',
+ :custom_field_values => {'2' => ''}}
+ assert_response 403
+ assert_not_equal "Bulk should fail", Journal.last.notes
+ end
+
def test_bullk_update_should_send_a_notification
@request.session[:user_id] = 2
ActionMailer::Base.deliveries.clear