array($id1, 'Integer'), 2 => array($id2, 'Integer'), 3 => array($cacheKey, 'String'), ); $mergeId = CRM_Core_DAO::singleValueQuery($query, $params); } $pos = array('foundEntry' => 0); if ($mergeId) { $pos['foundEntry'] = 1; if ($where) { $where = " AND {$where}"; } $p = array( 1 => array($mergeId, 'Integer'), 2 => array($cacheKey, 'String'), ); $sql = "SELECT pn.id, pn.entity_id1, pn.entity_id2, pn.data FROM civicrm_prevnext_cache pn {$join} "; $wherePrev = " WHERE pn.id < %1 AND pn.cacheKey = %2 {$where} ORDER BY ID DESC LIMIT 1"; $sqlPrev = $sql . $wherePrev; $dao = CRM_Core_DAO::executeQuery($sqlPrev, $p); if ($dao->fetch()) { $pos['prev']['id1'] = $dao->entity_id1; $pos['prev']['id2'] = $dao->entity_id2; $pos['prev']['mergeId'] = $dao->id; $pos['prev']['data'] = $dao->data; } $whereNext = " WHERE pn.id > %1 AND pn.cacheKey = %2 {$where} ORDER BY ID ASC LIMIT 1"; $sqlNext = $sql . $whereNext; $dao = CRM_Core_DAO::executeQuery($sqlNext, $p); if ($dao->fetch()) { $pos['next']['id1'] = $dao->entity_id1; $pos['next']['id2'] = $dao->entity_id2; $pos['next']['mergeId'] = $dao->id; $pos['next']['data'] = $dao->data; } } return $pos; } /** * @param int $id * @param NULL $cacheKey * @param string $entityTable */ public static function deleteItem($id = NULL, $cacheKey = NULL, $entityTable = 'civicrm_contact') { //clear cache $sql = "DELETE FROM civicrm_prevnext_cache WHERE entity_table = %1"; $params = array(1 => array($entityTable, 'String')); if (is_numeric($id)) { $sql .= " AND ( entity_id1 = %2 OR entity_id2 = %2 )"; $params[2] = array($id, 'Integer'); } if (isset($cacheKey)) { $sql .= " AND cacheKey LIKE %3"; $params[3] = array("{$cacheKey}%", 'String'); } CRM_Core_DAO::executeQuery($sql, $params); } /** * @param $id1 * @param $id2 * @param NULL $cacheKey * @param bool $isViceVersa * @param string $entityTable */ public static function deletePair($id1, $id2, $cacheKey = NULL, $isViceVersa = FALSE, $entityTable = 'civicrm_contact') { $sql = "DELETE FROM civicrm_prevnext_cache WHERE entity_table = %1"; $params = array(1 => array($entityTable, 'String')); $pair = !$isViceVersa ? "entity_id1 = %2 AND entity_id2 = %3" : "(entity_id1 = %2 AND entity_id2 = %3) OR (entity_id1 = %3 AND entity_id2 = %2)"; $sql .= " AND ( {$pair} )"; $params[2] = array($id1, 'Integer'); $params[3] = array($id2, 'Integer'); if (isset($cacheKey)) { $sql .= " AND cacheKey LIKE %4"; $params[4] = array("{$cacheKey}%", 'String'); // used % to address any row with conflict-cacheKey e.g "merge Individual_8_0_conflicts" } CRM_Core_DAO::executeQuery($sql, $params); } public static function markConflict($id1, $id2, $cacheKey, $conflicts) { if (empty($cacheKey) || empty($conflicts)) { return FALSE; } $sql = "SELECT pn.* FROM civicrm_prevnext_cache pn WHERE ((pn.entity_id1 = %1 AND pn.entity_id2 = %2) OR (pn.entity_id1 = %2 AND pn.entity_id2 = %1)) AND (cacheKey = %3 OR cacheKey = %4)"; $params = array( 1 => array($id1, 'Integer'), 2 => array($id2, 'Integer'), 3 => array("{$cacheKey}", 'String'), 4 => array("{$cacheKey}_conflicts", 'String'), ); $pncFind = CRM_Core_DAO::executeQuery($sql, $params); while ($pncFind->fetch()) { $data = $pncFind->data; if (!empty($data)) { $data = unserialize($data); $data['conflicts'] = implode(",", array_values($conflicts)); $pncUp = new CRM_Core_DAO_PrevNextCache(); $pncUp->id = $pncFind->id; if ($pncUp->find(TRUE)) { $pncUp->data = serialize($data); $pncUp->cacheKey = "{$cacheKey}_conflicts"; $pncUp->save(); } } } return TRUE; } /** * Retrieve from prev-next cache. * * @param string $cacheKey * @param string $join * @param string $where * @param int $offset * @param int $rowCount * * @param array $select * * @return array */ public static function retrieve($cacheKey, $join = NULL, $where = NULL, $offset = 0, $rowCount = 0, $select = array()) { $selectString = 'pn.*'; if (!empty($select)) { $aliasArray = array(); foreach ($select as $column => $alias) { $aliasArray[] = $column . ' as ' . $alias; } $selectString .= " , " . implode(' , ', $aliasArray); } $query = " SELECT SQL_CALC_FOUND_ROWS {$selectString} FROM civicrm_prevnext_cache pn {$join} WHERE (pn.cacheKey = %1 OR pn.cacheKey = %2) "; $params = array( 1 => array($cacheKey, 'String'), 2 => array("{$cacheKey}_conflicts", 'String'), ); if ($where) { $query .= " AND {$where}"; } if ($rowCount) { $offset = CRM_Utils_Type::escape($offset, 'Int'); $rowCount = CRM_Utils_Type::escape($rowCount, 'Int'); $query .= " LIMIT {$offset}, {$rowCount}"; } $dao = CRM_Core_DAO::executeQuery($query, $params); $main = array(); $count = 0; while ($dao->fetch()) { if (self::is_serialized($dao->data)) { $main[$count] = unserialize($dao->data); } else { $main[$count] = $dao->data; } if (!empty($select)) { $extraData = array(); foreach ($select as $dfield => $sfield) { $extraData[$sfield] = $dao->$sfield; } $main[$count] = array( 'prevnext_id' => $dao->id, 'is_selected' => $dao->is_selected, 'entity_id1' => $dao->entity_id1, 'entity_id2' => $dao->entity_id2, 'data' => $main[$count], ); $main[$count] = array_merge($main[$count], $extraData); } $count++; } return $main; } /** * @param $string * * @return bool */ public static function is_serialized($string) { return (@unserialize($string) !== FALSE); } /** * @param $values */ public static function setItem($values) { $insert = "INSERT INTO civicrm_prevnext_cache ( entity_table, entity_id1, entity_id2, cacheKey, data ) VALUES \n"; $query = $insert . implode(",\n ", $values); //dump the dedupe matches in the prevnext_cache table CRM_Core_DAO::executeQuery($query); } /** * @param $cacheKey * @param NULL $join * @param NULL $where * @param string $op * * @return int */ public static function getCount($cacheKey, $join = NULL, $where = NULL, $op = "=") { $query = " SELECT COUNT(*) FROM civicrm_prevnext_cache pn {$join} WHERE (pn.cacheKey $op %1 OR pn.cacheKey $op %2) "; if ($where) { $query .= " AND {$where}"; } $params = array( 1 => array($cacheKey, 'String'), 2 => array("{$cacheKey}_conflicts", 'String'), ); return (int) CRM_Core_DAO::singleValueQuery($query, $params, TRUE, FALSE); } /** * @param int $rgid * @param int $gid * @param NULL $cacheKeyString * * @return bool */ public static function refillCache($rgid = NULL, $gid = NULL, $cacheKeyString = NULL) { if (!$cacheKeyString && $rgid) { $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type'); $cacheKeyString = "merge {$contactType}"; $cacheKeyString .= $rgid ? "_{$rgid}" : '_0'; $cacheKeyString .= $gid ? "_{$gid}" : '_0'; } if (!$cacheKeyString) { return FALSE; } // 1. Clear cache if any $sql = "DELETE FROM civicrm_prevnext_cache WHERE cacheKey LIKE %1"; CRM_Core_DAO::executeQuery($sql, array(1 => array("{$cacheKeyString}%", 'String'))); // FIXME: we need to start using temp tables / queries here instead of arrays. // And cleanup code in CRM/Contact/Page/DedupeFind.php // 2. FILL cache $foundDupes = array(); if ($rgid && $gid) { $foundDupes = CRM_Dedupe_Finder::dupesInGroup($rgid, $gid); } elseif ($rgid) { $foundDupes = CRM_Dedupe_Finder::dupes($rgid); } if (!empty($foundDupes)) { $cids = $displayNames = $values = 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); while ($dao->fetch()) { $displayNames[$dao->id] = $dao->display_name; } $session = CRM_Core_Session::singleton(); $userId = $session->get('userID'); foreach ($foundDupes as $dupes) { $srcID = $dupes[0]; $dstID = $dupes[1]; if ($dstID == $userId) { $srcID = $dupes[1]; $dstID = $dupes[0]; } $row = array( 'srcID' => $srcID, 'srcName' => $displayNames[$srcID], 'dstID' => $dstID, 'dstName' => $displayNames[$dstID], 'weight' => $dupes[2], 'canMerge' => TRUE, ); $data = CRM_Core_DAO::escapeString(serialize($row)); $values[] = " ( 'civicrm_contact', $srcID, $dstID, '$cacheKeyString', '$data' ) "; } self::setItem($values); } } public static function cleanupCache() { // clean up all prev next caches older than $cacheTimeIntervalDays days $cacheTimeIntervalDays = 2; // first find all the cacheKeys that match this $sql = " DELETE pn, c FROM civicrm_cache c INNER JOIN civicrm_prevnext_cache pn ON c.path = pn.cacheKey WHERE c.group_name = %1 AND c.created_date < date_sub( NOW( ), INTERVAL %2 day ) "; $params = array( 1 => array('CiviCRM Search PrevNextCache', 'String'), 2 => array($cacheTimeIntervalDays, 'Integer'), ); CRM_Core_DAO::executeQuery($sql, $params); } /** * Save checkbox selections. * * @param $cacheKey * @param string $action * @param array $cIds * @param string $entity_table */ public static function markSelection($cacheKey, $action = 'unselect', $cIds = NULL, $entity_table = 'civicrm_contact') { if (!$cacheKey) { return; } $params = array(); $entity_whereClause = " AND entity_table = '{$entity_table}'"; if ($cIds && $cacheKey && $action) { if (is_array($cIds)) { $cIdFilter = "(" . implode(',', $cIds) . ")"; $whereClause = " WHERE cacheKey LIKE %1 AND (entity_id1 IN {$cIdFilter} OR entity_id2 IN {$cIdFilter}) "; } else { $whereClause = " WHERE cacheKey LIKE %1 AND (entity_id1 = %2 OR entity_id2 = %2) "; $params[2] = array("{$cIds}", 'Integer'); } if ($action == 'select') { $whereClause .= "AND is_selected = 0"; $sql = "UPDATE civicrm_prevnext_cache SET is_selected = 1 {$whereClause} {$entity_whereClause}"; $params[1] = array("{$cacheKey}%", 'String'); } elseif ($action == 'unselect') { $whereClause .= "AND is_selected = 1"; $sql = "UPDATE civicrm_prevnext_cache SET is_selected = 0 {$whereClause} {$entity_whereClause}"; $params[1] = array("%{$cacheKey}%", 'String'); } // default action is reseting } elseif (!$cIds && $cacheKey && $action == 'unselect') { $sql = " UPDATE civicrm_prevnext_cache SET is_selected = 0 WHERE cacheKey LIKE %1 AND is_selected = 1 {$entity_whereClause} "; $params[1] = array("{$cacheKey}%", 'String'); } CRM_Core_DAO::executeQuery($sql, $params); } /** * Get the selections. * * @param string $cacheKey * Cache key. * @param string $action * Action. * $action : get - get only selection records * getall - get all the records of the specified cache key * @param string $entity_table * Entity table. * * @return array|NULL */ public static function getSelection($cacheKey, $action = 'get', $entity_table = 'civicrm_contact') { if (!$cacheKey) { return NULL; } $params = array(); $entity_whereClause = " AND entity_table = '{$entity_table}'"; if ($cacheKey && ($action == 'get' || $action == 'getall')) { $actionGet = ($action == "get") ? " AND is_selected = 1 " : ""; $sql = " SELECT entity_id1, entity_id2 FROM civicrm_prevnext_cache WHERE cacheKey LIKE %1 $actionGet $entity_whereClause ORDER BY id "; $params[1] = array("{$cacheKey}%", 'String'); $contactIds = array($cacheKey => array()); $cIdDao = CRM_Core_DAO::executeQuery($sql, $params); while ($cIdDao->fetch()) { if ($cIdDao->entity_id1 == $cIdDao->entity_id2) { $contactIds[$cacheKey][$cIdDao->entity_id1] = 1; } } return $contactIds; } } /** * @return array */ public static function getSelectedContacts() { $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String'); $cacheKey = "civicrm search {$qfKey}"; $query = " SELECT * FROM civicrm_prevnext_cache WHERE cacheKey LIKE %1 AND is_selected=1 AND cacheKey NOT LIKE %2 "; $params1[1] = array("{$cacheKey}%", 'String'); $params1[2] = array("{$cacheKey}_alphabet%", 'String'); $dao = CRM_Core_DAO::executeQuery($query, $params1); $val = array(); while ($dao->fetch()) { $val[] = $dao->data; } return $val; } /** * @param CRM_Core_Form $form * @param array $params * * @return mixed */ public static function buildSelectedContactPager(&$form, &$params) { $params['status'] = ts('Contacts %%StatusMessage%%'); $params['csvString'] = NULL; $params['buttonTop'] = 'PagerTopButton'; $params['buttonBottom'] = 'PagerBottomButton'; $params['rowCount'] = $form->get(CRM_Utils_Pager::PAGE_ROWCOUNT); if (!$params['rowCount']) { $params['rowCount'] = CRM_Utils_Pager::ROWCOUNT; } $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $form); $cacheKey = "civicrm search {$qfKey}"; $query = " SELECT count(id) FROM civicrm_prevnext_cache WHERE cacheKey LIKE %1 AND is_selected = 1 AND cacheKey NOT LIKE %2 "; $params1[1] = array("{$cacheKey}%", 'String'); $params1[2] = array("{$cacheKey}_alphabet%", 'String'); $paramsTotal = CRM_Core_DAO::singleValueQuery($query, $params1); $params['total'] = $paramsTotal; $form->_pager = new CRM_Utils_Pager($params); $form->assign_by_ref('pager', $form->_pager); list($offset, $rowCount) = $form->_pager->getOffsetAndRowCount(); $params['offset'] = $offset; $params['rowCount1'] = $rowCount; return $params; } }