1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006-2023 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'csv'
class ImportsController < ApplicationController
before_action :find_import, :only => [:show, :settings, :mapping, :run]
before_action :authorize_import
layout :import_layout
helper :issues
helper :queries
def new
@import = import_type.new
end
def create
@import = import_type.new
@import.user = User.current
@import.file = params[:file]
@import.set_default_settings(:project_id => params[:project_id])
if @import.save
redirect_to import_settings_path(@import)
else
render :action => 'new'
end
end
def show
end
def settings
if request.post? && @import.parse_file
if @import.total_items == 0
flash.now[:error] = l(:error_no_data_in_file)
else
redirect_to import_mapping_path(@import)
end
end
rescue CSV::MalformedCSVError, EncodingError => e
if e.is_a?(CSV::MalformedCSVError) && !e.message.include?('Invalid byte sequence')
flash.now[:error] = l(:error_invalid_csv_file_or_settings, e.message)
else
flash.now[:error] = l(:error_invalid_file_encoding, :encoding => ERB::Util.h(@import.settings['encoding']))
end
rescue SystemCallError => e
flash.now[:error] = l(:error_can_not_read_import_file)
end
def mapping
@custom_fields = @import.mappable_custom_fields
if request.get?
auto_map_fields
elsif request.post?
respond_to do |format|
format.html do
if params[:previous]
redirect_to import_settings_path(@import)
else
redirect_to import_run_path(@import)
end
end
format.js # updates mapping form on project or tracker change
end
end
end
def run
if request.post?
@current = @import.run(
:max_items => max_items_per_request,
:max_time => 10.seconds
)
respond_to do |format|
format.html do
if @import.finished?
redirect_to import_path(@import)
else
redirect_to import_run_path(@import)
end
end
format.js
end
end
end
def current_menu(project)
if import_layout == 'admin'
nil
else
:application_menu
end
end
private
def find_import
@import = Import.where(:user_id => User.current.id, :filename => params[:id]).first
if @import.nil?
render_404
return
elsif @import.finished? && action_name != 'show'
redirect_to import_path(@import)
return
end
update_from_params if request.post?
end
def update_from_params
if params[:import_settings].present?
@import.settings ||= {}
@import.settings.merge!(params[:import_settings].to_unsafe_hash)
@import.save!
end
end
def max_items_per_request
5
end
def import_layout
import_type && import_type.layout || 'base'
end
def menu_items
menu_item = import_type ? import_type.menu_item : nil
{self.controller_name.to_sym => {:actions => {}, :default => menu_item}}
end
def authorize_import
return render_404 unless import_type
return render_403 unless import_type.authorized?(User.current)
end
def import_type
return @import_type if defined? @import_type
@import_type =
if @import
@import.class
else
type =
begin
Object.const_get(params[:type])
rescue
nil
end
type && type < Import ? type : nil
end
end
def auto_map_fields
# Try to auto map fields only when settings['enconding'] is present
# otherwhise, the import fails for non UTF-8 files because the headers
# cannot be retrieved (Invalid byte sequence in UTF-8)
return if @import.settings['encoding'].blank?
mappings = @import.settings['mapping'] ||= {}
headers = @import.headers.map{|header| header&.downcase}
# Core fields
import_type::AUTO_MAPPABLE_FIELDS.each do |field_nm, label_nm|
next if mappings.include?(field_nm)
index = headers.index(field_nm) || headers.index(l(label_nm).downcase)
if index
mappings[field_nm] = index
end
end
# Custom fields
@custom_fields.each do |field|
field_nm = "cf_#{field.id}"
next if mappings.include?(field_nm)
index = headers.index(field_nm) || headers.index(field.name.downcase)
if index
mappings[field_nm] = index
end
end
mappings
end
end
|