PageRenderTime 59ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/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

Large files files are truncated, but you can click here to view the full file

  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( $valu

Large files files are truncated, but you can click here to view the full file