PageRenderTime 37ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/drupal/sites/all/modules/civicrm/CRM/Import/Parser/Contact.php

https://github.com/michaelmcandrew/ste
PHP | 1958 lines | 1423 code | 211 blank | 324 comment | 444 complexity | d52b784251cc6b633e913135b424aefd MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-1.0, LGPL-2.1
  1. <?php
  2. /*
  3. +--------------------------------------------------------------------+
  4. | CiviCRM version 3.4 |
  5. +--------------------------------------------------------------------+
  6. | Copyright CiviCRM LLC (c) 2004-2011 |
  7. +--------------------------------------------------------------------+
  8. | This file is a part of CiviCRM. |
  9. | |
  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. |
  13. | |
  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. |
  18. | |
  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. +--------------------------------------------------------------------+
  26. */
  27. /**
  28. *
  29. * @package CRM
  30. * @copyright CiviCRM LLC (c) 2004-2011
  31. * $Id$
  32. *
  33. */
  34. require_once 'CRM/Import/Parser.php';
  35. civicrm_api_include('utils', false, 2);
  36. /**
  37. * class to parse contact csv files
  38. */
  39. class CRM_Import_Parser_Contact extends CRM_Import_Parser
  40. {
  41. protected $_mapperKeys;
  42. protected $_mapperLocType;
  43. protected $_mapperPhoneType;
  44. protected $_mapperImProvider;
  45. protected $_mapperWebsiteType;
  46. protected $_mapperRelated;
  47. protected $_mapperRelatedContactType;
  48. protected $_mapperRelatedContactDetails;
  49. protected $_mapperRelatedContactEmailType;
  50. protected $_mapperRelatedContactImProvider;
  51. protected $_mapperRelatedContactWebsiteType;
  52. protected $_relationships;
  53. protected $_emailIndex;
  54. protected $_firstNameIndex;
  55. protected $_lastNameIndex;
  56. protected $_householdNameIndex;
  57. protected $_organizationNameIndex;
  58. protected $_allEmails;
  59. protected $_phoneIndex;
  60. protected $_updateWithId;
  61. protected $_retCode;
  62. protected $_externalIdentifierIndex;
  63. protected $_allExternalIdentifiers;
  64. protected $_parseStreetAddress;
  65. /**
  66. * Array of succesfully imported contact id's
  67. *
  68. * @array
  69. */
  70. protected $_newContacts;
  71. /**
  72. * line count id
  73. *
  74. * @var int
  75. */
  76. protected $_lineCount;
  77. /**
  78. * Array of succesfully imported related contact id's
  79. *
  80. * @array
  81. */
  82. protected $_newRelatedContacts;
  83. /**
  84. * array of all the contacts whose street addresses are not parsed
  85. * of this import process
  86. * @var array
  87. */
  88. protected $_unparsedStreetAddressContacts;
  89. /**
  90. * class constructor
  91. */
  92. function __construct( &$mapperKeys, $mapperLocType = null, $mapperPhoneType = null,
  93. $mapperImProvider = null, $mapperRelated = null, $mapperRelatedContactType=null,
  94. $mapperRelatedContactDetails = null, $mapperRelatedContactLocType = null,
  95. $mapperRelatedContactPhoneType = null, $mapperRelatedContactImProvider = null,
  96. $mapperWebsiteType = null, $mapperRelatedContactWebsiteType = null )
  97. {
  98. parent::__construct();
  99. $this->_mapperKeys =& $mapperKeys;
  100. $this->_mapperLocType =& $mapperLocType;
  101. $this->_mapperPhoneType =& $mapperPhoneType;
  102. $this->_mapperWebsiteType = $mapperWebsiteType;
  103. // get IM service provider type id for contact
  104. $this->_mapperImProvider =& $mapperImProvider;
  105. $this->_mapperRelated =& $mapperRelated;
  106. $this->_mapperRelatedContactType =& $mapperRelatedContactType;
  107. $this->_mapperRelatedContactDetails =& $mapperRelatedContactDetails;
  108. $this->_mapperRelatedContactLocType =& $mapperRelatedContactLocType;
  109. $this->_mapperRelatedContactPhoneType =& $mapperRelatedContactPhoneType;
  110. $this->_mapperRelatedContactWebsiteType = $mapperRelatedContactWebsiteType;
  111. // get IM service provider type id for related contact
  112. $this->_mapperRelatedContactImProvider =& $mapperRelatedContactImProvider;
  113. }
  114. /**
  115. * the initializer code, called before the processing
  116. *
  117. * @return void
  118. * @access public
  119. */
  120. function init( )
  121. {
  122. require_once 'CRM/Contact/BAO/Contact.php';
  123. require_once 'CRM/Core/BAO/Address.php';
  124. $contactFields = CRM_Contact_BAO_Contact::importableFields( $this->_contactType );
  125. // exclude the address options disabled in the Address Settings
  126. $fields = CRM_Core_BAO_Address::validateAddressOptions( $contactFields );
  127. //CRM-5125
  128. //supporting import for contact subtypes
  129. $csType = null;
  130. if ( !empty($this->_contactSubType) ) {
  131. //custom fields for sub type
  132. $subTypeFields = CRM_Core_BAO_CustomField::getFieldsForImport( $this->_contactSubType );
  133. if ( !empty($subTypeFields) ) {
  134. foreach($subTypeFields as $customSubTypeField => $details ) {
  135. $fields[$customSubTypeField] = $details;
  136. }
  137. }
  138. }
  139. //Relationship importables
  140. $this->_relationships = $relations = CRM_Contact_BAO_Relationship::getContactRelationshipType( null, null, null, $this->_contactType,
  141. false, 'label', true, $this->_contactSubType );
  142. asort($relations);
  143. foreach ($relations as $key => $var) {
  144. list( $type ) = explode( '_', $key );
  145. $relationshipType[$key]['title'] = $var;
  146. $relationshipType[$key]['headerPattern'] = '/' . preg_quote( $var, '/' ) . '/';
  147. $relationshipType[$key]['import'] = true;
  148. $relationshipType[$key]['relationship_type_id'] = $type;
  149. $relationshipType[$key]['related'] = true;
  150. }
  151. if ( !empty($relationshipType) ) {
  152. $fields = array_merge( $fields,
  153. array( 'related' => array( 'title' => ts('- related contact info -') ) ),
  154. $relationshipType );
  155. }
  156. foreach ($fields as $name => $field) {
  157. $this->addField( $name,
  158. $field['title'],
  159. CRM_Utils_Array::value( 'type' , $field ),
  160. CRM_Utils_Array::value( 'headerPattern' , $field ),
  161. CRM_Utils_Array::value( 'dataPattern' , $field ),
  162. CRM_Utils_Array::value( 'hasLocationType', $field ) );
  163. }
  164. $this->_newContacts = array( );
  165. $this->setActiveFields( $this->_mapperKeys );
  166. $this->setActiveFieldLocationTypes( $this->_mapperLocType );
  167. $this->setActiveFieldPhoneTypes( $this->_mapperPhoneType );
  168. $this->setActiveFieldWebsiteTypes( $this->_mapperWebsiteType );
  169. //set active fields of IM provider of contact
  170. $this->setActiveFieldImProviders( $this->_mapperImProvider );
  171. //related info
  172. $this->setActiveFieldRelated( $this->_mapperRelated );
  173. $this->setActiveFieldRelatedContactType( $this->_mapperRelatedContactType );
  174. $this->setActiveFieldRelatedContactDetails( $this->_mapperRelatedContactDetails );
  175. $this->setActiveFieldRelatedContactLocType( $this->_mapperRelatedContactLocType );
  176. $this->setActiveFieldRelatedContactPhoneType( $this->_mapperRelatedContactPhoneType );
  177. $this->setActiveFieldRelatedContactWebsiteType( $this->_mapperRelatedContactWebsiteType );
  178. //set active fields of IM provider of related contact
  179. $this->setActiveFieldRelatedContactImProvider( $this->_mapperRelatedContactImProvider );
  180. $this->_phoneIndex = -1;
  181. $this->_emailIndex = -1;
  182. $this->_firstNameIndex = -1;
  183. $this->_lastNameIndex = -1;
  184. $this->_householdNameIndex = -1;
  185. $this->_organizationNameIndex = -1;
  186. $this->_externalIdentifierIndex = -1;
  187. $index = 0 ;
  188. foreach ( $this->_mapperKeys as $key ) {
  189. if ( substr( $key, 0, 5 ) == 'email' && substr( $key, 0, 14 ) != 'email_greeting') {
  190. $this->_emailIndex = $index;
  191. $this->_allEmails = array( );
  192. }
  193. if ( substr( $key, 0, 5 ) == 'phone' ) {
  194. $this->_phoneIndex = $index;
  195. }
  196. if ( $key == 'first_name' ) {
  197. $this->_firstNameIndex = $index;
  198. }
  199. if ( $key == 'last_name' ) {
  200. $this->_lastNameIndex = $index;
  201. }
  202. if ( $key == 'household_name' ) {
  203. $this->_householdNameIndex = $index;
  204. }
  205. if ( $key == 'organization_name' ) {
  206. $this->_organizationNameIndex = $index;
  207. }
  208. if ( $key == 'external_identifier' ) {
  209. $this->_externalIdentifierIndex = $index;
  210. $this->_allExternalIdentifiers = array( );
  211. }
  212. $index++;
  213. }
  214. $this->_updateWithId = false;
  215. if ( in_array('id', $this->_mapperKeys ) ||
  216. ( $this->_externalIdentifierIndex >= 0 &&
  217. in_array( $this->_onDuplicate, array( CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::DUPLICATE_FILL ) ) ) ) {
  218. $this->_updateWithId = true;
  219. }
  220. require_once 'CRM/Core/BAO/Preferences.php';
  221. $this->_parseStreetAddress = CRM_Utils_Array::value( 'street_address_parsing',
  222. CRM_Core_BAO_Preferences::valueOptions( 'address_options' ),
  223. false );
  224. }
  225. /**
  226. * handle the values in mapField mode
  227. *
  228. * @param array $values the array of values belonging to this line
  229. *
  230. * @return boolean
  231. * @access public
  232. */
  233. function mapField( &$values )
  234. {
  235. return CRM_Import_Parser::VALID;
  236. }
  237. /**
  238. * handle the values in preview mode
  239. *
  240. * @param array $values the array of values belonging to this line
  241. *
  242. * @return boolean the result of this processing
  243. * @access public
  244. */
  245. function preview( &$values )
  246. {
  247. return $this->summary($values);
  248. }
  249. /**
  250. * handle the values in summary mode
  251. *
  252. * @param array $values the array of values belonging to this line
  253. *
  254. * @return boolean the result of this processing
  255. * @access public
  256. */
  257. function summary( &$values )
  258. {
  259. $response = $this->setActiveFieldValues( $values );
  260. $errorMessage = null;
  261. $errorRequired = false;
  262. switch ($this->_contactType) {
  263. case 'Individual' :
  264. $missingNames = array( );
  265. if ( $this->_firstNameIndex < 0 ||
  266. !CRM_Utils_Array::value( $this->_firstNameIndex, $values ) ) {
  267. $errorRequired = true;
  268. $missingNames[] = ts('First Name');
  269. }
  270. if ( $this->_lastNameIndex < 0 ||
  271. !CRM_Utils_Array::value( $this->_lastNameIndex, $values ) ) {
  272. $errorRequired = true;
  273. $missingNames[] = ts('Last Name');
  274. }
  275. if ( $errorRequired ) {
  276. $and = ' ' . ts('and') . ' ';
  277. $errorMessage = ts('Missing required fields:') . ' ' . implode( $and, $missingNames );
  278. }
  279. break;
  280. case 'Household' :
  281. if ( $this->_householdNameIndex < 0 ||
  282. !CRM_Utils_Array::value( $this->_householdNameIndex, $values ) ) {
  283. $errorRequired = true;
  284. $errorMessage = ts('Missing required fields:') . ' ' . ts('Household Name');
  285. }
  286. break;
  287. case 'Organization' :
  288. if ( $this->_organizationNameIndex < 0 ||
  289. !CRM_Utils_Array::value( $this->_organizationNameIndex, $values ) ) {
  290. $errorRequired = true;
  291. $errorMessage = ts('Missing required fields:') . ' ' . ts('Organization Name');
  292. }
  293. break;
  294. }
  295. $statusFieldName = $this->_statusFieldName;
  296. if ( $this->_emailIndex >= 0 ) {
  297. /* If we don't have the required fields, bail */
  298. if ($this->_contactType == 'Individual' &&! $this->_updateWithId ) {
  299. if ($errorRequired && ! CRM_Utils_Array::value($this->_emailIndex, $values)) {
  300. if ( $errorMessage ) {
  301. $errorMessage .= ' ' . ts('OR') . ' ' . ts('Email Address');
  302. } else {
  303. $errorMessage = ts('Missing required field:') . ' ' . ts('Email Address');
  304. }
  305. array_unshift($values, $errorMessage);
  306. $importRecordParams = array($statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage);
  307. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  308. return CRM_Import_Parser::ERROR;
  309. }
  310. }
  311. $email = CRM_Utils_Array::value( $this->_emailIndex, $values );
  312. if ( $email ) {
  313. /* If the email address isn't valid, bail */
  314. if (! CRM_Utils_Rule::email($email)) {
  315. $errorMessage = ts('Invalid Email address');
  316. array_unshift($values, $errorMessage);
  317. $importRecordParams = array($statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage);
  318. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  319. return CRM_Import_Parser::ERROR;
  320. }
  321. /* otherwise, count it and move on */
  322. $this->_allEmails[$email] = $this->_lineCount;
  323. }
  324. } else if ($errorRequired && ! $this->_updateWithId) {
  325. if ( $errorMessage ) {
  326. $errorMessage .= ' ' . ts('OR') . ' ' . ts('Email Address');
  327. } else {
  328. $errorMessage = ts('Missing required field:') . ' ' . ts('Email Address');
  329. }
  330. array_unshift($values, $errorMessage);
  331. $importRecordParams = array($statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage);
  332. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  333. return CRM_Import_Parser::ERROR;
  334. }
  335. //check for duplicate external Identifier
  336. $externalID = CRM_Utils_Array::value( $this->_externalIdentifierIndex, $values );
  337. if ( $externalID ) {
  338. /* If it's a dupe,external Identifier */
  339. if ( $externalDupe = CRM_Utils_Array::value( $externalID,
  340. $this->_allExternalIdentifiers ) ) {
  341. $errorMessage = ts('External Identifier conflicts with record %1', array(1 => $externalDupe));
  342. array_unshift($values, $errorMessage);
  343. $importRecordParams = array($statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage);
  344. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  345. return CRM_Import_Parser::ERROR;
  346. }
  347. //otherwise, count it and move on
  348. $this->_allExternalIdentifiers[$externalID] = $this->_lineCount;
  349. }
  350. //Checking error in custom data
  351. $params =& $this->getActiveFieldParams( );
  352. $params['contact_type'] = $this->_contactType;
  353. //date-format part ends
  354. $errorMessage = null;
  355. //checking error in custom data
  356. $this->isErrorInCustomData($params, $errorMessage);
  357. //checking error in core data
  358. $this->isErrorInCoreData($params, $errorMessage);
  359. if ( $errorMessage ) {
  360. $tempMsg = "Invalid value for field(s) : $errorMessage";
  361. // put the error message in the import record in the DB
  362. $importRecordParams = array($statusFieldName => 'ERROR', "${statusFieldName}Msg" => $tempMsg);
  363. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  364. array_unshift($values, $tempMsg);
  365. $errorMessage = null;
  366. return CRM_Import_Parser::ERROR;
  367. }
  368. //if user correcting errors by walking back
  369. //need to reset status ERROR msg to null
  370. //now currently we are having valid data.
  371. $importRecordParams = array( $statusFieldName => 'NEW' );
  372. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  373. return CRM_Import_Parser::VALID;
  374. }
  375. /**
  376. * handle the values in import mode
  377. *
  378. * @param int $onDuplicate the code for what action to take on duplicates
  379. * @param array $values the array of values belonging to this line
  380. *
  381. * @return boolean the result of this processing
  382. * @access public
  383. */
  384. function import( $onDuplicate, &$values, $doGeocodeAddress = false )
  385. {
  386. $config =& CRM_Core_Config::singleton( );
  387. $this->_unparsedStreetAddressContacts = array( );
  388. if ( ! $doGeocodeAddress ) {
  389. // CRM-5854, reset the geocode method to null to prevent geocoding
  390. $config->geocodeMethod = null;
  391. }
  392. // first make sure this is a valid line
  393. //$this->_updateWithId = false;
  394. $response = $this->summary( $values );
  395. $statusFieldName = $this->_statusFieldName;
  396. if ( $response != CRM_Import_Parser::VALID ) {
  397. $importRecordParams = array($statusFieldName => 'INVALID', "${statusFieldName}Msg" => "Invalid (Error Code: $response)");
  398. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  399. return $response;
  400. }
  401. $params =& $this->getActiveFieldParams( );
  402. $formatted = array('contact_type' => $this->_contactType);
  403. static $contactFields = null;
  404. if ( $contactFields == null) {
  405. require_once "CRM/Contact/DAO/Contact.php";
  406. $contactFields =& CRM_Contact_DAO_Contact::import( );
  407. }
  408. //check if external identifier exists in database
  409. if ( CRM_Utils_Array::value('external_identifier', $params ) &&
  410. ( CRM_Utils_Array::value('id', $params ) ||
  411. in_array( $onDuplicate, array( CRM_Import_Parser::DUPLICATE_SKIP, CRM_Import_Parser::DUPLICATE_NOCHECK ) ) ) ) {
  412. require_once "CRM/Contact/BAO/Contact.php";
  413. if ( $internalCid = CRM_Core_DAO::getFieldValue( 'CRM_Contact_DAO_Contact',
  414. $params['external_identifier'],
  415. 'id',
  416. 'external_identifier' ) ) {
  417. if ( $internalCid != CRM_Utils_Array::value('id', $params) ) {
  418. $errorMessage = ts('External Identifier already exists in database.');
  419. array_unshift($values, $errorMessage);
  420. $importRecordParams = array($statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage);
  421. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  422. return CRM_Import_Parser::DUPLICATE;
  423. }
  424. }
  425. }
  426. if ( !empty($this->_contactSubType) ) {
  427. $params['contact_sub_type'] = $this->_contactSubType;
  428. }
  429. if ( $subType = CRM_Utils_Array::value('contact_sub_type', $params) ) {
  430. if ( CRM_Contact_BAO_ContactType::isExtendsContactType($subType, $this->_contactType,false,'label') ) {
  431. $subTypes = CRM_Contact_BAO_ContactType::subTypePairs($this->_contactType, false, null );
  432. $params['contact_sub_type'] = array_search( $subType ,$subTypes );
  433. } elseif ( !CRM_Contact_BAO_ContactType::isExtendsContactType($subType, $this->_contactType) ) {
  434. $message = "Mismatched or Invalid Contact SubType.";
  435. array_unshift($values, $message);
  436. return CRM_Import_Parser::NO_MATCH;
  437. }
  438. }
  439. //get contact id to format common data in update/fill mode,
  440. //if external identifier is present, CRM-4423
  441. if ( $this->_updateWithId &&
  442. ! CRM_Utils_Array::value( 'id', $params ) &&
  443. CRM_Utils_Array::value( 'external_identifier', $params ) ) {
  444. if ( $cid = CRM_Core_DAO::getFieldValue( 'CRM_Contact_DAO_Contact',
  445. $params['external_identifier'], 'id',
  446. 'external_identifier' ) ) {
  447. $formatted['id'] = $cid;
  448. }
  449. }
  450. //format common data, CRM-4062
  451. $this->formatCommonData( $params, $formatted, $contactFields );
  452. $relationship = false;
  453. $createNewContact = true;
  454. // Support Match and Update Via Contact ID
  455. if ( $this->_updateWithId ) {
  456. $createNewContact = false;
  457. if ( !CRM_Utils_Array::value('id', $params) && CRM_Utils_Array::value('external_identifier', $params) ) {
  458. if ( $cid ) {
  459. $params['id'] = $cid;
  460. } else {
  461. //update contact if dedupe found contact id, CRM-4148
  462. $dedupeParams = $formatted;
  463. //special case to check dedupe if external id present.
  464. //if we send external id dedupe will stop.
  465. unset( $dedupeParams['external_identifier'] );
  466. $checkDedupe = _civicrm_duplicate_formatted_contact( $dedupeParams );
  467. if ( civicrm_duplicate( $checkDedupe ) ) {
  468. $matchingContactIds = explode( ',', $checkDedupe['error_message']['params'][0] );
  469. if ( count( $matchingContactIds ) == 1 ) {
  470. $params['id'] = array_pop( $matchingContactIds );
  471. } else {
  472. $message = "More than one matching contact found for given criteria.";
  473. array_unshift($values, $message);
  474. $this->_retCode = CRM_Import_Parser::NO_MATCH;
  475. }
  476. } else {
  477. $createNewContact = true;
  478. }
  479. }
  480. }
  481. $error = _civicrm_duplicate_formatted_contact($formatted);
  482. if ( civicrm_duplicate($error) ) {
  483. $matchedIDs = explode( ',', $error['error_message']['params'][0] );
  484. if ( count( $matchedIDs) >= 1 ) {
  485. $updateflag = true;
  486. foreach ($matchedIDs as $contactId) {
  487. if ($params['id'] == $contactId) {
  488. $contactType = CRM_Core_DAO::getFieldValue( 'CRM_Contact_DAO_Contact',
  489. $params['id'],
  490. 'contact_type' );
  491. if ($formatted['contact_type'] == $contactType ) {
  492. //validation of subtype for update mode
  493. //CRM-5125
  494. $contactSubType = null;
  495. if ( CRM_Utils_Array::value('contact_sub_type', $params) ) {
  496. $contactSubType = CRM_Core_DAO::getFieldValue( 'CRM_Contact_DAO_Contact',
  497. $params['id'],
  498. 'contact_sub_type' );
  499. }
  500. if ( !empty($contactSubType) &&
  501. ( !CRM_Contact_BAO_ContactType::isAllowEdit($params['id'], $contactSubType) &&
  502. $contactSubType != CRM_Utils_Array::value('contact_sub_type', $formatted) ) ) {
  503. $message = "Mismatched contact SubTypes :";
  504. array_unshift($values, $message);
  505. $updateflag = false;
  506. $this->_retCode = CRM_Import_Parser::NO_MATCH;
  507. } else {
  508. $newContact = $this->createContact( $formatted, $contactFields,
  509. $onDuplicate, $contactId, false );
  510. $updateflag = false;
  511. $this->_retCode = CRM_Import_Parser::VALID;
  512. }
  513. } else {
  514. $message = "Mismatched contact Types :";
  515. array_unshift($values, $message);
  516. $updateflag = false;
  517. $this->_retCode = CRM_Import_Parser::NO_MATCH;
  518. }
  519. }
  520. }
  521. if ( $updateflag ) {
  522. $message = "Mismatched contact IDs OR Mismatched contact Types :" ;
  523. array_unshift($values, $message);
  524. $this->_retCode = CRM_Import_Parser::NO_MATCH;
  525. }
  526. }
  527. } else {
  528. $contactType = null;
  529. if ( CRM_Utils_Array::value( 'id', $params ) ) {
  530. $contactType = CRM_Core_DAO::getFieldValue( 'CRM_Contact_DAO_Contact',
  531. $params['id'],
  532. 'contact_type' );
  533. if ( $contactType ) {
  534. if ($formatted['contact_type'] == $contactType ) {
  535. //validation of subtype for update mode
  536. //CRM-5125
  537. $contactSubType = null;
  538. if ( CRM_Utils_Array::value('contact_sub_type', $params) ) {
  539. $contactSubType = CRM_Core_DAO::getFieldValue( 'CRM_Contact_DAO_Contact',
  540. $params['id'],
  541. 'contact_sub_type' );
  542. }
  543. if ( !empty($contactSubType) &&
  544. (!CRM_Contact_BAO_ContactType::isAllowEdit($params['id'], $contactSubType) &&
  545. $contactSubType != CRM_Utils_Array::value('contact_sub_type', $formatted) ) ) {
  546. $message = "Mismatched contact SubTypes :";
  547. array_unshift($values, $message);
  548. $this->_retCode = CRM_Import_Parser::NO_MATCH;
  549. } else {
  550. $newContact = $this->createContact( $formatted, $contactFields,
  551. $onDuplicate, $params['id'], false );
  552. $this->_retCode = CRM_Import_Parser::VALID;
  553. }
  554. } else {
  555. $message = "Mismatched contact Types :";
  556. array_unshift($values, $message);
  557. $this->_retCode = CRM_Import_Parser::NO_MATCH;
  558. }
  559. } else {
  560. // we should avoid multiple errors for single record
  561. // since we have already retCode and we trying to force again.
  562. if ( $this->_retCode != CRM_Import_Parser::NO_MATCH ) {
  563. $message ="No contact found for this contact ID:".$params['id'] ;
  564. array_unshift($values, $message);
  565. $this->_retCode = CRM_Import_Parser::NO_MATCH;
  566. }
  567. }
  568. } else {
  569. //CRM-4148
  570. //now we want to create new contact on update/fill also.
  571. $createNewContact = true;
  572. }
  573. }
  574. if (is_a( $newContact, 'CRM_Contact_BAO_Contact' )) {
  575. $relationship = true;
  576. } else if (is_a( $error, 'CRM_Core_Error' )) {
  577. $newContact = $error;
  578. $relationship = true;
  579. }
  580. }
  581. //fixed CRM-4148
  582. //now we create new contact in update/fill mode also.
  583. if ( $createNewContact ) {
  584. //CRM-4430, don't carry if not submitted.
  585. foreach ( array( 'prefix', 'suffix', 'gender' ) as $name ) {
  586. if ( array_key_exists( $name, $formatted ) ) {
  587. if ( in_array( $name, array( 'prefix', 'suffix' ) ) ) {
  588. $formattedName = "individual_{$name}";
  589. $formatted[$formattedName] = CRM_Core_OptionGroup::getValue( $formattedName, (string)$formatted[$name] );
  590. } else {
  591. $formatted[$name] = CRM_Core_OptionGroup::getValue( $name, (string)$formatted[$name] );
  592. }
  593. }
  594. }
  595. $newContact = $this->createContact( $formatted, $contactFields, $onDuplicate );
  596. }
  597. $contactID = null;
  598. if ( is_object( $newContact ) || ( $newContact instanceof CRM_Contact_BAO_Contact ) ) {
  599. $relationship = true;
  600. $newContact = clone( $newContact );
  601. $contactID = $newContact->id;
  602. $this->_newContacts[] = $contactID;
  603. //get return code if we create new contact in update mode, CRM-4148
  604. if ( $this->_updateWithId ) {
  605. $this->_retCode = CRM_Import_Parser::VALID;
  606. }
  607. } else if ( civicrm_duplicate( $newContact ) ) {
  608. // if duplicate, no need of further processing
  609. if ( $onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP ) {
  610. $errorMessage = "Skipping duplicate record";
  611. array_unshift( $values, $errorMessage );
  612. $importRecordParams = array( $statusFieldName => 'DUPLICATE', "${statusFieldName}Msg" => $errorMessage );
  613. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  614. return CRM_Import_Parser::DUPLICATE;
  615. }
  616. $relationship = true;
  617. $contactID = $newContact['error_message']['params'][0];
  618. if ( !in_array( $contactID, $this->_newContacts ) ) {
  619. $this->_newContacts[] = $contactID;
  620. }
  621. }
  622. if ( $contactID ) {
  623. // call import hook
  624. require_once 'CRM/Utils/Hook.php';
  625. $currentImportID = end($values);
  626. $hookParams = array( 'contactID' => $contactID,
  627. 'importID' => $currentImportID,
  628. 'importTempTable' => $this->_tableName,
  629. 'fieldHeaders' => $this->_mapperKeys,
  630. 'fields' => $this->_activeFields );
  631. CRM_Utils_Hook::import( 'Contact',
  632. 'process',
  633. $this,
  634. $hookParams );
  635. }
  636. if ( $relationship ) {
  637. $primaryContactId = null;
  638. if ( civicrm_duplicate($newContact) ) {
  639. if ( CRM_Utils_Rule::integer( $newContact['error_message']['params'][0] ) ) {
  640. $primaryContactId = $newContact['error_message']['params'][0];
  641. }
  642. } else {
  643. $primaryContactId = $newContact->id;
  644. }
  645. if ( ( civicrm_duplicate($newContact) || is_a( $newContact, 'CRM_Contact_BAO_Contact' ) )
  646. && $primaryContactId ) {
  647. //relationship contact insert
  648. foreach ($params as $key => $field) {
  649. list($id, $first, $second) = CRM_Utils_System::explode('_', $key, 3);
  650. if ( !($first == 'a' && $second == 'b') && !($first == 'b' && $second == 'a') ) {
  651. continue;
  652. }
  653. $relationType = new CRM_Contact_DAO_RelationshipType();
  654. $relationType->id = $id;
  655. $relationType->find(true);
  656. $direction = "contact_sub_type_$second";
  657. $formatting = array('contact_type' => $params[$key]['contact_type']);
  658. //set subtype for related contact CRM-5125
  659. if ( isset($relationType->$direction) ) {
  660. //validation of related contact subtype for update mode
  661. if ( $relCsType = CRM_Utils_Array::value('contact_sub_type', $params[$key])
  662. && $relCsType != $relationType->$direction ) {
  663. $errorMessage = ts( "Mismatched or Invalid contact subtype found for this related contact" );
  664. array_unshift($values, $errorMessage);
  665. return CRM_Import_Parser::NO_MATCH;
  666. } else {
  667. $formatting['contact_sub_type'] = $relationType->$direction;
  668. }
  669. }
  670. $relationType->free( );
  671. $contactFields = null;
  672. $contactFields = CRM_Contact_DAO_Contact::import( );
  673. //Relation on the basis of External Identifier.
  674. if ( !CRM_Utils_Array::value( 'id' , $params[$key] ) && !empty( $params[$key]['external_identifier'] ) ) {
  675. $params[$key]['id'] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
  676. $params[$key]['external_identifier'],'id',
  677. 'external_identifier' );
  678. }
  679. // check for valid related contact id in update/fill mode, CRM-4424
  680. if ( in_array( $onDuplicate,
  681. array( CRM_Import_Parser::DUPLICATE_UPDATE,
  682. CRM_Import_Parser::DUPLICATE_FILL ) ) &&
  683. CRM_Utils_Array::value( 'id', $params[$key] ) ) {
  684. $relatedContactType = CRM_Core_DAO::getFieldValue( 'CRM_Contact_DAO_Contact',
  685. $params[$key]['id'],
  686. 'contact_type' );
  687. if ( ! $relatedContactType ) {
  688. $errorMessage = ts( "No contact found for this related contact ID: %1", array( 1 => $params[$key]['id'] ) );
  689. array_unshift($values, $errorMessage);
  690. return CRM_Import_Parser::NO_MATCH;
  691. } else {
  692. //validation of related contact subtype for update mode
  693. //CRM-5125
  694. $relatedCsType = null;
  695. if ( CRM_Utils_Array::value('contact_sub_type', $formatting) ) {
  696. $relatedCsType = CRM_Core_DAO::getFieldValue( 'CRM_Contact_DAO_Contact',
  697. $params[$key]['id'],
  698. 'contact_sub_type' );
  699. }
  700. if ( !empty($relatedCsType) &&
  701. (!CRM_Contact_BAO_ContactType::isAllowEdit($params[$key]['id'], $relatedCsType) &&
  702. $relatedCsType != CRM_Utils_Array::value( 'contact_sub_type', $formatting ) ) ) {
  703. $errorMessage = ts( "Mismatched or Invalid contact subtype found for this related contact ID: %1", array( 1 => $params[$key]['id'] ) );
  704. array_unshift($values, $errorMessage);
  705. return CRM_Import_Parser::NO_MATCH;
  706. } else {
  707. // get related contact id to format data in update/fill mode,
  708. //if external identifier is present, CRM-4423
  709. $formatting['id'] = $params[$key]['id'];
  710. }
  711. }
  712. }
  713. //format common data, CRM-4062
  714. $this->formatCommonData( $field, $formatting, $contactFields );
  715. //do we have enough fields to create related contact.
  716. $allowToCreate = $this->checkRelatedContactFields( $key, $formatting );
  717. if ( !$allowToCreate ) {
  718. $errorMessage = ts( 'Related contact required fields are missing.' );
  719. array_unshift($values, $errorMessage);
  720. return CRM_Import_Parser::NO_MATCH;
  721. }
  722. //fixed for CRM-4148
  723. if ( $params[$key]['id'] ) {
  724. $contact = array( 'contact_id' => $params[$key]['id'] );
  725. $defaults = array( );
  726. $relatedNewContact = CRM_Contact_BAO_Contact::retrieve( $contact, $defaults );
  727. } else {
  728. $relatedNewContact = $this->createContact( $formatting, $contactFields,
  729. $onDuplicate, null, false );
  730. }
  731. if ( is_object( $relatedNewContact ) || ( $relatedNewContact instanceof CRM_Contact_BAO_Contact ) ) {
  732. $relatedNewContact = clone($relatedNewContact);
  733. }
  734. $matchedIDs = array( );
  735. // To update/fill contact, get the matching contact Ids if duplicate contact found
  736. // otherwise get contact Id from object of related contact
  737. if ( is_array( $relatedNewContact ) && civicrm_error( $relatedNewContact ) ) {
  738. if ( civicrm_duplicate($relatedNewContact) ) {
  739. $matchedIDs = explode(',',$relatedNewContact['error_message']['params'][0]);
  740. } else {
  741. $errorMessage = $relatedNewContact['error_message'];
  742. array_unshift( $values, $errorMessage );
  743. $importRecordParams = array($statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage);
  744. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  745. return CRM_Import_Parser::ERROR;
  746. }
  747. } else {
  748. $matchedIDs[] = $relatedNewContact->id;
  749. }
  750. // update/fill related contact after getting matching Contact Ids, CRM-4424
  751. if ( in_array( $onDuplicate, array( CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::DUPLICATE_FILL ) ) ) {
  752. //validation of related contact subtype for update mode
  753. //CRM-5125
  754. $relatedCsType = null;
  755. if ( CRM_Utils_Array::value('contact_sub_type', $formatting) ) {
  756. $relatedCsType = CRM_Core_DAO::getFieldValue( 'CRM_Contact_DAO_Contact',
  757. $matchedIDs[0],
  758. 'contact_sub_type' );
  759. }
  760. if ( !empty($relatedCsType) &&
  761. (!CRM_Contact_BAO_ContactType::isAllowEdit($matchedIDs[0], $relatedCsType) &&
  762. $relatedCsType != CRM_Utils_Array::value('contact_sub_type', $formatting) ) ) {
  763. $errorMessage = ts( "Mismatched or Invalid contact subtype found for this related contact." );
  764. array_unshift($values, $errorMessage);
  765. return CRM_Import_Parser::NO_MATCH;
  766. } else {
  767. $updatedContact = $this->createContact( $formatting, $contactFields, $onDuplicate, $matchedIDs[0] );
  768. }
  769. }
  770. static $relativeContact = array( ) ;
  771. if ( civicrm_duplicate( $relatedNewContact ) ) {
  772. if ( count( $matchedIDs ) >= 1 ) {
  773. $relContactId = $matchedIDs[0];
  774. //add relative contact to count during update & fill mode.
  775. //logic to make count distinct by contact id.
  776. if ( $this->_newRelatedContacts || ! empty( $relativeContact ) ) {
  777. $reContact = array_keys( $relativeContact, $relContactId );
  778. if ( empty( $reContact ) ) {
  779. $this->_newRelatedContacts[] = $relativeContact[] = $relContactId;
  780. }
  781. } else {
  782. $this->_newRelatedContacts[] = $relativeContact[] = $relContactId;
  783. }
  784. }
  785. } else {
  786. $relContactId = $relatedNewContact->id;
  787. $this->_newRelatedContacts[] = $relativeContact[] = $relContactId;
  788. }
  789. if ( civicrm_duplicate( $relatedNewContact ) ||
  790. ( $relatedNewContact instanceof CRM_Contact_BAO_Contact ) ) {
  791. //fix for CRM-1993.Checks for duplicate related contacts
  792. if ( count( $matchedIDs ) >= 1 ) {
  793. //if more than one duplicate contact
  794. //found, create relationship with first contact
  795. // now create the relationship record
  796. $relationParams = array( );
  797. $relationParams = array('relationship_type_id' => $key,
  798. 'contact_check' => array( $relContactId => 1),
  799. 'is_active' => 1,
  800. 'skipRecentView' => true
  801. );
  802. // we only handle related contact success, we ignore failures for now
  803. // at some point wold be nice to have related counts as separate
  804. $relationIds = array('contact' => $primaryContactId);
  805. list( $valid, $invalid, $duplicate, $saved, $relationshipIds ) =
  806. CRM_Contact_BAO_Relationship::create( $relationParams, $relationIds );
  807. if ( $valid || $duplicate ) {
  808. $relationIds['contactTarget'] = $relContactId;
  809. $action = ( $duplicate ) ? CRM_Core_Action::UPDATE : CRM_Core_Action::ADD;
  810. CRM_Contact_BAO_Relationship::relatedMemberships( $primaryContactId,
  811. $relationParams,
  812. $relationIds,
  813. $action );
  814. }
  815. //handle current employer, CRM-3532
  816. if ( $valid ) {
  817. require_once 'CRM/Core/PseudoConstant.php';
  818. $allRelationships = CRM_Core_PseudoConstant::relationshipType( 'name' );
  819. $relationshipTypeId = str_replace( array('_a_b', '_b_a'), array('', ''), $key );
  820. $relationshipType = str_replace( $relationshipTypeId . '_', '', $key );
  821. $orgId = $individualId = null;
  822. if ( $allRelationships[$relationshipTypeId]["name_{$relationshipType}"] == 'Employee of' ) {
  823. $orgId = $relContactId;
  824. $individualId = $primaryContactId;
  825. } else if ( $allRelationships[$relationshipTypeId]["name_{$relationshipType}"] == 'Employer of' ) {
  826. $orgId = $primaryContactId;
  827. $individualId = $relContactId;
  828. }
  829. if ( $orgId && $individualId ) {
  830. $currentEmpParams[$individualId] = $orgId;
  831. require_once 'CRM/Contact/BAO/Contact/Utils.php';
  832. CRM_Contact_BAO_Contact_Utils::setCurrentEmployer( $currentEmpParams );
  833. }
  834. }
  835. }
  836. }
  837. }
  838. }
  839. }
  840. if( $this->_updateWithId ) {
  841. //return warning if street address is unparsed, CRM-5886
  842. return $this->processMessage( $values, $statusFieldName, $this->_retCode );
  843. }
  844. //dupe checking
  845. if ( is_array( $newContact ) && civicrm_error( $newContact ) ) {
  846. $code = null;
  847. if ( ( $code = CRM_Utils_Array::value( 'code', $newContact['error_message'] ) ) &&
  848. ( $code == CRM_Core_Error::DUPLICATE_CONTACT ) ) {
  849. $urls = array( );
  850. // need to fix at some stage and decide if the error will return an
  851. // array or string, crude hack for now
  852. if ( is_array( $newContact['error_message']['params'][0] ) ) {
  853. $cids = $newContact['error_message']['params'][0];
  854. } else {
  855. $cids = explode( ',', $newContact['error_message']['params'][0] );
  856. }
  857. foreach ($cids as $cid) {
  858. $urls[] = CRM_Utils_System::url('civicrm/contact/view',
  859. 'reset=1&cid=' . $cid, true);
  860. }
  861. $url_string = implode("\n", $urls);
  862. // If we duplicate more than one record, skip no matter what
  863. if (count($cids) > 1) {
  864. $errorMessage = ts('Record duplicates multiple contacts');
  865. $importRecordParams = array($statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage);
  866. //combine error msg to avoid mismatch between error file columns.
  867. $errorMessage .= "\n" . $url_string;
  868. array_unshift($values, $errorMessage);
  869. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  870. return CRM_Import_Parser::ERROR;
  871. }
  872. // Params only had one id, so shift it out
  873. $contactId = array_shift( $cids );
  874. $cid = null;
  875. $vals = array( 'contact_id' => $contactId );
  876. if ($onDuplicate == CRM_Import_Parser::DUPLICATE_REPLACE) {
  877. $result = civicrm_replace_contact_formatted( $contactId, $formatted, $contactFields );
  878. $cid = $result['result'];
  879. } else if ($onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE) {
  880. $newContact = $this->createContact( $formatted, $contactFields, $onDuplicate, $contactId );
  881. } else if ($onDuplicate == CRM_Import_Parser::DUPLICATE_FILL) {
  882. $newContact = $this->createContact( $formatted, $contactFields, $onDuplicate, $contactId );
  883. } // else skip does nothing and just returns an error code.
  884. if ( $cid ) {
  885. $contact = array( 'contact_id' => $cid );
  886. $defaults = array( );
  887. $newContact = CRM_Contact_BAO_Contact::retrieve( $contact, $defaults );
  888. }
  889. if ( civicrm_error( $newContact ) ) {
  890. $contactID = $newContact['error_message']['params'][0];
  891. if ( !in_array( $contactID, $this->_newContacts ) ) {
  892. $this->_newContacts[] = $contactID;
  893. }
  894. }
  895. //CRM-262 No Duplicate Checking
  896. if ($onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) {
  897. array_unshift( $values, $url_string );
  898. $importRecordParams = array($statusFieldName => 'DUPLICATE', "${statusFieldName}Msg" => "Skipping duplicate record");
  899. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  900. return CRM_Import_Parser::DUPLICATE;
  901. }
  902. $importRecordParams = array($statusFieldName => 'IMPORTED');
  903. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  904. //return warning if street address is not parsed, CRM-5886
  905. return $this->processMessage( $values, $statusFieldName, CRM_Import_Parser::VALID );
  906. } else {
  907. // Not a dupe, so we had an error
  908. $errorMessage = $newContact['error_message'];
  909. array_unshift( $values, $errorMessage );
  910. $importRecordParams = array($statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage);
  911. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  912. return CRM_Import_Parser::ERROR;
  913. }
  914. }
  915. // sleep(3);
  916. return $this->processMessage( $values, $statusFieldName, CRM_Import_Parser::VALID );
  917. }
  918. /**
  919. * Get the array of succesfully imported contact id's
  920. *
  921. * @return array
  922. * @access public
  923. */
  924. function &getImportedContacts()
  925. {
  926. return $this->_newContacts;
  927. }
  928. /**
  929. * Get the array of succesfully imported related contact id's
  930. *
  931. * @return array
  932. * @access public
  933. */
  934. function &getRelatedImportedContacts()
  935. {
  936. return $this->_newRelatedContacts;
  937. }
  938. /**
  939. * the initializer code, called before the processing
  940. *
  941. * @return void
  942. * @access public
  943. */
  944. function fini( )
  945. {
  946. }
  947. /**
  948. * function to check if an error in custom data
  949. *
  950. * @param String $errorMessage A string containing all the error-fields.
  951. *
  952. * @access public
  953. */
  954. function isErrorInCustomData($params, &$errorMessage)
  955. {
  956. $session = CRM_Core_Session::singleton();
  957. $dateType = $session->get("dateTypes");
  958. //CRM-5125
  959. //add custom fields for contact sub type
  960. $csType = NULL;
  961. if ( !empty($this->_contactSubType) ) {
  962. $csType = $this->_contactSubType;
  963. }
  964. if ( CRM_Utils_Array::value('contact_sub_type', $params) ) {
  965. $csType = CRM_Utils_Array::value('contact_sub_type', $params);
  966. }
  967. $customFields = CRM_Core_BAO_CustomField::getFields( $params['contact_type'], false, false, $csType );
  968. $addressCustomFields = CRM_Core_BAO_CustomField::getFields( 'Address' );
  969. $customFields = $customFields + $addressCustomFields;
  970. foreach ($params as $key => $value) {
  971. if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) {
  972. /* check if it's a valid custom field id */
  973. if ( !array_key_exists($customFieldID, $customFields)) {
  974. self::addToErrorMsg(ts('field ID'), $errorMessage);
  975. }
  976. //For address custom fields, we do get actual custom field value as an inner array of
  977. //values so need to modify
  978. if( array_key_exists( $customFieldID, $addressCustomFields ) ) {
  979. $value = $value[0][$key];
  980. }
  981. /* validate the data against the CF type */
  982. if ( $value ) {
  983. if ($customFields[$customFieldID]['data_type'] == 'Date') {
  984. if( CRM_Utils_Date::convertToDefaultDate( $params, $dateType, $key )) {
  985. $value = $params[$key];
  986. } else {
  987. self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
  988. }
  989. } else if ( $customFields[$customFieldID]['data_type'] == 'Boolean') {
  990. if (CRM_Utils_String::strtoboolstr($value) === false) {
  991. self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
  992. }
  993. }
  994. // need not check for label filed import
  995. $htmlType = array( 'CheckBox','Multi-Select','AdvMulti-Select','Select',
  996. 'Radio','Multi-Select State/Province' ,'Multi-Select Country' );
  997. if ( ! in_array( $customFields[$customFieldID]['html_type'], $htmlType ) ||
  998. $customFields[$customFieldID]['data_type'] =='Boolean' ||
  999. $customFields[$customFieldID]['data_type'] == 'ContactReference' ) {
  1000. $valid =
  1001. CRM_Core_BAO_CustomValue::typecheck($customFields[$customFieldID]['data_type'], $value);
  1002. if (! $valid) {
  1003. self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
  1004. }
  1005. }
  1006. // check for values for custom fields for checkboxes and multiselect
  1007. if ( $customFields[$customFieldID]['html_type'] == 'CheckBox' ||
  1008. $customFields[$customFieldID]['html_type'] == 'AdvMulti-Select' ||
  1009. $customFields[$customFieldID]['html_type'] == 'Multi-Select' ) {
  1010. $value = trim( $value );
  1011. $value = str_replace('|', ',', $value);
  1012. $mulValues = explode( ',' , $value );
  1013. $customOption = CRM_Core_BAO_CustomOption::getCustomOption( $customFieldID, true );
  1014. foreach( $mulValues as $v1 ) {
  1015. if ( strlen( $v1 ) == 0 ) {
  1016. continue;
  1017. }
  1018. $flag = false;
  1019. foreach( $customOption as $v2 ) {
  1020. if (( strtolower(trim($v2['label'])) == strtolower(trim($v1))) ||
  1021. ( strtolower(trim($v2['value'])) == strtolower(trim($v1)))) {
  1022. $flag = true;
  1023. }
  1024. }
  1025. if (! $flag ) {
  1026. self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
  1027. }
  1028. }
  1029. } else if ( $customFields[$customFieldID]['html_type'] == 'Select' ||
  1030. ( $customFields[$customFieldID]['html_type'] =='Radio' &&
  1031. $customFields[$customFieldID]['data_type'] !='Boolean' ) ) {
  1032. $customOption = CRM_Core_BAO_CustomOption::getCustomOption( $customFieldID, true );
  1033. $flag = false;
  1034. foreach( $customOption as $v2 ) {
  1035. if (( strtolower(trim($v2['label'])) == strtolower(trim($value)) ) ||
  1036. ( strtolower(trim($v2['value'])) == strtolower(trim($value)) )) {
  1037. $flag = true;
  1038. }
  1039. }
  1040. if (! $flag ) {
  1041. self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
  1042. }
  1043. } else if ( $customFields[$customFieldID]['html_type'] == 'Multi-Select State/Province' ) {
  1044. $mulValues = explode( ',' , $value );
  1045. foreach( $mulValues as $stateValue ) {
  1046. if ( $stateValue) {
  1047. if( self::in_value(trim($stateValue),CRM_Core_PseudoConstant::stateProvinceAbbreviation())
  1048. || self::in_value(trim($stateValue), CRM_Core_PseudoConstant::stateProvince())) {
  1049. continue;
  1050. } else {
  1051. self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
  1052. }
  1053. }
  1054. }
  1055. } else if ( $customFields[$customFieldID]['html_type'] == 'Multi-Select Country' ) {
  1056. $mulValues = explode( ',' , $value );
  1057. foreach ( $mulValues as $countryValue ) {
  1058. if ( $countryValue ) {
  1059. CRM_Core_PseudoConstant::populate( $countryNames, 'CRM_Core_DAO_Country',
  1060. true, 'name', 'is_active' );
  1061. CRM_Core_PseudoConstant::populate( $countryIsoCodes,
  1062. 'CRM_Core_DAO_Country',true,
  1063. 'iso_code');
  1064. $config = CRM_Core_Config::singleton( );
  1065. $limitCodes = $config->countryLimit( );
  1066. $error = true;
  1067. foreach ( array( $countryNames, $countryIsoCodes, $limitCodes ) as $values ) {
  1068. if ( in_array( trim($countryValue), $values ) ) {
  1069. $error = false;
  1070. break;
  1071. }
  1072. }
  1073. if ( $error ) {
  1074. self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
  1075. }
  1076. }
  1077. }
  1078. }
  1079. }
  1080. } else if ( is_array($params[$key]) &&
  1081. isset( $params[$key]["contact_type"] ) ) {
  1082. //CRM-5125
  1083. //supporting custom data of related contact subtypes
  1084. if ( array_key_exists($key, $this->_relationships) ) {
  1085. $relation = $key;
  1086. }else if ( CRM_Utils_Array::key($key, $this->_relationships) ) {
  1087. $relation = CRM_Utils_Array::key($key, $this->_relationships);
  1088. }
  1089. if ( !empty($relation) ) {
  1090. list($id, $first, $second) = CRM_Utils_System::explode('_', $relation, 3);
  1091. $direction = "contact_sub_type_$second";
  1092. require_once 'CRM/Contact/BAO/RelationshipType.php';
  1093. $relationshipType = new CRM_Contact_BAO_RelationshipType( );
  1094. $relationshipType->id = $id;
  1095. if ( $relationshipType->find( true ) ) {
  1096. if ( isset($relationshipType->$direction) ) {
  1097. $params[$key]['contact_sub_type'] = $relationshipType->$direction;
  1098. }
  1099. }
  1100. $relationshipType->free( );
  1101. }
  1102. self::isErrorInCustomData( $params[$key] ,$errorMessage );
  1103. }
  1104. }
  1105. }
  1106. /**
  1107. * Check if value present in all genders or
  1108. * as a substring of any gender value, if yes than return corresponding gender.
  1109. * eg value might be m/M, ma/MA, mal/MAL, male return 'Male'
  1110. * but if value is 'maleabc' than return false
  1111. *
  1112. * @param string $gender check this value across gender values.
  1113. *
  1114. * retunr gender value / false
  1115. * @access public
  1116. */
  1117. public function checkGender( $gender )
  1118. {
  1119. $gender = trim( $gender, '.' );
  1120. if ( !$gender ) {
  1121. return false;
  1122. }
  1123. $allGenders = CRM_Core_PseudoConstant::gender( );
  1124. foreach( $allGenders as $key => $value ) {
  1125. if ( strlen( $gender ) > strlen( $value ) ) {
  1126. continue;
  1127. }
  1128. if ( $gender == $value ) {
  1129. return $value;
  1130. }
  1131. if ( substr_compare( $value, $gender, 0, strlen( $gender ), true ) === 0 ) {
  1132. return $value;
  1133. }
  1134. }
  1135. return false;
  1136. }
  1137. /**
  1138. * function to check if an error in Core( non-custom fields ) field
  1139. *
  1140. * @param String $errorMessage A string containing all the error-fields.
  1141. *
  1142. * @access public
  1143. */
  1144. function isErrorInCoreData($params, &$errorMessage)
  1145. {
  1146. require_once 'CRM/Core/OptionGroup.php';
  1147. foreach ($params as $key => $value) {
  1148. if ( $value ) {
  1149. $session = CRM_Core_Session::singleton();
  1150. $dateType = $session->get("dateTypes");
  1151. switch( $key ) {
  1152. case 'birth_date':
  1153. if( CRM_Utils_Date::convertToDefaultDate( $params, $dateType, $key )) {
  1154. if (! CRM_Utils_Rule::date($params[$key])) {
  1155. self::addToErrorMsg(ts('Birth Date'), $errorMessage);
  1156. }
  1157. } else {
  1158. self::addToErrorMsg(ts('Birth-Date'), $errorMessage);
  1159. }
  1160. break;
  1161. case 'deceased_date':
  1162. if( CRM_Utils_Date::convertToDefaultDate( $params, $dateType, $key )) {
  1163. if (! CRM_Utils_Rule::date($params[$key])) {
  1164. self::addToErrorMsg(ts('Deceased Date'), $errorMessage);
  1165. }
  1166. } else {
  1167. self::addToErrorMsg(ts('Deceased Date'), $errorMessage);
  1168. }
  1169. break;
  1170. case 'is_deceased':
  1171. if (CRM_Utils_String::strtoboolstr($value) === false) {
  1172. self::addToErrorMsg(ts('Is Deceased'), $errorMessage);
  1173. }
  1174. break;
  1175. case 'gender':
  1176. if ( !self::checkGender( $value ) ) {
  1177. self::addToErrorMsg(ts('Gender'), $errorMessage);
  1178. }
  1179. break;
  1180. case 'preferred_communication_method':
  1181. $preffComm = array( );
  1182. $preffComm = explode(',', $value);
  1183. foreach ($preffComm as $v) {
  1184. if (!self::in_value( trim($v), CRM_Core_PseudoConstant::pcm())) {
  1185. self::addToErrorMsg(ts('Preferred Communication Method'), $errorMessage);
  1186. }
  1187. }
  1188. break;
  1189. case 'preferred_mail_format':
  1190. if(!array_key_exists(strtolower($value),array_change_key_case(CRM_Core_SelectValues::pmf(), CASE_LOWER))) {
  1191. self::addToErrorMsg(ts('Preferred Mail Format'), $errorMessage);
  1192. }
  1193. break;
  1194. case 'individual_prefix':
  1195. if (! self::in_value($value,CRM_Core_PseudoConstant::individualPrefix())) {
  1196. self::addToErrorMsg(ts('Individual Prefix'), $errorMessage);
  1197. }
  1198. break;
  1199. case 'individual_suffix':
  1200. if (!self::in_value($value,CRM_Core_PseudoConstant::individualSuffix())) {
  1201. self::addToErrorMsg(ts('Individual Suffix'), $errorMessage);
  1202. }
  1203. break;
  1204. case 'state_province':
  1205. if ( ! empty( $value )) {
  1206. foreach($value as $stateValue ) {
  1207. if ( $stateValue['state_province']) {
  1208. if( self::in_value($stateValue['state_province'],CRM_Core_PseudoConstant::stateProvinceAbbreviation())
  1209. || self::in_value($stateValue['state_province'], CRM_Core_PseudoConstant::stateProvince())) {
  1210. continue;
  1211. } else {
  1212. self::addToErrorMsg(ts('State / Province'), $errorMessage);
  1213. }
  1214. }
  1215. }
  1216. }
  1217. break;
  1218. case 'country':
  1219. if (!empty( $value ) ) {
  1220. foreach($value as $stateValue ) {
  1221. if ( $stateValue['country'] ) {
  1222. CRM_Core_PseudoConstant::populate( $countryNames, 'CRM_Core_DAO_Country',
  1223. true, 'name', 'is_active' );
  1224. CRM_Core_PseudoConstant::populate( $countryIsoCodes,
  1225. 'CRM_Core_DAO_Country',true,
  1226. 'iso_code');
  1227. $config = CRM_Core_Config::singleton();
  1228. $limitCodes = $config->countryLimit( );
  1229. //If no country is selected in
  1230. //localization then take all countries
  1231. if ( empty($limitCodes )) {
  1232. $limitCodes = $countryIsoCodes;
  1233. }
  1234. if ( self::in_value($stateValue['country'], $limitCodes) || self::in_value($stateValue['country'], CRM_Core_PseudoConstant::country())) {
  1235. continue;
  1236. } else {
  1237. if( self::in_value($stateValue['country'], $countryIsoCodes) || self::in_value($stateValue['country'], $countryNames)) {
  1238. self::addToErrorMsg(ts('Country input value is in table but not "available": "This Country is valid but is NOT in the list of Available Countries currently configured for your site. This can be viewed and modifed from Global Settings >> Localization." '), $errorMessage);
  1239. }
  1240. else {
  1241. self::addToErrorMsg(ts('Country input value not in country table: "The Country value appears to be invalid. It does not match any value in CiviCRM table of countries."'), $errorMessage);
  1242. }
  1243. }
  1244. }
  1245. }
  1246. }
  1247. break;
  1248. case 'county':
  1249. if ( !empty( $value ) ) {
  1250. foreach ( $value as $county ) {
  1251. if( $county['county'] ) {
  1252. $countyNames = CRM_Core_PseudoConstant::county( );
  1253. if ( !in_array( $county['county'], $countyNames ) ) {
  1254. self::addToErrorMsg( ts('County input value not in county table: The County value appears to be invalid. It does not match any value in CiviCRM table of counties.'), $errorMessage );
  1255. }
  1256. }
  1257. }
  1258. }
  1259. break;
  1260. case 'geo_code_1' :
  1261. if (!empty( $value ) ) {
  1262. foreach($value as $codeValue ) {
  1263. if ( $codeValue['geo_code_1'] ) {
  1264. if ( CRM_Utils_Rule::numeric($codeValue['geo_code_1'])) {
  1265. continue;
  1266. } else {
  1267. self::addToErrorMsg(ts('Geo code 1'), $errorMessage);
  1268. }
  1269. }
  1270. }
  1271. }
  1272. break;
  1273. case 'geo_code_2' :
  1274. if (!empty( $value ) ) {
  1275. foreach($value as $codeValue ) {
  1276. if ( $codeValue['geo_code_2'] ) {
  1277. if ( CRM_Utils_Rule::numeric($codeValue['geo_code_2'])) {
  1278. continue;
  1279. } else {
  1280. self::addToErrorMsg(ts('Geo code 2'), $errorMessage);
  1281. }
  1282. }
  1283. }
  1284. }
  1285. break;
  1286. //check for any error in email/postal greeting, addressee,
  1287. //custom email/postal greeting, custom addressee, CRM-4575
  1288. case 'email_greeting':
  1289. $emailGreetingFilter = array( 'contact_type' => $this->_contactType,
  1290. 'greeting_type' => 'email_greeting' );
  1291. if ( !self::in_value($value, CRM_Core_PseudoConstant::greeting($emailGreetingFilter) ) ) {
  1292. self::addToErrorMsg(ts('Email Greeting must be one of the configured format options. Check Administer >> Option Lists >> Email Greetings for valid values'), $errorMessage);
  1293. }
  1294. break;
  1295. case 'postal_greeting':
  1296. $postalGreetingFilter = array( 'contact_type' => $this->_contactType,
  1297. 'greeting_type' => 'postal_greeting' );
  1298. if ( !self::in_value($value, CRM_Core_PseudoConstant::greeting($postalGreetingFilter) ) ) {
  1299. self::addToErrorMsg(ts('Postal Greeting must be one of the configured format options. Check Administer >> Option Lists >> Postal Greetings for valid values'), $errorMessage);
  1300. }
  1301. break;
  1302. case 'addressee':
  1303. $addresseeFilter = array( 'contact_type' => $this->_contactType,
  1304. 'greeting_type' => 'addressee' );
  1305. if ( !self::in_value($value,CRM_Core_PseudoConstant::greeting($addresseeFilter) ) ) {
  1306. self::addToErrorMsg(ts('Addressee must be one of the configured format options. Check Administer >> Option Lists >> Addressee for valid values'), $errorMessage);
  1307. }
  1308. break;
  1309. case 'email_greeting_custom' :
  1310. if ( array_key_exists('email_greeting', $params) ) {
  1311. $emailGreetingLabel = key( CRM_Core_OptionGroup::values('email_greeting', true, null,
  1312. null, 'AND v.name = "Customized"' ));
  1313. if ( CRM_Utils_Array::value( 'email_greeting', $params ) != $emailGreetingLabel ) {
  1314. self::addToErrorMsg(ts('Email Greeting - Custom'), $errorMessage);
  1315. }
  1316. }
  1317. break;
  1318. case 'postal_greeting_custom' :
  1319. if ( array_key_exists('postal_greeting', $params) ) {
  1320. $postalGreetingLabel = key( CRM_Core_OptionGroup::values('postal_greeting', true,
  1321. null, null, 'AND v.name = "Customized"' ));
  1322. if ( CRM_Utils_Array::value( 'postal_greeting', $params ) != $postalGreetingLabel ) {
  1323. self::addToErrorMsg(ts('Postal Greeting - Custom'), $errorMessage);
  1324. }
  1325. }
  1326. break;
  1327. case 'addressee_custom' :
  1328. if ( array_key_exists('addressee', $params) ) {
  1329. $addresseeLabel = key( CRM_Core_OptionGroup::values('addressee', true, null, null,
  1330. 'AND v.name = "Customized"' ));
  1331. if ( CRM_Utils_Array::value( 'addressee', $params ) != $addresseeLabel ) {
  1332. self::addToErrorMsg(ts('Addressee - Custom'), $errorMessage);
  1333. }
  1334. }
  1335. break;
  1336. case 'url':
  1337. if ( is_array( $value ) ) {
  1338. foreach ( $value as $values ) {
  1339. if ( CRM_Utils_Array::value( 'url', $values ) &&
  1340. !CRM_Utils_Rule::url( $values['url'] ) ) {
  1341. self::addToErrorMsg(ts('Website'), $errorMessage);
  1342. break;
  1343. }
  1344. }
  1345. }
  1346. break;
  1347. case 'do_not_email':
  1348. case 'do_not_phone':
  1349. case 'do_not_mail' :
  1350. case 'do_not_sms' :
  1351. case 'do_not_trade':
  1352. if( CRM_Utils_Rule::boolean( $value )== false ) {
  1353. $key = ucwords( str_replace( "_", " ", $key ) );
  1354. self::addToErrorMsg($key, $errorMessage);
  1355. }
  1356. break;
  1357. case 'email':
  1358. if ( is_array( $value ) ) {
  1359. foreach ( $value as $values ) {
  1360. if ( CRM_Utils_Array::value( 'email', $values ) &&
  1361. !CRM_Utils_Rule::email( $values['email'] ) ) {
  1362. self::addToErrorMsg($key, $errorMessage);
  1363. break;
  1364. }
  1365. }
  1366. }
  1367. break;
  1368. default :
  1369. if ( is_array( $params[$key] ) &&
  1370. isset( $params[$key]["contact_type"] ) ) {
  1371. //check for any relationship data ,FIX ME
  1372. self::isErrorInCoreData($params[$key],$errorMessage);
  1373. }
  1374. }
  1375. }
  1376. }
  1377. }
  1378. /**
  1379. * function to ckeck a value present or not in a array
  1380. *
  1381. * @return ture if value present in array or retun false
  1382. *
  1383. * @access public
  1384. */
  1385. function in_value($value , $valueArray)
  1386. {
  1387. foreach( $valueArray as $key => $v ) {
  1388. //fix for CRM-1514
  1389. if ( strtolower( trim($v, "." ) ) == strtolower( trim( $value, "." ) ) ) {
  1390. return true;
  1391. }
  1392. }
  1393. return false;
  1394. }
  1395. /**
  1396. * function to build error-message containing error-fields
  1397. *
  1398. * @param String $errorName A string containing error-field name.
  1399. * @param String $errorMessage A string containing all the error-fields, where the new errorName is concatenated.
  1400. *
  1401. * @static
  1402. * @access public
  1403. */
  1404. static function addToErrorMsg($errorName, &$errorMessage)
  1405. {
  1406. if ($errorMessage) {
  1407. $errorMessage .= "; $errorName";
  1408. } else {
  1409. $errorMessage = $errorName;
  1410. }
  1411. }
  1412. /**
  1413. * method for creating contact
  1414. *
  1415. *
  1416. */
  1417. function createContact( &$formatted, &$contactFields, $onDuplicate, $contactId = null, $requiredCheck = true )
  1418. {
  1419. $dupeCheck = false;
  1420. $newContact = null;
  1421. if ( is_null( $contactId ) && ($onDuplicate != CRM_Import_Parser::DUPLICATE_NOCHECK) ) {
  1422. $dupeCheck = (bool)($onDuplicate);
  1423. }
  1424. //get the prefix id etc if exists
  1425. CRM_Contact_BAO_Contact::resolveDefaults($formatted, true);
  1426. civicrm_api_include('contact', false, 2);
  1427. // setting required check to false, CRM-2839
  1428. // plus we do our own required check in import
  1429. $error = civicrm_contact_check_params( $formatted, $dupeCheck, true, false );
  1430. if ( ( is_null( $error ) ) &&
  1431. ( civicrm_error( _civicrm_validate_formatted_contact($formatted) ) ) ) {
  1432. $error = _civicrm_validate_formatted_contact($formatted);
  1433. }
  1434. $newContact = $error;
  1435. if ( is_null( $error ) ) {
  1436. if ( $contactId ) {
  1437. $this->formatParams( $formatted, $onDuplicate, (int)$contactId );
  1438. }
  1439. // pass doNotResetCache flag since resetting and rebuilding cache could be expensive.
  1440. $config =& CRM_Core_Config::singleton( );
  1441. $config->doNotResetCache = 1;
  1442. $cid = CRM_Contact_BAO_Contact::createProfileContact( $formatted, $contactFields,
  1443. $contactId, null, null,
  1444. $formatted['contact_type'] );
  1445. $config->doNotResetCache = 0;
  1446. $contact = array( 'contact_id' => $cid );
  1447. $defaults = array( );
  1448. $newContact = CRM_Contact_BAO_Contact::retrieve($contact, $defaults );
  1449. }
  1450. //get the id of the contact whose street address is not parsable, CRM-5886
  1451. if ( $this->_parseStreetAddress && $newContact->address ) {
  1452. foreach ( $newContact->address as $address ) {
  1453. if ( $address['street_address'] &&
  1454. ( ! CRM_Utils_Array::value( 'street_number', $address ) ||
  1455. ! CRM_Utils_Array::value( 'street_name', $address ) ) ) {
  1456. $this->_unparsedStreetAddressContacts[] = array( 'id' => $newContact->id,
  1457. 'streetAddress' => $address['street_address'] );
  1458. }
  1459. }
  1460. }
  1461. return $newContact;
  1462. }
  1463. /**
  1464. * format params for update and fill mode
  1465. *
  1466. * @param $params array referance to an array containg all the
  1467. * values for import
  1468. * @param $onDuplicate int
  1469. * @param $cid int contact id
  1470. */
  1471. function formatParams( &$params, $onDuplicate, $cid )
  1472. {
  1473. if ( $onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP ) {
  1474. return;
  1475. }
  1476. $contactParams = array( 'contact_id' => $cid );
  1477. $defaults = array( );
  1478. $contactObj = CRM_Contact_BAO_Contact::retrieve( $contactParams, $defaults );
  1479. $modeUpdate = $modeFill = false;
  1480. if ( $onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE ) {
  1481. $modeUpdate = true;
  1482. }
  1483. if ( $onDuplicate == CRM_Import_Parser::DUPLICATE_FILL ) {
  1484. $modeFill = true;
  1485. }
  1486. require_once 'CRM/Core/BAO/CustomGroup.php';
  1487. $groupTree = CRM_Core_BAO_CustomGroup::getTree($params['contact_type'],CRM_Core_DAO::$_nullObject,
  1488. $cid,0,null);
  1489. CRM_Core_BAO_CustomGroup::setDefaults( $groupTree, $defaults, false, false );
  1490. $locationFields = array( 'email' => 'email',
  1491. 'phone' => 'phone',
  1492. 'im' => 'name',
  1493. 'website' => 'website',
  1494. 'address' => 'address' );
  1495. $contact = get_object_vars( $contactObj );
  1496. foreach( $params as $key => $value ) {
  1497. if ( $key == 'id' || $key == 'contact_type' ) {
  1498. continue;
  1499. }
  1500. if ( array_key_exists( $key, $locationFields ) ) {
  1501. continue;
  1502. } else if ( in_array( $key , array( 'email_greeting', 'postal_greeting', 'addressee' ) ) ) {
  1503. // CRM-4575, need to null custom
  1504. if ( $params["{$key}_id"] != 4 ) {
  1505. $params["{$key}_custom"] = 'null';
  1506. }
  1507. unset( $params[$key] );
  1508. } else if ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key)) {
  1509. $custom = true;
  1510. } else {
  1511. $getValue = CRM_Utils_Array::retrieveValueRecursive($contact, $key);
  1512. if ( $key == 'contact_source' ) {
  1513. $params['source'] = $params[$key];
  1514. unset( $params[$key] );
  1515. }
  1516. if ( $modeFill && isset( $getValue ) ) {
  1517. unset( $params[$key] );
  1518. }
  1519. }
  1520. }
  1521. foreach ( $locationFields as $locKeys ) {
  1522. if ( is_array( $params[$locKeys] ) ) {
  1523. foreach ( $params[$locKeys] as $key => $value ) {
  1524. if ( $modeFill ) {
  1525. $getValue = CRM_Utils_Array::retrieveValueRecursive( $contact, $locKeys );
  1526. if ( isset( $getValue ) ) {
  1527. foreach ( $getValue as $cnt => $values ) {
  1528. if ( $locKeys == 'website' ) {
  1529. if ( ( $getValue[$cnt]['website_type_id'] ==
  1530. $params[$locKeys][$key]['website_type_id'] ) ) {
  1531. unset( $params[$locKeys][$key] );
  1532. }
  1533. } else {
  1534. if ( $getValue[$cnt]['location_type_id']
  1535. == $params[$locKeys][$key]['location_type_id'] ) {
  1536. unset( $params[$locKeys][$key] );
  1537. }
  1538. }
  1539. }
  1540. }
  1541. }
  1542. }
  1543. if ( count( $params[$locKeys] ) == 0 ) unset( $params[$locKeys] );
  1544. }
  1545. }
  1546. }
  1547. /**
  1548. * convert any given date string to default date array.
  1549. *
  1550. * @param array $params has given date-format
  1551. * @param array $formatted store formatted date in this array
  1552. * @param int $dateType type of date
  1553. * @param string $dateParam index of params
  1554. * @static
  1555. */
  1556. function formatCustomDate( &$params, &$formatted, $dateType, $dateParam )
  1557. {
  1558. //fix for CRM-2687
  1559. CRM_Utils_Date::convertToDefaultDate( $params, $dateType, $dateParam );
  1560. $formatted[$dateParam] = CRM_Utils_Date::processDate( $params[$dateParam] );
  1561. }
  1562. /**
  1563. * format common params data to proper format to store.
  1564. *
  1565. * @param array $params contain record values.
  1566. * @param array $formatted array of formatted data.
  1567. * @param array $contactFields contact DAO fields.
  1568. * @static
  1569. */
  1570. function formatCommonData( $params, &$formatted, &$contactFields )
  1571. {
  1572. $csType = array( CRM_Utils_Array::value('contact_type', $formatted) );
  1573. //CRM-5125
  1574. //add custom fields for contact sub type
  1575. if ( !empty($this->_contactSubType) ) {
  1576. $csType = $this->_contactSubType;
  1577. }
  1578. if ( $relCsType = CRM_Utils_Array::value('contact_sub_type', $formatted) ) {
  1579. $csType = $relCsType;
  1580. }
  1581. $customFields = CRM_Core_BAO_CustomField::getFields( $formatted['contact_type'], false, false, $csType );
  1582. $addressCustomFields = CRM_Core_BAO_CustomField::getFields( 'Address' );
  1583. $customFields = $customFields + $addressCustomFields;
  1584. //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
  1585. $elements = array( 'email_greeting_custom' => 'email_greeting',
  1586. 'postal_greeting_custom' => 'postal_greeting',
  1587. 'addressee_custom' => 'addressee' );
  1588. foreach( $elements as $k => $v ) {
  1589. if ( array_key_exists($k, $params) && !(array_key_exists($v, $params) ) ) {
  1590. $label = key( CRM_Core_OptionGroup::values( $v, true, null, null, 'AND v.name = "Customized"' ) );
  1591. $params[$v] = $label;
  1592. }
  1593. }
  1594. //format date first
  1595. $session = CRM_Core_Session::singleton();
  1596. $dateType = $session->get("dateTypes");
  1597. foreach ( $params as $key => $val ) {
  1598. if ( $customFieldID = CRM_Core_BAO_CustomField::getKeyID( $key ) ) {
  1599. //we should not update Date to null, CRM-4062
  1600. if ( $val && ( $customFields[$customFieldID]['data_type'] == 'Date' ) ) {
  1601. self::formatCustomDate( $params, $formatted, $dateType, $key );
  1602. unset( $params[$key] );
  1603. } else if ( $customFields[$customFieldID]['data_type'] == 'Boolean' ) {
  1604. $params[$key] = CRM_Utils_String::strtoboolstr( $val );
  1605. }
  1606. }
  1607. if ( $key == 'birth_date' && $val ) {
  1608. CRM_Utils_Date::convertToDefaultDate( $params, $dateType, $key );
  1609. } else if ( $key == 'deceased_date' && $val ) {
  1610. CRM_Utils_Date::convertToDefaultDate( $params, $dateType, $key );
  1611. } else if ( $key == 'is_deceased' && $val ) {
  1612. $params[$key] = CRM_Utils_String::strtoboolstr( $val );
  1613. } else if ( $key == 'gender' ) {
  1614. //CRM-4360
  1615. $params[$key] = $this->checkGender( $val );
  1616. }
  1617. }
  1618. //now format custom data.
  1619. foreach ( $params as $key => $field ) {
  1620. if ( $field == null || $field === '' ) {
  1621. continue;
  1622. }
  1623. if ( is_array( $field ) ) {
  1624. $isAddressCustomField = false;
  1625. foreach ( $field as $value ) {
  1626. $break = false;
  1627. if ( is_array( $value ) ) {
  1628. foreach ( $value as $name => $testForEmpty ) {
  1629. if ( $addressCustomFieldID = CRM_Core_BAO_CustomField::getKeyID( $name ) ) {
  1630. $isAddressCustomField = true;
  1631. break;
  1632. }
  1633. // check if $value does not contain IM provider or phoneType
  1634. if ( ( $name !== 'phone_type_id' || $name !== 'provider_id' )
  1635. && ( $testForEmpty === '' || $testForEmpty == null ) ) {
  1636. $break = true;
  1637. break;
  1638. }
  1639. }
  1640. } else {
  1641. $break = true;
  1642. }
  1643. if ( !$break ) {
  1644. _civicrm_add_formatted_param( $value, $formatted );
  1645. }
  1646. }
  1647. if ( !$isAddressCustomField ) {
  1648. continue;
  1649. }
  1650. }
  1651. $formatValues = array( $key => $field );
  1652. if ( ( $key !== 'preferred_communication_method' ) &&
  1653. ( array_key_exists( $key, $contactFields ) ) ) {
  1654. // due to merging of individual table and
  1655. // contact table, we need to avoid
  1656. // preferred_communication_method forcefully
  1657. $formatValues['contact_type'] = $formatted['contact_type'];
  1658. }
  1659. if ( $key == 'id' && isset( $field ) ) {
  1660. $formatted[$key] = $field;
  1661. }
  1662. _civicrm_add_formatted_param( $formatValues, $formatted );
  1663. //Handling Custom Data
  1664. if ( ( $customFieldID = CRM_Core_BAO_CustomField::getKeyID( $key ) )
  1665. && array_key_exists( $customFieldID, $customFields ) ) {
  1666. //get the html type.
  1667. $type = $customFields[$customFieldID]['html_type'];
  1668. _civicrm_add_custom_formatted_param( $customFieldID, $key, $field, $formatted, $type );
  1669. }
  1670. }
  1671. // parse street address, CRM-5450
  1672. if ( $this->_parseStreetAddress ) {
  1673. require_once 'CRM/Core/BAO/Address.php';
  1674. if ( array_key_exists( 'address', $formatted ) && is_array( $formatted['address'] ) ) {
  1675. foreach ( $formatted['address'] as $instance => &$address ) {
  1676. $streetAddress = CRM_Utils_Array::value( 'street_address', $address );
  1677. if ( empty( $streetAddress ) ) continue;
  1678. // parse address field.
  1679. $parsedFields = CRM_Core_BAO_Address::parseStreetAddress( $streetAddress );
  1680. //street address consider to be parsed properly,
  1681. //If we get street_name and street_number.
  1682. if ( !CRM_Utils_Array::value( 'street_name', $parsedFields ) ||
  1683. !CRM_Utils_Array::value( 'street_number', $parsedFields ) ) {
  1684. $parsedFields = array_fill_keys( array_keys($parsedFields), '' );
  1685. }
  1686. // merge parse address w/ main address block.
  1687. $address = array_merge( $address, $parsedFields );
  1688. }
  1689. }
  1690. }
  1691. }
  1692. /**
  1693. * Function to generate status and error message for unparsed street address records.
  1694. *
  1695. * @param array $values the array of values belonging to each row
  1696. * @param array $statusFieldName store formatted date in this array
  1697. * @access public
  1698. */
  1699. function processMessage( &$values, $statusFieldName, $returnCode )
  1700. {
  1701. if ( empty( $this->_unparsedStreetAddressContacts ) ) {
  1702. $importRecordParams = array( $statusFieldName => 'IMPORTED' );
  1703. } else {
  1704. $errorMessage = ts( "Record imported successfully but unable to parse the street address: " );
  1705. foreach ( $this->_unparsedStreetAddressContacts as $contactInfo => $contactValue ) {
  1706. $contactUrl = CRM_Utils_System::url( 'civicrm/contact/add', 'reset=1&action=update&cid=' . $contactValue['id'], true, null, false );
  1707. $errorMessage .= "\n Contact ID:". $contactValue['id'] . " <a href=\"$contactUrl\"> " . $contactValue['streetAddress'] . "</a>";
  1708. }
  1709. array_unshift( $values, $errorMessage );
  1710. $importRecordParams = array( $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage );
  1711. $returnCode = CRM_Import_Parser::UNPARSED_ADDRESS_WARNING;
  1712. }
  1713. $this->updateImportRecord( $values[count($values)-1], $importRecordParams );
  1714. return $returnCode;
  1715. }
  1716. function checkRelatedContactFields( $relKey, $params )
  1717. {
  1718. //avoid blank contact creation.
  1719. $allowToCreate = false;
  1720. //build the mapper field array.
  1721. static $relatedContactFields = array( );
  1722. if ( !isset( $relatedContactFields[$relKey] ) ) {
  1723. foreach ( $this->_mapperRelated as $key => $name ) {
  1724. if ( !$name ) continue;
  1725. if ( !is_array( $relatedContactFields[$name] ) ) {
  1726. $relatedContactFields[$name] = array( );
  1727. }
  1728. $fldName = CRM_Utils_Array::value( $key, $this->_mapperRelatedContactDetails );
  1729. if ( $fldName == 'url' ) $fldName = 'website';
  1730. if ( $fldName ) {
  1731. $relatedContactFields[$name][] = $fldName;
  1732. }
  1733. }
  1734. }
  1735. //validate for passed data.
  1736. if ( is_array( $relatedContactFields[$relKey] ) ) {
  1737. foreach ( $relatedContactFields[$relKey] as $fld ) {
  1738. if ( CRM_Utils_Array::value( $fld, $params ) ) {
  1739. $allowToCreate = true;
  1740. break;
  1741. }
  1742. }
  1743. }
  1744. return $allowToCreate;
  1745. }
  1746. }