From 165ebc0db06b92149cb141fe4e32f443149b3803 Mon Sep 17 00:00:00 2001 From: Prasad <prasad@vtiger.com> Date: Fri, 3 May 2024 23:37:48 +0530 Subject: [PATCH] Fixes #782: Ignore case and accents wheng grouping duplicate records --- include/utils/VtlibUtils.php | 32 +++++++++++++++++++++++++ modules/Vtiger/models/FindDuplicate.php | 8 +++---- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/include/utils/VtlibUtils.php b/include/utils/VtlibUtils.php index 87b4ba224..9f0390e74 100644 --- a/include/utils/VtlibUtils.php +++ b/include/utils/VtlibUtils.php @@ -744,6 +744,38 @@ function str_replace_json($search, $replace, $subject){ return json_decode(str_replace($search, $replace, json_encode($subject)), true); } +/** + * Case-insensitive comparision of string ignore accents. + * @param string $lv - left + * @param string $rv - right + * @return stcasecmp ascii comparision + */ +function strcasecmp_accents($lv, $rv) { + $lvenc = mb_detect_encoding($lv); + $rvenc = mb_detect_encoding($rv); + if ($lvenc != $rvenc) { + if ($lvenc != "ASCII") $lv = iconv($lvenc, "ASCII//TRANSLIT", $lv); + if ($rvenc != "ASCII") $rv = iconv($rvenc, "ASCII//TRANSLIT", $rv); + } + return strcasecmp($lv, $rv); +} + +/** + * Callback function to use based on available environment support. + */ +function strcasecmp_accents_callback() { + // when mb & iconv is available - set the locale and return accents netural comparision + // otherwise return standard strcasecmp + if (function_exists('mb_detect_encoding') && function_exists('iconv')) { + setlocale(LC_CTYPE, "en_US.utf8"); // required to make iconv (UTF-8 to ASCII/TRANSLIT) + $callback = "strcasecmp_accents"; + } else { + $callback = "strcasecmp"; + } + return $callback; +} + + /** * To purify malicious html event attributes * @param <String> $value diff --git a/modules/Vtiger/models/FindDuplicate.php b/modules/Vtiger/models/FindDuplicate.php index a916c72db..892cb538d 100644 --- a/modules/Vtiger/models/FindDuplicate.php +++ b/modules/Vtiger/models/FindDuplicate.php @@ -69,7 +69,7 @@ class Vtiger_FindDuplicate_Model extends Vtiger_Base_Model { $entries = array(); for($i=0; $i<$rows; $i++) { // row will have value with (index and column names) - $row = $db->query_result_rowdata($result, $i); + $row = $db->raw_query_result_rowdata($result, $i); // retrieve UTF-8 values. // we should discard values with index for comparisions $entries[] = array_filter($row, function($k) { return !is_numeric($k); }, ARRAY_FILTER_USE_KEY); } @@ -91,15 +91,13 @@ class Vtiger_FindDuplicate_Model extends Vtiger_Base_Model { // make copy of current row $slicedArray = array_slice($row, 0); - // prepare for map comparisions - array_walk($temp, 'lower_array'); - array_walk($slicedArray, 'lower_array'); unset($temp["recordid"]); // remove id which will obviously vary. unset($slicedArray["recordid"]); // if there is any value difference between (temp = prev) and (slicedArray = current) // group them separately. - $arrDiff = array_diff($temp, $slicedArray); + $arrDiff = array_udiff($temp, $slicedArray, strcasecmp_accents_callback()); // use case-less accent-less comparision. + if(php7_count($arrDiff) > 0) { $groupCount++; $temp = $slicedArray; -- GitLab