You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006-2019 Jean-Philippe Lang
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; either version 2
  8. # of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. class IssueImport < Import
  19. def self.menu_item
  20. :issues
  21. end
  22. def self.authorized?(user)
  23. user.allowed_to?(:import_issues, nil, :global => true)
  24. end
  25. # Returns the objects that were imported
  26. def saved_objects
  27. object_ids = saved_items.pluck(:obj_id)
  28. objects = Issue.where(:id => object_ids).order(:id).preload(:tracker, :priority, :status)
  29. end
  30. # Returns a scope of projects that user is allowed to
  31. # import issue to
  32. def allowed_target_projects
  33. Project.allowed_to(user, :import_issues)
  34. end
  35. def project
  36. project_id = mapping['project_id'].to_i
  37. allowed_target_projects.find_by_id(project_id) || allowed_target_projects.first
  38. end
  39. # Returns a scope of trackers that user is allowed to
  40. # import issue to
  41. def allowed_target_trackers
  42. Issue.allowed_target_trackers(project, user)
  43. end
  44. def tracker
  45. if mapping['tracker'].to_s =~ /\Avalue:(\d+)\z/
  46. tracker_id = $1.to_i
  47. allowed_target_trackers.find_by_id(tracker_id)
  48. end
  49. end
  50. # Returns true if missing categories should be created during the import
  51. def create_categories?
  52. user.allowed_to?(:manage_categories, project) &&
  53. mapping['create_categories'] == '1'
  54. end
  55. # Returns true if missing versions should be created during the import
  56. def create_versions?
  57. user.allowed_to?(:manage_versions, project) &&
  58. mapping['create_versions'] == '1'
  59. end
  60. def mappable_custom_fields
  61. if tracker
  62. issue = Issue.new
  63. issue.project = project
  64. issue.tracker = tracker
  65. issue.editable_custom_field_values(user).map(&:custom_field)
  66. elsif project
  67. project.all_issue_custom_fields
  68. else
  69. []
  70. end
  71. end
  72. private
  73. def build_object(row, item)
  74. issue = Issue.new
  75. issue.author = user
  76. issue.notify = !!ActiveRecord::Type::Boolean.new.cast(settings['notifications'])
  77. tracker_id = nil
  78. if tracker
  79. tracker_id = tracker.id
  80. elsif tracker_name = row_value(row, 'tracker')
  81. tracker_id = allowed_target_trackers.named(tracker_name).first.try(:id)
  82. end
  83. attributes = {
  84. 'project_id' => mapping['project_id'],
  85. 'tracker_id' => tracker_id,
  86. 'subject' => row_value(row, 'subject'),
  87. 'description' => row_value(row, 'description')
  88. }
  89. if status_name = row_value(row, 'status')
  90. if status_id = IssueStatus.named(status_name).first.try(:id)
  91. attributes['status_id'] = status_id
  92. end
  93. end
  94. issue.send :safe_attributes=, attributes, user
  95. attributes = {}
  96. if priority_name = row_value(row, 'priority')
  97. if priority_id = IssuePriority.active.named(priority_name).first.try(:id)
  98. attributes['priority_id'] = priority_id
  99. end
  100. end
  101. if issue.project && category_name = row_value(row, 'category')
  102. if category = issue.project.issue_categories.named(category_name).first
  103. attributes['category_id'] = category.id
  104. elsif create_categories?
  105. category = issue.project.issue_categories.build
  106. category.name = category_name
  107. if category.save
  108. attributes['category_id'] = category.id
  109. end
  110. end
  111. end
  112. if assignee_name = row_value(row, 'assigned_to')
  113. if assignee = Principal.detect_by_keyword(issue.assignable_users, assignee_name)
  114. attributes['assigned_to_id'] = assignee.id
  115. end
  116. end
  117. if issue.project && version_name = row_value(row, 'fixed_version')
  118. version =
  119. issue.project.versions.named(version_name).first ||
  120. issue.project.shared_versions.named(version_name).first
  121. if version
  122. attributes['fixed_version_id'] = version.id
  123. elsif create_versions?
  124. version = issue.project.versions.build
  125. version.name = version_name
  126. if version.save
  127. attributes['fixed_version_id'] = version.id
  128. end
  129. end
  130. end
  131. if is_private = row_value(row, 'is_private')
  132. if yes?(is_private)
  133. attributes['is_private'] = '1'
  134. end
  135. end
  136. if parent_issue_id = row_value(row, 'parent_issue_id')
  137. if parent_issue_id =~ /\A(#)?(\d+)\z/
  138. parent_issue_id = $2.to_i
  139. if $1
  140. attributes['parent_issue_id'] = parent_issue_id
  141. else
  142. if parent_issue_id > item.position
  143. add_callback(parent_issue_id, 'set_as_parent', item.position)
  144. elsif issue_id = items.where(:position => parent_issue_id).first.try(:obj_id)
  145. attributes['parent_issue_id'] = issue_id
  146. end
  147. end
  148. else
  149. attributes['parent_issue_id'] = parent_issue_id
  150. end
  151. end
  152. if start_date = row_date(row, 'start_date')
  153. attributes['start_date'] = start_date
  154. end
  155. if due_date = row_date(row, 'due_date')
  156. attributes['due_date'] = due_date
  157. end
  158. if estimated_hours = row_value(row, 'estimated_hours')
  159. attributes['estimated_hours'] = estimated_hours
  160. end
  161. if done_ratio = row_value(row, 'done_ratio')
  162. attributes['done_ratio'] = done_ratio
  163. end
  164. attributes['custom_field_values'] = issue.custom_field_values.inject({}) do |h, v|
  165. value = case v.custom_field.field_format
  166. when 'date'
  167. row_date(row, "cf_#{v.custom_field.id}")
  168. else
  169. row_value(row, "cf_#{v.custom_field.id}")
  170. end
  171. if value
  172. h[v.custom_field.id.to_s] = v.custom_field.value_from_keyword(value, issue)
  173. end
  174. h
  175. end
  176. issue.send :safe_attributes=, attributes, user
  177. if issue.tracker_id != tracker_id
  178. issue.tracker_id = nil
  179. end
  180. issue
  181. end
  182. # Callback that sets issue as the parent of a previously imported issue
  183. def set_as_parent_callback(issue, child_position)
  184. child_id = items.where(:position => child_position).first.try(:obj_id)
  185. return unless child_id
  186. child = Issue.find_by_id(child_id)
  187. return unless child
  188. child.parent_issue_id = issue.id
  189. child.save!
  190. issue.reload
  191. end
  192. end