forked from xurizaemon/csvimport
-
Notifications
You must be signed in to change notification settings - Fork 2
/
csvimport.module
215 lines (202 loc) · 7.39 KB
/
csvimport.module
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
205
206
207
208
209
210
211
212
213
214
215
<?php
/**
* Demonstration module.
*
* - Provide form for upload of a CSV file.
* - On submission, trigger a batch task which iterates through each row in the file.
*/
/**
* Implement hook_menu()
*/
function csvimport_menu() {
$items['admin/content/csv_import'] = array(
'title' => 'Import CSV',
'description' => 'Import content from a <abbr title="Comma Separated Values">CSV</abbr> or <abbr title="Tab Separated Values">TSV</abbr> file.',
// 'access callback' => 'user_access',
'access arguments' => array('administer site configuration'),
'page callback' => 'drupal_get_form',
'page arguments' => array('csvimport_form'),
// 'file' => 'csvimport.admin.inc',
);
return $items ;
}
/**
* Build a form to upload CSV to.
*/
function csvimport_form() {
$form['#attributes'] = array(
'enctype' => 'multipart/form-data'
);
$form['csvfile'] = array(
'#title' => t('CSV File'),
'#type' => 'file',
'#description' => ($max_size = parse_size(ini_get('upload_max_filesize'))) ? t('Due to server restrictions, the <strong>maximum upload file size is !max_size</strong>. Files that exceed this size will be disregarded.', array('!max_size' => format_size($max_size))) : '',
) ;
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Commence Import'),
) ;
$form['#validate'] = array(
'csvimport_validate_fileupload',
'csvimport_form_validate',
) ;
return $form ;
}
/**
* Validate the file upload. It must be a CSV, and we must
* successfully save it to our import directory.
*/
function csvimport_validate_fileupload(&$form, &$form_state) {
$validators = array(
'file_validate_extensions' => array( 'csv CSV' ),
) ;
if ( $file = file_save_upload('csvfile', $validators, 'temporary://') ) {
// The file was saved using file_save_upload() and was added to
// the files table as a temporary file. We'll make a copy and let
// the garbage collector delete the original upload.
$csv_dir = 'temporary://csvfile';
$directory_exists = file_prepare_directory($csv_dir, FILE_CREATE_DIRECTORY);
if ($directory_exists) {
$destination = $csv_dir .'/' . $file->filename;
if (file_copy($file, $destination, FILE_EXISTS_REPLACE)) {
$form_state['values']['csvupload'] = $destination;
}
else {
form_set_error('csvimport', t('Unable to copy upload file to !dest', array('!dest' => $destination)));
}
}
}
}
/**
* Validate the upload. Ensure that the CSV looks something like we
* expect it to.
*/
function csvimport_form_validate(&$form, &$form_state) {
if ( isset( $form_state['values']['csvupload'] ) ) {
if ( $handle = fopen($form_state['values']['csvupload'], 'r') ) {
$line_count = 1 ;
$first = TRUE ;
if ( $line = fgetcsv($handle, 4096) ) {
/**
* Validate the uploaded CSV here.
*
* The example CSV happens to have cell A1 ($line[0]) as
* below; we validate it only.
*
* You'll probably want to check several headers, eg:
* if ( $line[0] == 'Index' || $line[1] != 'Supplier' || $line[2] != 'Title' )
*/
// if ( $line[0] != 'Example CSV for csvimport.module - http://github.com/xurizaemon/csvimport' ) {
// form_set_error('csvfile', t('Sorry, this file does not match the expected format.')) ;
// }
}
fclose($handle);
}
else {
form_set_error('csvfile', t('Unable to read uploaded file !filepath', array('!filepath' => $form_state['values']['csvupload'])));
}
}
}
/**
* Handle form submission. Read the CSV into a set of batch operations
* and fire them off.
*/
function csvimport_form_submit(&$form, &$form_state) {
$batch = array(
'title' => t('Importing CSV ...'),
'operations' => array(),
'init_message' => t('Commencing'),
'progress_message' => t('Processed @current out of @total.'),
'error_message' => t('An error occurred during processing'),
'finished' => 'csvimport_import_finished',
) ;
if ( isset( $form_state['values']['csvupload'] ) ) {
if ( $handle = fopen($form_state['values']['csvupload'], 'r') ) {
$batch['operations'][] = array('_csvimport_remember_filename', array( $form_state['values']['csvupload'] ) ) ;
$line_count = 1 ;
$first = TRUE ;
$line = fgetcsv($handle, 4096);
while ( $line = fgetcsv($handle, 4096) ) {
/**
* we use base64_encode to ensure we don't overload the batch
* processor by stuffing complex objects into it
*/
$batch['operations'][] = array('_csvimport_import_line', array(array_map('base64_encode', $line)));
}
fclose($handle);
} // we caught this in csvimport_form_validate()
} // we caught this in csvimport_form_validate()
batch_set($batch);
}
/**
* Handle batch completion.
*/
function csvimport_import_finished($success, $results, $operations) {
if ( !empty($results['failed_rows']) ) {
$dir = 'public://csvimport' ;
if (file_prepare_directory( $dir, FILE_CREATE_DIRECTORY ) ) {
$csv_filename = 'failed_rows-'. basename($results['uploaded_filename']); // we validated extension on upload
$csv_filepath = $dir .'/'. $csv_filename;
$targs = array(
'!csv_url' => l(check_plain($csv_filename), file_create_url($csv_filepath)),
'%csv_filename' => $csv_filename,
'%csv_filepath' => $csv_filepath,
) ;
if ( $handle = fopen($csv_filepath, 'w+') ) {
foreach( $results['failed_rows'] as $failed_row ) {
fputcsv($handle, $failed_row);
}
fclose($handle);
drupal_set_message(t('Some rows failed to import. You may download a CSV of these rows: !csv_url', $targs), 'error');
}
else {
drupal_set_message(t('Some rows failed to import, but unable to write error CSV to %csv_filepath', $targs), 'error');
}
}
else {
drupal_set_message(t('Some rows failed to import, but unable to create directory for error CSV at %csv_directory', $targs), 'error');
}
}
return t('The CSV import has completed.');
}
/**
* Remember the uploaded CSV filename
*
* @TODO is there a better way to pass a value from inception of the
* batch to the finished function?
*/
function _csvimport_remember_filename($filename, &$context) {
$context['results']['uploaded_filename'] = $filename ;
}
/**
* Process a single line.
*/
function _csvimport_import_line($line, &$context) {
$context['results']['rows_imported']++;
$line = $cleaned_line = array_map('base64_decode', $line);
/**
* Simply show the import row count.
*/
$context['message'] = t('Importing row !c', array( '!c' => $context['results']['rows_imported'] ));
/**
* Alternatively, our example CSV happens to have the title in the
* third column, so we can uncomment this line to display "Importing
* Blahblah" as each row is parsed.
*
* You can comment out the line above if you uncomment this one.
*/
$context['message'] = t('Importing %title', array('%title' => $line[2]));
/**
* In order to slow importing and debug better, we can uncomment
* this line to make each import slightly slower.
*/
usleep(2500);
/**
* If the first two columns in the row are "ROW", "FAILS" then we
* will add that row to the CSV we'll return to the importing person
* after the import completes.
*/
if ( $line[1] == 'ROW' && $line[2] == 'FAILS' ) {
$context['results']['failed_rows'][] = $line ;
}
}