3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2017 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2017
33 abstract class CRM_Contact_Import_Parser
extends CRM_Import_Parser
{
35 protected $_tableName;
38 * Total number of lines in file
45 * Running total number of un-matched Contacts.
47 protected $_unMatchCount;
50 * Array of unmatched lines
55 * Total number of contacts with unparsed addresses
57 protected $_unparsedAddressCount;
60 * Filename of mismatch data
64 protected $_misMatchFilemName;
66 protected $_primaryKeyName;
67 protected $_statusFieldName;
77 * Dedupe rule group id to use if set
81 public $_dedupeRuleGroupID = NULL;
86 * @param string $tableName
87 * @param array $mapper
89 * @param int $contactType
90 * @param string $primaryKeyName
91 * @param string $statusFieldName
92 * @param int $onDuplicate
93 * @param int $statusID
94 * @param int $totalRowCount
95 * @param bool $doGeocodeAddress
97 * @param string $contactSubType
98 * @param int $dedupeRuleGroupID
105 $mode = self
::MODE_PREVIEW
,
106 $contactType = self
::CONTACT_INDIVIDUAL
,
107 $primaryKeyName = '_id',
108 $statusFieldName = '_status',
109 $onDuplicate = self
::DUPLICATE_SKIP
,
111 $totalRowCount = NULL,
112 $doGeocodeAddress = FALSE,
113 $timeout = CRM_Contact_Import_Parser
::DEFAULT_TIMEOUT
,
114 $contactSubType = NULL,
115 $dedupeRuleGroupID = NULL
118 // TODO: Make the timeout actually work
119 $this->_onDuplicate
= $onDuplicate;
120 $this->_dedupeRuleGroupID
= $dedupeRuleGroupID;
122 switch ($contactType) {
123 case CRM_Import_Parser
::CONTACT_INDIVIDUAL
:
124 $this->_contactType
= 'Individual';
127 case CRM_Import_Parser
::CONTACT_HOUSEHOLD
:
128 $this->_contactType
= 'Household';
131 case CRM_Import_Parser
::CONTACT_ORGANIZATION
:
132 $this->_contactType
= 'Organization';
135 $this->_contactSubType
= $contactSubType;
139 $this->_rowCount
= $this->_warningCount
= 0;
140 $this->_invalidRowCount
= $this->_validCount
= 0;
141 $this->_totalCount
= $this->_conflictCount
= 0;
143 $this->_errors
= array();
144 $this->_warnings
= array();
145 $this->_conflicts
= array();
146 $this->_unparsedAddresses
= array();
148 $this->_tableName
= $tableName;
149 $this->_primaryKeyName
= $primaryKeyName;
150 $this->_statusFieldName
= $statusFieldName;
152 if ($mode == self
::MODE_MAPFIELD
) {
153 $this->_rows
= array();
156 $this->_activeFieldCount
= count($this->_activeFields
);
159 if ($mode == self
::MODE_IMPORT
) {
160 //get the key of email field
161 foreach ($mapper as $key => $value) {
162 if (strtolower($value) == 'email') {
172 $config = CRM_Core_Config
::singleton();
173 $statusFile = "{$config->uploadDir}status_{$statusID}.txt";
174 $status = "<div class='description'> " . ts('No processing status reported yet.') . "</div>";
176 //do not force the browser to display the save dialog, CRM-7640
177 $contents = json_encode(array(0, $status));
179 file_put_contents($statusFile, $contents);
181 $startTimestamp = $currTimestamp = $prevTimestamp = time();
184 // get the contents of the temp. import table
185 $query = "SELECT * FROM $tableName";
186 if ($mode == self
::MODE_IMPORT
) {
187 $query .= " WHERE $statusFieldName = 'NEW'";
189 $dao = new CRM_Core_DAO();
190 $db = $dao->getDatabaseConnection();
191 $result = $db->query($query);
193 while ($values = $result->fetchRow(DB_FETCHMODE_ORDERED
)) {
196 /* trim whitespace around the values */
197 foreach ($values as $k => $v) {
198 $values[$k] = trim($v, " \t\r\n");
200 if (CRM_Utils_System
::isNull($values)) {
204 $this->_totalCount++
;
206 if ($mode == self
::MODE_MAPFIELD
) {
207 $returnCode = $this->mapField($values);
209 elseif ($mode == self
::MODE_PREVIEW
) {
210 $returnCode = $this->preview($values);
212 elseif ($mode == self
::MODE_SUMMARY
) {
213 $returnCode = $this->summary($values);
215 elseif ($mode == self
::MODE_IMPORT
) {
216 //print "Running parser in import mode<br/>\n";
217 $returnCode = $this->import($onDuplicate, $values, $doGeocodeAddress);
218 if ($statusID && (($this->_rowCount %
$skip) == 0)) {
219 $currTimestamp = time();
220 $totalTime = ($currTimestamp - $startTimestamp);
221 $time = ($currTimestamp - $prevTimestamp);
222 $recordsLeft = $totalRowCount - $this->_rowCount
;
223 if ($recordsLeft < 0) {
226 $estimatedTime = ($recordsLeft / $skip) * $time;
227 $estMinutes = floor($estimatedTime / 60);
229 if ($estMinutes > 1) {
230 $timeFormatted = $estMinutes . ' ' . ts('minutes') . ' ';
231 $estimatedTime = $estimatedTime - ($estMinutes * 60);
233 $timeFormatted .= round($estimatedTime) . ' ' . ts('seconds');
234 $processedPercent = (int ) (($this->_rowCount
* 100) / $totalRowCount);
235 $statusMsg = ts('%1 of %2 records - %3 remaining',
236 array(1 => $this->_rowCount
, 2 => $totalRowCount, 3 => $timeFormatted)
239 <div class=\"description\">
240 <strong>{$statusMsg}</strong>
244 $contents = json_encode(array($processedPercent, $status));
246 file_put_contents($statusFile, $contents);
248 $prevTimestamp = $currTimestamp;
253 $returnCode = self
::ERROR
;
256 // note that a line could be valid but still produce a warning
257 if ($returnCode & self
::VALID
) {
258 $this->_validCount++
;
259 if ($mode == self
::MODE_MAPFIELD
) {
260 $this->_rows
[] = $values;
261 $this->_activeFieldCount
= max($this->_activeFieldCount
, count($values));
265 if ($returnCode & self
::WARNING
) {
266 $this->_warningCount++
;
267 if ($this->_warningCount
< $this->_maxWarningCount
) {
268 $this->_warningCount
[] = $line;
272 if ($returnCode & self
::ERROR
) {
273 $this->_invalidRowCount++
;
274 array_unshift($values, $this->_rowCount
);
275 $this->_errors
[] = $values;
278 if ($returnCode & self
::CONFLICT
) {
279 $this->_conflictCount++
;
280 array_unshift($values, $this->_rowCount
);
281 $this->_conflicts
[] = $values;
284 if ($returnCode & self
::NO_MATCH
) {
285 $this->_unMatchCount++
;
286 array_unshift($values, $this->_rowCount
);
287 $this->_unMatch
[] = $values;
290 if ($returnCode & self
::DUPLICATE
) {
291 if ($returnCode & self
::MULTIPLE_DUPE
) {
292 /* TODO: multi-dupes should be counted apart from singles
293 * on non-skip action */
295 $this->_duplicateCount++
;
296 array_unshift($values, $this->_rowCount
);
297 $this->_duplicates
[] = $values;
298 if ($onDuplicate != self
::DUPLICATE_SKIP
) {
299 $this->_validCount++
;
303 if ($returnCode & self
::UNPARSED_ADDRESS_WARNING
) {
304 $this->_unparsedAddressCount++
;
305 array_unshift($values, $this->_rowCount
);
306 $this->_unparsedAddresses
[] = $values;
308 // we give the derived class a way of aborting the process
309 // note that the return code could be multiple code or'ed together
310 if ($returnCode & self
::STOP
) {
314 // if we are done processing the maxNumber of lines, break
315 if ($this->_maxLinesToProcess
> 0 && $this->_validCount
>= $this->_maxLinesToProcess
) {
319 // clean up memory from dao's
320 CRM_Core_DAO
::freeResult();
322 // see if we've hit our timeout yet
323 /* if ( $the_thing_with_the_stuff ) {
328 if ($mode == self
::MODE_PREVIEW ||
$mode == self
::MODE_IMPORT
) {
329 $customHeaders = $mapper;
331 $customfields = CRM_Core_BAO_CustomField
::getFields($this->_contactType
);
332 foreach ($customHeaders as $key => $value) {
333 if ($id = CRM_Core_BAO_CustomField
::getKeyID($value)) {
334 $customHeaders[$key] = $customfields[$id][0];
338 if ($this->_invalidRowCount
) {
339 // removed view url for invlaid contacts
340 $headers = array_merge(array(
346 $this->_errorFileName
= self
::errorFileName(self
::ERROR
);
347 self
::exportCSV($this->_errorFileName
, $headers, $this->_errors
);
349 if ($this->_conflictCount
) {
350 $headers = array_merge(array(
356 $this->_conflictFileName
= self
::errorFileName(self
::CONFLICT
);
357 self
::exportCSV($this->_conflictFileName
, $headers, $this->_conflicts
);
359 if ($this->_duplicateCount
) {
360 $headers = array_merge(array(
362 ts('View Contact URL'),
367 $this->_duplicateFileName
= self
::errorFileName(self
::DUPLICATE
);
368 self
::exportCSV($this->_duplicateFileName
, $headers, $this->_duplicates
);
370 if ($this->_unMatchCount
) {
371 $headers = array_merge(array(
378 $this->_misMatchFilemName
= self
::errorFileName(self
::NO_MATCH
);
379 self
::exportCSV($this->_misMatchFilemName
, $headers, $this->_unMatch
);
381 if ($this->_unparsedAddressCount
) {
382 $headers = array_merge(array(
384 ts('Contact Edit URL'),
388 $this->_errorFileName
= self
::errorFileName(self
::UNPARSED_ADDRESS_WARNING
);
389 self
::exportCSV($this->_errorFileName
, $headers, $this->_unparsedAddresses
);
392 //echo "$this->_totalCount,$this->_invalidRowCount,$this->_conflictCount,$this->_duplicateCount";
393 return $this->fini();
397 * Given a list of the importable field keys that the user has selected.
398 * set the active fields array to this list
400 * @param array $fieldKeys
401 * Mapped array of values.
403 public function setActiveFields($fieldKeys) {
404 $this->_activeFieldCount
= count($fieldKeys);
405 foreach ($fieldKeys as $key) {
406 if (empty($this->_fields
[$key])) {
407 $this->_activeFields
[] = new CRM_Contact_Import_Field('', ts('- do not import -'));
410 $this->_activeFields
[] = clone($this->_fields
[$key]);
418 public function setActiveFieldLocationTypes($elements) {
419 for ($i = 0; $i < count($elements); $i++
) {
420 $this->_activeFields
[$i]->_hasLocationType
= $elements[$i];
430 public function setActiveFieldPhoneTypes($elements) {
431 for ($i = 0; $i < count($elements); $i++
) {
432 $this->_activeFields
[$i]->_phoneType
= $elements[$i];
439 public function setActiveFieldWebsiteTypes($elements) {
440 for ($i = 0; $i < count($elements); $i++
) {
441 $this->_activeFields
[$i]->_websiteType
= $elements[$i];
446 * Set IM Service Provider type fields.
448 * @param array $elements
449 * IM service provider type ids.
451 public function setActiveFieldImProviders($elements) {
452 for ($i = 0; $i < count($elements); $i++
) {
453 $this->_activeFields
[$i]->_imProvider
= $elements[$i];
460 public function setActiveFieldRelated($elements) {
461 for ($i = 0; $i < count($elements); $i++
) {
462 $this->_activeFields
[$i]->_related
= $elements[$i];
469 public function setActiveFieldRelatedContactType($elements) {
470 for ($i = 0; $i < count($elements); $i++
) {
471 $this->_activeFields
[$i]->_relatedContactType
= $elements[$i];
478 public function setActiveFieldRelatedContactDetails($elements) {
479 for ($i = 0; $i < count($elements); $i++
) {
480 $this->_activeFields
[$i]->_relatedContactDetails
= $elements[$i];
487 public function setActiveFieldRelatedContactLocType($elements) {
488 for ($i = 0; $i < count($elements); $i++
) {
489 $this->_activeFields
[$i]->_relatedContactLocType
= $elements[$i];
494 * Set active field for related contact's phone type.
496 * @param array $elements
498 public function setActiveFieldRelatedContactPhoneType($elements) {
499 for ($i = 0; $i < count($elements); $i++
) {
500 $this->_activeFields
[$i]->_relatedContactPhoneType
= $elements[$i];
507 public function setActiveFieldRelatedContactWebsiteType($elements) {
508 for ($i = 0; $i < count($elements); $i++
) {
509 $this->_activeFields
[$i]->_relatedContactWebsiteType
= $elements[$i];
514 * Set IM Service Provider type fields for related contacts.
516 * @param array $elements
517 * IM service provider type ids of related contact.
519 public function setActiveFieldRelatedContactImProvider($elements) {
520 for ($i = 0; $i < count($elements); $i++
) {
521 $this->_activeFields
[$i]->_relatedContactImProvider
= $elements[$i];
526 * Format the field values for input to the api.
529 * (reference ) associative array of name/value pairs
531 public function &getActiveFieldParams() {
534 for ($i = 0; $i < $this->_activeFieldCount
; $i++
) {
535 if ($this->_activeFields
[$i]->_name
== 'do_not_import') {
539 if (isset($this->_activeFields
[$i]->_value
)) {
540 if (isset($this->_activeFields
[$i]->_hasLocationType
)) {
541 if (!isset($params[$this->_activeFields
[$i]->_name
])) {
542 $params[$this->_activeFields
[$i]->_name
] = array();
546 $this->_activeFields
[$i]->_name
=> $this->_activeFields
[$i]->_value
,
547 'location_type_id' => $this->_activeFields
[$i]->_hasLocationType
,
550 if (isset($this->_activeFields
[$i]->_phoneType
)) {
551 $value['phone_type_id'] = $this->_activeFields
[$i]->_phoneType
;
554 // get IM service Provider type id
555 if (isset($this->_activeFields
[$i]->_imProvider
)) {
556 $value['provider_id'] = $this->_activeFields
[$i]->_imProvider
;
559 $params[$this->_activeFields
[$i]->_name
][] = $value;
561 elseif (isset($this->_activeFields
[$i]->_websiteType
)) {
563 $this->_activeFields
[$i]->_name
=> $this->_activeFields
[$i]->_value
,
564 'website_type_id' => $this->_activeFields
[$i]->_websiteType
,
567 $params[$this->_activeFields
[$i]->_name
][] = $value;
570 if (!isset($params[$this->_activeFields
[$i]->_name
])) {
571 if (!isset($this->_activeFields
[$i]->_related
)) {
572 $params[$this->_activeFields
[$i]->_name
] = $this->_activeFields
[$i]->_value
;
576 //minor fix for CRM-4062
577 if (isset($this->_activeFields
[$i]->_related
)) {
578 if (!isset($params[$this->_activeFields
[$i]->_related
])) {
579 $params[$this->_activeFields
[$i]->_related
] = array();
582 if (!isset($params[$this->_activeFields
[$i]->_related
]['contact_type']) && !empty($this->_activeFields
[$i]->_relatedContactType
)) {
583 $params[$this->_activeFields
[$i]->_related
]['contact_type'] = $this->_activeFields
[$i]->_relatedContactType
;
586 if (isset($this->_activeFields
[$i]->_relatedContactLocType
) && !empty($this->_activeFields
[$i]->_value
)) {
587 if (!empty($params[$this->_activeFields
[$i]->_related
][$this->_activeFields
[$i]->_relatedContactDetails
]) &&
588 !is_array($params[$this->_activeFields
[$i]->_related
][$this->_activeFields
[$i]->_relatedContactDetails
])
590 $params[$this->_activeFields
[$i]->_related
][$this->_activeFields
[$i]->_relatedContactDetails
] = array();
593 $this->_activeFields
[$i]->_relatedContactDetails
=> $this->_activeFields
[$i]->_value
,
594 'location_type_id' => $this->_activeFields
[$i]->_relatedContactLocType
,
597 if (isset($this->_activeFields
[$i]->_relatedContactPhoneType
)) {
598 $value['phone_type_id'] = $this->_activeFields
[$i]->_relatedContactPhoneType
;
601 // get IM service Provider type id for related contact
602 if (isset($this->_activeFields
[$i]->_relatedContactImProvider
)) {
603 $value['provider_id'] = $this->_activeFields
[$i]->_relatedContactImProvider
;
606 $params[$this->_activeFields
[$i]->_related
][$this->_activeFields
[$i]->_relatedContactDetails
][] = $value;
608 elseif (isset($this->_activeFields
[$i]->_relatedContactWebsiteType
)) {
609 $params[$this->_activeFields
[$i]->_related
][$this->_activeFields
[$i]->_relatedContactDetails
][] = array(
610 'url' => $this->_activeFields
[$i]->_value
,
611 'website_type_id' => $this->_activeFields
[$i]->_relatedContactWebsiteType
,
615 $params[$this->_activeFields
[$i]->_related
][$this->_activeFields
[$i]->_relatedContactDetails
] = $this->_activeFields
[$i]->_value
;
627 public function getColumnPatterns() {
629 foreach ($this->_fields
as $name => $field) {
630 $values[$name] = $field->_columnPattern
;
636 * @param string $name
639 * @param string $headerPattern
640 * @param string $dataPattern
641 * @param bool $hasLocationType
643 public function addField(
644 $name, $title, $type = CRM_Utils_Type
::T_INT
,
645 $headerPattern = '//', $dataPattern = '//',
646 $hasLocationType = FALSE
648 $this->_fields
[$name] = new CRM_Contact_Import_Field($name, $title, $type, $headerPattern, $dataPattern, $hasLocationType);
650 $this->_fields
['doNotImport'] = new CRM_Contact_Import_Field($name, $title, $type, $headerPattern, $dataPattern, $hasLocationType);
655 * Store parser values.
657 * @param CRM_Core_Session $store
661 public function set($store, $mode = self
::MODE_SUMMARY
) {
662 $store->set('rowCount', $this->_rowCount
);
663 $store->set('fields', $this->getSelectValues());
664 $store->set('fieldTypes', $this->getSelectTypes());
666 $store->set('columnPatterns', $this->getColumnPatterns());
667 $store->set('dataPatterns', $this->getDataPatterns());
668 $store->set('columnCount', $this->_activeFieldCount
);
670 $store->set('totalRowCount', $this->_totalCount
);
671 $store->set('validRowCount', $this->_validCount
);
672 $store->set('invalidRowCount', $this->_invalidRowCount
);
673 $store->set('conflictRowCount', $this->_conflictCount
);
674 $store->set('unMatchCount', $this->_unMatchCount
);
676 switch ($this->_contactType
) {
678 $store->set('contactType', CRM_Import_Parser
::CONTACT_INDIVIDUAL
);
682 $store->set('contactType', CRM_Import_Parser
::CONTACT_HOUSEHOLD
);
686 $store->set('contactType', CRM_Import_Parser
::CONTACT_ORGANIZATION
);
689 if ($this->_invalidRowCount
) {
690 $store->set('errorsFileName', $this->_errorFileName
);
692 if ($this->_conflictCount
) {
693 $store->set('conflictsFileName', $this->_conflictFileName
);
695 if (isset($this->_rows
) && !empty($this->_rows
)) {
696 $store->set('dataValues', $this->_rows
);
699 if ($this->_unMatchCount
) {
700 $store->set('mismatchFileName', $this->_misMatchFilemName
);
703 if ($mode == self
::MODE_IMPORT
) {
704 $store->set('duplicateRowCount', $this->_duplicateCount
);
705 $store->set('unparsedAddressCount', $this->_unparsedAddressCount
);
706 if ($this->_duplicateCount
) {
707 $store->set('duplicatesFileName', $this->_duplicateFileName
);
709 if ($this->_unparsedAddressCount
) {
710 $store->set('errorsFileName', $this->_errorFileName
);
713 //echo "$this->_totalCount,$this->_invalidRowCount,$this->_conflictCount,$this->_duplicateCount";
717 * Export data to a CSV file.
719 * @param string $fileName
720 * @param array $header
723 public static function exportCSV($fileName, $header, $data) {
725 if (file_exists($fileName) && !is_writable($fileName)) {
726 CRM_Core_Error
::movedSiteError($fileName);
728 //hack to remove '_status', '_statusMsg' and '_id' from error file
729 $errorValues = array();
730 $dbRecordStatus = array('IMPORTED', 'ERROR', 'DUPLICATE', 'INVALID', 'NEW');
731 foreach ($data as $rowCount => $rowValues) {
733 foreach ($rowValues as $key => $val) {
734 if (in_array($val, $dbRecordStatus) && $count == (count($rowValues) - 3)) {
737 $errorValues[$rowCount][$key] = $val;
741 $data = $errorValues;
744 $fd = fopen($fileName, 'w');
746 foreach ($header as $key => $value) {
747 $header[$key] = "\"$value\"";
749 $config = CRM_Core_Config
::singleton();
750 $output[] = implode($config->fieldSeparator
, $header);
752 foreach ($data as $datum) {
753 foreach ($datum as $key => $value) {
754 $datum[$key] = "\"$value\"";
756 $output[] = implode($config->fieldSeparator
, $datum);
758 fwrite($fd, implode("\n", $output));
763 * Update the record with PK $id in the import database table.
766 * @param array $params
768 public function updateImportRecord($id, &$params) {
769 $statusFieldName = $this->_statusFieldName
;
770 $primaryKeyName = $this->_primaryKeyName
;
772 if ($statusFieldName && $primaryKeyName) {
773 $dao = new CRM_Core_DAO();
774 $db = $dao->getDatabaseConnection();
776 $query = "UPDATE $this->_tableName
777 SET $statusFieldName = ?,
778 ${statusFieldName}Msg = ?
779 WHERE $primaryKeyName = ?";
781 $params[$statusFieldName],
782 CRM_Utils_Array
::value("${statusFieldName}Msg", $params),
786 //print "Running query: $query<br/>With arguments: ".$params[$statusFieldName].", ".$params["${statusFieldName}Msg"].", $id<br/>";
788 $db->query($query, $args);
793 * Format common params data to proper format to store.
795 * @param array $params
796 * Contain record values.
797 * @param array $formatted
798 * Array of formatted data.
799 * @param array $contactFields
800 * Contact DAO fields.
802 public function formatCommonData($params, &$formatted, &$contactFields) {
804 CRM_Utils_Array
::value('contact_type', $formatted),
808 //add custom fields for contact sub type
809 if (!empty($this->_contactSubType
)) {
810 $csType = $this->_contactSubType
;
813 if ($relCsType = CRM_Utils_Array
::value('contact_sub_type', $formatted)) {
814 $csType = $relCsType;
817 $customFields = CRM_Core_BAO_CustomField
::getFields($formatted['contact_type'], FALSE, FALSE, $csType);
819 $addressCustomFields = CRM_Core_BAO_CustomField
::getFields('Address');
820 $customFields = $customFields +
$addressCustomFields;
822 //if a Custom Email Greeting, Custom Postal Greeting or Custom Addressee is mapped, and no "Greeting / Addressee Type ID" is provided, then automatically set the type = Customized, CRM-4575
824 'email_greeting_custom' => 'email_greeting',
825 'postal_greeting_custom' => 'postal_greeting',
826 'addressee_custom' => 'addressee',
828 foreach ($elements as $k => $v) {
829 if (array_key_exists($k, $params) && !(array_key_exists($v, $params))) {
830 $label = key(CRM_Core_OptionGroup
::values($v, TRUE, NULL, NULL, 'AND v.name = "Customized"'));
831 $params[$v] = $label;
836 $session = CRM_Core_Session
::singleton();
837 $dateType = $session->get("dateTypes");
838 foreach ($params as $key => $val) {
839 $customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key);
840 if ($customFieldID &&
841 !array_key_exists($customFieldID, $addressCustomFields)
843 //we should not update Date to null, CRM-4062
844 if ($val && ($customFields[$customFieldID]['data_type'] == 'Date')) {
846 CRM_Contact_Import_Parser_Contact
::formatCustomDate($params, $formatted, $dateType, $key);
848 elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') {
849 if (empty($val) && !is_numeric($val) && $this->_onDuplicate
== CRM_Import_Parser
::DUPLICATE_FILL
) {
850 //retain earlier value when Import mode is `Fill`
851 unset($params[$key]);
854 $params[$key] = CRM_Utils_String
::strtoboolstr($val);
859 if ($key == 'birth_date' && $val) {
860 CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key);
862 elseif ($key == 'deceased_date' && $val) {
863 CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key);
864 $params['is_deceased'] = 1;
866 elseif ($key == 'is_deceased' && $val) {
867 $params[$key] = CRM_Utils_String
::strtoboolstr($val);
869 elseif ($key == 'gender') {
871 $params[$key] = $this->checkGender($val);
875 //now format custom data.
876 foreach ($params as $key => $field) {
877 if (is_array($field)) {
878 $isAddressCustomField = FALSE;
879 foreach ($field as $value) {
881 if (is_array($value)) {
882 foreach ($value as $name => $testForEmpty) {
883 if ($addressCustomFieldID = CRM_Core_BAO_CustomField
::getKeyID($name)) {
884 $isAddressCustomField = TRUE;
887 // check if $value does not contain IM provider or phoneType
888 if (($name !== 'phone_type_id' ||
$name !== 'provider_id') && ($testForEmpty === '' ||
$testForEmpty == NULL)) {
899 $this->formatContactParameters($value, $formatted);
902 if (!$isAddressCustomField) {
907 $formatValues = array(
911 if (($key !== 'preferred_communication_method') && (array_key_exists($key, $contactFields))) {
912 // due to merging of individual table and
913 // contact table, we need to avoid
914 // preferred_communication_method forcefully
915 $formatValues['contact_type'] = $formatted['contact_type'];
918 if ($key == 'id' && isset($field)) {
919 $formatted[$key] = $field;
921 $this->formatContactParameters($formatValues, $formatted);
923 //Handling Custom Data
924 // note: Address custom fields will be handled separately inside formatContactParameters
925 if (($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) &&
926 array_key_exists($customFieldID, $customFields) &&
927 !array_key_exists($customFieldID, $addressCustomFields)
930 $extends = CRM_Utils_Array
::value('extends', $customFields[$customFieldID]);
931 $htmlType = CRM_Utils_Array
::value('html_type', $customFields[$customFieldID]);
935 case 'Autocomplete-Select':
936 if ($customFields[$customFieldID]['data_type'] == 'String' ||
$customFields[$customFieldID]['data_type'] == 'Int') {
937 $customOption = CRM_Core_BAO_CustomOption
::getCustomOption($customFieldID, TRUE);
938 foreach ($customOption as $customValue) {
939 $val = CRM_Utils_Array
::value('value', $customValue);
940 $label = CRM_Utils_Array
::value('label', $customValue);
941 $label = strtolower($label);
942 $value = strtolower(trim($formatted[$key]));
943 if (($value == $label) ||
($value == strtolower($val))) {
944 $params[$key] = $formatted[$key] = $val;
951 case 'AdvMulti-Select':
954 if (!empty($formatted[$key]) && !empty($params[$key])) {
955 $mulValues = explode(',', $formatted[$key]);
956 $customOption = CRM_Core_BAO_CustomOption
::getCustomOption($customFieldID, TRUE);
957 $formatted[$key] = array();
958 $params[$key] = array();
959 foreach ($mulValues as $v1) {
960 foreach ($customOption as $v2) {
961 if ((strtolower($v2['label']) == strtolower(trim($v1))) ||
962 (strtolower($v2['value']) == strtolower(trim($v1)))
964 if ($htmlType == 'CheckBox') {
965 $params[$key][$v2['value']] = $formatted[$key][$v2['value']] = 1;
968 $params[$key][] = $formatted[$key][] = $v2['value'];
979 if (!empty($key) && ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) && array_key_exists($customFieldID, $customFields) &&
980 !array_key_exists($customFieldID, $addressCustomFields)
982 // @todo calling api functions directly is not supported
983 _civicrm_api3_custom_format_params($params, $formatted, $extends);
986 // to check if not update mode and unset the fields with empty value.
987 if (!$this->_updateWithId
&& array_key_exists('custom', $formatted)) {
988 foreach ($formatted['custom'] as $customKey => $customvalue) {
989 if (empty($formatted['custom'][$customKey][-1]['is_required'])) {
990 $formatted['custom'][$customKey][-1]['is_required'] = $customFields[$customKey]['is_required'];
992 $emptyValue = CRM_Utils_Array
::value('value', $customvalue[-1]);
993 if (!isset($emptyValue)) {
994 unset($formatted['custom'][$customKey]);
999 // parse street address, CRM-5450
1000 if ($this->_parseStreetAddress
) {
1001 if (array_key_exists('address', $formatted) && is_array($formatted['address'])) {
1002 foreach ($formatted['address'] as $instance => & $address) {
1003 $streetAddress = CRM_Utils_Array
::value('street_address', $address);
1004 if (empty($streetAddress)) {
1007 // parse address field.
1008 $parsedFields = CRM_Core_BAO_Address
::parseStreetAddress($streetAddress);
1010 //street address consider to be parsed properly,
1011 //If we get street_name and street_number.
1012 if (empty($parsedFields['street_name']) ||
empty($parsedFields['street_number'])) {
1013 $parsedFields = array_fill_keys(array_keys($parsedFields), '');
1016 // merge parse address w/ main address block.
1017 $address = array_merge($address, $parsedFields);
1024 * Format contact parameters.
1026 * @todo this function needs re-writing & re-merging into the main function.
1030 * @param array $values
1031 * @param array $params
1035 protected function formatContactParameters(&$values, &$params) {
1036 // Crawl through the possible classes:
1049 // Cache the various object fields
1050 static $fields = array();
1052 // first add core contact values since for other Civi modules they are not added
1053 $contactFields = CRM_Contact_DAO_Contact
::fields();
1054 _civicrm_api3_store_values($contactFields, $values, $params);
1056 if (isset($values['contact_type'])) {
1057 // we're an individual/household/org property
1059 $fields[$values['contact_type']] = CRM_Contact_DAO_Contact
::fields();
1061 _civicrm_api3_store_values($fields[$values['contact_type']], $values, $params);
1065 if (isset($values['individual_prefix'])) {
1066 if (!empty($params['prefix_id'])) {
1067 $prefixes = CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'prefix_id');
1068 $params['prefix'] = $prefixes[$params['prefix_id']];
1071 $params['prefix'] = $values['individual_prefix'];
1076 if (isset($values['individual_suffix'])) {
1077 if (!empty($params['suffix_id'])) {
1078 $suffixes = CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'suffix_id');
1079 $params['suffix'] = $suffixes[$params['suffix_id']];
1082 $params['suffix'] = $values['individual_suffix'];
1088 if (isset($values['email_greeting'])) {
1089 if (!empty($params['email_greeting_id'])) {
1090 $emailGreetingFilter = array(
1091 'contact_type' => CRM_Utils_Array
::value('contact_type', $params),
1092 'greeting_type' => 'email_greeting',
1094 $emailGreetings = CRM_Core_PseudoConstant
::greeting($emailGreetingFilter);
1095 $params['email_greeting'] = $emailGreetings[$params['email_greeting_id']];
1098 $params['email_greeting'] = $values['email_greeting'];
1104 if (isset($values['postal_greeting'])) {
1105 if (!empty($params['postal_greeting_id'])) {
1106 $postalGreetingFilter = array(
1107 'contact_type' => CRM_Utils_Array
::value('contact_type', $params),
1108 'greeting_type' => 'postal_greeting',
1110 $postalGreetings = CRM_Core_PseudoConstant
::greeting($postalGreetingFilter);
1111 $params['postal_greeting'] = $postalGreetings[$params['postal_greeting_id']];
1114 $params['postal_greeting'] = $values['postal_greeting'];
1119 if (isset($values['addressee'])) {
1120 if (!empty($params['addressee_id'])) {
1121 $addresseeFilter = array(
1122 'contact_type' => CRM_Utils_Array
::value('contact_type', $params),
1123 'greeting_type' => 'addressee',
1125 $addressee = CRM_Core_PseudoConstant
::addressee($addresseeFilter);
1126 $params['addressee'] = $addressee[$params['addressee_id']];
1129 $params['addressee'] = $values['addressee'];
1134 if (isset($values['gender'])) {
1135 if (!empty($params['gender_id'])) {
1136 $genders = CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'gender_id');
1137 $params['gender'] = $genders[$params['gender_id']];
1140 $params['gender'] = $values['gender'];
1145 if (!empty($values['preferred_communication_method'])) {
1147 $pcm = array_change_key_case(array_flip(CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'preferred_communication_method')), CASE_LOWER
);
1149 $preffComm = explode(',', $values['preferred_communication_method']);
1150 foreach ($preffComm as $v) {
1151 $v = strtolower(trim($v));
1152 if (array_key_exists($v, $pcm)) {
1153 $comm[$pcm[$v]] = 1;
1157 $params['preferred_communication_method'] = $comm;
1161 // format the website params.
1162 if (!empty($values['url'])) {
1163 static $websiteFields;
1164 if (!is_array($websiteFields)) {
1165 $websiteFields = CRM_Core_DAO_Website
::fields();
1167 if (!array_key_exists('website', $params) ||
1168 !is_array($params['website'])
1170 $params['website'] = array();
1173 $websiteCount = count($params['website']);
1174 _civicrm_api3_store_values($websiteFields, $values,
1175 $params['website'][++
$websiteCount]
1181 // get the formatted location blocks into params - w/ 3.0 format, CRM-4605
1182 if (!empty($values['location_type_id'])) {
1183 $blockTypes = array(
1187 'openid' => 'OpenID',
1188 'phone_ext' => 'Phone',
1190 foreach ($blockTypes as $blockFieldName => $block) {
1191 if (!array_key_exists($blockFieldName, $values)) {
1195 // block present in value array.
1196 if (!array_key_exists($blockFieldName, $params) ||
!is_array($params[$blockFieldName])) {
1197 $params[$blockFieldName] = array();
1200 if (!array_key_exists($block, $fields)) {
1201 $className = "CRM_Core_DAO_$block";
1202 $fields[$block] = $className::fields();
1205 $blockCnt = count($params[$blockFieldName]);
1207 // copy value to dao field name.
1208 if ($blockFieldName == 'im') {
1209 $values['name'] = $values[$blockFieldName];
1212 _civicrm_api3_store_values($fields[$block], $values,
1213 $params[$blockFieldName][++
$blockCnt]
1216 if ($values['location_type_id'] === 'Primary') {
1217 if (!empty($params['id'])) {
1218 $primary = civicrm_api3($block, 'get', array('return' => 'location_type_id', 'contact_id' => $params['id'], 'is_primary' => 1, 'sequential' => 1));
1220 $defaultLocationType = CRM_Core_BAO_LocationType
::getDefault();
1221 $values['location_type_id'] = (isset($primary) && $primary['count']) ?
$primary['values'][0]['location_type_id'] : $defaultLocationType->id
;
1222 $values['is_primary'] = 1;
1225 if (empty($params['id']) && ($blockCnt == 1)) {
1226 $params[$blockFieldName][$blockCnt]['is_primary'] = TRUE;
1229 // we only process single block at a time.
1233 // handle address fields.
1234 if (!array_key_exists('address', $params) ||
!is_array($params['address'])) {
1235 $params['address'] = array();
1238 if (!array_key_exists('Address', $fields)) {
1239 $fields['Address'] = CRM_Core_DAO_Address
::fields();
1242 // Note: we doing multiple value formatting here for address custom fields, plus putting into right format.
1243 // The actual formatting (like date, country ..etc) for address custom fields is taken care of while saving
1244 // the address in CRM_Core_BAO_Address::create method
1245 if (!empty($values['location_type_id'])) {
1246 static $customFields = array();
1247 if (empty($customFields)) {
1248 $customFields = CRM_Core_BAO_CustomField
::getFields('Address');
1250 // make a copy of values, as we going to make changes
1251 $newValues = $values;
1252 foreach ($values as $key => $val) {
1253 $customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key);
1254 if ($customFieldID && array_key_exists($customFieldID, $customFields)) {
1255 // mark an entry in fields array since we want the value of custom field to be copied
1256 $fields['Address'][$key] = NULL;
1258 $htmlType = CRM_Utils_Array
::value('html_type', $customFields[$customFieldID]);
1259 switch ($htmlType) {
1261 case 'AdvMulti-Select':
1262 case 'Multi-Select':
1264 $mulValues = explode(',', $val);
1265 $customOption = CRM_Core_BAO_CustomOption
::getCustomOption($customFieldID, TRUE);
1266 $newValues[$key] = array();
1267 foreach ($mulValues as $v1) {
1268 foreach ($customOption as $v2) {
1269 if ((strtolower($v2['label']) == strtolower(trim($v1))) ||
1270 (strtolower($v2['value']) == strtolower(trim($v1)))
1272 if ($htmlType == 'CheckBox') {
1273 $newValues[$key][$v2['value']] = 1;
1276 $newValues[$key][] = $v2['value'];
1286 // consider new values
1287 $values = $newValues;
1290 _civicrm_api3_store_values($fields['Address'], $values, $params['address'][$values['location_type_id']]);
1292 $addressFields = array(
1296 'supplemental_address_1',
1297 'supplemental_address_2',
1298 'supplemental_address_3',
1299 'StateProvince.name',
1302 foreach ($addressFields as $field) {
1303 if (array_key_exists($field, $values)) {
1304 if (!array_key_exists('address', $params)) {
1305 $params['address'] = array();
1307 $params['address'][$values['location_type_id']][$field] = $values[$field];
1311 if ($values['location_type_id'] === 'Primary') {
1312 if (!empty($params['id'])) {
1313 $primary = civicrm_api3('Address', 'get', array('return' => 'location_type_id', 'contact_id' => $params['id'], 'is_primary' => 1, 'sequential' => 1));
1315 $defaultLocationType = CRM_Core_BAO_LocationType
::getDefault();
1316 $params['address'][$values['location_type_id']]['location_type_id'] = (isset($primary) && $primary['count']) ?
$primary['values'][0]['location_type_id'] : $defaultLocationType->id
;
1317 $params['address'][$values['location_type_id']]['is_primary'] = 1;
1323 if (isset($values['note'])) {
1325 if (!isset($params['note'])) {
1326 $params['note'] = array();
1328 $noteBlock = count($params['note']) +
1;
1330 $params['note'][$noteBlock] = array();
1331 if (!isset($fields['Note'])) {
1332 $fields['Note'] = CRM_Core_DAO_Note
::fields();
1335 // get the current logged in civicrm user
1336 $session = CRM_Core_Session
::singleton();
1337 $userID = $session->get('userID');
1340 $values['contact_id'] = $userID;
1343 _civicrm_api3_store_values($fields['Note'], $values, $params['note'][$noteBlock]);
1348 // Check for custom field values
1350 if (empty($fields['custom'])) {
1351 $fields['custom'] = &CRM_Core_BAO_CustomField
::getFields(CRM_Utils_Array
::value('contact_type', $values),
1352 FALSE, FALSE, NULL, NULL, FALSE, FALSE, FALSE
1356 foreach ($values as $key => $value) {
1357 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) {
1358 // check if it's a valid custom field id
1360 if (!array_key_exists($customFieldID, $fields['custom'])) {
1361 return civicrm_api3_create_error('Invalid custom field ID');
1364 $params[$key] = $value;