/** * @param CRM_Utils_Sort $sort * * @return string */ public function buildPrevNextCache($sort) { $cacheKey = 'civicrm search ' . $this->_key; // We should clear the cache in following conditions: // 1. when starting from scratch, i.e new search // 2. if records are sorted // get current page requested $pageNum = CRM_Utils_Request::retrieve('crmPID', 'Integer', CRM_Core_DAO::$_nullObject); // get the current sort order $currentSortID = CRM_Utils_Request::retrieve('crmSID', 'String', CRM_Core_DAO::$_nullObject); $session = CRM_Core_Session::singleton(); // get previous sort id $previousSortID = $session->get('previousSortID'); // check for current != previous to ensure cache is not reset if paging is done without changing // sort criteria if (!$pageNum || !empty($currentSortID) && $currentSortID != $previousSortID) { CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKey, 'civicrm_contact'); // this means it's fresh search, so set pageNum=1 if (!$pageNum) { $pageNum = 1; } } // set the current sort as previous sort if (!empty($currentSortID)) { $session->set('previousSortID', $currentSortID); } $pageSize = CRM_Utils_Request::retrieve('crmRowCount', 'Integer', CRM_Core_DAO::$_nullObject, FALSE, 50); $firstRecord = ($pageNum - 1) * $pageSize; //for alphabetic pagination selection save $sortByCharacter = CRM_Utils_Request::retrieve('sortByCharacter', 'String', CRM_Core_DAO::$_nullObject); //for text field pagination selection save $countRow = CRM_Core_BAO_PrevNextCache::getCount($cacheKey, NULL, "entity_table = 'civicrm_contact'"); // $sortByCharacter triggers a refresh in the prevNext cache if ($sortByCharacter && $sortByCharacter != 'all') { $cacheKey .= "_alphabet"; $this->fillupPrevNextCache($sort, $cacheKey); } elseif ($firstRecord >= $countRow) { $this->fillupPrevNextCache($sort, $cacheKey, $countRow, 500 + $firstRecord - $countRow); } return $cacheKey; }
/** * Clear the contact cache so things are kosher. We started off being super aggressive with clearing * caches, but are backing off from this with every release. Compromise between ease of coding versus * performance versus being accurate at that very instant * * @param $contactID * The contactID that was edited / deleted. */ public static function clearContactCaches($contactID = NULL) { // clear acl cache if any. CRM_ACL_BAO_Cache::resetCache(); if (empty($contactID)) { // also clear prev/next dedupe cache - if no contactID passed in CRM_Core_BAO_PrevNextCache::deleteItem(); } // reset the group contact cache for this group CRM_Contact_BAO_GroupContactCache::remove(); }
/** * Delete a contact and all its associated records. * * @param int $id * Id of the contact to delete. * @param bool $restore * Whether to actually restore, not delete. * @param bool $skipUndelete * Whether to force contact delete or not. * * @return bool * Was contact deleted? */ public static function deleteContact($id, $restore = FALSE, $skipUndelete = FALSE) { if (!$id) { return FALSE; } // If trash is disabled in system settings then we always skip if (!CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'contact_undelete', NULL, 1)) { $skipUndelete = TRUE; } // make sure we have edit permission for this contact // before we delete if ($skipUndelete && !CRM_Core_Permission::check('delete contacts') || $restore && !CRM_Core_Permission::check('access deleted contacts')) { return FALSE; } // CRM-12929 // Restrict contact to be delete if contact has financial trxns $error = NULL; if ($skipUndelete && CRM_Financial_BAO_FinancialItem::checkContactPresent(array($id), $error)) { return FALSE; } // make sure this contact_id does not have any membership types $membershipTypeID = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $id, 'id', 'member_of_contact_id'); if ($membershipTypeID) { return FALSE; } $contact = new CRM_Contact_DAO_Contact(); $contact->id = $id; if (!$contact->find(TRUE)) { return FALSE; } $contactType = $contact->contact_type; // currently we only clear employer cache. // we are now deleting inherited membership if any. if ($contact->contact_type == 'Organization') { $action = $restore ? CRM_Core_Action::ENABLE : CRM_Core_Action::DISABLE; $relationshipDtls = CRM_Contact_BAO_Relationship::getRelationship($id); if (!empty($relationshipDtls)) { foreach ($relationshipDtls as $rId => $details) { CRM_Contact_BAO_Relationship::disableEnableRelationship($rId, $action); } } CRM_Contact_BAO_Contact_Utils::clearAllEmployee($id); } if ($restore) { return self::contactTrashRestore($contact, TRUE); } // start a new transaction $transaction = new CRM_Core_Transaction(); if ($skipUndelete) { CRM_Utils_Hook::pre('delete', $contactType, $id, CRM_Core_DAO::$_nullArray); //delete billing address if exists. CRM_Contribute_BAO_Contribution::deleteAddress(NULL, $id); // delete the log entries since we dont have triggers enabled as yet $logDAO = new CRM_Core_DAO_Log(); $logDAO->entity_table = 'civicrm_contact'; $logDAO->entity_id = $id; $logDAO->delete(); // delete contact participants CRM-12155 CRM_Event_BAO_Participant::deleteContactParticipant($id); // delete contact contributions CRM-12155 CRM_Contribute_BAO_Contribution::deleteContactContribution($id); // do activity cleanup, CRM-5604 CRM_Activity_BAO_Activity::cleanupActivity($id); // delete all notes related to contact CRM_Core_BAO_Note::cleanContactNotes($id); // delete cases related to contact $contactCases = CRM_Case_BAO_Case::retrieveCaseIdsByContactId($id); if (!empty($contactCases)) { foreach ($contactCases as $caseId) { //check if case is associate with other contact or not. $caseContactId = CRM_Case_BAO_Case::getCaseClients($caseId); if (count($caseContactId) <= 1) { CRM_Case_BAO_Case::deleteCase($caseId); } } } $contact->delete(); } else { self::contactTrashRestore($contact); } //delete the contact id from recently view CRM_Utils_Recent::delContact($id); // Update the group contact cache if ($restore) { CRM_Contact_BAO_GroupContactCache::remove(); } else { CRM_Contact_BAO_GroupContactCache::removeContact($id); } // delete any dupe cache entry CRM_Core_BAO_PrevNextCache::deleteItem($id); $transaction->commit(); if ($skipUndelete) { CRM_Utils_Hook::post('delete', $contactType, $contact->id, $contact); } // also reset the DB_DO global array so we can reuse the memory // http://issues.civicrm.org/jira/browse/CRM-4387 CRM_Core_DAO::freeResult(); return TRUE; }
function buildPrevNextCache($sort) { //for prev/next pagination $crmPID = CRM_Utils_Request::retrieve('crmPID', 'Integer', CRM_Core_DAO::$_nullObject); if (!$crmPID) { $cacheKey = "civicrm search {$this->_key}"; CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKey, 'civicrm_contact'); $sql = $this->_query->searchQuery(0, 0, $sort, FALSE, FALSE, FALSE, FALSE, TRUE, $this->_campaignWhereClause, NULL, $this->_campaignFromClause); list($select, $from) = explode(' FROM ', $sql); $insertSQL = "\nINSERT INTO civicrm_prevnext_cache ( entity_table, entity_id1, entity_id2, cacheKey, data )\nSELECT 'civicrm_contact', contact_a.id, contact_a.id, '{$cacheKey}', contact_a.display_name\nFROM {$from}\n"; CRM_Core_Error::ignoreException(); $result = CRM_Core_DAO::executeQuery($insertSQL); CRM_Core_Error::setCallback(); if (is_a($result, 'DB_Error')) { return; } // also record an entry in the cache key table, so we can delete it periodically CRM_Core_BAO_Cache::setItem($cacheKey, 'CiviCRM Search PrevNextCache', $cacheKey); } }
/** * Process the form submission. * * * @return void */ public function postProcess() { $values = $this->exportValues(); //FIXME: Handle logic to replace is_default column by usage // reset used column to General (since there can only // be one 'Supervised' or 'Unsupervised' rule) if ($values['used'] != 'General') { $query = "\nUPDATE civicrm_dedupe_rule_group\n SET used = 'General'\n WHERE contact_type = %1\n AND used = %2"; $queryParams = array(1 => array($this->_contactType, 'String'), 2 => array($values['used'], 'String')); CRM_Core_DAO::executeQuery($query, $queryParams); } $rgDao = new CRM_Dedupe_DAO_RuleGroup(); if ($this->_action & CRM_Core_Action::UPDATE) { $rgDao->id = $this->_rgid; } $rgDao->title = $values['title']; $rgDao->is_reserved = CRM_Utils_Array::value('is_reserved', $values, FALSE); $rgDao->used = $values['used']; $rgDao->contact_type = $this->_contactType; $rgDao->threshold = $values['threshold']; $rgDao->save(); // make sure name is set only during insert if ($this->_action & CRM_Core_Action::ADD) { // generate name based on title $rgDao->name = CRM_Utils_String::titleToVar($values['title']) . "_{$rgDao->id}"; $rgDao->save(); } // lets skip updating of fields for reserved dedupe group if ($rgDao->is_reserved) { CRM_Core_Session::setStatus(ts("The rule '%1' has been saved.", array(1 => $rgDao->title)), ts('Saved'), 'success'); return; } $ruleDao = new CRM_Dedupe_DAO_Rule(); $ruleDao->dedupe_rule_group_id = $rgDao->id; $ruleDao->delete(); $ruleDao->free(); $substrLenghts = array(); $tables = array(); $daoObj = new CRM_Core_DAO(); $database = $daoObj->database(); for ($count = 0; $count < self::RULES_COUNT; $count++) { if (empty($values["where_{$count}"])) { continue; } list($table, $field) = explode('.', CRM_Utils_Array::value("where_{$count}", $values)); $length = !empty($values["length_{$count}"]) ? CRM_Utils_Array::value("length_{$count}", $values) : NULL; $weight = $values["weight_{$count}"]; if ($table and $field) { $ruleDao = new CRM_Dedupe_DAO_Rule(); $ruleDao->dedupe_rule_group_id = $rgDao->id; $ruleDao->rule_table = $table; $ruleDao->rule_field = $field; $ruleDao->rule_length = $length; $ruleDao->rule_weight = $weight; $ruleDao->save(); $ruleDao->free(); if (!array_key_exists($table, $tables)) { $tables[$table] = array(); } $tables[$table][] = $field; } // CRM-6245: we must pass table/field/length triples to the createIndexes() call below if ($length) { if (!isset($substrLenghts[$table])) { $substrLenghts[$table] = array(); } //CRM-13417 to avoid fatal error "Incorrect prefix key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique prefix keys, 1089" $schemaQuery = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS\n WHERE TABLE_SCHEMA = '{$database}' AND\n TABLE_NAME = '{$table}' AND COLUMN_NAME = '{$field}';"; $dao = CRM_Core_DAO::executeQuery($schemaQuery); if ($dao->fetch()) { // set the length to null for all the fields where prefix length is not supported. eg. int,tinyint,date,enum etc dataTypes. if ($dao->COLUMN_NAME == $field && !in_array($dao->DATA_TYPE, array('char', 'varchar', 'binary', 'varbinary', 'text', 'blob'))) { $length = NULL; } elseif ($dao->COLUMN_NAME == $field && !empty($dao->CHARACTER_MAXIMUM_LENGTH) && $length > $dao->CHARACTER_MAXIMUM_LENGTH) { //set the length to CHARACTER_MAXIMUM_LENGTH in case the length provided by the user is greater than the limit $length = $dao->CHARACTER_MAXIMUM_LENGTH; } } $substrLenghts[$table][$field] = $length; } } // also create an index for this dedupe rule // CRM-3837 CRM_Utils_Hook::dupeQuery($ruleDao, 'dedupeIndexes', $tables); CRM_Core_BAO_SchemaHandler::createIndexes($tables, 'dedupe_index', $substrLenghts); //need to clear cache of deduped contacts //based on the previous rule $cacheKey = "merge {$this->_contactType}_{$this->_rgid}_%"; CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKey); CRM_Core_Session::setStatus(ts("The rule '%1' has been saved.", array(1 => $rgDao->title)), ts('Saved'), 'success'); }
/** * Based on the provided two contact_ids and a set of tables, move the belongings of the * other contact to the main one - be it Location / CustomFields or Contact .. related info. * A superset of moveContactBelongings() function. * * @param int $mainId * Main contact with whom merge has to happen. * @param int $otherId * Duplicate contact which would be deleted after merge operation. * * @param $migrationInfo * * @return bool */ public static function moveAllBelongings($mainId, $otherId, $migrationInfo) { if (empty($migrationInfo)) { return FALSE; } $qfZeroBug = 'e8cddb72-a257-11dc-b9cc-0016d3330ee9'; $relTables = CRM_Dedupe_Merger::relTables(); $moveTables = $locBlocks = $tableOperations = array(); foreach ($migrationInfo as $key => $value) { if ($value == $qfZeroBug) { $value = '0'; } if ((in_array(substr($key, 5), CRM_Dedupe_Merger::getContactFields()) || substr($key, 0, 12) == 'move_custom_') && $value != NULL) { $submitted[substr($key, 5)] = $value; } elseif (substr($key, 0, 14) == 'move_location_' and $value != NULL) { $locField = explode('_', $key); $fieldName = $locField[2]; $fieldCount = $locField[3]; $operation = CRM_Utils_Array::value('operation', $migrationInfo['location'][$fieldName][$fieldCount]); // default operation is overwrite. if (!$operation) { $operation = 2; } $locBlocks[$fieldName][$fieldCount]['operation'] = $operation; $locBlocks[$fieldName][$fieldCount]['locTypeId'] = CRM_Utils_Array::value('locTypeId', $migrationInfo['location'][$fieldName][$fieldCount]); } elseif (substr($key, 0, 15) == 'move_rel_table_' and $value == '1') { $moveTables = array_merge($moveTables, $relTables[substr($key, 5)]['tables']); if (array_key_exists('operation', $migrationInfo)) { foreach ($relTables[substr($key, 5)]['tables'] as $table) { if (array_key_exists($key, $migrationInfo['operation'])) { $tableOperations[$table] = $migrationInfo['operation'][$key]; } } } } } // **** Do location related migration: if (!empty($locBlocks)) { $locComponent = array('email' => 'Email', 'phone' => 'Phone', 'im' => 'IM', 'openid' => 'OpenID', 'address' => 'Address'); $primaryBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mainId, array('is_primary' => 1)); $billingBlockIds = CRM_Contact_BAO_Contact::getLocBlockIds($mainId, array('is_billing' => 1)); foreach ($locBlocks as $name => $block) { if (!is_array($block) || CRM_Utils_System::isNull($block)) { continue; } $daoName = 'CRM_Core_DAO_' . $locComponent[$name]; $primaryDAOId = array_key_exists($name, $primaryBlockIds) ? array_pop($primaryBlockIds[$name]) : NULL; $billingDAOId = array_key_exists($name, $billingBlockIds) ? array_pop($billingBlockIds[$name]) : NULL; foreach ($block as $blkCount => $values) { $locTypeId = CRM_Utils_Array::value('locTypeId', $values, 1); $operation = CRM_Utils_Array::value('operation', $values, 2); $otherBlockId = CRM_Utils_Array::value($blkCount, $migrationInfo['other_details']['loc_block_ids'][$name]); // keep 1-1 mapping for address - loc type. $idKey = $blkCount; if (array_key_exists($name, $locComponent)) { $idKey = $locTypeId; } if (isset($migrationInfo['main_details']['loc_block_ids'][$name])) { $mainBlockId = CRM_Utils_Array::value($idKey, $migrationInfo['main_details']['loc_block_ids'][$name]); } if (!$otherBlockId) { continue; } // for the block which belongs to other-contact, link the contact to main-contact $otherBlockDAO = new $daoName(); $otherBlockDAO->id = $otherBlockId; $otherBlockDAO->contact_id = $mainId; $otherBlockDAO->location_type_id = $locTypeId; // if main contact already has primary & billing, set the flags to 0. if ($primaryDAOId) { $otherBlockDAO->is_primary = 0; } if ($billingDAOId) { $otherBlockDAO->is_billing = 0; } // overwrite - need to delete block which belongs to main-contact. if (isset($mainBlockId) && $mainBlockId && $operation == 2) { $deleteDAO = new $daoName(); $deleteDAO->id = $mainBlockId; $deleteDAO->find(TRUE); // if we about to delete a primary / billing block, set the flags for new block // that we going to assign to main-contact if ($primaryDAOId && $primaryDAOId == $deleteDAO->id) { $otherBlockDAO->is_primary = 1; } if ($billingDAOId && $billingDAOId == $deleteDAO->id) { $otherBlockDAO->is_billing = 1; } $deleteDAO->delete(); $deleteDAO->free(); } $otherBlockDAO->update(); $otherBlockDAO->free(); } } } // **** Do tables related migrations if (!empty($moveTables)) { CRM_Dedupe_Merger::moveContactBelongings($mainId, $otherId, $moveTables, $tableOperations); unset($moveTables, $tableOperations); } // **** Do contact related migrations CRM_Dedupe_Merger::moveContactBelongings($mainId, $otherId); // FIXME: fix gender, prefix and postfix, so they're edible by createProfileContact() $names['gender'] = array('newName' => 'gender_id', 'groupName' => 'gender'); $names['individual_prefix'] = array('newName' => 'prefix_id', 'groupName' => 'individual_prefix'); $names['individual_suffix'] = array('newName' => 'suffix_id', 'groupName' => 'individual_suffix'); $names['communication_style'] = array('newName' => 'communication_style_id', 'groupName' => 'communication_style'); $names['addressee'] = array('newName' => 'addressee_id', 'groupName' => 'addressee'); $names['email_greeting'] = array('newName' => 'email_greeting_id', 'groupName' => 'email_greeting'); $names['postal_greeting'] = array('newName' => 'postal_greeting_id', 'groupName' => 'postal_greeting'); CRM_Core_OptionGroup::lookupValues($submitted, $names, TRUE); // fix custom fields so they're edible by createProfileContact() static $treeCache = array(); if (!array_key_exists($migrationInfo['main_details']['contact_type'], $treeCache)) { $treeCache[$migrationInfo['main_details']['contact_type']] = CRM_Core_BAO_CustomGroup::getTree($migrationInfo['main_details']['contact_type'], CRM_Core_DAO::$_nullObject, NULL, -1); } $cgTree =& $treeCache[$migrationInfo['main_details']['contact_type']]; $cFields = array(); foreach ($cgTree as $key => $group) { if (!isset($group['fields'])) { continue; } foreach ($group['fields'] as $fid => $field) { $cFields[$fid]['attributes'] = $field; } } if (!isset($submitted)) { $submitted = array(); } foreach ($submitted as $key => $value) { if (substr($key, 0, 7) == 'custom_') { $fid = (int) substr($key, 7); if (empty($cFields[$fid])) { continue; } $htmlType = $cFields[$fid]['attributes']['html_type']; switch ($htmlType) { case 'File': $customFiles[] = $fid; unset($submitted["custom_{$fid}"]); break; case 'Select Country': case 'Select State/Province': $submitted[$key] = CRM_Core_BAO_CustomField::getDisplayValue($value, $fid, $cFields); break; case 'CheckBox': case 'AdvMulti-Select': case 'Multi-Select': case 'Multi-Select Country': case 'Multi-Select State/Province': // Merge values from both contacts for multivalue fields, CRM-4385 // get the existing custom values from db. $customParams = array('entityID' => $mainId, $key => TRUE); $customfieldValues = CRM_Core_BAO_CustomValueTable::getValues($customParams); if (!empty($customfieldValues[$key])) { $existingValue = explode(CRM_Core_DAO::VALUE_SEPARATOR, $customfieldValues[$key]); if (is_array($existingValue) && !empty($existingValue)) { $mergeValue = $submmtedCustomValue = array(); if ($value) { $submmtedCustomValue = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value); } //hack to remove null and duplicate values from array. foreach (array_merge($submmtedCustomValue, $existingValue) as $k => $v) { if ($v != '' && !in_array($v, $mergeValue)) { $mergeValue[] = $v; } } //keep state and country as array format. //for checkbox and m-select format w/ VALUE_SEPARATOR if (in_array($htmlType, array('CheckBox', 'Multi-Select', 'AdvMulti-Select'))) { $submitted[$key] = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, $mergeValue) . CRM_Core_DAO::VALUE_SEPARATOR; } else { $submitted[$key] = $mergeValue; } } } elseif (in_array($htmlType, array('Multi-Select Country', 'Multi-Select State/Province'))) { //we require submitted values should be in array format if ($value) { $mergeValueArray = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value); //hack to remove null values from array. $mergeValue = array(); foreach ($mergeValueArray as $k => $v) { if ($v != '') { $mergeValue[] = $v; } } $submitted[$key] = $mergeValue; } } break; default: break; } } } // **** Do file custom fields related migrations // FIXME: move this someplace else (one of the BAOs) after discussing // where to, and whether CRM_Core_BAO_File::deleteFileReferences() shouldn't actually, // like, delete a file... if (!isset($customFiles)) { $customFiles = array(); } foreach ($customFiles as $customId) { list($tableName, $columnName, $groupID) = CRM_Core_BAO_CustomField::getTableColumnGroup($customId); // get the contact_id -> file_id mapping $fileIds = array(); $sql = "SELECT entity_id, {$columnName} AS file_id FROM {$tableName} WHERE entity_id IN ({$mainId}, {$otherId})"; $dao = CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray); while ($dao->fetch()) { $fileIds[$dao->entity_id] = $dao->file_id; } $dao->free(); // delete the main contact's file if (!empty($fileIds[$mainId])) { CRM_Core_BAO_File::deleteFileReferences($fileIds[$mainId], $mainId, $customId); } // move the other contact's file to main contact //NYSS need to INSERT or UPDATE depending on whether main contact has an existing record if (CRM_Core_DAO::singleValueQuery("SELECT id FROM {$tableName} WHERE entity_id = {$mainId}")) { $sql = "UPDATE {$tableName} SET {$columnName} = {$fileIds[$otherId]} WHERE entity_id = {$mainId}"; } else { $sql = "INSERT INTO {$tableName} ( entity_id, {$columnName} ) VALUES ( {$mainId}, {$fileIds[$otherId]} )"; } CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray); if (CRM_Core_DAO::singleValueQuery("\n SELECT id\n FROM civicrm_entity_file\n WHERE entity_table = '{$tableName}' AND file_id = {$fileIds[$otherId]}")) { $sql = "\n UPDATE civicrm_entity_file\n SET entity_id = {$mainId}\n WHERE entity_table = '{$tableName}' AND file_id = {$fileIds[$otherId]}"; } else { $sql = "\n INSERT INTO civicrm_entity_file ( entity_table, entity_id, file_id )\n VALUES ( '{$tableName}', {$mainId}, {$fileIds[$otherId]} )"; } CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray); } // move view only custom fields CRM-5362 $viewOnlyCustomFields = array(); foreach ($submitted as $key => $value) { $fid = (int) substr($key, 7); if (array_key_exists($fid, $cFields) && !empty($cFields[$fid]['attributes']['is_view'])) { $viewOnlyCustomFields[$key] = $value; } } // special case to set values for view only, CRM-5362 if (!empty($viewOnlyCustomFields)) { $viewOnlyCustomFields['entityID'] = $mainId; CRM_Core_BAO_CustomValueTable::setValues($viewOnlyCustomFields); } // **** Delete other contact & update prev-next caching $otherParams = array('contact_id' => $otherId, 'id' => $otherId, 'version' => 3); if (CRM_Core_Permission::check('merge duplicate contacts') && CRM_Core_Permission::check('delete contacts')) { // if ext id is submitted then set it null for contact to be deleted if (!empty($submitted['external_identifier'])) { $query = "UPDATE civicrm_contact SET external_identifier = null WHERE id = {$otherId}"; CRM_Core_DAO::executeQuery($query); } civicrm_api('contact', 'delete', $otherParams); CRM_Core_BAO_PrevNextCache::deleteItem($otherId); } // FIXME: else part /* else { */ /* CRM_Core_Session::setStatus( ts('Do not have sufficient permission to delete duplicate contact.') ); */ /* } */ // CRM-15681 merge sub_types if ($other_sub_types = CRM_Utils_array::value('contact_sub_type', $migrationInfo['other_details'])) { if ($main_sub_types = CRM_Utils_array::value('contact_sub_type', $migrationInfo['main_details'])) { $submitted['contact_sub_type'] = array_unique(array_merge($main_sub_types, $other_sub_types)); } else { $submitted['contact_sub_type'] = $other_sub_types; } } // **** Update contact related info for the main contact if (!empty($submitted)) { $submitted['contact_id'] = $mainId; //update current employer field if ($currentEmloyerId = CRM_Utils_Array::value('current_employer_id', $submitted)) { if (!CRM_Utils_System::isNull($currentEmloyerId)) { $submitted['current_employer'] = $submitted['current_employer_id']; } else { $submitted['current_employer'] = ''; } unset($submitted['current_employer_id']); } //CRM-14312 include prefix/suffix from mainId if not overridden for proper construction of display/sort name if (!isset($submitted['prefix_id']) && !empty($migrationInfo['main_details']['prefix_id'])) { $submitted['prefix_id'] = $migrationInfo['main_details']['prefix_id']; } if (!isset($submitted['suffix_id']) && !empty($migrationInfo['main_details']['suffix_id'])) { $submitted['suffix_id'] = $migrationInfo['main_details']['suffix_id']; } CRM_Contact_BAO_Contact::createProfileContact($submitted, CRM_Core_DAO::$_nullArray, $mainId); unset($submitted); } CRM_Utils_Hook::post('merge', 'Contact', $mainId, CRM_Core_DAO::$_nullObject); return TRUE; }
/** * Clear the contact cache so things are kosher. We started off being super aggressive with clearing * caches, but are backing off from this with every release. Compromise between ease of coding versus * performance versus being accurate at that very instant * * @param $contactID * The contactID that was edited / deleted. */ public static function clearContactCaches($contactID = NULL) { // clear acl cache if any. CRM_ACL_BAO_Cache::resetCache(); if (empty($contactID)) { // also clear prev/next dedupe cache - if no contactID passed in CRM_Core_BAO_PrevNextCache::deleteItem(); } CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush(); }
/** * Browse all rule groups. */ public function run() { $gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this, FALSE, 0); $action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 0); $context = CRM_Utils_Request::retrieve('context', 'String', $this); $limit = CRM_Utils_Request::retrieve('limit', 'Integer', $this); $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive'); $urlQry = "reset=1&rgid={$rgid}&gid={$gid}&limit={$limit}"; $this->assign('urlQuery', $urlQry); $session = CRM_Core_Session::singleton(); $contactIds = $session->get('selectedSearchContactIds'); if ($context == 'search' || !empty($contactIds)) { $context = 'search'; $this->assign('backURL', $session->readUserContext()); } if ($action & CRM_Core_Action::RENEW) { // empty cache if ($rgid) { CRM_Core_BAO_PrevNextCache::deleteItem(NULL, CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid)); } CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry . "&action=update")); } elseif ($action & CRM_Core_Action::MAP) { // do a batch merge if requested $result = CRM_Dedupe_Merger::batchMerge($rgid, $gid, 'safe', TRUE, 75); $skippedCount = CRM_Utils_Request::retrieve('skipped', 'Positive', $this, FALSE, 0); $skippedCount = $skippedCount + count($result['skipped']); $mergedCount = CRM_Utils_Request::retrieve('merged', 'Positive', $this, FALSE, 0); $mergedCount = $mergedCount + count($result['merged']); if (empty($result['merged']) && empty($result['skipped'])) { $message = ''; if ($mergedCount >= 1) { $message = ts("%1 pairs of duplicates were merged", array(1 => $mergedCount)); } if ($skippedCount >= 1) { $message = $message ? "{$message} and " : ''; $message .= ts("%1 pairs of duplicates were skipped due to conflict", array(1 => $skippedCount)); } $message .= ts(" during the batch merge process with safe mode."); CRM_Core_Session::setStatus($message, ts('Merge Complete'), 'success'); CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry . "&action=update")); } else { $urlQry .= "&action=map&skipped={$skippedCount}&merged={$mergedCount}"; CRM_Utils_System::jsRedirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry), ts('Batch Merge Task in progress'), ts('The batch merge task is still in progress. This page will be refreshed automatically.')); } } if ($action & CRM_Core_Action::UPDATE || $action & CRM_Core_Action::BROWSE) { $cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this, FALSE, 0); $this->action = CRM_Core_Action::UPDATE; $urlQry .= '&snippet=4'; if ($context == 'conflicts') { $urlQry .= "&selected=1"; } $this->assign('sourceUrl', CRM_Utils_System::url('civicrm/ajax/dedupefind', $urlQry, FALSE, NULL, FALSE)); //reload from cache table $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid); $stats = CRM_Dedupe_Merger::getMergeStatsMsg($cacheKeyString); if ($stats) { CRM_Core_Session::setStatus($stats); // reset so we not displaying same message again CRM_Dedupe_Merger::resetMergeStats($cacheKeyString); } $join = CRM_Dedupe_Merger::getJoinOnDedupeTable(); $where = "de.id IS NULL"; if ($context == 'conflicts') { $where .= " AND pn.is_selected = 1"; } $this->_mainContacts = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $where); if (empty($this->_mainContacts)) { if ($context == 'conflicts') { // if the current screen was intended to list only selected contacts, move back to full dupe list CRM_Utils_System::redirect(CRM_Utils_System::url(CRM_Utils_System::currentPath(), $urlQry . '&action=update')); } if ($gid) { $foundDupes = $this->get("dedupe_dupes_{$gid}"); if (!$foundDupes) { $foundDupes = CRM_Dedupe_Finder::dupesInGroup($rgid, $gid, $limit); } $this->set("dedupe_dupes_{$gid}", $foundDupes); } elseif (!empty($contactIds)) { $foundDupes = $this->get("search_dedupe_dupes_{$gid}"); if (!$foundDupes) { $foundDupes = CRM_Dedupe_Finder::dupes($rgid, $contactIds); } $this->set("search_dedupe_dupes_{$gid}", $foundDupes); } else { $foundDupes = $this->get('dedupe_dupes'); if (!$foundDupes) { $foundDupes = CRM_Dedupe_Finder::dupes($rgid, array(), TRUE, $limit); } $this->set('dedupe_dupes', $foundDupes); } if (!$foundDupes) { $ruleGroup = new CRM_Dedupe_BAO_RuleGroup(); $ruleGroup->id = $rgid; $ruleGroup->find(TRUE); $session = CRM_Core_Session::singleton(); $session->setStatus(ts('No possible duplicates were found using %1 rule.', array(1 => $ruleGroup->name)), ts('None Found'), 'info'); $url = CRM_Utils_System::url('civicrm/contact/deduperules', 'reset=1'); if ($context == 'search') { $url = $session->readUserContext(); } CRM_Utils_System::redirect($url); } else { $mainContacts = CRM_Dedupe_Finder::parseAndStoreDupePairs($foundDupes, $cacheKeyString); if ($cid) { $this->_cid = $cid; } if ($gid) { $this->_gid = $gid; } $this->_rgid = $rgid; $this->_mainContacts = $mainContacts; $session = CRM_Core_Session::singleton(); if ($this->_cid) { $session->pushUserContext(CRM_Utils_System::url('civicrm/contact/deduperules', $urlQry . "&action=update&cid={$this->_cid}")); } else { $session->pushUserContext(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry . "&action=update")); } } } else { if ($cid) { $this->_cid = $cid; } if ($gid) { $this->_gid = $gid; } $this->_rgid = $rgid; } $this->assign('action', $this->action); $this->browse(); } else { $this->action = CRM_Core_Action::UPDATE; $this->edit($this->action); $this->assign('action', $this->action); } $this->assign('context', $context); // parent run return parent::run(); }
/** * Delete information about merges for the given string. * * @param $cacheKeyString */ public static function resetMergeStats($cacheKeyString) { CRM_Core_BAO_PrevNextCache::deleteItem(NULL, "{$cacheKeyString}_stats"); }
function fillupPrevNextCache($sort, $cacheKey = NULL) { if (!$cacheKey) { $cacheKey = "civicrm search {$this->_key}"; } CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKey, 'civicrm_contact'); // lets fill up the prev next cache here, so view can scroll thru $sql = $this->_query->searchQuery(0, 0, $sort, FALSE, FALSE, FALSE, TRUE, TRUE, NULL); // CRM-9096 // due to limitations in our search query writer, the above query does not work // in cases where the query is being sorted on a non-contact table // this results in a fatal error :( // see below for the gross hack of trapping the error and not filling // the prev next cache in this situation // the other alternative of running the FULL query will just be incredibly inefficient // and slow things down way too much on large data sets / complex queries $insertSQL = "\nINSERT INTO civicrm_prevnext_cache ( entity_table, entity_id1, entity_id2, cacheKey, data )\nSELECT 'civicrm_contact', contact_a.id, contact_a.id, '{$cacheKey}', contact_a.display_name\n"; $replaceSQL = "SELECT contact_a.id as id"; $sql = str_replace($replaceSQL, $insertSQL, $sql); CRM_Core_Error::ignoreException(); $result = CRM_Core_DAO::executeQuery($sql); CRM_Core_Error::setCallback(); if (is_a($result, 'DB_Error')) { // oops the above query failed, so lets just ignore it // and return // we print a sorry cant figure it out on view page return; } // also record an entry in the cache key table, so we can delete it periodically CRM_Core_BAO_Cache::setItem($cacheKey, 'CiviCRM Search PrevNextCache', $cacheKey); }
static function clearContactCaches() { // clear acl cache if any. CRM_ACL_BAO_Cache::resetCache(); // also clear prev/next dedupe cache CRM_Core_BAO_PrevNextCache::deleteItem(); // reset the group contact cache for this group CRM_Contact_BAO_GroupContactCache::remove(); }
/** * The post processing of the form gets done here. * * Key things done during post processing are * - check for reset or next request. if present, skip post processing. * - now check if user requested running a saved search, if so, then * the form values associated with the saved search are used for searching. * - if user has done a submit with new values the regular post submission is * done. * The processing consists of using a Selector / Controller framework for getting the * search results. */ public function postProcess() { // get user submitted values // get it from controller only if form has been submitted, else preProcess has set this if (!empty($_POST)) { $this->_formValues = $this->controller->exportValues($this->_name); $this->normalizeFormValues(); } CRM_Core_BAO_CustomValue::fixCustomFieldValue($this->_formValues); $this->_params = CRM_Relationship_BAO_Query::convertFormValues($this->_formValues); $this->_returnProperties =& $this->returnProperties(); if ($this->_done) { return; } $this->_done = TRUE; //for prev/next pagination $crmPID = CRM_Utils_Request::retrieve('crmPID', 'Integer', CRM_Core_DAO::$_nullObject); if (array_key_exists($this->_searchButtonName, $_POST) || $this->_force && !$crmPID) { //reset the cache table for new search $cacheKey = "civicrm search {$this->controller->_key}"; CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKey); } //get the button name $buttonName = $this->controller->getButtonName(); if (isset($this->_componentMode) && empty($this->_formValues['component_mode'])) { $this->_formValues['component_mode'] = $this->_componentMode; } if (isset($this->_operator) && empty($this->_formValues['operator'])) { $this->_formValues['operator'] = $this->_operator; } if (empty($this->_formValues['qfKey'])) { $this->_formValues['qfKey'] = $this->controller->_key; } $this->set('type', $this->_action); $this->set('formValues', $this->_formValues); $this->set('queryParams', $this->_params); $this->set('returnProperties', $this->_returnProperties); if ($buttonName == $this->_actionButtonName) { // check actionName and if next, then do not repeat a search, since we are going to the next page // hack, make sure we reset the task values $stateMachine = $this->controller->getStateMachine(); $formName = $stateMachine->getTaskFormName(); $this->controller->resetPage($formName); return; } else { $output = CRM_Core_Selector_Controller::SESSION; // create the selector, controller and run - store results in session $searchChildGroups = TRUE; $setDynamic = FALSE; if (strpos(self::$_selectorName, 'CRM_Relationship_Selector') !== FALSE) { $selector = new self::$_selectorName($this->_customSearchClass, $this->_formValues, $this->_params, $this->_returnProperties, $this->_action, FALSE, $this->_context, $this->_contextMenu); $setDynamic = TRUE; } else { $selector = new self::$_selectorName($this->_params, $this->_action, NULL, FALSE, NULL, "search", "advanced"); } $selector->setKey($this->controller->_key); // added the sorting character to the form array $config = CRM_Core_Config::singleton(); // do this only for contact search $sortID = NULL; if ($this->get(CRM_Utils_Sort::SORT_ID)) { $sortID = CRM_Utils_Sort::sortIDValue($this->get(CRM_Utils_Sort::SORT_ID), $this->get(CRM_Utils_Sort::SORT_DIRECTION)); } $controller = new CRM_Relationship_Selector_Controller($selector, $this->get(CRM_Utils_Pager::PAGE_ID), $sortID, CRM_Core_Action::VIEW, $this, $output); $controller->setEmbedded(TRUE); $controller->setDynamicAction($setDynamic); $controller->run(); } }
/** * Delete a contact and all its associated records * * @param int $id id of the contact to delete * @param bool $restore whether to actually restore, not delete * @param bool $skipUndelete whether to force contact delete or not * * @return boolean true if contact deleted, false otherwise * @access public * @static */ static function deleteContact($id, $restore = FALSE, $skipUndelete = FALSE) { if (!$id) { return FALSE; } // make sure we have edit permission for this contact // before we delete if ($skipUndelete && !CRM_Core_Permission::check('delete contacts') || $restore && !CRM_Core_Permission::check('access deleted contacts')) { return FALSE; } // make sure this contact_id does not have any membership types $membershipTypeID = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $id, 'id', 'member_of_contact_id'); if ($membershipTypeID) { return FALSE; } $contact = new CRM_Contact_DAO_Contact(); $contact->id = $id; if (!$contact->find(TRUE)) { return FALSE; } $contactType = $contact->contact_type; $action = $restore ? 'restore' : 'delete'; CRM_Utils_Hook::pre($action, $contactType, $id, CRM_Core_DAO::$_nullArray); if ($restore) { self::contactTrashRestore($contact, TRUE); CRM_Utils_Hook::post($action, $contactType, $contact->id, $contact); return TRUE; } // currently we only clear employer cache. // we are not deleting inherited membership if any. if ($contact->contact_type == 'Organization') { CRM_Contact_BAO_Contact_Utils::clearAllEmployee($id); } // start a new transaction $transaction = new CRM_Core_Transaction(); $config = CRM_Core_Config::singleton(); if ($skipUndelete or !$config->contactUndelete) { //delete billing address if exists. CRM_Contribute_BAO_Contribution::deleteAddress(NULL, $id); // delete the log entries since we dont have triggers enabled as yet $logDAO = new CRM_Core_DAO_Log(); $logDAO->entity_table = 'civicrm_contact'; $logDAO->entity_id = $id; $logDAO->delete(); // do activity cleanup, CRM-5604 CRM_Activity_BAO_Activity::cleanupActivity($id); // delete all notes related to contact CRM_Core_BAO_Note::cleanContactNotes($id); $contact->delete(); } else { self::contactTrashRestore($contact); } //delete the contact id from recently view CRM_Utils_Recent::delContact($id); // reset the group contact cache for this group CRM_Contact_BAO_GroupContactCache::remove(); // delete any dupe cache entry CRM_Core_BAO_PrevNextCache::deleteItem($id); $transaction->commit(); CRM_Utils_Hook::post('delete', $contactType, $contact->id, $contact); // also reset the DB_DO global array so we can reuse the memory // http://issues.civicrm.org/jira/browse/CRM-4387 CRM_Core_DAO::freeResult(); return TRUE; }
/** * Common post processing. */ public function postProcess() { /* * sometime we do a postProcess early on, so we dont need to repeat it * this will most likely introduce some more bugs :( */ if ($this->_done) { return; } $this->_done = TRUE; //for prev/next pagination $crmPID = CRM_Utils_Request::retrieve('crmPID', 'Integer', CRM_Core_DAO::$_nullObject); if (array_key_exists($this->_searchButtonName, $_POST) || $this->_force && !$crmPID) { //reset the cache table for new search $cacheKey = "civicrm search {$this->controller->_key}"; CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKey); } //get the button name $buttonName = $this->controller->getButtonName(); if (isset($this->_ufGroupID) && empty($this->_formValues['uf_group_id'])) { $this->_formValues['uf_group_id'] = $this->_ufGroupID; } if (isset($this->_componentMode) && empty($this->_formValues['component_mode'])) { $this->_formValues['component_mode'] = $this->_componentMode; } if (isset($this->_operator) && empty($this->_formValues['operator'])) { $this->_formValues['operator'] = $this->_operator; } if (empty($this->_formValues['qfKey'])) { $this->_formValues['qfKey'] = $this->controller->_key; } if (!CRM_Core_Permission::check('access deleted contacts')) { unset($this->_formValues['deleted_contacts']); } $this->set('type', $this->_action); $this->set('formValues', $this->_formValues); $this->set('queryParams', $this->_params); $this->set('returnProperties', $this->_returnProperties); if ($buttonName == $this->_actionButtonName) { // check actionName and if next, then do not repeat a search, since we are going to the next page // hack, make sure we reset the task values $stateMachine = $this->controller->getStateMachine(); $formName = $stateMachine->getTaskFormName(); $this->controller->resetPage($formName); return; } else { $output = CRM_Core_Selector_Controller::SESSION; // create the selector, controller and run - store results in session $searchChildGroups = TRUE; if ($this->get('isAdvanced')) { $searchChildGroups = FALSE; } $setDynamic = FALSE; if (strpos(self::$_selectorName, 'CRM_Contact_Selector') !== FALSE) { $selector = new self::$_selectorName($this->_customSearchClass, $this->_formValues, $this->_params, $this->_returnProperties, $this->_action, FALSE, $searchChildGroups, $this->_context, $this->_contextMenu); $setDynamic = TRUE; } else { $selector = new self::$_selectorName($this->_params, $this->_action, NULL, FALSE, NULL, "search", "advanced"); } $selector->setKey($this->controller->_key); // added the sorting character to the form array $config = CRM_Core_Config::singleton(); // do this only for contact search if ($setDynamic && $config->includeAlphabeticalPager) { // Don't recompute if we are just paging/sorting if ($this->_reset || empty($_GET['crmPID']) && empty($_GET['crmSID']) && !$this->_sortByCharacter) { $aToZBar = CRM_Utils_PagerAToZ::getAToZBar($selector, $this->_sortByCharacter); $this->set('AToZBar', $aToZBar); } } $sortID = NULL; if ($this->get(CRM_Utils_Sort::SORT_ID)) { $sortID = CRM_Utils_Sort::sortIDValue($this->get(CRM_Utils_Sort::SORT_ID), $this->get(CRM_Utils_Sort::SORT_DIRECTION)); } $controller = new CRM_Contact_Selector_Controller($selector, $this->get(CRM_Utils_Pager::PAGE_ID), $sortID, CRM_Core_Action::VIEW, $this, $output); $controller->setEmbedded(TRUE); $controller->setDynamicAction($setDynamic); $controller->run(); } }
/** * Browse all rule groups. */ public function run() { $gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this, FALSE, 0); $action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 0); $context = CRM_Utils_Request::retrieve('context', 'String', $this); $session = CRM_Core_Session::singleton(); $contactIds = $session->get('selectedSearchContactIds'); if ($context == 'search' || !empty($contactIds)) { $context = 'search'; $this->assign('backURL', $session->readUserContext()); } if ($action & CRM_Core_Action::RENEW) { // empty cache $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE, 0); if ($rgid) { $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type'); $cacheKeyString = "merge {$contactType}"; $cacheKeyString .= $rgid ? "_{$rgid}" : '_0'; $cacheKeyString .= $gid ? "_{$gid}" : '_0'; CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKeyString); } $urlQry = "reset=1&action=update&rgid={$rgid}"; if ($gid) { $urlQry .= "&gid={$gid}"; } CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry)); } elseif ($action & CRM_Core_Action::MAP) { // do a batch merge if requested $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE, 0); $result = CRM_Dedupe_Merger::batchMerge($rgid, $gid, 'safe', TRUE, 75); $skippedCount = CRM_Utils_Request::retrieve('skipped', 'Positive', $this, FALSE, 0); $skippedCount = $skippedCount + count($result['skipped']); $mergedCount = CRM_Utils_Request::retrieve('merged', 'Positive', $this, FALSE, 0); $mergedCount = $mergedCount + count($result['merged']); if (empty($result['merged']) && empty($result['skipped'])) { $message = ''; if ($mergedCount >= 1) { $message = ts("%1 pairs of duplicates were merged", array(1 => $mergedCount)); } if ($skippedCount >= 1) { $message = $message ? "{$message} and " : ''; $message .= ts("%1 pairs of duplicates were skipped due to conflict", array(1 => $skippedCount)); } $message .= ts(" during the batch merge process with safe mode."); CRM_Core_Session::setStatus($message, ts('Merge Complete'), 'success'); $urlQry = "reset=1&action=update&rgid={$rgid}"; if ($gid) { $urlQry .= "&gid={$gid}"; } CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry)); } else { $urlQry = "reset=1&action=map&rgid={$rgid}"; if ($gid) { $urlQry .= "&gid={$gid}"; } $urlQry .= "&skipped={$skippedCount}&merged={$mergedCount}"; CRM_Utils_System::jsRedirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry), ts('Batch Merge Task in progress'), ts('The batch merge task is still in progress. This page will be refreshed automatically.')); } } if ($action & CRM_Core_Action::UPDATE || $action & CRM_Core_Action::BROWSE) { $cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this, FALSE, 0); $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE, 0); $this->action = CRM_Core_Action::UPDATE; //calculate the $contactType if ($rgid) { $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type'); } $sourceParams = 'snippet=4'; if ($gid) { $sourceParams .= "&gid={$gid}"; } if ($rgid) { $sourceParams .= "&rgid={$rgid}"; } if ($context == 'conflicts') { $sourceParams .= "&selected=1"; } $this->assign('sourceUrl', CRM_Utils_System::url('civicrm/ajax/dedupefind', $sourceParams, FALSE, NULL, FALSE)); //reload from cache table $cacheKeyString = "merge {$contactType}"; $cacheKeyString .= $rgid ? "_{$rgid}" : '_0'; $cacheKeyString .= $gid ? "_{$gid}" : '_0'; $stats = CRM_Dedupe_Merger::getMergeStatsMsg($cacheKeyString); if ($stats) { CRM_Core_Session::setStatus($stats); // reset so we not displaying same message again CRM_Dedupe_Merger::resetMergeStats($cacheKeyString); } $join = "LEFT JOIN civicrm_dedupe_exception de ON ( pn.entity_id1 = de.contact_id1 AND\n pn.entity_id2 = de.contact_id2 )"; $where = "de.id IS NULL"; if ($context == 'conflicts') { $where .= " AND pn.is_selected = 1"; } $this->_mainContacts = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $where); if (empty($this->_mainContacts)) { if ($context == 'conflicts') { // if the current screen was intended to list only selected contacts, move back to full dupe list $sourceParams = 'reset=1&action=update'; if ($gid) { $sourceParams .= "&gid={$gid}"; } if ($rgid) { $sourceParams .= "&rgid={$rgid}"; } CRM_Utils_System::redirect(CRM_Utils_System::url(CRM_Utils_System::currentPath(), $sourceParams)); } if ($gid) { $foundDupes = $this->get("dedupe_dupes_{$gid}"); if (!$foundDupes) { $foundDupes = CRM_Dedupe_Finder::dupesInGroup($rgid, $gid); } $this->set("dedupe_dupes_{$gid}", $foundDupes); } elseif (!empty($contactIds)) { $foundDupes = $this->get("search_dedupe_dupes_{$gid}"); if (!$foundDupes) { $foundDupes = CRM_Dedupe_Finder::dupes($rgid, $contactIds); } $this->get("search_dedupe_dupes_{$gid}", $foundDupes); } else { $foundDupes = $this->get('dedupe_dupes'); if (!$foundDupes) { $foundDupes = CRM_Dedupe_Finder::dupes($rgid); } $this->set('dedupe_dupes', $foundDupes); } if (!$foundDupes) { $ruleGroup = new CRM_Dedupe_BAO_RuleGroup(); $ruleGroup->id = $rgid; $ruleGroup->find(TRUE); $session = CRM_Core_Session::singleton(); $session->setStatus(ts('No possible duplicates were found using %1 rule.', array(1 => $ruleGroup->name)), ts('None Found'), 'info'); $url = CRM_Utils_System::url('civicrm/contact/deduperules', 'reset=1'); if ($context == 'search') { $url = $session->readUserContext(); } CRM_Utils_System::redirect($url); } else { $cids = array(); foreach ($foundDupes as $dupe) { $cids[$dupe[0]] = 1; $cids[$dupe[1]] = 1; } $cidString = implode(', ', array_keys($cids)); $sql = "SELECT id, display_name FROM civicrm_contact WHERE id IN ({$cidString}) ORDER BY sort_name"; $dao = new CRM_Core_DAO(); $dao->query($sql); $displayNames = array(); while ($dao->fetch()) { $displayNames[$dao->id] = $dao->display_name; } // FIXME: sort the contacts; $displayName // is already sort_name-sorted, so use that // (also, consider sorting by dupe count first) // lobo - change the sort to by threshold value // so the more likely dupes are sorted first $session = CRM_Core_Session::singleton(); $userId = $session->get('userID'); $mainContacts = $permission = array(); foreach ($foundDupes as $dupes) { $srcID = $dupes[0]; $dstID = $dupes[1]; if ($dstID == $userId) { $srcID = $dupes[1]; $dstID = $dupes[0]; } /*** * Eliminate this since it introduces 3 queries PER merge row * and hence is very expensive * CRM-8822 * if ( !array_key_exists( $srcID, $permission ) ) { * $permission[$srcID] = CRM_Contact_BAO_Contact_Permission::allow( $srcID, CRM_Core_Permission::EDIT ); * } * if ( !array_key_exists( $dstID, $permission ) ) { * $permission[$dstID] = CRM_Contact_BAO_Contact_Permission::allow( $dstID, CRM_Core_Permission::EDIT ); * } * * $canMerge = ( $permission[$dstID] && $permission[$srcID] ); * */ // we'll do permission checking during the merge process $canMerge = TRUE; $mainContacts[] = $row = array('srcID' => $srcID, 'srcName' => $displayNames[$srcID], 'dstID' => $dstID, 'dstName' => $displayNames[$dstID], 'weight' => $dupes[2], 'canMerge' => $canMerge); $data = CRM_Core_DAO::escapeString(serialize($row)); $values[] = " ( 'civicrm_contact', {$srcID}, {$dstID}, '{$cacheKeyString}', '{$data}' ) "; } if ($cid) { $this->_cid = $cid; } if ($gid) { $this->_gid = $gid; } $this->_rgid = $rgid; $this->_mainContacts = $mainContacts; CRM_Core_BAO_PrevNextCache::setItem($values); $session = CRM_Core_Session::singleton(); if ($this->_cid) { $session->pushUserContext(CRM_Utils_System::url('civicrm/contact/deduperules', "action=update&rgid={$this->_rgid}&gid={$this->_gid}&cid={$this->_cid}")); } else { $session->pushUserContext(CRM_Utils_System::url('civicrm/contact/dedupefind', "reset=1&action=update&rgid={$this->_rgid}")); } } } else { if ($cid) { $this->_cid = $cid; } if ($gid) { $this->_gid = $gid; } $this->_rgid = $rgid; } $this->assign('action', $this->action); $this->browse(); } else { $this->action = CRM_Core_Action::UPDATE; $this->edit($this->action); $this->assign('action', $this->action); } $this->assign('context', $context); // parent run return parent::run(); }
/** * @param $sort * * @return string */ function buildPrevNextCache($sort) { $cacheKey = 'civicrm search ' . $this->_key; // Get current page requested $pageNum = CRM_Utils_Request::retrieve('crmPID', 'Integer', CRM_Core_DAO::$_nullObject); // When starting from scratch, clear any old cache if (!$pageNum) { CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKey, 'civicrm_contact'); $pageNum = 1; } $pageSize = CRM_Utils_Request::retrieve('crmRowCount', 'Integer', CRM_Core_DAO::$_nullObject, FALSE, 50); $firstRecord = ($pageNum - 1) * $pageSize; //for alphabetic pagination selection save $sortByCharacter = CRM_Utils_Request::retrieve('sortByCharacter', 'String', CRM_Core_DAO::$_nullObject); //for text field pagination selection save $countRow = CRM_Core_BAO_PrevNextCache::getCount($cacheKey, NULL, "entity_table = 'civicrm_contact'"); // $sortByCharacter triggers a refresh in the prevNext cache if ($sortByCharacter && $sortByCharacter != 'all') { $cacheKey .= "_alphabet"; $this->fillupPrevNextCache($sort, $cacheKey); } elseif ($firstRecord >= $countRow) { $this->fillupPrevNextCache($sort, $cacheKey, $countRow, 500); } return $cacheKey; }
/** * Function to process the form * * @access public * * @return None */ public function postProcess() { $values = $this->exportValues(); $isDefault = CRM_Utils_Array::value('is_default', $values, FALSE); // reset defaults if ($isDefault) { $query = "\nUPDATE civicrm_dedupe_rule_group \n SET is_default = 0\n WHERE contact_type = %1 \n AND level = %2"; $queryParams = array(1 => array($this->_contactType, 'String'), 2 => array($values['level'], 'String')); CRM_Core_DAO::executeQuery($query, $queryParams); } $rgDao = new CRM_Dedupe_DAO_RuleGroup(); if ($this->_action & CRM_Core_Action::UPDATE) { $rgDao->id = $this->_rgid; } $rgDao->title = $values['title']; $rgDao->is_reserved = CRM_Utils_Array::value('is_reserved', $values, FALSE); $rgDao->is_default = $isDefault; $rgDao->level = $values['level']; $rgDao->contact_type = $this->_contactType; $rgDao->threshold = $values['threshold']; $rgDao->save(); // make sure name is set only during insert if ($this->_action & CRM_Core_Action::ADD) { // generate name based on title $rgDao->name = CRM_Utils_String::titleToVar($values['title']) . "_{$rgDao->id}"; $rgDao->save(); } // lets skip updating of fields for reserved dedupe group if ($rgDao->is_reserved) { CRM_Core_Session::setStatus(ts('The rule \'%1\' has been saved.', array(1 => $rgDao->title))); return; } $ruleDao = new CRM_Dedupe_DAO_Rule(); $ruleDao->dedupe_rule_group_id = $rgDao->id; $ruleDao->delete(); $ruleDao->free(); $substrLenghts = array(); $tables = array(); for ($count = 0; $count < self::RULES_COUNT; $count++) { if (!CRM_Utils_Array::value("where_{$count}", $values)) { continue; } list($table, $field) = explode('.', CRM_Utils_Array::value("where_{$count}", $values)); $length = CRM_Utils_Array::value("length_{$count}", $values) ? CRM_Utils_Array::value("length_{$count}", $values) : NULL; $weight = $values["weight_{$count}"]; if ($table and $field) { $ruleDao = new CRM_Dedupe_DAO_Rule(); $ruleDao->dedupe_rule_group_id = $rgDao->id; $ruleDao->rule_table = $table; $ruleDao->rule_field = $field; $ruleDao->rule_length = $length; $ruleDao->rule_weight = $weight; $ruleDao->save(); $ruleDao->free(); if (!array_key_exists($table, $tables)) { $tables[$table] = array(); } $tables[$table][] = $field; } // CRM-6245: we must pass table/field/length triples to the createIndexes() call below if ($length) { if (!isset($substrLenghts[$table])) { $substrLenghts[$table] = array(); } $substrLenghts[$table][$field] = $length; } } // also create an index for this dedupe rule // CRM-3837 CRM_Core_BAO_SchemaHandler::createIndexes($tables, 'dedupe_index', $substrLenghts); //need to clear cache of deduped contacts //based on the previous rule $cacheKey = "merge {$this->_contactType}_{$this->_rgid}_%"; CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKey); CRM_Core_Session::setStatus(ts('The rule \'%1\' has been saved.', array(1 => $rgDao->title))); }