]> source.dussan.org Git - redmine.git/commitdiff
Allow forward reference to parent when importing issues (#22701).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sat, 21 Jan 2017 09:08:40 +0000 (09:08 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sat, 21 Jan 2017 09:08:40 +0000 (09:08 +0000)
git-svn-id: http://svn.redmine.org/redmine/trunk@16241 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/models/import.rb
app/models/issue_import.rb
test/fixtures/files/import_subtasks.csv [new file with mode: 0644]
test/unit/issue_import_test.rb

index 22b90b6ac589b5387c85bc1cf47bc3815be5e7c6..b3aa914afc1ff118db7bd12934f65f81e1d00eed 100644 (file)
@@ -138,6 +138,24 @@ class Import < ActiveRecord::Base
     settings['mapping'] || {}
   end
 
+  # Adds a callback that will be called after the item at given position is imported
+  def add_callback(position, name, *args)
+    settings['callbacks'] ||= {}
+    settings['callbacks'][position.to_i] ||= []
+    settings['callbacks'][position.to_i] << [name, args]
+    save!
+  end
+
+  # Executes the callbacks for the given object
+  def do_callbacks(position, object)
+    if callbacks = (settings['callbacks'] || {}).delete(position)
+      callbacks.each do |name, args|
+        send "#{name}_callback", object, *args
+      end
+      save!
+    end
+  end
+
   # Imports items and returns the position of the last processed item
   def run(options={})
     max_items = options[:max_items]
@@ -157,7 +175,7 @@ class Import < ActiveRecord::Base
         item = items.build
         item.position = position
 
-        if object = build_object(row)
+        if object = build_object(row, item)
           if object.save
             item.obj_id = object.id
           else
@@ -167,6 +185,8 @@ class Import < ActiveRecord::Base
 
         item.save!
         imported += 1
+
+        do_callbacks(item.position, object)
       end
       current = position
     end
index 4ecd4b517b26d26fdaddb82f4ea3073ecbb16fb6..83a104b37de7a6919d5c98b57f293d70f540c528 100644 (file)
@@ -74,7 +74,7 @@ class IssueImport < Import
 
   private
 
-  def build_object(row)
+  def build_object(row, item)
     issue = Issue.new
     issue.author = user
     issue.notify = false
@@ -139,11 +139,15 @@ class IssueImport < Import
     end
     if parent_issue_id = row_value(row, 'parent_issue_id')
       if parent_issue_id =~ /\A(#)?(\d+)\z/
-        parent_issue_id = $2
+        parent_issue_id = $2.to_i
         if $1
           attributes['parent_issue_id'] = parent_issue_id
-        elsif issue_id = items.where(:position => parent_issue_id).first.try(:obj_id)
-          attributes['parent_issue_id'] = issue_id
+        else
+          if parent_issue_id > item.position
+            add_callback(parent_issue_id, 'set_as_parent', item.position)
+          elsif issue_id = items.where(:position => parent_issue_id).first.try(:obj_id)
+            attributes['parent_issue_id'] = issue_id
+          end
         end
       else
         attributes['parent_issue_id'] = parent_issue_id
@@ -183,4 +187,17 @@ class IssueImport < Import
 
     issue
   end
+
+  # Callback that sets issue as the parent of a previously imported issue
+  def set_as_parent_callback(issue, child_position)
+    child_id = items.where(:position => child_position).first.try(:obj_id)
+    return unless child_id
+
+    child = Issue.find_by_id(child_id)
+    return unless child
+
+    child.parent_issue_id = issue.id
+    child.save!
+    issue.reload
+  end
 end
diff --git a/test/fixtures/files/import_subtasks.csv b/test/fixtures/files/import_subtasks.csv
new file mode 100644 (file)
index 0000000..1e78951
--- /dev/null
@@ -0,0 +1,5 @@
+row;tracker;subject;parent
+1;bug;Root;
+2;bug;Child 1;1
+3;bug;Grand-child;4
+4;bug;Child 2;1
index 9f238cafef67432a309fa977818dfcc262d9a2f8..8a0d2436642b0ce2cbfbceed009bf19f0eb6aad6 100644 (file)
@@ -115,6 +115,19 @@ class IssueImportTest < ActiveSupport::TestCase
     assert_equal 2, issues[2].parent_id
   end
 
+  def test_backward_and_forward_reference_to_parent_should_work
+    import = generate_import('import_subtasks.csv')
+    import.settings = {
+      'separator' => ";", 'wrapper' => '"', 'encoding' => "UTF-8",
+      'mapping' => {'project_id' => '1', 'tracker' => '1', 'subject' => '2', 'parent_issue_id' => '3'}
+    }
+    import.save!
+
+    root, child1, grandchild, child2 = new_records(Issue, 4) { import.run }
+    assert_equal root, child1.parent
+    assert_equal child2, grandchild.parent
+  end
+
   def test_assignee_should_be_set
     import = generate_import_with_mapping
     import.mapping.merge!('assigned_to' => '11')