CRM_Utils_JSON::output(array('status' => ($status) ? $oper : $status));
}
- public static function getDedupes() {
-
- $sEcho = CRM_Utils_Type::escape($_REQUEST['sEcho'], 'Integer');
- $offset = isset($_REQUEST['iDisplayStart']) ? CRM_Utils_Type::escape($_REQUEST['iDisplayStart'], 'Integer') : 0;
- $rowCount = isset($_REQUEST['iDisplayLength']) ? CRM_Utils_Type::escape($_REQUEST['iDisplayLength'], 'Integer') : 25;
- $sort = 'sort_name';
- $sortOrder = isset($_REQUEST['sSortDir_0']) ? CRM_Utils_Type::escape($_REQUEST['sSortDir_0'], 'String') : 'asc';
-
- $gid = isset($_REQUEST['gid']) ? CRM_Utils_Type::escape($_REQUEST['gid'], 'Integer') : 0;
- $rgid = isset($_REQUEST['rgid']) ? CRM_Utils_Type::escape($_REQUEST['rgid'], 'Integer') : 0;
+ static function getDedupes() {
+ $offset = isset($_REQUEST['start']) ? CRM_Utils_Type::escape($_REQUEST['start'], 'Integer') : 0;
+ $rowCount = isset($_REQUEST['length']) ? CRM_Utils_Type::escape($_REQUEST['length'], 'Integer') : 25;
+
+ $gid = isset($_REQUEST['gid']) ? CRM_Utils_Type::escape($_REQUEST['gid'], 'Integer') : 0;
+ $rgid = isset($_REQUEST['rgid']) ? CRM_Utils_Type::escape($_REQUEST['rgid'], 'Integer') : 0;
+ $selected = isset($_REQUEST['selected']) ? CRM_Utils_Type::escape($_REQUEST['selected'], 'Integer') : 0;
+ if ($rowCount < 0) {
+ $rowCount = 0;
+ }
$contactType = '';
if ($rgid) {
$contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type');
}
- $cacheKeyString = "merge {$contactType}_{$rgid}_{$gid}";
- $searchRows = array();
- $selectorElements = array('src', 'dst', 'weight', 'actions');
+ $cacheKeyString = "merge {$contactType}_{$rgid}_{$gid}";
+ $searchRows = array();
+ $selectorElements = array('is_selected', 'is_selected_input', 'src_image', 'src', 'src_email', 'src_street', 'src_postcode', 'dst_image', 'dst', 'dst_email', 'dst_street', 'dst_postcode', 'conflicts', 'weight', 'actions');
- $join = "LEFT JOIN civicrm_dedupe_exception de ON ( pn.entity_id1 = de.contact_id1 AND
- pn.entity_id2 = de.contact_id2 )";
- $where = "de.id IS NULL";
+ foreach ($_REQUEST['columns'] as $columnInfo) {
+ if (!empty($columnInfo['search']['value'])) {
+ ${$columnInfo['data']} = CRM_Utils_Type::escape($columnInfo['search']['value'], 'String');
+ }
+ }
+ $join = '';
+ $where = array();
+ $searchData = CRM_Utils_Array::value('search', $_REQUEST);
+ if ($src || !empty($searchData['value']) ) {
+ $src = $src ? $src : $searchData['value'];
+ $where[] = " cc1.display_name LIKE '%{$src}%'";
+ }
+ if ($dst || !empty($searchData['value'])) {
+ $dst = $dst ? $dst : $searchData['value'];
+ $where[] = " cc2.display_name LIKE '%{$dst}%'";
+ }
+ if ($src_email || !empty($searchData['value'])) {
+ $src_email = $src_email ? $src_email : $searchData['value'];
+ $where[] = " (ce1.is_primary = 1 AND ce1.email LIKE '%{$src_email}%')";
+ }
+ if ($dst_email || !empty($searchData['value'])) {
+ $dst_email = $dst_email ? $dst_email : $searchData['value'];
+ $where[] = " (ce2.is_primary = 1 AND ce2.email LIKE '%{$dst_email}%')";
+ }
+ if ($src_postcode || !empty($searchData['value'])) {
+ $src_postcode = $src_postcode ? $src_postcode : $searchData['value'];
+ $where[] = " (ca1.is_primary = 1 AND ca1.postal_code LIKE '%{$src_postcode}%')";
+ }
+ if ($dst_postcode || !empty($searchData['value'])) {
+ $dst_postcode = $dst_postcode ? $dst_postcode : $searchData['value'];
+ $where[] = " (ca2.is_primary = 1 AND ca2.postal_code LIKE '%{$dst_postcode}%')";
+ }
+ if ($src_street || !empty($searchData['value'])) {
+ $src_street = $src_street ? $src_street : $searchData['value'];
+ $where[] = " (ca1.is_primary = 1 AND ca1.street_address LIKE '%{$src_street}%')";
+ }
+ if ($dst_street || !empty($searchData['value'])) {
+ $dst_street = $dst_street ? $dst_street : $searchData['value'];
+ $where[] = " (ca2.is_primary = 1 AND ca2.street_address LIKE '%{$dst_street}%')";
+ }
+ if (!empty($searchData['value'])) {
+ $whereClause = ' ( '.implode(' OR ', $where).' ) ';
+ }
+ else {
+ if (!empty($where)) {
+ $whereClause = implode(' AND ', $where);
+ }
+ }
+ $whereClause .= $whereClause ? ' AND de.id IS NULL' : ' de.id IS NULL';
- $iFilteredTotal = $iTotal = CRM_Core_BAO_PrevNextCache::getCount($cacheKeyString, $join, $where);
- $mainContacts = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $where, $offset, $rowCount);
+ if ($selected) {
+ $whereClause .= ' AND pn.is_selected = 1';
+ }
+ $join .= " LEFT JOIN civicrm_dedupe_exception de ON ( pn.entity_id1 = de.contact_id1 AND pn.entity_id2 = de.contact_id2 )";
+
+
+ $select = array(
+ 'cc1.contact_type' => 'src_contact_type',
+ 'cc1.display_name' => 'src_display_name',
+ 'cc1.contact_sub_type'=> 'src_contact_sub_type',
+ 'cc2.contact_type' => 'dst_contact_type',
+ 'cc2.display_name' => 'dst_display_name',
+ 'cc2.contact_sub_type'=> 'dst_contact_sub_type',
+ 'ce1.email' => 'src_email',
+ 'ce2.email' => 'dst_email',
+ 'ca1.postal_code' => 'src_postcode',
+ 'ca2.postal_code' => 'dst_postcode',
+ 'ca1.street_address' => 'src_street',
+ 'ca2.street_address' => 'dst_street'
+ );
+
+ if($select) {
+ $join .= " INNER JOIN civicrm_contact cc1 ON cc1.id = pn.entity_id1";
+ $join .= " INNER JOIN civicrm_contact cc2 ON cc2.id = pn.entity_id2";
+ $join .= " LEFT JOIN civicrm_email ce1 ON (ce1.contact_id = pn.entity_id1 AND ce1.is_primary = 1 )";
+ $join .= " LEFT JOIN civicrm_email ce2 ON (ce2.contact_id = pn.entity_id2 AND ce2.is_primary = 1 )";
+ $join .= " LEFT JOIN civicrm_address ca1 ON (ca1.contact_id = pn.entity_id1 AND ca1.is_primary = 1 )";
+ $join .= " LEFT JOIN civicrm_address ca2 ON (ca2.contact_id = pn.entity_id2 AND ca2.is_primary = 1 )";
+ }
+ $iTotal = CRM_Core_BAO_PrevNextCache::getCount($cacheKeyString, $join, $whereClause);
+ foreach ($_REQUEST['order'] as $orderInfo) {
+ if (!empty($orderInfo['column'])) {
+ $orderColumnNumber = $orderInfo['column'];
+ $dir = $orderInfo['dir'];
+ }
+ }
+ $columnDetails = CRM_Utils_Array::value($orderColumnNumber, $_REQUEST['columns']);
+ if(!empty($columnDetails)) {
+ switch ($columnDetails['data']) {
+ case 'src':
+ $whereClause .= " ORDER BY cc1.display_name {$dir}";
+ break;
+ case 'src_email':
+ $whereClause .= " ORDER BY ce1.email {$dir}";
+ break;
+ case 'src_street':
+ $whereClause .= " ORDER BY ca1.street_address {$dir}";
+ break;
+ case 'src_postcode':
+ $whereClause .= " ORDER BY ca1.postal_code {$dir}";
+ break;
+ case 'dst':
+ $whereClause .= " ORDER BY cc2.display_name {$dir}";
+ break;
+ case 'dst_email':
+ $whereClause .= " ORDER BY ce2.email {$dir}";
+ break;
+ case 'dst_street':
+ $whereClause .= " ORDER BY ca2.street_address {$dir}";
+ break;
+ case 'dst_postcode':
+ $whereClause .= " ORDER BY ca2.postal_code {$dir}";
+ break;
+ default:
+ break;
+ }
+ }
- foreach ($mainContacts as $mainId => $main) {
- $searchRows[$mainId]['src'] = CRM_Utils_System::href($main['srcName'], 'civicrm/contact/view', "reset=1&cid={$main['srcID']}");
- $searchRows[$mainId]['dst'] = CRM_Utils_System::href($main['dstName'], 'civicrm/contact/view', "reset=1&cid={$main['dstID']}");
- $searchRows[$mainId]['weight'] = CRM_Utils_Array::value('weight', $main);
+ $dupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $whereClause, $offset, $rowCount, $select);
+ $iFilteredTotal = CRM_Core_DAO::singleValueQuery("SELECT FOUND_ROWS()");
+
+ $count = 0;
+ foreach ($dupePairs as $key => $pairInfo) {
+ $pair =& $pairInfo['data'];
+ $srcContactSubType = CRM_Utils_Array::value('src_contact_sub_type', $pairInfo);
+ $dstContactSubType = CRM_Utils_Array::value('dst_contact_sub_type', $pairInfo);
+ $srcTypeImage = CRM_Contact_BAO_Contact_Utils::getImage($srcContactSubType ?
+ $srcContactSubType : $pairInfo['src_contact_type'],
+ FALSE,
+ $pair['srcID']
+ );
+ $dstTypeImage = CRM_Contact_BAO_Contact_Utils::getImage($dstContactSubType ?
+ $dstContactSubType : $pairInfo['dst_contact_type'],
+ FALSE,
+ $pair['dstID']
+ );
- if (!empty($main['canMerge'])) {
- $mergeParams = "reset=1&cid={$main['srcID']}&oid={$main['dstID']}&action=update&rgid={$rgid}";
+ $searchRows[$count]['is_selected'] = $pairInfo['is_selected'];
+ $searchRows[$count]['is_selected_input'] = "<input type='checkbox' class='crm-dedupe-select' name='pnid_{$pairInfo['prevnext_id']}' value='{$pairInfo['is_selected']}' onclick='toggleDedupeSelect(this)'>";
+ $searchRows[$count]['src_image'] = $srcTypeImage;
+ $searchRows[$count]['src'] = CRM_Utils_System::href($pair['srcName'], 'civicrm/contact/view', "reset=1&cid={$pair['srcID']}");
+ $searchRows[$count]['src_email'] = CRM_Utils_Array::value('src_email', $pairInfo);
+ $searchRows[$count]['src_street'] = CRM_Utils_Array::value('src_street', $pairInfo);
+ $searchRows[$count]['src_postcode'] = CRM_Utils_Array::value('src_postcode', $pairInfo);
+ $searchRows[$count]['dst_image'] = $dstTypeImage;
+ $searchRows[$count]['dst'] = CRM_Utils_System::href($pair['dstName'], 'civicrm/contact/view', "reset=1&cid={$pair['dstID']}");
+ $searchRows[$count]['dst_email'] = CRM_Utils_Array::value('dst_email', $pairInfo);
+ $searchRows[$count]['dst_street'] = CRM_Utils_Array::value('dst_street', $pairInfo);
+ $searchRows[$count]['dst_postcode'] = CRM_Utils_Array::value('dst_postcode', $pairInfo);
+ $searchRows[$count]['conflicts'] = CRM_Utils_Array::value('conflicts', $pair);
+ $searchRows[$count]['weight'] = CRM_Utils_Array::value('weight', $pair);
+
+ if (!empty($pair['canMerge'])) {
+ $mergeParams = "reset=1&cid={$pair['srcID']}&oid={$pair['dstID']}&action=update&rgid={$rgid}";
if ($gid) {
$mergeParams .= "&gid={$gid}";
}
- $searchRows[$mainId]['actions'] = '<a class="action-item crm-hover-button" href="' . CRM_Utils_System::url('civicrm/contact/merge', $mergeParams) . '">' . ts('merge') . '</a>';
- $searchRows[$mainId]['actions'] .= "<a class='action-item crm-hover-button crm-notDuplicate' href='#' onClick=\"processDupes( {$main['srcID']}, {$main['dstID']}, 'dupe-nondupe', 'dupe-listing'); return false;\">" . ts('not a duplicate') . "</a>";
+ $searchRows[$count]['actions'] = CRM_Utils_System::href(ts('merge'), 'civicrm/contact/merge', $mergeParams);
+ $searchRows[$count]['actions'] .= " | <a id='notDuplicate' href='#' onClick=\"processDupes( {$pair['srcID']}, {$pair['dstID']}, 'dupe-nondupe', 'dupe-listing'); return false;\">" . ts('not a duplicate') . "</a>";
}
else {
- $searchRows[$mainId]['actions'] = '<em>' . ts('Insufficient access rights - cannot merge') . '</em>';
+ $searchRows[$count]['actions'] = '<em>' . ts('Insufficient access rights - cannot merge') . '</em>';
}
+ $count++;
}
- CRM_Utils_System::setHttpHeader('Content-Type', 'application/json');
- echo CRM_Utils_JSON::encodeDataTableSelector($searchRows, $sEcho, $iTotal, $iFilteredTotal, $selectorElements);
+ header('Content-Type: application/json');
+ echo CRM_Utils_JSON::encodeDataTable($searchRows, $iTotal, $iFilteredTotal, $selectorElements);
CRM_Utils_System::civiExit();
}
CRM_Utils_JSON::output($addressVal);
}
+ static function toggleDedupeSelect() {
+ $rgid = CRM_Utils_Type::escape($_REQUEST['rgid'], 'Integer');
+ $gid = CRM_Utils_Type::escape($_REQUEST['gid'], 'Integer');
+ $pnid = $_REQUEST['pnid'];
+ $isSelected = CRM_Utils_Type::escape($_REQUEST['is_selected'], 'Boolean');
+
+ $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type');
+ $cacheKeyString = "merge $contactType";
+ $cacheKeyString .= $rgid ? "_{$rgid}" : '_0';
+ $cacheKeyString .= $gid ? "_{$gid}" : '_0';
+
+ $params = array(
+ 1 => array($isSelected, 'Boolean'),
+ 3 => array("$cacheKeyString%", 'String') // using % to address rows with conflicts as well
+ );
+
+ //check pnid is_array or integer
+ $whereClause = NULL;
+ if (is_array($pnid) && !CRM_Utils_Array::crmIsEmptyArray($pnid)) {
+ $pnid = implode(', ', $pnid);
+ $pnid = CRM_Utils_Type::escape($pnid, 'String');
+ $whereClause = " id IN ( {$pnid} ) ";
+ }
+ else {
+ $pnid = CRM_Utils_Type::escape($pnid, 'Integer');
+ $whereClause = " id = %2";
+ $params[2] = array($pnid, 'Integer');
+ }
+
+ $sql = "UPDATE civicrm_prevnext_cache SET is_selected = %1 WHERE {$whereClause} AND cacheKey LIKE %3";
+ CRM_Core_DAO::executeQuery($sql, $params);
+
+ CRM_Utils_System::civiExit();
+ }
+
/**
* Retrieve contact relationships.
*/
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, TRUE);
+ $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']);
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));
$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
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) {
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.5 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2014 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+*/
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC (c) 2004-2014
+ * $Id$
+ *
+ */
+class CRM_Contact_Page_DedupeMerge extends CRM_Core_Page{
+
+ const BATCHLIMIT = 2;
+
+ /**
+ * Browse all rule groups
+ *
+ * @return void
+ * @access public
+ */
+ function run() {
+ $runner = self::getRunner();
+ if ($runner) {
+ // Run Everything in the Queue via the Web.
+ $runner->runAllViaWeb();
+ } else {
+ CRM_Core_Session::setStatus(ts('Nothing to merge.'));
+ }
+
+ // parent run
+ return parent::run();
+ }
+
+ static function getRunner() {
+ $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE, 0);
+ $gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this, FALSE, 0);
+ $action = CRM_Utils_Request::retrieve('action', 'String', CRM_Core_DAO::$_nullObject);
+ $mode = CRM_Utils_Request::retrieve('mode', 'String', CRM_Core_DAO::$_nullObject, FALSE, 'safe');
+
+ $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type');
+ $cacheKeyString = "merge {$contactType}";
+ $cacheKeyString .= $rgid ? "_{$rgid}" : '_0';
+ $cacheKeyString .= $gid ? "_{$gid}" : '_0';
+
+ // Setup the Queue
+ $queue = CRM_Queue_Service::singleton()->create(array(
+ 'name' => $cacheKeyString,
+ 'type' => 'Sql',
+ 'reset' => TRUE,
+ ));
+
+ $where = NULL;
+ if ($action == CRM_Core_Action::MAP) {
+ $where = "pn.is_selected = 1";
+ $isSelected = 1;
+ } else {
+ // else merge all (2)
+ $isSelected = 2;
+ }
+
+ $urlQry = "reset=1&action=update&rgid={$rgid}";
+ $urlQry = $gid ? ($urlQry . "&gid={$gid}") : $urlQry;
+
+ $total = CRM_Core_BAO_PrevNextCache::getCount($cacheKeyString, NULL, $where);
+ if ($total <= 0) {
+ // Nothing to do.
+ CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry));
+ }
+
+ // reset merge stats, so we compute new stats
+ CRM_Dedupe_Merger::resetMergeStats($cacheKeyString);
+
+ for ($i = 1; $i <= ceil($total/self::BATCHLIMIT); $i++) {
+ $task = new CRM_Queue_Task(
+ array ('CRM_Contact_Page_DedupeMerge', 'callBatchMerge'),
+ array($rgid, $gid, $mode, TRUE, self::BATCHLIMIT, $isSelected),
+ "Processed " . $i*self::BATCHLIMIT . " pair of duplicates out of " . $total
+ );
+
+ // Add the Task to the Queue
+ $queue->createItem($task);
+ }
+
+ // Setup the Runner
+ $urlQry .= "&context=conflicts";
+ $runner = new CRM_Queue_Runner(array(
+ 'title' => ts('Merging Duplicates..'),
+ 'queue' => $queue,
+ 'errorMode'=> CRM_Queue_Runner::ERROR_ABORT,
+ 'onEndUrl' => CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry, TRUE, NULL, FALSE),
+ ));
+
+ return $runner;
+ }
+
+ /**
+ * Collect Mailchimp data into temporary working table.
+ */
+ static function callBatchMerge(CRM_Queue_TaskContext $ctx, $rgid, $gid = NULL, $mode = 'safe', $autoFlip = TRUE, $batchLimit = 1, $isSelected = 2) {
+ $result = CRM_Dedupe_Merger::batchMerge($rgid, $gid, $mode, $autoFlip, $batchLimit, $isSelected);
+
+ return CRM_Queue_Task::TASK_SUCCESS;
+ }
+}
+
if (isset($cacheKey)) {
$sql .= " AND cacheKey LIKE %4";
- $params[4] = array("{$cacheKey}%", 'String');
+ $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);
}
+ 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_keys($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;
+ }
+
/**
* @param $cacheKey
* @param NULL $join
*
* @return array
*/
- public static function retrieve($cacheKey, $join = NULL, $where = NULL, $offset = 0, $rowCount = 0) {
+ 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 data
+SELECT SQL_CALC_FOUND_ROWS {$selectString}
FROM civicrm_prevnext_cache pn
{$join}
-WHERE cacheKey = %1
+WHERE (pn.cacheKey = %1 OR pn.cacheKey = %2)
";
- $params = array(1 => array($cacheKey, 'String'));
+ $params = array(
+ 1 => array($cacheKey, 'String'),
+ 2 => array("{$cacheKey}_conflicts", 'String')
+ );
if ($where) {
$query .= " AND {$where}";
$dao = CRM_Core_DAO::executeQuery($query, $params);
- $main = array();
+ $main = array();
+ $count = 0;
while ($dao->fetch()) {
if (self::is_serialized($dao->data)) {
- $main[] = unserialize($dao->data);
+ $main[$count] = unserialize($dao->data);
}
else {
- $main[] = $dao->data;
+ $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
*
$query = "
SELECT COUNT(*) FROM civicrm_prevnext_cache pn
{$join}
-WHERE cacheKey $op %1
+WHERE (pn.cacheKey $op %1 OR pn.cacheKey $op %2)
";
if ($where) {
$query .= " AND {$where}";
}
- $params = array(1 => array($cacheKey, 'String'));
+ $params = array(
+ 1 => array($cacheKey, 'String'),
+ 2 => array("{$cacheKey}_conflicts", 'String')
+ );
return (int) CRM_Core_DAO::singleValueQuery($query, $params, TRUE, FALSE);
}
<page_callback>CRM_Contact_Page_AJAX::getDedupes</page_callback>
<access_arguments>merge duplicate contacts</access_arguments>
</item>
+<item>
+ <path>civicrm/contact/dedupemerge</path>
+ <title>Batch Merge Duplicate Contacts</title>
+ <page_callback>CRM_Contact_Page_DedupeMerge</page_callback>
+ <access_arguments>merge duplicate contacts</access_arguments>
+</item>
<item>
<path>civicrm/dedupe/exception</path>
<title>Dedupe Exceptions</title>
<page_callback>CRM_Contact_Page_AJAX::selectUnselectContacts</page_callback>
<access_arguments>access CiviCRM</access_arguments>
</item>
+<item>
+ <path>civicrm/ajax/toggleDedupeSelect</path>
+ <page_callback>CRM_Contact_Page_AJAX::toggleDedupeSelect</page_callback>
+ <access_arguments>merge duplicate contacts</access_arguments>
+</item>
<item>
<path>civicrm/activity/sms/add</path>
<path_arguments>action=add</path_arguments>
*
* @return array|bool
*/
- public static function batchMerge($rgid, $gid = NULL, $mode = 'safe', $autoFlip = TRUE, $redirectForPerformance = FALSE) {
+ public static function batchMerge($rgid, $gid = NULL, $mode = 'safe', $autoFlip = TRUE, $batchLimit = 1, $isSelected = 2) {
$contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type');
$cacheKeyString = "merge {$contactType}";
$cacheKeyString .= $rgid ? "_{$rgid}" : '_0';
$join = "LEFT JOIN civicrm_dedupe_exception de ON ( pn.entity_id1 = de.contact_id1 AND
pn.entity_id2 = de.contact_id2 )";
- $limit = $redirectForPerformance ? 75 : 1;
- $where = "de.id IS NULL LIMIT {$limit}";
+ $where = "de.id IS NULL";
+ if ($isSelected === 0 || $isSelected === 1) {
+ $where .= " AND pn.is_selected = {$isSelected}";
+ }// else consider all dupe pairs
+ $where .= " LIMIT {$batchLimit}";
+
+ $redirectForPerformance = ($batchLimit > 1) ? TRUE : FALSE;
$dupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $where);
- if (empty($dupePairs) && !$redirectForPerformance) {
+ if (empty($dupePairs) && !$redirectForPerformance && $isSelected == 2) {
// If we haven't found any dupes, probably cache is empty.
// Try filling cache and give another try.
CRM_Core_BAO_PrevNextCache::refillCache($rgid, $gid, $cacheKeyString);
return CRM_Dedupe_Merger::merge($dupePairs, $cacheParams, $mode, $autoFlip, $redirectForPerformance);
}
+ static function updateMergeStats($cacheKeyString, $result = array()) {
+ // gather latest stats
+ $merged = count($result['merged']);
+ $skipped = count($result['skipped']);
+
+ if ($merged <= 0 && $skipped <= 0) {
+ return;
+ }
+
+ // get previous stats
+ $previousStats = CRM_Core_BAO_PrevNextCache::retrieve("{$cacheKeyString}_stats");
+ if (!empty($previousStats)) {
+ if ($previousStats[0]['merged']) {
+ $merged = $merged + $previousStats[0]['merged'];
+ }
+ if ($previousStats[0]['skipped']) {
+ $skipped = $skipped + $previousStats[0]['skipped'];
+ }
+ }
+
+ // delete old stats
+ CRM_Dedupe_Merger::resetMergeStats($cacheKeyString);
+
+ // store the updated stats
+ $data = array(
+ 'merged' => $merged,
+ 'skipped' => $skipped,
+ );
+ $data = CRM_Core_DAO::escapeString(serialize($data));
+
+ $values = array();
+ $values[] = " ( 'civicrm_contact', 0, 0, '{$cacheKeyString}_stats', '$data' ) ";
+ CRM_Core_BAO_PrevNextCache::setItem($values);
+ }
+
+ static function resetMergeStats($cacheKeyString) {
+ return CRM_Core_BAO_PrevNextCache::deleteItem(NULL, "{$cacheKeyString}_stats");
+ }
+
+ static function getMergeStats($cacheKeyString) {
+ $stats = CRM_Core_BAO_PrevNextCache::retrieve("{$cacheKeyString}_stats");
+ if (!empty($stats)) {
+ $stats = $stats[0];
+ }
+ return $stats;
+ }
+
+ static function getMergeStatsMsg($cacheKeyString) {
+ $msg = '';
+ $stats = CRM_Dedupe_Merger::getMergeStats($cacheKeyString);
+ if (!empty($stats['merged'])) {
+ $msg = "{$stats['merged']} " . ts(' Contact(s) were merged. ');
+ }
+ if (!empty($stats['skipped'])) {
+ $msg .= $stats['skipped'] . ts(' Contact(s) were skipped.');
+ }
+ return $msg;
+ }
+
/**
* Merge given set of contacts. Performs core operation.
*
$migrationInfo['rows'] = &$rowsElementsAndInfo['rows'];
// go ahead with merge if there is no conflict
- if (!CRM_Dedupe_Merger::skipMerge($mainId, $otherId, $migrationInfo, $mode)) {
+ $conflicts = array();
+ if (!CRM_Dedupe_Merger::skipMerge($mainId, $otherId, $migrationInfo, $mode, $conflicts)) {
CRM_Dedupe_Merger::moveAllBelongings($mainId, $otherId, $migrationInfo);
$resultStats['merged'][] = array('main_id' => $mainId, 'other_id' => $otherId);
}
$resultStats['skipped'][] = array('main_id' => $mainId, 'other_id' => $otherId);
}
- // delete entry from PrevNextCache table so we don't consider the pair next time
- // pair may have been flipped, so make sure we delete using both orders
- CRM_Core_BAO_PrevNextCache::deletePair($mainId, $otherId, $cacheKeyString);
- CRM_Core_BAO_PrevNextCache::deletePair($otherId, $mainId, $cacheKeyString);
+ // store any conflicts
+ if (!empty($conflicts)) {
+ CRM_Core_BAO_PrevNextCache::markConflict($mainId, $otherId, $cacheKeyString, $conflicts);
+ } else {
+ // delete entry from PrevNextCache table so we don't consider the pair next time
+ // pair may have been flipped, so make sure we delete using both orders
+ CRM_Core_BAO_PrevNextCache::deletePair($mainId, $otherId, $cacheKeyString, TRUE);
+ }
CRM_Core_DAO::freeResult();
unset($rowsElementsAndInfo, $migrationInfo);
unset($dupePairs);
}
}
+
+ CRM_Dedupe_Merger::updateMergeStats($cacheKeyString, $resultStats);
return $resultStats;
}
*
* @return bool
*/
- public static function skipMerge($mainId, $otherId, &$migrationInfo, $mode = 'safe') {
- $conflicts = array();
+ public static function skipMerge($mainId, $otherId, &$migrationInfo, $mode = 'safe', &$conflicts = array()) {
+ //$conflicts = array();
$migrationData = array(
'old_migration_info' => $migrationInfo,
'mode' => $mode,
$migrationData['fields_in_conflict'] = $conflicts;
CRM_Utils_Hook::merge('batch', $migrationData, $mainId, $otherId);
$conflicts = $migrationData['fields_in_conflict'];
+ // allow hook to override / manipulate migrationInfo as well
+ $migrationInfo = $migrationData['old_migration_info'];
if (!empty($conflicts)) {
foreach ($conflicts as $key => $val) {
if ($val === NULL and $mode == 'safe') {
- // un-resolved conflicts still present. Lets skip this merge.
+ // un-resolved conflicts still present. Lets skip this merge after saving the conflict / reason.
return TRUE;
}
else {
return $sOutput;
}
+ /**
+ * This function is used to encode data for new dataTable plugin v1.10 and greater
+ * @return string
+ *
+ */
+ static function encodeDataTable($params, $iTotal, $iFilteredTotal, $selectorElements) {
+ $sOutput = '{';
+ $sOutput .= '"recordsTotal": ' . $iTotal . ', ';
+ $sOutput .= '"recordsFiltered": ' . $iFilteredTotal . ', ';
+ $sOutput .= '"data": [ ';
+ foreach ($params as $key => $value) {
+ $addcomma = FALSE;
+ $sOutput .= "{";
+ foreach ($selectorElements as $element) {
+ if ($addcomma) {
+ $sOutput .= ",";
+ }
+ //CRM-7130 --lets addslashes to only double quotes,
+ //since we are using it to quote the field value.
+ //str_replace helps to provide a break for new-line
+ $sOutput .= '"' . $element . '":' . '"' . addcslashes(str_replace(array("\r\n", "\n", "\r"), '<br />', $value[$element]), '"\\') . '"';
+
+ //remove extra spaces and tab character that breaks dataTable CRM-12551
+ $sOutput = preg_replace("/\s+/", " ", $sOutput);
+ $addcomma = TRUE;
+ }
+ $sOutput .= "},";
+ }
+ $sOutput = substr_replace($sOutput, "", -1);
+ $sOutput .= '] }';
+
+ return $sOutput;
+ }
}
*}
{if $action eq 2 || $action eq 16}
<div class="form-item">
- <table class='pagerDisplay'>
+ <div class="crm-accordion-wrapper crm-search_filters-accordion">
+ <div class="crm-accordion-header">
+ {ts}Filter Contacts{/ts}</a>
+ </div><!-- /.crm-accordion-header -->
+ <div class="crm-accordion-body">
+ <table class="no-border form-layout-compressed" id="searchOptions" style="width:100%;">
+ <tr>
+ <td class="crm-contact-form-block-contact1">
+ <label for="contact1">{ts}Contact 1{/ts}</label><br />
+ <input type="text" placeholder="Search Contact1" search-column="2" />
+ </td>
+ <td class="crm-contact-form-block-contact2">
+ <label for="contact2">{ts}Contact 2{/ts}</label><br />
+ <input type="text" placeholder="Search Contact2" search-column="5" />
+ </td>
+ <td class="crm-contact-form-block-email1">
+ <label for="email1">{ts}Email 1{/ts}</label><br />
+ <input type="text" placeholder="Search Email1" search-column="3" />
+ </td>
+ <td class="crm-contact-form-block-email2">
+ <label for="email2">{ts}Email 2{/ts}</label><br />
+ <input type="text" placeholder="Search Email2" search-column="6" />
+ </td>
+ </tr>
+ <tr>
+ <td class="crm-contact-form-block-street-address1">
+ <label for="street-adddress1">{ts}Street Address 1{/ts}</label><br />
+ <input type="text" placeholder="Search Street Address1" search-column="7" />
+ </td>
+ <td class="crm-contact-form-block-street-address2">
+ <label for="street-adddress2">{ts}Street Address 2{/ts}</label><br />
+ <input type="text" placeholder="Search Street Address2" search-column="8" />
+ </td>
+ <td class="crm-contact-form-block-postcode1">
+ <label for="postcode1">{ts}Postcode 1{/ts}</label><br />
+ <input type="text" placeholder="Search Postcode1" search-column="9" />
+ </td>
+ <td class="crm-contact-form-block-postcode2">
+ <label for="postcode2">{ts}Postcode 2{/ts}</label><br />
+ <input type="text" placeholder="Search Postcode2" search-column="10" />
+ </td>
+ </tr>
+ </table>
+ </div><!-- /.crm-accordion-body -->
+ </div><!-- /.crm-accordion-wrapper -->
+ <div>
+ Show / Hide columns:
+ <input type='checkbox' id ='steet-address' class='toggle-vis' data-column-main="7" data-column-dupe="8" >
+ <label for="steet-address">{ts}Street Address{/ts} </label>
+ <input type='checkbox' id ='post-code' class='toggle-vis' data-column-main="9" data-column-dupe="10" >
+ <label for="post-code">{ts}Post Code{/ts} </label>
+ <input type='checkbox' id ='conflicts' class='toggle-vis' data-column-main="11" >
+ <label for="conflicts">{ts}Conflicts{/ts} </label>
+ <input type='checkbox' id ='threshold' class='toggle-vis' data-column-main="12" >
+ <label for="threshold">{ts}Threshold{/ts} </label>
+ </div><br/>
+ <span id="dupePairs_length_selection">
+ <input type='checkbox' id ='crm-dedupe-display-selection' name="display-selection">
+ <label for="display-selection">{ts}Within Selections{/ts} </label>
+ </span>
+ <table id="dupePairs" class="nestedActivitySelector form-layout-compressed" cellspacing="0" width="100%">
<thead>
- <tr class="columnheader"><th id="nosort">{ts}Contact{/ts} 1</th><th id="nosort">{ts}Contact{/ts} 2 ({ts}Duplicate{/ts})</th><th id="nosort">{ts}Threshold{/ts}</th><th id="nosort"> </th></tr>
+ <tr class="columnheader">
+ <th class="crm-dedupe-selection"><input type="checkbox" value="0" name="pnid_all" class="crm-dedupe-select-all"></th>
+ <th class="crm-empty"> </th>
+ <th class="crm-contact">{ts}Contact{/ts} 1</th>
+ <th class="crm-empty"> </th>
+ <th class="crm-contact-duplicate">{ts}Contact{/ts} 2 ({ts}Duplicate{/ts})</th>
+ <th class="crm-contact">{ts}Email{/ts} 1</th>
+ <th class="crm-contact-duplicate">{ts}Email{/ts} 2 ({ts}Duplicate{/ts})</th>
+ <th class="crm-contact">{ts}Street Address{/ts} 1</th>
+ <th class="crm-contact-duplicate">{ts}Street Address{/ts} 2 ({ts}Duplicate{/ts})</th>
+ <th class="crm-contact">{ts}Postcode{/ts} 1</th>
+ <th class="crm-contact-duplicate">{ts}Postcode{/ts} 2 ({ts}Duplicate{/ts})</th>
+ <th class="crm-contact-conflicts">{ts}Conflicts{/ts}</th>
+ <th class="crm-threshold">{ts}Threshold{/ts}</th>
+ <th class="crm-empty"> </th>
+ </tr>
</thead>
+ <tbody>
+ </tbody>
</table>
- {include file="CRM/common/jsortable.tpl" sourceUrl=$sourceUrl useAjax=1 }
{if $cid}
<table style="width: 45%; float: left; margin: 10px;">
<tr class="columnheader"><th colspan="2">{ts 1=$main_contacts[$cid]}Merge %1 with{/ts}</th></tr>
{if $context eq 'search'}
{crmButton href=$backURL icon="close"}{ts}Done{/ts}{/crmButton}
+{elseif $context eq 'conflicts'}
+ {if $gid}
+ {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&gid=`$gid`&action=map&mode=aggressive" a=1}{/capture}
+ {else}
+ {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&action=map&mode=aggressive" a=1}{/capture}
+ {/if}
+ <a href="{$backURL}" title="{ts}Batch Merge Duplicate Contacts{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the selected duplicates. The operation will run in force merge mode - all selected duplicates will be merged into main contacts even in case of any conflicts. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span>{ts}Force Merge Selected Duplicates{/ts}</span></a>
+
+ {if $gid}
+ {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&gid=`$gid`&mode=aggressive" a=1}{/capture}
+ {else}
+ {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&mode=aggressive" a=1}{/capture}
+ {/if}
+ <a href="{$backURL}" title="{ts}Batch Merge Duplicate Contacts{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the listed duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span>{ts}Force Merge All Duplicates{/ts}</span></a>
+
+ {if $gid}
+ {capture assign=backURL}{crmURL p="civicrm/contact/dedupefind" q="reset=1&action=update&rgid=`$rgid`&gid=`$gid`" a=1}{/capture}
+ {else}
+ {capture assign=backURL}{crmURL p="civicrm/contact/dedupefind" q="reset=1&action=update&rgid=`$rgid`" a=1}{/capture}
+ {/if}
+ <a href="{$backURL}" title="{ts}List All Duplicates{/ts}" class="button"><span>{ts}List All Duplicates{/ts}</span></a>
{else}
{if $gid}
{capture assign=backURL}{crmURL p="civicrm/contact/dedupefind" q="reset=1&rgid=`$rgid`&gid=`$gid`&action=renew" a=1}{/capture}
</a>
{if $gid}
- {capture assign=backURL}{crmURL p="civicrm/contact/dedupefind" q="reset=1&rgid=`$rgid`&gid=`$gid`&action=map" a=1}{/capture}
+ {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&gid=`$gid`&action=map" a=1}{/capture}
{else}
- {capture assign=backURL}{crmURL p="civicrm/contact/dedupefind" q="reset=1&rgid=`$rgid`&action=map" a=1}{/capture}
+ {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&action=map" a=1}{/capture}
{/if}
- <a href="{$backURL}" title="{ts}Batch Merge Duplicate Contacts{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the listed duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button">
- <span><div class="icon ui-icon-script"></div> {ts}Batch Merge Duplicates{/ts}</span>
- </a>
+ <a href="{$backURL}" title="{ts}Batch Merge Duplicate Contacts{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the selected duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><div class="icon ui-icon-script"></div>{ts}Batch Merge Selected Duplicates{/ts}</span></a>
+
+ {if $gid}
+ {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&gid=`$gid`" a=1}{/capture}
+ {else}
+ {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`" a=1}{/capture}
+ {/if}
+ <a href="{$backURL}" title="{ts}Batch Merge Duplicate Contacts{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the listed duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><div class="icon ui-icon-script"></div>{ts}Batch Merge All Duplicates{/ts}</span></a>
{capture assign=backURL}{crmURL p="civicrm/contact/deduperules" q="reset=1" a=1}{/capture}
<a href="{$backURL}" class="button crm-button-type-cancel">
{include file='CRM/common/dedupe.tpl'}
{literal}
<script type="text/javascript">
-var oTable = null;
+CRM.$(function($) {
+ var sourceUrl = {/literal}'{$sourceUrl}'{literal};
+ var context = {/literal}'{$context}'{literal};
+ $('#dupePairs').DataTable({
+ //"scrollX": true, // doesn't work with hover popup for for icons
+ "lengthMenu": [[10, 25, 50, 100, 1000, 2000, -1], [10, 25, 50, 100, 1000, 2000, "All"]],
+ "dom": 'flrtip',
+ "processing": true,
+ "serverSide": true,
+ "ajax": sourceUrl,
+ "columns" : [
+ {data: "is_selected_input"},
+ {data: "src_image"},
+ {data: "src"},
+ {data: "dst_image"},
+ {data: "dst"},
+ {data: "src_email"},
+ {data: "dst_email"},
+ {data: "src_street"},
+ {data: "dst_street"},
+ {data: "src_postcode"},
+ {data: "dst_postcode"},
+ {data: "conflicts"},
+ {data: "weight"},
+ {data: "actions"},
+ ],
+ "order": [], // removes default order by for column 1 (checkbox)
+ "columnDefs": [
+ {
+ "targets": [0, 1, 3, 13],
+ "orderable": false
+ },
+ {
+ "targets": [7, 8, 9, 10, 11, 12],
+ "visible": false
+ }
+ ],
+ rowCallback: function (row, data) {
+ // Set the checked state of the checkbox in the table
+ $('input.crm-dedupe-select', row).prop('checked', data.is_selected == 1);
+ if (data.is_selected == 1) {
+ $(row).toggleClass('crm-row-selected');
+ }
+ // for action column at the last, set nowrap
+ $('td:last', row).attr('nowrap','nowrap');
+ }
+ });
+
+ // redraw datatable if searching within selected records
+ $('#crm-dedupe-display-selection').on('click', function(){
+ reloadUrl = sourceUrl;
+ if($(this).prop('checked')){
+ reloadUrl = sourceUrl+'&selected=1';
+ }
+ $('#dupePairs').DataTable().ajax.url(reloadUrl).draw();
+ });
+
+ $('#dupePairs_length_selection').appendTo('#dupePairs_length');
+
+ // apply selected class on click of a row
+ $('#dupePairs tbody').on('click', 'tr', function() {
+ $(this).toggleClass('crm-row-selected');
+ $('input.crm-dedupe-select', this).prop('checked', $(this).hasClass('crm-row-selected'));
+ var ele = $('input.crm-dedupe-select', this);
+ toggleDedupeSelect(ele, 0);
+ });
+
+ $('#dupePairs thead tr .crm-dedupe-selection').on('click', function() {
+ var checked = $('.crm-dedupe-select-all').prop('checked');
+ if (checked) {
+ $("#dupePairs tbody tr input[type='checkbox']").prop('checked', true);
+ $("#dupePairs tbody tr").addClass('crm-row-selected');
+ }
+ else{
+ $("#dupePairs tbody tr input[type='checkbox']").prop('checked', false);
+ $("#dupePairs tbody tr").removeClass('crm-row-selected');
+ }
+ var ele = $('#dupePairs tbody tr');
+ toggleDedupeSelect(ele, 1);
+ });
+
+ // inline search boxes placed in tfoot
+ $('#dupePairsColFilters thead th').each( function () {
+ var title = $('#dupePairs thead th').eq($(this).index()).text();
+ if (title.length > 1) {
+ $(this).html( '<input type="text" placeholder="Search '+title+'" />' );
+ }
+ });
+
+ // apply dataTable
+ var table = $('#dupePairs').DataTable();
+
+ // apply the search
+ $('#searchOptions input').on( 'keyup change', function () {
+ table
+ .column($(this).attr('search-column'))
+ .search(this.value)
+ .draw();
+ });
+
+ // show / hide columns
+ $('input.toggle-vis').on('click', function (e) {
+ var column = table.column( $(this).attr('data-column-main') );
+ column.visible( ! column.visible() );
+
+ if ($(this).attr('data-column-dupe')) {
+ column = table.column( $(this).attr('data-column-dupe') );
+ column.visible( ! column.visible() );
+ }
+ });
+
+ if(context == 'conflicts') {
+ $('#conflicts').attr('checked', true);
+ var column = table.column( $('#conflicts').attr('data-column-main') );
+ column.visible( ! column.visible() );
+ }
+});
+
+function toggleDedupeSelect(element, isMultiple) {
+ if (!isMultiple) {
+ var is_selected = CRM.$(element).prop('checked') ? 1: 0;
+ var id = CRM.$(element).prop('name').substr(5);
+ }
+ else {
+ var id = [];
+ CRM.$(element).each(function() {
+ var sth = CRM.$('input.crm-dedupe-select', this);
+ id.push(CRM.$(sth).prop('name').substr(5));
+ });
+ var is_selected = CRM.$('.crm-dedupe-select-all').prop('checked') ? 1 : 0;
+ }
+
+ var dataUrl = {/literal}"{crmURL p='civicrm/ajax/toggleDedupeSelect' h=0 q='snippet=4'}"{literal};
+ var rgid = {/literal}"{$rgid}"{literal};
+ var gid = {/literal}"{$gid}"{literal};
+
+ rgid = rgid.length > 0 ? rgid : 0;
+ gid = gid.length > 0 ? gid : 0;
+
+ CRM.$.post(dataUrl, {pnid: id, rgid: rgid, gid: gid, is_selected: is_selected}, function (data) {
+ // nothing to do for now
+ }, 'json');
+}
</script>
{/literal}
--- /dev/null
+{*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.5 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2014 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+*}