From aa94d40ad0ef4325c44b39df8bbb5e6b1dfd73e8 Mon Sep 17 00:00:00 2001 From: ATM john Date: Sun, 6 Jun 2021 21:12:03 +0200 Subject: [PATCH] WIP - create validation method for common object --- htdocs/core/class/commonobject.class.php | 179 ++++++++++++----------- htdocs/core/class/validate.class.php | 114 ++++++++++++++- htdocs/langs/en_US/validate.lang | 9 +- 3 files changed, 215 insertions(+), 87 deletions(-) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 12f88fc0a58..ae5f0d33216 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -7286,11 +7286,12 @@ abstract class CommonObject } /** - * Return validation test for a field + * Return validation test result for a field * - * @param array $val Array of properties of field to show - * @param string $key Key of attribute - * @return int >0 if OK, <0 if KO , 0 no test available. + * @param array $val Array of properties of field to show + * @param string $fieldKey Key of attribute + * @param string $fieldValue value of attribute + * @return bool return false if fail true on success, see $this->error for error message */ public function validateField($val, $fieldKey, $fieldValue) { @@ -7301,7 +7302,7 @@ abstract class CommonObject // TODO : ask @eldy to know if need to use another error field to separate error msg $this->error = ''; // error will be use for form error display so must be clear before - if(!isset($val[$fieldKey])){ + if (!isset($val[$fieldKey])) { return false; } @@ -7310,13 +7311,13 @@ abstract class CommonObject $type = $val[$fieldKey]['type']; $required = false; - if(isset($val[$fieldKey]['notnull']) && $val[$fieldKey]['notnull'] === 1){ + if (isset($val[$fieldKey]['notnull']) && $val[$fieldKey]['notnull'] === 1) { // 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). $required = true; } $maxSize = 0; - + $minSize = 0; // // PREPARE Elements @@ -7360,52 +7361,98 @@ abstract class CommonObject // TEST Value // - // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) + // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) or just for reuse $validate = new Validate($this->db, $langs); + // little trick : to perform tests with good performances sort tests by quick to low + + // + // COMMON TESTS + // + + // Required test and empty value if($required && !$validate->isNotEmptyString($fieldValue)){ $this->error = $validate->error; - return -1; + return false; + } + elseif (!$required && !$validate->isNotEmptyString($fieldValue)) { + // if no value sent and the field is not mandatory, no need to perform tests + return true; } - + // MAX Size test if(!empty($maxSize) && !$validate->isMaxLength($fieldValue, $maxSize)){ $this->error = $validate->error; - return -1; + return false; } + // MIN Size test + if(!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)){ + $this->error = $validate->error; + return false; + } + // + // TESTS for TYPE + // if (in_array($type, array('date', 'datetime', 'timestamp'))) { - if(!$validate->isTimestamp($fieldValue)){ + if (!$validate->isTimestamp($fieldValue)) { $this->error = $validate->error; - return -1; - } + return false; + } else { return true; } } elseif ($type == 'duration') { - // int - } elseif (in_array($type, array('double', 'real', 'price'))) { + if(!$validate->isDuration($fieldValue)){ + $this->error = $validate->error; + return false; + } else { return true; } + } + elseif (in_array($type, array('double', 'real', 'price'))) + { // is numeric - } elseif ($type == 'boolean') { - // is bool - } elseif ($type == 'mail') { + if(!$validate->isDuration($fieldValue)){ + $this->error = $validate->error; + return false; + } else { return true; } + } + elseif ($type == 'boolean') + { + if(!$validate->isBool($fieldValue)){ + $this->error = $validate->error; + return false; + } else { return true; } + } + elseif ($type == 'mail') + { if(!$validate->isEmail($fieldValue)){ $this->error = $validate->error; - return -1; + return false; } - } elseif ($type == 'url') { + } + elseif ($type == 'url') + { if(!$validate->isUrl($fieldValue)){ $this->error = $validate->error; - return -1; - } - } elseif ($type == 'phone') { - - } elseif ($type == 'select' || $type == 'radio') { - // isset in list - if(!isset($param['options'][$fieldValue])){ - - } - } elseif ($type == 'sellist' || $type == 'chkbxlst') { + return false; + } else { return true; } + } + elseif ($type == 'phone') + { + if (!$validate->isPhone($fieldValue)) { + $this->error = $validate->error; + return false; + } else { return true; } + } + elseif ($type == 'select' || $type == 'radio') + { + if (!isset($param['options'][$fieldValue])) { + $this->error = $langs->trans('RequireValidValue'); + return false; + } else { return true; } + } + elseif ($type == 'sellist' || $type == 'chkbxlst') + { $param_list = array_keys($param['options']); $InfoFieldList = explode(":", $param_list[0]); $value_arr = explode(',', $fieldValue); @@ -7416,63 +7463,25 @@ abstract class CommonObject $selectkey = $InfoFieldList[2]; } - // TODO tester toute les valeur du tableau séparement - - $sql = 'SELECT '.$selectkey; - $sql .= ' FROM '.MAIN_DB_PREFIX.$InfoFieldList[0]; - if ($selectkey == 'rowid' && empty($value)) { - $sql .= " WHERE ".$selectkey."=0"; - } else { - $sql .= " WHERE ".$selectkey." IN ('".implode(',',$value_arr)."')"; - } - - dol_syslog(get_class($this).':validateField:$type=sellist', LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) { - $num = $this->db->num_rows($resql); - if (empty($num)) { - // error value not found - $this->error = 'error msg'; - return false; - } else { - return true; - } - - } else { - dol_syslog(get_class($this).'::validateField error '.$this->db->lasterror(), LOG_WARNING); + if(!isInDb($value_arr, $InfoFieldList[0], $selectkey)){ + $this->error = $validate->error; return false; - } - } elseif ($type == 'link') { - - // only if something to display (perf) - if (!empty($fieldValue)) { - $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath' - $InfoFieldList = explode(":", $param_list[0]); - $classname = $InfoFieldList[0]; - $classpath = $InfoFieldList[1]; - if (!empty($classpath)) { - dol_include_once($InfoFieldList[1]); - if ($classname && class_exists($classname)) { - $object = new $classname($this->db); - if($object->fetch($fieldValue)>0){ - return true; - } - $this->error = 'class not found for validation'; - } else { - $this->error = 'Error bad setup of extrafield'; - } - return false; - } else { - $this->error = 'Error bad setup of extrafield'; - return false; - } - } - else { - // TODO vérifier si requis - } + } else { return true; } + } + elseif ($type == 'link') + { + $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath' + $InfoFieldList = explode(":", $param_list[0]); + $classname = $InfoFieldList[0]; + $classpath = $InfoFieldList[1]; + if(!$validate->isFetchable($fieldValue, $classname, $classpath)){ + $this->error = $validate->error; + return false; + } else { return true; } } - return 0; + // if no test failled all is ok + return true; } /** diff --git a/htdocs/core/class/validate.class.php b/htdocs/core/class/validate.class.php index fa02018118b..ac01ce76531 100644 --- a/htdocs/core/class/validate.class.php +++ b/htdocs/core/class/validate.class.php @@ -122,7 +122,22 @@ class Validate public function isTimestamp($stamp) { if (!is_numeric($stamp) && (int)$stamp == $stamp) { - $this->error = $this->outputLang->trans('RequireValideDate'); + $this->error = $this->outputLang->trans('RequireValidDate'); + return false; + } + return true; + } + + /** + * Check for phone validity + * + * @param string $phone Phone string to validate + * @return boolean Validity is ok or not + */ + public function isPhone($phone) + { + if (!preg_match('/^[+0-9. ()-]*$/ui', $phone)) { + $this->error = $this->outputLang->trans('RequireValidPhone'); return false; } return true; @@ -191,4 +206,101 @@ class Validate return true; } + /** + * Check Duration validity + * + * @param string $duration to validate + * @return boolean Validity is ok or not + */ + public function isDuration($duration) + { + if (!is_int($duration) && $duration >= 0) { + $this->error = $this->outputLang->trans('RequireValidDuration'); + return false; + } + return true; + } + + /** + * Check for boolean validity + * + * @param boolean $bool Boolean to validate + * @return boolean Validity is ok or not + */ + public function isBool($bool) + { + if(!(is_null($bool) || is_bool($bool) || preg_match('/^[0|1]{1}$/ui', $bool))){ + $this->error = $this->outputLang->trans('RequireValidBool'); + return false; + } + return true; + } + + /** + * Check for all values in db + * + * @param array $values Boolean to validate + * @param string $table the db table name without MAIN_DB_PREFIX + * @param string $col the target col + * @return boolean Validity is ok or not + * @throws Exception + */ + public function isInDb($values, $table, $col) + { + if (!is_array($values)) { + $value_arr = array($values); + } else { + $value_arr = $values; + } + + if (!count($value_arr)) { + $this->error = $this->outputLang->trans('RequireValue'); + return false; + } + + foreach ($value_arr as $val){ + $val = $this->db->escape($val); + $sql = 'SELECT ' . $col . ' FROM ' . MAIN_DB_PREFIX . $table . " WHERE " . $col ." = '" . $val . "'"; // nore quick than count(*) to check existing of a row + $resql = $this->db->getRow($sql); + if ($resql) { + continue; + } else { + $this->error = $this->outputLang->trans('RequireValidExistingElement'); + return false; + } + } + + return true; + } + + /** + * Check for all values in db + * + * @param array $values Boolean to validate + * @param string $classname the class name + * @param string $classpath the class path + * @return boolean Validity is ok or not + * @throws Exception + */ + public function isFetchable($values, $classname, $classpath) + { + if (!empty($classpath)) { + if (dol_include_once($classpath)) { + if ($classname && class_exists($classname)) { + /** @var CommonObject $object */ + $object = new $classname($this->db); + + if (!is_callable(array($object, 'fetch')) || !is_callable(array($object, 'isExistingObject'))) { + $this->error = $this->outputLang->trans('BadSetupOfFieldFetchNotCallable'); + return false; + } + + if (!empty($object->table_element) && $object->isExistingObject($object->table_element, $values)) { + return true; + } else { $this->error = $this->outputLang->trans('RequireValidExistingElement'); } + } else { $this->error = $this->outputLang->trans('BadSetupOfFieldClassNotFoundForValidation'); } + } else { $this->error = $this->outputLang->trans('BadSetupOfFieldFileNotFound'); } + } else { $this->error = $this->outputLang->trans('BadSetupOfField'); } + return false; + } } diff --git a/htdocs/langs/en_US/validate.lang b/htdocs/langs/en_US/validate.lang index 1464805b853..bd25b6c0c74 100644 --- a/htdocs/langs/en_US/validate.lang +++ b/htdocs/langs/en_US/validate.lang @@ -8,5 +8,12 @@ RequireValidEmail = Email address is not valid RequireMaxLength = Length must be less than %s chars RequireMinLength = Length must be more than %s char(s) RequireValidUrl = Require valid URL -RequireValideDate = Require a valid date +RequireValidDate = Require a valid date RequireANotEmptyValue = Is required +RequireValidDuration = Require a valid duration +RequireValidExistingElement = Require an existing value +RequireValidBool = Require a valid boolean +BadSetupOfField = Error bad setup of field +BadSetupOfFieldClassNotFoundForValidation = Error bad setup of field : Class not found for validation +BadSetupOfFieldFileNotFound = Error bad setup of field : File not found for inclusion +BadSetupOfFieldFetchNotCallable = Error bad setup of field : Fetch not callable on class