end
def create
- @relation = IssueRelation.new
- @relation.issue_from = @issue
- @relation.safe_attributes = params[:relation]
- @relation.init_journals(User.current)
+ saved = false
+ params_relation = params[:relation]
+ unsaved_relations = []
+
+ relation_issues_to_id.each do |issue_to_id|
+ params_relation[:issue_to_id] = issue_to_id
+
+ @relation = IssueRelation.new
+ @relation.issue_from = @issue
+ @relation.safe_attributes = params_relation
+ @relation.init_journals(User.current)
- begin
- saved = @relation.save
- rescue ActiveRecord::RecordNotUnique
- saved = false
- @relation.errors.add :base, :taken
+ unless saved = @relation.save
+ saved = false
+ unsaved_relations << @relation
+ end
end
respond_to do |format|
format.html {redirect_to issue_path(@issue)}
format.js do
@relations = @issue.reload.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible?}
+ @unsaved_relations = unsaved_relations
end
format.api do
if saved
rescue ActiveRecord::RecordNotFound
render_404
end
+
+ def relation_issues_to_id
+ params[:relation].require(:issue_to_id).split(',').reject(&:blank?)
+ rescue ActionController::ParameterMissing => e
+ # We return a empty array just to loop once and return a validation error
+ # ToDo: Find a better method to return an error if the param is missing.
+ ['']
+ end
end
values = IssueRelation::TYPES
values.keys.sort_by{|k| values[k][:order]}.collect{|k| [l(values[k][:name]), k]}
end
+
+ def relation_error_messages(relations)
+ messages = {}
+ relations.each do |item|
+ item.errors.full_messages.each do |message|
+ messages[message] ||= []
+ messages[message] << item
+ end
+ end
+
+ messages.map do |message, items|
+ ids = items.map(&:issue_to_id).compact
+ if ids.empty?
+ message
+ else
+ "#{message}: ##{ids.join(', ')}"
+ end
+ end
+ end
end
-<%= error_messages_for 'relation' %>
-
+<% unsaved_relations_ids = '' %>
+<% if @unsaved_relations && @unsaved_relations.any? %>
+ <% unsaved_relations_ids = @unsaved_relations.map(&:issue_to_id).compact.join(", ") %>
+ <div id="errorExplanation">
+ <ul>
+ <% relation_error_messages(@unsaved_relations).each do |message| %>
+ <li><%= message %></li>
+ <% end %>
+ </ul>
+ </div>
+<% end %>
<p><%= f.select :relation_type, collection_for_relation_type_select, {}, :onchange => "setPredecessorFieldsVisibility();" %>
-<%= l(:label_issue) %> #<%= f.text_field :issue_to_id, :size => 10 %>
+<%= l(:label_issue) %> #<%= f.text_field :issue_to_id, :value => unsaved_relations_ids, :size => 10 %>
<span id="predecessor_fields" style="display:none;">
<%= l(:field_delay) %>: <%= f.text_field :delay, :size => 3 %> <%= l(:label_day_plural) %>
</span>
assert_include 'Related issue cannot be blank', response.body
end
+ def test_bulk_create_with_multiple_issue_to_id_issues
+ assert_difference 'IssueRelation.count', +3 do
+ post :create, :params => {
+ :issue_id => 1,
+ :relation => {
+ # js autocomplete adds a comma at the end
+ # issue to id should accept both id and hash with id
+ :issue_to_id => '2,3,#7, ',
+ :relation_type => 'relates',
+ :delay => ''
+ }
+ },
+ :xhr => true
+ end
+
+ assert_response :success
+ relations = IssueRelation.where(:issue_from_id => 1, :issue_to_id => [2, 3, 7])
+ assert_equal 3, relations.count
+ # all relations types should be 'relates'
+ relations.map {|r| assert_equal 'relates', r.relation_type}
+
+ # no error messages should be returned in the response
+ assert_not_include 'id=\"errorExplanation\"', response.body
+ end
+
+ def test_bulk_create_should_show_errors
+ assert_difference 'IssueRelation.count', +3 do
+ post :create, :params => {
+ :issue_id => 1,
+ :relation => {
+ :issue_to_id => '1,2,3,4,5,7',
+ :relation_type => 'relates',
+ :delay => ''
+ }
+ },
+ :xhr => true
+ end
+
+ assert_response :success
+ assert_equal 'text/javascript', response.media_type
+ # issue #1 is invalid
+ assert_include 'Related issue is invalid: #1', response.body
+ # issues #4 and #5 can't be related by default
+ assert_include 'Related issue cannot be blank', response.body
+ assert_include 'Related issue doesn't belong to the same project', response.body
+ end
+
def test_destroy
assert_difference 'IssueRelation.count', -1 do
delete(:destroy, :params => {:id => '2'})