2
0
forked from Wavyzz/dolibarr

Debug custom report filter management

This commit is contained in:
Laurent Destailleur
2024-02-16 01:19:53 +01:00
parent 2677cd9ff7
commit 7107b5feb3
6 changed files with 194 additions and 64 deletions

View File

@@ -331,8 +331,8 @@ class DolibarrApi
protected function _checkFilters($sqlfilters, &$error = '') protected function _checkFilters($sqlfilters, &$error = '')
{ {
// phpcs:enable // phpcs:enable
$firstandlastparenthesis = 0;
return dolCheckFilters($sqlfilters, $error); return dolCheckFilters($sqlfilters, $error, $firstandlastparenthesis);
} }
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps

View File

@@ -10699,40 +10699,9 @@ class Form
// Split the criteria on each AND // Split the criteria on each AND
//var_dump($search_component_params_hidden); //var_dump($search_component_params_hidden);
$nbofchars = dol_strlen($search_component_params_hidden); $arrayofandtags = dolForgeExplodeAnd($search_component_params_hidden);
$arrayofandtags = array();
$i = 0;
$s = '';
$countparenthesis = 0;
while ($i < $nbofchars) {
$char = dol_substr($search_component_params_hidden, $i, 1);
if ($char == '(') {
$countparenthesis++;
} elseif ($char == ')') {
$countparenthesis--;
}
if ($countparenthesis == 0) {
$char2 = dol_substr($search_component_params_hidden, $i+1, 1);
$char3 = dol_substr($search_component_params_hidden, $i+2, 1);
if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
// We found a AND
$arrayofandtags[] = trim($s);
$s = '';
$i+=2;
} else {
$s .= $char;
}
} else {
$s .= $char;
}
$i++;
}
if ($s) {
$arrayofandtags[] = trim($s);
}
// $arrayofandtags is now array( '...' , '...', ...)
// Show each AND part // Show each AND part
foreach ($arrayofandtags as $tmpkey => $tmpval) { foreach ($arrayofandtags as $tmpkey => $tmpval) {
$errormessage = ''; $errormessage = '';
@@ -10792,22 +10761,38 @@ class Form
$ret .= '</div>'; $ret .= '</div>';
$ret .= "<!-- Field to enter a generic filter string: t.ref:like:'SO-%', t.date_creation:<:'20160101', t.date_creation:<:'2016-01-01 12:30:00', t.nature:is:NULL, t.field2:isnot:NULL -->\n"; $ret .= "<!-- Field to enter a generic filter string: t.ref:like:'SO-%', t.date_creation:<:'20160101', t.date_creation:<:'2016-01-01 12:30:00', t.nature:is:NULL, t.field2:isnot:NULL -->\n";
$ret .= '<input type="text" placeholder="' . $langs->trans("Search") . '" name="search_component_params_input" class="noborderbottom search_component_input" value="">'; $ret .= '<input type="text" placeholder="' . $langs->trans("Filters") . '" name="search_component_params_input" class="noborderbottom search_component_input" value="">';
$ret .= '</div>'; $ret .= '</div>';
$ret .= '</div>'; $ret .= '</div>';
$ret .= '<script> $ret .= '<script>
jQuery(".tagsearchdelete").click(function() { jQuery(".tagsearchdelete").click(function(e) {
var filterid = $(this).parents().data("ufilterid"); var filterid = $(this).parents().attr("data-ufilterid");
console.log("We click to delete a criteria nb "+filterid); console.log("We click to delete the criteria nb "+filterid);
// TODO Update the search_component_params_hidden with all data-ufilter except the one delete and post page
// Regenerate the search_component_params_hidden with all data-ufilter except the one to delete, and post the page
var newparamstring = \'\';
$(\'.tagsearch\').each(function(index, element) {
console.log(element);
tmpfilterid = $(this).attr("data-ufilterid");
if (tmpfilterid != filterid) {
// We keep this criteria
if (newparamstring == \'\') {
newparamstring = $(this).attr("data-ufilter");
} else {
newparamstring = newparamstring + \' AND \' + $(this).attr("data-ufilter");
}
}
});
console.log(newparamstring);
// We repost the form
$(this).closest(\'form\').submit();
}); });
</script> </script>
'; ';
return $ret; return $ret;
} }

View File

@@ -163,12 +163,16 @@ $extrafields->fetch_name_optionals_label('all'); // We load all extrafields defi
$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_'); $search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
$search_component_params = array(''); $search_component_params = array('');
$search_component_params_hidden = GETPOST('search_component_params_hidden', 'alphanohtml'); $search_component_params_hidden = trim(GETPOST('search_component_params_hidden', 'alphanohtml'));
$search_component_params_input = trim(GETPOST('search_component_params_input', 'alphanohtml'));
//var_dump($search_component_params_hidden);
//var_dump($search_component_params_input);
// For the case we enter a criteria manually, the search_component_params_input will be defined and must be used in priority $arrayofandtagshidden = dolForgeExplodeAnd($search_component_params_hidden);
if (GETPOST('search_component_params_input', 'alphanohtml')) { $arrayofandtagsinput = dolForgeExplodeAnd($search_component_params_input);
$search_component_params_hidden = GETPOST('search_component_params_input', 'alphanohtml');
} $search_component_params_hidden = implode(' AND ', array_merge($arrayofandtagshidden, $arrayofandtagsinput));
//var_dump($search_component_params_hidden);
$MAXUNIQUEVALFORGROUP = 20; $MAXUNIQUEVALFORGROUP = 20;
$MAXMEASURESINBARGRAPH = 20; $MAXMEASURESINBARGRAPH = 20;

View File

@@ -12517,9 +12517,10 @@ function jsonOrUnserialize($stringtodecode)
/** /**
* forgeSQLFromUniversalSearchCriteria * forgeSQLFromUniversalSearchCriteria
* *
* @param string $filter String with universal search string. Must be '(aaa:bbb:...) OR (ccc:ddd:...) ...' with * @param string $filter String with universal search string. Must be '(aaa:bbb:ccc) OR (ddd:eeee:fff) ...' with
* aaa is a field name (with alias or not) and * aaa is a field name (with alias or not) and
* bbb is one of this operator '=', '<', '>', '<=', '>=', '!=', 'in', 'notin', 'like', 'notlike', 'is', 'isnot'. * bbb is one of this operator '=', '<', '>', '<=', '>=', '!=', 'in', 'notin', 'like', 'notlike', 'is', 'isnot'.
* ccc must not contains ( or )
* Example: '((client:=:1) OR ((client:>=:2) AND (client:<=:3))) AND (client:!=:8) AND (nom:like:'a%')' * Example: '((client:=:1) OR ((client:>=:2) AND (client:<=:3))) AND (client:!=:8) AND (nom:like:'a%')'
* @param string $errorstr Error message string * @param string $errorstr Error message string
* @param int $noand 1=Do not add the AND before the condition string. * @param int $noand 1=Do not add the AND before the condition string.
@@ -12535,8 +12536,9 @@ function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand =
} }
$regexstring = '\(([a-zA-Z0-9_\.]+:[<>!=insotlke]+:[^\(\)]+)\)'; // Must be (aaa:bbb:...) with aaa is a field name (with alias or not) and bbb is one of this operator '=', '<', '>', '<=', '>=', '!=', 'in', 'notin', 'like', 'notlike', 'is', 'isnot' $regexstring = '\(([a-zA-Z0-9_\.]+:[<>!=insotlke]+:[^\(\)]+)\)'; // Must be (aaa:bbb:...) with aaa is a field name (with alias or not) and bbb is one of this operator '=', '<', '>', '<=', '>=', '!=', 'in', 'notin', 'like', 'notlike', 'is', 'isnot'
$firstandlastparenthesis = 0;
if (!dolCheckFilters($filter, $errorstr)) { if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
if ($noerror) { if ($noerror) {
return '1 = 2'; return '1 = 2';
} else { } else {
@@ -12560,35 +12562,118 @@ function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand =
return ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).($nopar ? "" : ')'); return ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).($nopar ? "" : ')');
} }
/**
* Explode an universal search string with AND parts
*
* @param string $sqlfilters Universal SQL filter string. Must have been trimmed before.
* @return array Array of AND
*/
function dolForgeExplodeAnd($sqlfilters)
{
$arrayofandtags = array();
$nbofchars = dol_strlen($sqlfilters);
$i = 0;
$s = '';
$countparenthesis = 0;
while ($i < $nbofchars) {
$char = dol_substr($sqlfilters, $i, 1);
if ($char == '(') {
$countparenthesis++;
} elseif ($char == ')') {
$countparenthesis--;
}
if ($countparenthesis == 0) {
$char2 = dol_substr($sqlfilters, $i+1, 1);
$char3 = dol_substr($sqlfilters, $i+2, 1);
if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
// We found a AND
$s = trim($s);
if (!preg_match('/^\(.*\)$/', $s)) {
$s = '('.$s.')';
}
$arrayofandtags[] = $s;
$s = '';
$i+=2;
} else {
$s .= $char;
}
} else {
$s .= $char;
}
$i++;
}
if ($s) {
$s = trim($s);
if (!preg_match('/^\(.*\)$/', $s)) {
$s = '('.$s.')';
}
$arrayofandtags[] = $s;
}
return $arrayofandtags;
}
/** /**
* Return if a $sqlfilters parameter has a valid balance of parenthesis * Return if a $sqlfilters parameter has a valid balance of parenthesis
* *
* @param string $sqlfilters sqlfilter string * @param string $sqlfilters Universal SQL filter string. Must have been trimmed before.
* @param string $error Error message * @param string $error Returned error message
* @return boolean True if valid, False if not valid ($error is filled with the reason in such a case) * @param int $parenthesislevel Returned level of global parenthesis that we can remove/siplify, 0 if error or we cant simplify.
* @return boolean True if valid, False if not valid ($error returned parameter is filled with the reason in such a case)
*/ */
function dolCheckFilters($sqlfilters, &$error = '') function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
{ {
//$regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; //$regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)';
//$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters); //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
$tmp = $sqlfilters; $tmp = $sqlfilters;
$i = 0;
$nb = strlen($tmp); $nb = dol_strlen($tmp);
$counter = 0; $counter = 0;
$parenthesislevel = 0;
$error = '';
$i = 0;
while ($i < $nb) { while ($i < $nb) {
if ($tmp[$i] == '(') { $char = dol_substr($tmp, $i, 1);
if ($char == '(') {
if ($i == $parenthesislevel && $parenthesislevel == $counter) {
// We open a parenthesis and it is the first char
$parenthesislevel++;
}
$counter++; $counter++;
} } elseif ($char == ')') {
if ($tmp[$i] == ')') { $nbcharremaining = ($nb - $i - 1);
if ($nbcharremaining >= $counter) {
$parenthesislevel = min($parenthesislevel, $counter - 1);
}
if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
$parenthesislevel = $counter;
}
$counter--; $counter--;
} }
if ($counter < 0) { if ($counter < 0) {
$error = "Wrond balance of parenthesis in sqlfilters=".$sqlfilters; $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
$parenthesislevel = 0;
dol_syslog($error, LOG_WARNING); dol_syslog($error, LOG_WARNING);
return false; return false;
} }
$i++; $i++;
} }
if ($counter > 0) {
$error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
$parenthesislevel = 0;
dol_syslog($error, LOG_WARNING);
return false;
}
return true; return true;
} }
@@ -12703,7 +12788,7 @@ function dolForgeCriteriaCallback($matches)
*/ */
function getTimelineIcon($actionstatic, &$histo, $key) function getTimelineIcon($actionstatic, &$histo, $key)
{ {
global $conf, $langs; global $langs;
$out = '<!-- timeline icon -->'."\n"; $out = '<!-- timeline icon -->'."\n";
$iconClass = 'fa fa-comments'; $iconClass = 'fa fa-comments';

View File

@@ -383,8 +383,8 @@ class User extends CommonObject
public $fields = array( public $fields = array(
'rowid'=>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'index'=>1, 'position'=>1, 'comment'=>'Id'), 'rowid'=>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'index'=>1, 'position'=>1, 'comment'=>'Id'),
'lastname'=>array('type'=>'varchar(50)', 'label'=>'LastName', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'showoncombobox'=>1, 'index'=>1, 'position'=>20, 'searchall'=>1), 'lastname'=>array('type'=>'varchar(50)', 'label'=>'Lastname', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'showoncombobox'=>1, 'index'=>1, 'position'=>20, 'searchall'=>1),
'firstname'=>array('type'=>'varchar(50)', 'label'=>'FirstName', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'showoncombobox'=>1, 'index'=>1, 'position'=>10, 'searchall'=>1), 'firstname'=>array('type'=>'varchar(50)', 'label'=>'Firstname', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'showoncombobox'=>1, 'index'=>1, 'position'=>10, 'searchall'=>1),
'ref_employee'=>array('type'=>'varchar(50)', 'label'=>'RefEmployee', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'showoncombobox'=>1, 'index'=>1, 'position'=>30, 'searchall'=>1), 'ref_employee'=>array('type'=>'varchar(50)', 'label'=>'RefEmployee', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'showoncombobox'=>1, 'index'=>1, 'position'=>30, 'searchall'=>1),
'national_registration_number'=>array('type'=>'varchar(50)', 'label'=>'NationalRegistrationNumber', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'showoncombobox'=>1, 'index'=>1, 'position'=>40, 'searchall'=>1) 'national_registration_number'=>array('type'=>'varchar(50)', 'label'=>'NationalRegistrationNumber', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'showoncombobox'=>1, 'index'=>1, 'position'=>40, 'searchall'=>1)
); );
@@ -4132,12 +4132,10 @@ class User extends CommonObject
$this->findUserIdByEmailCache[$email] = -1; $this->findUserIdByEmailCache[$email] = -1;
global $conf;
$sql = 'SELECT rowid'; $sql = 'SELECT rowid';
$sql .= ' FROM '.$this->db->prefix().'user'; $sql .= ' FROM '.$this->db->prefix().'user';
if (getDolGlobalString('AGENDA_DISABLE_EXACT_USER_EMAIL_COMPARE_FOR_EXTERNAL_CALENDAR')) { if (getDolGlobalString('AGENDA_DISABLE_EXACT_USER_EMAIL_COMPARE_FOR_EXTERNAL_CALENDAR')) {
$sql .= " WHERE email LIKE '%".$this->db->escape($email)."%'"; $sql .= " WHERE email LIKE '%".$this->db->escape($this->db->escapeforlike($email))."%'";
} else { } else {
$sql .= " WHERE email = '".$this->db->escape($email)."'"; $sql .= " WHERE email = '".$this->db->escape($email)."'";
} }

View File

@@ -176,6 +176,64 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase
} }
/**
* testDolCheckFilters
*
* @return boolean
*/
public function testDolCheckFilters()
{
global $conf, $langs, $db;
// A sql with global parenthesis at level 2
$error = '';
$parenthesislevel = 0;
$sql = '(( ... (a:=:1) .éééé. (b:=:1) ... ))';
$result = dolCheckFilters($sql, $error, $parenthesislevel);
$this->assertEquals(2, $parenthesislevel);
$this->assertTrue($result);
// A sql with global parenthesis at level 2
$error = '';
$parenthesislevel = 0;
$sql = '(((((a:=:1) ... ) .éééé.. (b:=:1) ..) ... ))';
$result = dolCheckFilters($sql, $error, $parenthesislevel);
$this->assertEquals(2, $parenthesislevel);
$this->assertTrue($result);
// A sql with global parenthesis at level 2
$error = '';
$parenthesislevel = 0;
$sql = '((... (((a:=:1) ... ( .éééé.. (b:=:1) ..)))))';
$result = dolCheckFilters($sql, $error, $parenthesislevel);
$this->assertEquals(2, $parenthesislevel);
$this->assertTrue($result);
// A sql with global parenthesis at level 0
$error = '';
$parenthesislevel = 0;
$sql = '(a:=:1) ... (b:=:1) éééé ...';
$result = dolCheckFilters($sql, $error, $parenthesislevel);
$this->assertEquals(0, $parenthesislevel);
$this->assertTrue($result);
// A sql with bad balance
$error = '';
$parenthesislevel = 0;
$sql = '((((a:=:1) ... (b:=:1) éééé ..))';
$result = dolCheckFilters($sql, $error, $parenthesislevel);
$this->assertEquals(0, $parenthesislevel);
$this->assertFalse($result);
// A sql with bad balance
$error = '';
$parenthesislevel = 0;
$sql = '(((a:=:1) ... (b:=:1) éééé ..)))';
$result = dolCheckFilters($sql, $error, $parenthesislevel);
$this->assertEquals(0, $parenthesislevel);
$this->assertFalse($result);
}
/** /**
* testDolForgeCriteriaCallback * testDolForgeCriteriaCallback
* *