/** * Bulk import of model records * * The actual meat of the import process happens here, this is called recursively via * AJAX to import sets of records. This is a refactored and generalized version of the * old Contacts importRecords. */ public function actionImportModelRecords() { if (isset($_POST['count']) && file_exists($path = $this->safePath('data.csv')) && isset($_POST['model'])) { ini_set('auto_detect_line_endings', 1); // Account for Mac based CSVs if possible $importedIds = array(); $modelName = ucfirst($_POST['model']); $count = $_POST['count']; // Number of records to import $metaData = $_SESSION['metaData']; $importMap = $_SESSION['importMap']; $fp = fopen($path, 'r+'); fseek($fp, $_SESSION['offset']); // Seek to the right file offset // Whether the user has mapped the ID field $mappedId = false; // validate meta data if (!ArrayUtil::setEquals(array_keys($importMap), $metaData)) { throw new CHttpException(400, Yii::t('app', 'Bad import map')); } for ($i = 0; $i < $count; $i++) { // Loop through and start importing $csvLine = fgetcsv($fp, 0, $_SESSION['delimeter'], $_SESSION['enclosure']); if ($csvLine !== false && !is_null($csvLine)) { if ($csvLine === array(null)) { // Skip empty lines continue; } if (count($csvLine) > count($metaData)) { $csvLine = array_slice($csvLine, 0, count($metaData)); } else { if (count($csvLine) < count($metaData)) { $csvLine = array_pad($csvLine, count($metaData), null); } } unset($_POST); // Nix all invalid multibyte sequences to avoid errors $csvLine = array_map('Formatter::mbSanitize', $csvLine); if (!empty($metaData) && !empty($csvLine)) { $importAttributes = array_combine($metaData, $csvLine); } else { continue; } // Locate an existing model to update, if requested, otherwise create // a new model to populate $model = new $modelName(); foreach ($metaData as $attribute) { if ($importMap[$attribute] === 'id') { $mappedId = true; } $isActionText = $modelName === 'Actions' && $importMap[$attribute] === 'actionDescription'; if ($importMap[$attribute] === 'applyTags') { $this->importTags($modelName, $importAttributes[$attribute]); continue; } if (isset($importMap[$attribute]) && ($model->hasAttribute($importMap[$attribute]) || $isActionText)) { $model = $this->importRecordAttribute($modelName, $model, $importMap[$attribute], $importAttributes[$attribute]); $_POST[$importMap[$attribute]] = $model->{$importMap}[$attribute]; } } $this->fixupImportedAttributes($modelName, $model); if (!$model->hasErrors() && $model->validate()) { $importedIds = $this->saveImportedModel($model, $modelName, $importedIds); } else { $this->markFailedRecord($model, $csvLine, $metaData); } } else { $this->finishImportBatch($modelName, $mappedId, true); return; } } // Change the offset to wherever we got to and continue. $_SESSION['offset'] = ftell($fp); $this->finishImportBatch($modelName, $mappedId); } }