importableFieldsMetadata; } /** * Set metadata for all importable fields in std getfields style format. * @param array $importableFieldsMetadata */ public function setImportableFieldsMetadata(array $importableFieldsMetadata) { $this->importableFieldsMetadata = $importableFieldsMetadata; } /** * Array of the fields that are actually part of the import process * the position in the array also dictates their position in the import * file * @var array */ protected $_activeFields; /** * Cache the count of active fields * * @var int */ protected $_activeFieldCount; /** * Cache of preview rows * * @var array */ protected $_rows; /** * Filename of error data * * @var string */ protected $_errorFileName; /** * Filename of conflict data * * @var string */ protected $_conflictFileName; /** * Filename of duplicate data * * @var string */ protected $_duplicateFileName; /** * Contact type * * @var int */ public $_contactType; /** * Contact sub-type * * @var int */ public $_contactSubType; /** * Class constructor. */ public function __construct() { $this->_maxLinesToProcess = 0; } /** * Abstract function definitions. */ abstract protected function init(); /** * @return mixed */ abstract protected function fini(); /** * Map field. * * @param array $values * * @return mixed */ abstract protected function mapField(&$values); /** * Preview. * * @param array $values * * @return mixed */ abstract protected function preview(&$values); /** * @param $values * * @return mixed */ abstract protected function summary(&$values); /** * @param $onDuplicate * @param $values * * @return mixed */ abstract protected function import($onDuplicate, &$values); /** * Set and validate field values. * * @param array $elements * array. * @param $erroneousField * reference. * * @return int */ public function setActiveFieldValues($elements, &$erroneousField) { $maxCount = count($elements) < $this->_activeFieldCount ? count($elements) : $this->_activeFieldCount; for ($i = 0; $i < $maxCount; $i++) { $this->_activeFields[$i]->setValue($elements[$i]); } // reset all the values that we did not have an equivalent import element for (; $i < $this->_activeFieldCount; $i++) { $this->_activeFields[$i]->resetValue(); } // now validate the fields and return false if error $valid = self::VALID; for ($i = 0; $i < $this->_activeFieldCount; $i++) { if (!$this->_activeFields[$i]->validate()) { // no need to do any more validation $erroneousField = $i; $valid = self::ERROR; break; } } return $valid; } /** * Format the field values for input to the api. * * @return array * (reference) associative array of name/value pairs */ public function &getActiveFieldParams() { $params = []; for ($i = 0; $i < $this->_activeFieldCount; $i++) { if (isset($this->_activeFields[$i]->_value) && !isset($params[$this->_activeFields[$i]->_name]) && !isset($this->_activeFields[$i]->_related) ) { $params[$this->_activeFields[$i]->_name] = $this->_activeFields[$i]->_value; } } return $params; } /** * Add progress bar to the import process. Calculates time remaining, status etc. * * @param $statusID * status id of the import process saved in $config->uploadDir. * @param bool $startImport * True when progress bar is to be initiated. * @param $startTimestamp * Initial timstamp when the import was started. * @param $prevTimestamp * Previous timestamp when this function was last called. * @param $totalRowCount * Total number of rows in the import file. * * @return NULL|$currTimestamp */ public function progressImport($statusID, $startImport = TRUE, $startTimestamp = NULL, $prevTimestamp = NULL, $totalRowCount = NULL) { $config = CRM_Core_Config::singleton(); $statusFile = "{$config->uploadDir}status_{$statusID}.txt"; if ($startImport) { $status = "
  " . ts('No processing status reported yet.') . "
"; //do not force the browser to display the save dialog, CRM-7640 $contents = json_encode([0, $status]); file_put_contents($statusFile, $contents); } else { $rowCount = isset($this->_rowCount) ? $this->_rowCount : $this->_lineCount; $currTimestamp = time(); $totalTime = ($currTimestamp - $startTimestamp); $time = ($currTimestamp - $prevTimestamp); $recordsLeft = $totalRowCount - $rowCount; if ($recordsLeft < 0) { $recordsLeft = 0; } $estimatedTime = ($recordsLeft / 50) * $time; $estMinutes = floor($estimatedTime / 60); $timeFormatted = ''; if ($estMinutes > 1) { $timeFormatted = $estMinutes . ' ' . ts('minutes') . ' '; $estimatedTime = $estimatedTime - ($estMinutes * 60); } $timeFormatted .= round($estimatedTime) . ' ' . ts('seconds'); $processedPercent = (int ) (($rowCount * 100) / $totalRowCount); $statusMsg = ts('%1 of %2 records - %3 remaining', [1 => $rowCount, 2 => $totalRowCount, 3 => $timeFormatted] ); $status = "
  {$statusMsg}
"; $contents = json_encode([$processedPercent, $status]); file_put_contents($statusFile, $contents); return $currTimestamp; } } /** * @return array */ public function getSelectValues() { $values = []; foreach ($this->_fields as $name => $field) { $values[$name] = $field->_title; } return $values; } /** * @return array */ public function getSelectTypes() { $values = []; foreach ($this->_fields as $name => $field) { if (isset($field->_hasLocationType)) { $values[$name] = $field->_hasLocationType; } } return $values; } /** * @return array */ public function getHeaderPatterns() { $values = []; foreach ($this->_fields as $name => $field) { if (isset($field->_headerPattern)) { $values[$name] = $field->_headerPattern; } } return $values; } /** * @return array */ public function getDataPatterns() { $values = []; foreach ($this->_fields as $name => $field) { $values[$name] = $field->_dataPattern; } return $values; } /** * Remove single-quote enclosures from a value array (row). * * @param array $values * @param string $enclosure * * @return void */ public static function encloseScrub(&$values, $enclosure = "'") { if (empty($values)) { return; } foreach ($values as $k => $v) { $values[$k] = preg_replace("/^$enclosure(.*)$enclosure$/", '$1', $v); } } /** * Setter function. * * @param int $max * * @return void */ public function setMaxLinesToProcess($max) { $this->_maxLinesToProcess = $max; } /** * Determines the file extension based on error code. * * @var $type error code constant * @return string */ public static function errorFileName($type) { $fileName = NULL; if (empty($type)) { return $fileName; } $config = CRM_Core_Config::singleton(); $fileName = $config->uploadDir . "sqlImport"; switch ($type) { case self::ERROR: $fileName .= '.errors'; break; case self::CONFLICT: $fileName .= '.conflicts'; break; case self::DUPLICATE: $fileName .= '.duplicates'; break; case self::NO_MATCH: $fileName .= '.mismatch'; break; case self::UNPARSED_ADDRESS_WARNING: $fileName .= '.unparsedAddress'; break; } return $fileName; } /** * Determines the file name based on error code. * * @var $type error code constant * @return string */ public static function saveFileName($type) { $fileName = NULL; if (empty($type)) { return $fileName; } switch ($type) { case self::ERROR: $fileName = 'Import_Errors.csv'; break; case self::CONFLICT: $fileName = 'Import_Conflicts.csv'; break; case self::DUPLICATE: $fileName = 'Import_Duplicates.csv'; break; case self::NO_MATCH: $fileName = 'Import_Mismatch.csv'; break; case self::UNPARSED_ADDRESS_WARNING: $fileName = 'Import_Unparsed_Address.csv'; break; } return $fileName; } /** * Check if contact is a duplicate . * * @param array $formatValues * * @return array */ protected function checkContactDuplicate(&$formatValues) { //retrieve contact id using contact dedupe rule $formatValues['contact_type'] = $this->_contactType; $formatValues['version'] = 3; require_once 'CRM/Utils/DeprecatedUtils.php'; $error = _civicrm_api3_deprecated_check_contact_dedupe($formatValues); return $error; } /** * Parse a field which could be represented by a label or name value rather than the DB value. * * We will try to match name first but if not available then see if we have a label that can be converted to a name. * * @param string|int|null $submittedValue * @param array $fieldSpec * Metadata for the field * * @return mixed */ protected function parsePseudoConstantField($submittedValue, $fieldSpec) { /* @var \CRM_Core_DAO $bao */ $bao = $fieldSpec['bao']; // For historical reasons use validate as context - ie disabled name matches ARE permitted. $nameOptions = $bao::buildOptions($fieldSpec['name'], 'validate'); if (!isset($nameOptions[$submittedValue])) { $labelOptions = array_flip($bao::buildOptions($fieldSpec['name'], 'match')); if (isset($labelOptions[$submittedValue])) { return array_search($labelOptions[$submittedValue], $nameOptions, TRUE); } } return ''; } }