'fieldset', '#title' => t('Upload delimited files'), '#collapsible' => TRUE, '#collapsed' => TRUE, 'uploadhelp' => array( '#prefix' => '
', '#value' => t('Uploading a new file imports the contents of the file into a database table and adds it to the list above. Uploading a new copy of a previously-uploaded file replaces the file data - it should be reanalyzed before doing anything further. Note that any existing comments, ignored fields, and excluded rows are preserved when reloading the data. Also note that the first line of the file is assumed to contain field names.'), '#suffix' => '
', ), 'file' => array( '#type' => 'file', '#title' => t('Upload file'), '#size' => 48, '#description' => t('File containing the data to be imported.'), ), 'tablename' => array( '#type' => 'textfield', '#title' => t('Table name'), '#size' => 64, '#maxlength' => 64, '#description' => t('Name of the database table to create from this file. If left blank, the uploaded filename (without its extension) will be used to name the table. For example, uploading OldRecords.csv will create the table OldRecords.') ), 'delimitedsubmit' => array( '#type' => 'submit', '#value' => t('Import new file'), ), ); return $fieldsets; } /** * Implementation of hook_tw_form_submit(). */ function tw_import_delimited_tw_form_submit_delimited($values) { $file = file_save_upload('delimited'); if ($file) { // Move the uploaded file to the tw_delimited directory $dest = _tw_delimited_file_name($file->filename); if ($dest) { file_move($file, $dest, FILE_EXISTS_REPLACE); } else { drupal_set_message(t('Could not upload !filename', array('!filename' => $file->filename))); return NULL; } // Pick up the field names from the first row $offset = 0; $headers = _tw_delimited_get_row($file->filepath, $offset); if ($headers === FALSE) { return; } // If an explicit table name was not provided... if ($values['tablename']) { $tablename = trim($values['tablename']); } else { // ... Derive one from the filename (minus extension) $pieces = explode('.', $file->filename); $tablename = trim($pieces[0]); } if (!$tablename) { drupal_set_message(t('Could not derive a table name for !filename', array('!filename' => $file->filename))); } // Lowercase table names make views integration much easier $tablename = drupal_strtolower(preg_replace('/[^a-z0-9]/i', '_', $tablename)); // Truncate to 63 characters (valid for either MySQL or Postgres) $tablename = drupal_substr($tablename, 0, 63); // Build a schema definition for the table $schema = array( 'description' => t('Uploaded data file'), ); $seen = array(); $columns = array(); foreach ($headers as $i => $coltitle) { // Normalize field names - replace non-alphanumerics with underscores $colname = preg_replace('/[^a-z0-9]/i', '_', $coltitle); // Field names must be unique, generate variations if necessary if (!isset($seen[$coltitle])) { $seen[$coltitle] = 1; } else { $seen[$coltitle]++; } if ($seen[$coltitle] > 1) { $colname = $coltitle . '_' . $seen[$coltitle]; } // Default to big text fields, which can hold anything $schema['fields'][$colname] = array( 'type' => 'text', 'size' => 'big', 'not null' => FALSE, ); $columns[] = tw_quote_identifier($colname); } // TODO: Option to append instead of replace (i.e., skip the TRUNCATE) if (db_table_exists($tablename)) { $sql = "SELECT twtid FROM {tw_tables} WHERE dbconnection='%s' AND tablename='%s'"; $twtid = db_result(db_query($sql, 'default', $tablename)); if ($twtid) { db_query('TRUNCATE TABLE {' . $tablename . '}'); $prevexists = TRUE; } else { drupal_set_message(t('"!tablename" is not a table managed by Table Wizard. For initial import, please choose a table name that is not in use', array('!tablename' => $tablename))); return NULL; } } else { // Note that when using table prefixes, a prefix is prepended to the table name db_create_table($ret, $tablename, $schema); $prevexists = FALSE; } // Here we go - plug the data into the table using Batch API $srcinfo = new stdClass; $srcinfo->filepath = $file->filepath; $srcinfo->tablename = $tablename; $srcinfo->offset = $offset; $srcinfo->sql = 'INSERT INTO {' . $tablename . '} ' . '(' . implode(', ', $columns) . ') ' . 'VALUES(' . implode(', ', array_pad(array(), count($columns), "'%s'")) . ')'; if ($prevexists) { $srcinfo->success_msg = t('%filename has been imported into table %tablename, replacing the original data in that table', array('%filename' => $file->filename, '%tablename' => $tablename)); } else { $srcinfo->success_msg = t('%filename has been imported into new table %tablename. All fields have initially been defined as text, with no indexing - it is important to use your database management software to set appropriate data types for each field, and to define a single (unique, non-null) field as the primary key. After you have done this, reanalyze the table.', array('%filename' => $file->filename, '%tablename' => $tablename)); } $batch = array( 'title' => t('Importing records from %filename into %tablename', array('%filename' => $file->filename, '%tablename' => $tablename)), 'init_message' => t('Delimited file import initializing'), 'error_message' => t('Import failed'), 'progress_message' => '', 'operations' => array( array('_tw_import_delimited_import_file', array($srcinfo)), ), 'finished' => '_tw_import_delimited_finish', ); batch_set($batch); // Pass the table name back to Table Wizard return array($tablename); } else { drupal_set_message(t('Failed to upload delimited file')); } } /** * Generate the Drupal-relative path for storing imported files (creating the tw_delimited * directory if necessary). */ function _tw_delimited_file_name($file) { $dir = ""; if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC) { $dir .= str_replace('\\', '/', getcwd()) . '/'; } $dir .= file_directory_path() . '/tw_delimited'; if (file_check_directory($dir, TRUE)) { return $dir . '/' . $file; } else { return FALSE; } } /** * Get one row from the delimited file and return it as an array of * columns/fields. */ function _tw_delimited_get_row($filepath, &$offset = 0) { static $handle, $separator, $size = 10000; // If the requested file isn't already open... if (!isset($handle)) { $handle = fopen($filepath, 'r'); if ($handle === FALSE) { drupal_set_message(t('Could not open !filename', array('!filename' => $filepath))); return FALSE; } // If there's a tab character on the first line, use tab as the delimiter, otherwise comma $sample = fgets($handle); if (strpos($sample, "\t") === FALSE) { $separator = ','; } else { $separator = "\t"; } fseek($handle, $offset); } $result = fgetcsv($handle, $size, $separator); if ($result === FALSE) { fclose($handle); $handle = NULL; $currpath = NULL; $separator = NULL; } else { $offset = ftell($handle); } return $result; } function _tw_import_delimited_import_file($srcinfo, &$context) { if (!isset($context['sandbox']['total'])) { $context['sandbox']['total'] = filesize($srcinfo->filepath); $context['sandbox']['done'] = $srcinfo->offset; $context['sandbox']['rows'] = 0; } // Return for feedback every 10 seconds $timelimit = time() + 10; while ($fields = _tw_delimited_get_row($srcinfo->filepath, $context['sandbox']['done'])) { db_query($srcinfo->sql, $fields); $context['sandbox']['rows']++; if (time() > $timelimit) { break; } } if ($context['sandbox']['total'] > 0) { $context['finished'] = $context['sandbox']['done'] / $context['sandbox']['total']; } else { $context['finished'] = 1; } $context['message'] = t('%done of %total bytes imported
%rows rows inserted into %tablename', array('%done' => format_size($context['sandbox']['done']), '%total' => format_size($context['sandbox']['total']), '%rows' => $context['sandbox']['rows'], '%tablename' => $srcinfo->tablename)); if ($context['finished'] == 1) { $context['results']['srcinfo'] = $srcinfo; $context['message'] .= '
' . t('Analyzing table %tablename...', array('%tablename' => $srcinfo->tablename)); } } function _tw_import_delimited_finish($success, $results, $operations) { if ($success) { $srcinfo = $results['srcinfo']; unlink($srcinfo->filepath); drupal_set_message($srcinfo->success_msg); tw_perform_analysis($srcinfo->tablename); } }