Fix tool for generic to sql filters. Must not be saved into api files.

This commit is contained in:
Laurent Destailleur 2022-01-01 21:46:16 +01:00
parent 7fd601a29d
commit b41fdb6ec9
5 changed files with 105 additions and 89 deletions

View File

@ -302,49 +302,13 @@ class DolibarrApi
{
// phpcs:enable
//$regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)';
//$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
$tmp = trim($sqlfilters);
$i = 0; $nb = strlen($tmp);
if ($nb > 0 && $tmp[0] != '(') {
$error = "Bad sqlfilters (first and last characters must be open and closing parenthesis) = ".$sqlfilters;
dol_syslog($error, LOG_WARNING);
return false;
}
$counter = 0;
while ($i < $nb) {
if ($tmp[$i] == '(') {
$counter++;
}
if ($tmp[$i] == ')') {
$counter--;
// TODO: After a closing ), only a " or " or " and " or end of string is allowed.
}
if ($counter < 0) {
$error = "Bad sqlfilters (too many closing parenthesis) = ".$sqlfilters;
dol_syslog($error, LOG_WARNING);
return false;
}
$i++;
}
if ($counter > 0) {
$error = "Bad sqlfilters (too many opening parenthesis) = ".$sqlfilters;
dol_syslog($error, LOG_WARNING);
return false;
}
return true;
return dolCheckFilters($sqlfilters, $error);
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
/**
* Function to forge a SQL criteria
* Function to forge a SQL criteria from a Generic filter string
*
* @param array $matches Array of found string by regex search.
* Each entry is 1 and only 1 criteria.
@ -353,51 +317,6 @@ class DolibarrApi
*/
protected static function _forge_criteria_callback($matches)
{
// phpcs:enable
global $db;
//dol_syslog("Convert matches ".$matches[1]);
if (empty($matches[1])) {
return '';
}
$tmp = explode(':', $matches[1], 3);
if (count($tmp) < 3) {
return '';
}
// Sanitize operand
$operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
// Sanitize operator
$operator = strtoupper(preg_replace('/[^a-z<>=]/i', '', trim($tmp[1])));
// Only some operators are allowed.
if (! in_array($operator, array('LIKE', 'ULIKE', '<', '>', '<=', '>=', '=', '<>', 'IS', 'ISNOT', 'IN'))) {
return '';
}
if ($operator == 'ISNOT') {
$operator = 'IS NOT';
}
// Sanitize value
$tmpescaped = trim($tmp[2]);
$regbis = array();
if ($operator == 'IN') {
$tmpescaped = "(".$db->sanitize($tmpescaped, 1).")";
} elseif (in_array($operator, array('<', '>', '<=', '>=', '=', '<>'))) {
if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) { // If 'YYYY-MM-DD HH:MM:SS+X'
$tmpescaped = "'".$db->escape($regbis[1])."'";
} else {
$tmpescaped = ((float) $tmpescaped);
}
} else {
if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
$tmpescaped = "'".$db->escape($regbis[1])."'";
} else {
$tmpescaped = "'".$db->escape($tmpescaped)."'";
}
}
return $db->escape($operand).' '.$db->escape($operator)." ".$tmpescaped;
return dolForgeCriteriaCallback($matches);
}
}

View File

@ -9666,7 +9666,12 @@ class Form
$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
$ret .= '</div>';
$ret .= '<input type="hidden" name="search_component_params_hidden" class="search_component_params_hidden" value="'.GETPOST("search_component_params_hidden").'">';
$ret .= "<!-- Syntax of 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";
if (GETPOST('show_search_component_params_hidden', 'int')) {
$ret .= '<input type="hidden" name="show_search_component_params_hidden" value="1">';
}
$ret .= '<input type="'.(GETPOST('show_search_component_params_hidden', 'int') ? 'text' : 'hidden').'" name="search_component_params_hidden" class="search_component_params_hidden marginleftonly" value="'.GETPOST("search_component_params_hidden").'">';
// For compatibility with forms that show themself the search criteria in addition of this component, we output the fields
foreach ($arrayofcriterias as $criterias) {
foreach ($criterias as $criteriafamilykey => $criteriafamilyval) {

View File

@ -349,7 +349,7 @@ foreach ($arrayoftype as $key => $val) {
}
print $form->selectarray('objecttype', $newarrayoftype, $objecttype, 0, 0, 0, '', 1, 0, 0, '', 'minwidth200', 1);
if (empty($conf->use_javascript_ajax)) {
print '<input type="submit" class="button buttongen button-save" name="changeobjecttype" value="'.$langs->trans("Refresh").'">';
print '<input type="submit" class="button buttongen button-save nomargintop" name="changeobjecttype" value="'.$langs->trans("Refresh").'">';
} else {
print '<script type="text/javascript">
jQuery(document).ready(function() {
@ -456,7 +456,7 @@ if ($mode == 'graph') {
print '</div>';
}
print '<div class="divadvancedsearchfield">';
print '<input type="submit" class="button buttongen button-save" value="'.$langs->trans("Refresh").'">';
print '<input type="submit" class="button buttongen button-save nomargintop" value="'.$langs->trans("Refresh").'">';
print '</div>';
print '</div>';
print '</form>';

View File

@ -10597,3 +10597,90 @@ function jsonOrUnserialize($stringtodecode)
return $result;
}
/**
* Return if a $sqlfilters parameter is valid and will pass the preg_replace_callback() to replace Generic filter string with SQL filter string
* Example of usage:
* if ($sqlfilters) {
* $errormessage = '';
* if (dolCheckFilters($sqlfilters, $errormessage)) {
* $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
* $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'dolForgeCriteriaCallback', $sqlfilters).")";
* }
* }
*
* @param string $sqlfilters sqlfilter string
* @param string $error Error message
* @return boolean True if valid, False if not valid ($error is filled with the reason in such a case)
*/
function dolCheckFilters($sqlfilters, &$error = '')
{
//$regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)';
//$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
$tmp = $sqlfilters;
$i = 0; $nb = strlen($tmp);
$counter = 0;
while ($i < $nb) {
if ($tmp[$i] == '(') {
$counter++;
}
if ($tmp[$i] == ')') {
$counter--;
}
if ($counter < 0) {
$error = "Bad sqlfilters=".$sqlfilters;
dol_syslog($error, LOG_WARNING);
return false;
}
$i++;
}
return true;
}
/**
* Function to forge a SQL criteria from a Generic filter string.
* Example of usage:
* if ($sqlfilters) {
* $errormessage = '';
* if (dolCheckFilters($sqlfilters, $errormessage)) {
* $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
* $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'dolForgeCriteriaCallback', $sqlfilters).")";
* }
* }
*
* @param array $matches Array of found string by regex search.
* Example: "t.ref:like:'SO-%'" or "t.date_creation:<:'20160101'" or "t.date_creation:<:'2016-01-01 12:30:00'" or "t.nature:is:NULL"
* @return string Forged criteria. Example: "t.field like 'abc%'"
*/
function dolForgeCriteriaCallback($matches)
{
global $db;
//dol_syslog("Convert matches ".$matches[1]);
if (empty($matches[1])) {
return '';
}
$tmp = explode(':', $matches[1], 3);
if (count($tmp) < 3) {
return '';
}
$operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
$operator = strtoupper(preg_replace('/[^a-z<>=]/i', '', trim($tmp[1])));
$tmpescaped = trim($tmp[2]);
$regbis = array();
if ($operator == 'IN') {
$tmpescaped = "(".$db->sanitize($tmpescaped, 1).")";
} elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
$tmpescaped = "'".$db->escape($regbis[1])."'";
} else {
$tmpescaped = $db->sanitize($db->escape($tmpescaped));
}
return $db->escape($operand).' '.$db->escape($operator)." ".$tmpescaped;
}

View File

@ -983,7 +983,7 @@ div.divsearchfield {
padding-top: 2px;
}
.divadvancedsearchfield span.select2.select2-container.select2-container--default {
padding-bottom: 4px;
/* padding-bottom: 4px; */
}
.search_component_params {
/*display: flex; */
@ -5917,9 +5917,14 @@ input.select2-input {
border-right: none;
border-radius: 0 !important;
background: var(--inputbackgroundcolor);
line-height: normal;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice{
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
line-height: 1.4em;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice {
background-color: #ddd;
margin-top: 4px !important;
}
.select2-search__field