diff --git a/htdocs/api/class/api.class.php b/htdocs/api/class/api.class.php index 7660144f7ca..0247514b2a4 100644 --- a/htdocs/api/class/api.class.php +++ b/htdocs/api/class/api.class.php @@ -290,6 +290,7 @@ class DolibarrApi if (count($tmp) < 3) return ''; $tmpescaped=$tmp[2]; + $regbis = array(); if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) { $tmpescaped = "'".$db->escape($regbis[1])."'"; diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 66646ee4c4f..b4c52117ea9 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -5815,7 +5815,7 @@ class Form * Generic method to select a component from a combo list. * This is the generic method that will replace all specific existing methods. * - * @param string $objectdesc Objectclassname:Objectclasspath + * @param string $objectdesc ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]] * @param string $htmlname Name of HTML select component * @param int $preselectedvalue Preselected value (ID of element) * @param string $showempty ''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...) @@ -5837,12 +5837,16 @@ class Form $InfoFieldList = explode(":", $objectdesc); $classname=$InfoFieldList[0]; $classpath=$InfoFieldList[1]; + $addcreatebuttonornot=empty($InfoFieldList[2])?0:$InfoFieldList[2]; + $filter=empty($InfoFieldList[3])?'':$InfoFieldList[3]; + if (! empty($classpath)) { dol_include_once($classpath); if ($classname && class_exists($classname)) { $objecttmp = new $classname($this->db); + $objecttmp->filter = $filter; } } if (! is_object($objecttmp)) @@ -5855,12 +5859,12 @@ class Form if ($prefixforautocompletemode == 'societe') $prefixforautocompletemode='company'; $confkeyforautocompletemode=strtoupper($prefixforautocompletemode).'_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT - dol_syslog(get_class($this)."::selectForForms", LOG_DEBUG); + dol_syslog(get_class($this)."::selectForForms object->filter=".$objecttmp->filter, LOG_DEBUG); $out=''; if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->$confkeyforautocompletemode) && ! $forcecombo) { - $objectdesc=$classname.':'.$classpath; + $objectdesc=$classname.':'.$classpath.':'.$addcreatebuttonornot.':'.$filter; $urlforajaxcall = DOL_URL_ROOT.'/core/ajax/selectobject.php'; // No immediate load of all database @@ -5873,18 +5877,46 @@ class Form } else { - // Immediate load of all database + // Immediate load of table record. Note: filter is inside $objecttmp->filter $out.=$this->selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled); } return $out; } + /** + * Function to forge a SQL criteria + * + * @param array $matches Array of found string by regex search. Example: "t.ref:like:'SO-%'" or "t.date_creation:<:'20160101'" or "t.nature:is:NULL" + * @return string Forged criteria. Example: "t.field like 'abc%'" + */ + protected static function forgeCriteriaCallback($matches) + { + global $db; + + //dol_syslog("Convert matches ".$matches[1]); + if (empty($matches[1])) return ''; + $tmp=explode(':', $matches[1]); + if (count($tmp) < 3) return ''; + + $tmpescaped=$tmp[2]; + $regbis = array(); + if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) + { + $tmpescaped = "'".$db->escape($regbis[1])."'"; + } + else + { + $tmpescaped = $db->escape($tmpescaped); + } + return $db->escape($tmp[0]).' '.strtoupper($db->escape($tmp[1]))." ".$tmpescaped; + } + /** * Output html form to select an object. * Note, this function is called by selectForForms or by ajax selectobject.php * - * @param Object $objecttmp Object + * @param Object $objecttmp Object to knwo the table to scan for combo. * @param string $htmlname Name of HTML select component * @param int $preselectedvalue Preselected value (ID of element) * @param string $showempty ''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...) @@ -5902,6 +5934,8 @@ class Form { global $conf, $langs, $user; + //print "$objecttmp->filter, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled"; + $prefixforautocompletemode=$objecttmp->element; if ($prefixforautocompletemode == 'societe') $prefixforautocompletemode='company'; $confkeyforautocompletemode=strtoupper($prefixforautocompletemode).'_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT @@ -5925,19 +5959,28 @@ class Form // Search data $sql = "SELECT t.rowid, ".$fieldstoshow." FROM ".MAIN_DB_PREFIX .$objecttmp->table_element." as t"; if ($objecttmp->ismultientitymanaged == 2) - if (!$user->rights->societe->client->voir && !$user->societe_id) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + if (!$user->rights->societe->client->voir && !$user->socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql.= " WHERE 1=1"; if(! empty($objecttmp->ismultientitymanaged)) $sql.= " AND t.entity IN (".getEntity($objecttmp->table_element).")"; - if ($objecttmp->ismultientitymanaged == 1 && ! empty($user->societe_id)) - { - if ($objecttmp->element == 'societe') $sql.= " AND t.rowid = ".$user->societe_id; - else $sql.= " AND t.fk_soc = ".$user->societe_id; + if ($objecttmp->ismultientitymanaged == 1 && ! empty($user->socid)) { + if ($objecttmp->element == 'societe') $sql.= " AND t.rowid = ".$user->socid; + else $sql.= " AND t.fk_soc = ".$user->socid; } if ($searchkey != '') $sql.=natural_search(explode(',', $fieldstoshow), $searchkey); - if ($objecttmp->ismultientitymanaged == 2) - if (!$user->rights->societe->client->voir && !$user->societe_id) $sql.= " AND t.rowid = sc.fk_soc AND sc.fk_user = " .$user->id; + if ($objecttmp->ismultientitymanaged == 2) { + if (!$user->rights->societe->client->voir && !$user->socid) $sql.= " AND t.rowid = sc.fk_soc AND sc.fk_user = " .$user->id; + } + if ($objecttmp->filter) { // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')" + /*if (! DolibarrApi::_checkFilters($objecttmp->filter)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$objecttmp->filter); + }*/ + $regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql.=" AND (".preg_replace_callback('/'.$regexstring.'/', 'Form::forgeCriteriaCallback', $objecttmp->filter).")"; + } $sql.=$this->db->order($fieldstoshow, "ASC"); //$sql.=$this->db->plimit($limit, 0); + //print $sql; // Build output string $resql=$this->db->query($sql); diff --git a/htdocs/modulebuilder/template/class/myobject.class.php b/htdocs/modulebuilder/template/class/myobject.class.php index 0111bfa142e..1a6fa3915ae 100644 --- a/htdocs/modulebuilder/template/class/myobject.class.php +++ b/htdocs/modulebuilder/template/class/myobject.class.php @@ -64,7 +64,8 @@ class MyObject extends CommonObject /** - * 'type' if the field format ('integer', 'integer:Class:pathtoclass[:AddCreateButtonOrNot[:Filter]]', 'varchar(x)', 'double(24,8)', 'text', 'html', 'datetime', 'timestamp', 'float') + * 'type' if the field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'varchar(x)', 'double(24,8)', 'text', 'html', 'datetime', 'timestamp', 'float') + * Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)" * 'label' the translation key. * 'enabled' is a condition when the field must be managed. * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). Using a negative value means field is not shown by default on list but can be selected for viewing) diff --git a/htdocs/mrp/class/mo.class.php b/htdocs/mrp/class/mo.class.php index 18c8e1fa5dc..384893269ce 100644 --- a/htdocs/mrp/class/mo.class.php +++ b/htdocs/mrp/class/mo.class.php @@ -107,7 +107,7 @@ class Mo extends CommonObject 'fk_product' => array('type'=>'integer:Product:product/class/product.class.php:1', 'label'=>'Product', 'enabled'=>1, 'visible'=>1, 'position'=>35, 'notnull'=>1, 'index'=>1, 'comment'=>"Product to produce",), 'date_start_planned' => array('type'=>'datetime', 'label'=>'DateStartPlannedMo', 'enabled'=>1, 'visible'=>1, 'position'=>55, 'notnull'=>-1, 'index'=>1, 'help'=>'KeepEmptyForAsap'), 'date_end_planned' => array('type'=>'datetime', 'label'=>'DateEndPlannedMo', 'enabled'=>1, 'visible'=>1, 'position'=>56, 'notnull'=>-1, 'index'=>1,), - 'fk_bom' => array('type'=>'integer:Bom:bom/class/bom.class.php:0', 'filter'=>'active=1', 'label'=>'BOM', 'enabled'=>1, 'visible'=>1, 'position'=>33, 'notnull'=>-1, 'index'=>1, 'comment'=>"Original BOM",), + 'fk_bom' => array('type'=>'integer:Bom:bom/class/bom.class.php:0:t.status=1', 'filter'=>'active=1', 'label'=>'BOM', 'enabled'=>1, 'visible'=>1, 'position'=>33, 'notnull'=>-1, 'index'=>1, 'comment'=>"Original BOM",), 'fk_project' => array('type'=>'integer:Project:projet/class/project.class.php:1', 'label'=>'Project', 'enabled'=>1, 'visible'=>-1, 'position'=>52, 'notnull'=>-1, 'index'=>1,), 'status' => array('type'=>'integer', 'label'=>'Status', 'enabled'=>1, 'visible'=>4, 'position'=>1000, 'default'=>0, 'notnull'=>1, 'index'=>1, 'arrayofkeyval'=>array('0'=>'Brouillon', '1'=>'Validated', '2'=>'InProgress', '3'=>'Done', '-1'=>'Canceled')), );