| 1 | <?php |
| 2 | /* |
| 3 | +--------------------------------------------------------------------+ |
| 4 | | CiviCRM version 5 | |
| 5 | +--------------------------------------------------------------------+ |
| 6 | | Copyright CiviCRM LLC (c) 2004-2018 | |
| 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 2009 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 | * |
| 30 | * @package CRM |
| 31 | * @copyright CiviCRM LLC (c) 2004-2018 |
| 32 | */ |
| 33 | |
| 34 | /** |
| 35 | * This class acts like a psuedo-BAO for transient import job tables. |
| 36 | */ |
| 37 | class CRM_Contact_Import_ImportJob { |
| 38 | |
| 39 | protected $_tableName; |
| 40 | protected $_primaryKeyName; |
| 41 | protected $_statusFieldName; |
| 42 | |
| 43 | protected $_doGeocodeAddress; |
| 44 | protected $_invalidRowCount; |
| 45 | protected $_conflictRowCount; |
| 46 | protected $_onDuplicate; |
| 47 | protected $_dedupe; |
| 48 | protected $_newGroupName; |
| 49 | protected $_newGroupDesc; |
| 50 | protected $_newGroupType; |
| 51 | protected $_groups; |
| 52 | protected $_allGroups; |
| 53 | protected $_newTagName; |
| 54 | protected $_newTagDesc; |
| 55 | protected $_tag; |
| 56 | protected $_allTags; |
| 57 | |
| 58 | protected $_mapper; |
| 59 | protected $_mapperKeys = array(); |
| 60 | protected $_mapFields; |
| 61 | |
| 62 | protected $_parser; |
| 63 | |
| 64 | /** |
| 65 | * @param null $tableName |
| 66 | * @param null $createSql |
| 67 | * @param bool $createTable |
| 68 | * |
| 69 | * @throws Exception |
| 70 | */ |
| 71 | public function __construct($tableName = NULL, $createSql = NULL, $createTable = FALSE) { |
| 72 | $dao = new CRM_Core_DAO(); |
| 73 | $db = $dao->getDatabaseConnection(); |
| 74 | |
| 75 | if ($createTable) { |
| 76 | if (!$createSql) { |
| 77 | CRM_Core_Error::fatal('Either an existing table name or an SQL query to build one are required'); |
| 78 | } |
| 79 | |
| 80 | // FIXME: we should regen this table's name if it exists rather than drop it |
| 81 | if (!$tableName) { |
| 82 | $tableName = 'civicrm_import_job_' . md5(uniqid(rand(), TRUE)); |
| 83 | } |
| 84 | $db->query("DROP TABLE IF EXISTS $tableName"); |
| 85 | $db->query("CREATE TABLE $tableName ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci $createSql"); |
| 86 | } |
| 87 | |
| 88 | if (!$tableName) { |
| 89 | CRM_Core_Error::fatal('Import Table is required.'); |
| 90 | } |
| 91 | |
| 92 | $this->_tableName = $tableName; |
| 93 | } |
| 94 | |
| 95 | /** |
| 96 | * @return null|string |
| 97 | */ |
| 98 | public function getTableName() { |
| 99 | return $this->_tableName; |
| 100 | } |
| 101 | |
| 102 | /** |
| 103 | * @param bool $dropIfComplete |
| 104 | * |
| 105 | * @return bool |
| 106 | * @throws Exception |
| 107 | */ |
| 108 | public function isComplete($dropIfComplete = TRUE) { |
| 109 | if (!$this->_statusFieldName) { |
| 110 | CRM_Core_Error::fatal("Could not get name of the import status field"); |
| 111 | } |
| 112 | $query = "SELECT * FROM $this->_tableName |
| 113 | WHERE $this->_statusFieldName = 'NEW' LIMIT 1"; |
| 114 | $result = CRM_Core_DAO::executeQuery($query); |
| 115 | if ($result->fetch()) { |
| 116 | return FALSE; |
| 117 | } |
| 118 | if ($dropIfComplete) { |
| 119 | $query = "DROP TABLE $this->_tableName"; |
| 120 | CRM_Core_DAO::executeQuery($query); |
| 121 | } |
| 122 | return TRUE; |
| 123 | } |
| 124 | |
| 125 | /** |
| 126 | * @param array $params |
| 127 | */ |
| 128 | public function setJobParams(&$params) { |
| 129 | foreach ($params as $param => $value) { |
| 130 | $fldName = "_$param"; |
| 131 | $this->$fldName = $value; |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | /** |
| 136 | * @param CRM_Core_Form $form |
| 137 | * @param int $timeout |
| 138 | */ |
| 139 | public function runImport(&$form, $timeout = 55) { |
| 140 | $mapper = $this->_mapper; |
| 141 | $mapperFields = array(); |
| 142 | $parserParameters = CRM_Contact_Import_Parser_Contact::getParameterForParser(count($mapper)); |
| 143 | $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id'); |
| 144 | $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'); |
| 145 | $websiteTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Website', 'website_type_id'); |
| 146 | $locationTypes = array('Primary' => ts('Primary')) + CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); |
| 147 | |
| 148 | foreach ($mapper as $key => $value) { |
| 149 | |
| 150 | $fldName = CRM_Utils_Array::value(0, $mapper[$key]); |
| 151 | $header = array($this->_mapFields[$fldName]); |
| 152 | $selOne = CRM_Utils_Array::value(1, $mapper[$key]); |
| 153 | $selTwo = CRM_Utils_Array::value(2, $mapper[$key]); |
| 154 | $selThree = CRM_Utils_Array::value(3, $mapper[$key]); |
| 155 | $this->_mapperKeys[$key] = $fldName; |
| 156 | |
| 157 | //need to differentiate non location elements. |
| 158 | // @todo merge this with duplicate code on MapField class. |
| 159 | if ($selOne && (is_numeric($selOne) || $selOne === 'Primary')) { |
| 160 | if ($fldName == 'url') { |
| 161 | $header[] = $websiteTypes[$selOne]; |
| 162 | $parserParameters['mapperWebsiteType'][$key] = $selOne; |
| 163 | } |
| 164 | else { |
| 165 | $header[] = $locationTypes[$selOne]; |
| 166 | $parserParameters['mapperLocType'][$key] = $selOne; |
| 167 | if ($selTwo && is_numeric($selTwo)) { |
| 168 | if ($fldName == 'phone') { |
| 169 | $header[] = $phoneTypes[$selTwo]; |
| 170 | $parserParameters['mapperPhoneType'][$key] = $selTwo; |
| 171 | } |
| 172 | elseif ($fldName == 'im') { |
| 173 | $header[] = $imProviders[$selTwo]; |
| 174 | $parserParameters['mapperImProvider'][$key] = $selTwo; |
| 175 | } |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | $fldNameParts = explode('_', $fldName, 3); |
| 181 | $id = $fldNameParts[0]; |
| 182 | $first = isset($fldNameParts[1]) ? $fldNameParts[1] : NULL; |
| 183 | $second = isset($fldNameParts[2]) ? $fldNameParts[2] : NULL; |
| 184 | if (($first == 'a' && $second == 'b') || |
| 185 | ($first == 'b' && $second == 'a') |
| 186 | ) { |
| 187 | |
| 188 | $header[] = ucwords(str_replace("_", " ", $selOne)); |
| 189 | |
| 190 | $relationType = new CRM_Contact_DAO_RelationshipType(); |
| 191 | $relationType->id = $id; |
| 192 | $relationType->find(TRUE); |
| 193 | $parserParameters['relatedContactType'][$key] = $relationType->{"contact_type_$second"}; |
| 194 | |
| 195 | $parserParameters['mapperRelated'][$key] = $fldName; |
| 196 | if ($selOne) { |
| 197 | $parserParameters['relatedContactDetails'][$key] = $selOne; |
| 198 | if ($selTwo) { |
| 199 | if ($selOne == 'url') { |
| 200 | $header[] = $websiteTypes[$selTwo]; |
| 201 | $parserParameters[$key]['relatedContactWebsiteType'][$key] = $selTwo; |
| 202 | } |
| 203 | else { |
| 204 | $header[] = $locationTypes[$selTwo]; |
| 205 | $parserParameters['relatedContactLocType'][$key] = $selTwo; |
| 206 | if ($selThree) { |
| 207 | if ($selOne == 'phone') { |
| 208 | $header[] = $phoneTypes[$selThree]; |
| 209 | $parserParameters['relatedContactPhoneType'][$key] = $selThree; |
| 210 | } |
| 211 | elseif ($selOne == 'im') { |
| 212 | $header[] = $imProviders[$selThree]; |
| 213 | $parserParameters['relatedContactImProvider'][$key] = $selThree; |
| 214 | } |
| 215 | } |
| 216 | } |
| 217 | } |
| 218 | } |
| 219 | } |
| 220 | $mapperFields[] = implode(' - ', $header); |
| 221 | } |
| 222 | |
| 223 | $this->_parser = new CRM_Contact_Import_Parser_Contact( |
| 224 | $this->_mapperKeys, |
| 225 | $parserParameters['mapperLocType'], |
| 226 | $parserParameters['mapperPhoneType'], |
| 227 | $parserParameters['mapperImProvider'], |
| 228 | $parserParameters['mapperRelated'], |
| 229 | $parserParameters['relatedContactType'], |
| 230 | $parserParameters['relatedContactDetails'], |
| 231 | $parserParameters['relatedContactLocType'], |
| 232 | $parserParameters['relatedContactPhoneType'], |
| 233 | $parserParameters['relatedContactImProvider'], |
| 234 | $parserParameters['mapperWebsiteType'], |
| 235 | $parserParameters['relatedContactWebsiteType'] |
| 236 | ); |
| 237 | |
| 238 | $this->_parser->run($this->_tableName, $mapperFields, |
| 239 | CRM_Import_Parser::MODE_IMPORT, |
| 240 | $this->_contactType, |
| 241 | $this->_primaryKeyName, |
| 242 | $this->_statusFieldName, |
| 243 | $this->_onDuplicate, |
| 244 | $this->_statusID, |
| 245 | $this->_totalRowCount, |
| 246 | $this->_doGeocodeAddress, |
| 247 | CRM_Contact_Import_Parser::DEFAULT_TIMEOUT, |
| 248 | $this->_contactSubType, |
| 249 | $this->_dedupe |
| 250 | ); |
| 251 | |
| 252 | $contactIds = $this->_parser->getImportedContacts(); |
| 253 | |
| 254 | //get the related contactIds. CRM-2926 |
| 255 | $relatedContactIds = $this->_parser->getRelatedImportedContacts(); |
| 256 | if ($relatedContactIds) { |
| 257 | $contactIds = array_merge($contactIds, $relatedContactIds); |
| 258 | if ($form) { |
| 259 | $form->set('relatedCount', count($relatedContactIds)); |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | if ($this->_newGroupName || count($this->_groups)) { |
| 264 | $groupAdditions = $this->_addImportedContactsToNewGroup($contactIds, |
| 265 | $this->_newGroupName, |
| 266 | $this->_newGroupDesc, |
| 267 | $this->_newGroupType |
| 268 | ); |
| 269 | if ($form) { |
| 270 | $form->set('groupAdditions', $groupAdditions); |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | if ($this->_newTagName || count($this->_tag)) { |
| 275 | $tagAdditions = $this->_tagImportedContactsWithNewTag($contactIds, |
| 276 | $this->_newTagName, |
| 277 | $this->_newTagDesc |
| 278 | ); |
| 279 | if ($form) { |
| 280 | $form->set('tagAdditions', $tagAdditions); |
| 281 | } |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | /** |
| 286 | * @param $form |
| 287 | */ |
| 288 | public function setFormVariables($form) { |
| 289 | $this->_parser->set($form, CRM_Import_Parser::MODE_IMPORT); |
| 290 | } |
| 291 | |
| 292 | /** |
| 293 | * Add imported contacts. |
| 294 | * |
| 295 | * @param array $contactIds |
| 296 | * @param string $newGroupName |
| 297 | * @param string $newGroupDesc |
| 298 | * @param string $newGroupType |
| 299 | * |
| 300 | * @return array|bool |
| 301 | */ |
| 302 | private function _addImportedContactsToNewGroup( |
| 303 | $contactIds, |
| 304 | $newGroupName, $newGroupDesc, $newGroupType |
| 305 | ) { |
| 306 | |
| 307 | $newGroupId = NULL; |
| 308 | |
| 309 | if ($newGroupName) { |
| 310 | /* Create a new group */ |
| 311 | $newGroupType = isset($newGroupType) ? $newGroupType : array(); |
| 312 | $gParams = array( |
| 313 | 'title' => $newGroupName, |
| 314 | 'description' => $newGroupDesc, |
| 315 | 'group_type' => $newGroupType, |
| 316 | 'is_active' => TRUE, |
| 317 | ); |
| 318 | $group = CRM_Contact_BAO_Group::create($gParams); |
| 319 | $this->_groups[] = $newGroupId = $group->id; |
| 320 | } |
| 321 | |
| 322 | if (is_array($this->_groups)) { |
| 323 | $groupAdditions = array(); |
| 324 | foreach ($this->_groups as $groupId) { |
| 325 | $addCount = CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $groupId); |
| 326 | $totalCount = $addCount[1]; |
| 327 | if ($groupId == $newGroupId) { |
| 328 | $name = $newGroupName; |
| 329 | $new = TRUE; |
| 330 | } |
| 331 | else { |
| 332 | $name = $this->_allGroups[$groupId]; |
| 333 | $new = FALSE; |
| 334 | } |
| 335 | $groupAdditions[] = array( |
| 336 | 'url' => CRM_Utils_System::url('civicrm/group/search', |
| 337 | 'reset=1&force=1&context=smog&gid=' . $groupId |
| 338 | ), |
| 339 | 'name' => $name, |
| 340 | 'added' => $totalCount, |
| 341 | 'notAdded' => $addCount[2], |
| 342 | 'new' => $new, |
| 343 | ); |
| 344 | } |
| 345 | return $groupAdditions; |
| 346 | } |
| 347 | return FALSE; |
| 348 | } |
| 349 | |
| 350 | /** |
| 351 | * @param $contactIds |
| 352 | * @param string $newTagName |
| 353 | * @param $newTagDesc |
| 354 | * |
| 355 | * @return array|bool |
| 356 | */ |
| 357 | private function _tagImportedContactsWithNewTag( |
| 358 | $contactIds, |
| 359 | $newTagName, $newTagDesc |
| 360 | ) { |
| 361 | |
| 362 | $newTagId = NULL; |
| 363 | if ($newTagName) { |
| 364 | /* Create a new Tag */ |
| 365 | |
| 366 | $tagParams = array( |
| 367 | 'name' => $newTagName, |
| 368 | 'description' => $newTagDesc, |
| 369 | 'is_selectable' => TRUE, |
| 370 | 'used_for' => 'civicrm_contact', |
| 371 | ); |
| 372 | $id = array(); |
| 373 | $addedTag = CRM_Core_BAO_Tag::add($tagParams, $id); |
| 374 | $this->_tag[$addedTag->id] = 1; |
| 375 | } |
| 376 | //add Tag to Import |
| 377 | |
| 378 | if (is_array($this->_tag)) { |
| 379 | $tagAdditions = array(); |
| 380 | foreach ($this->_tag as $tagId => $val) { |
| 381 | $addTagCount = CRM_Core_BAO_EntityTag::addEntitiesToTag($contactIds, $tagId, 'civicrm_contact', FALSE); |
| 382 | $totalTagCount = $addTagCount[1]; |
| 383 | if (isset($addedTag) && $tagId == $addedTag->id) { |
| 384 | $tagName = $newTagName; |
| 385 | $new = TRUE; |
| 386 | } |
| 387 | else { |
| 388 | $tagName = $this->_allTags[$tagId]; |
| 389 | $new = FALSE; |
| 390 | } |
| 391 | $tagAdditions[] = array( |
| 392 | 'url' => CRM_Utils_System::url('civicrm/contact/search', |
| 393 | 'reset=1&force=1&context=smog&id=' . $tagId |
| 394 | ), |
| 395 | 'name' => $tagName, |
| 396 | 'added' => $totalTagCount, |
| 397 | 'notAdded' => $addTagCount[2], |
| 398 | 'new' => $new, |
| 399 | ); |
| 400 | } |
| 401 | return $tagAdditions; |
| 402 | } |
| 403 | return FALSE; |
| 404 | } |
| 405 | |
| 406 | /** |
| 407 | * @return array |
| 408 | */ |
| 409 | public static function getIncompleteImportTables() { |
| 410 | $dao = new CRM_Core_DAO(); |
| 411 | $database = $dao->database(); |
| 412 | $query = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA |
| 413 | WHERE TABLE_SCHEMA = ? AND |
| 414 | TABLE_NAME LIKE 'civicrm_import_job_%' |
| 415 | ORDER BY TABLE_NAME"; |
| 416 | $result = CRM_Core_DAO::executeQuery($query, array($database)); |
| 417 | $incompleteImportTables = array(); |
| 418 | while ($importTable = $result->fetch()) { |
| 419 | if (!$this->isComplete($importTable)) { |
| 420 | $incompleteImportTables[] = $importTable; |
| 421 | } |
| 422 | } |
| 423 | return $incompleteImportTables; |
| 424 | } |
| 425 | |
| 426 | } |