Merge pull request #17807 from atm-john/new_validation_method_for_common_object

New : Create validation method for common object
This commit is contained in:
Laurent Destailleur 2021-08-17 19:42:06 +02:00 committed by GitHub
commit 5201d87ba7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 646 additions and 20 deletions

View File

@ -105,6 +105,15 @@ if ($action == 'add' && !empty($permissiontoadd)) {
$error++;
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv($val['label'])), null, 'errors');
}
// Validation of fields values
if ($conf->global->MAIN_FEATURE_LEVEL >= 2 || !empty($conf->global->MAIN_ACTIVATE_VALIDATION_RESULT)) {
if (!$error && !empty($val['validate']) && is_callable(array($object, 'validateField'))) {
if (!$object->validateField($object->fields, $key, $value)) {
$error++;
}
}
}
}
// Fill array 'array_options' with data from add form
@ -204,6 +213,15 @@ if ($action == 'update' && !empty($permissiontoadd)) {
$error++;
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv($val['label'])), null, 'errors');
}
// Validation of fields values
if ($conf->global->MAIN_FEATURE_LEVEL >= 2 || !empty($conf->global->MAIN_ACTIVATE_VALIDATION_RESULT)) {
if (!$error && !empty($val['validate']) && is_callable(array($object, 'validateField'))) {
if (!$object->validateField($object->fields, $key, $value)) {
$error++;
}
}
}
}
// Fill array 'array_options' with data from add form

View File

@ -73,6 +73,11 @@ abstract class CommonObject
*/
public $errors = array();
/**
* @var array To store error results of ->validateField()
*/
public $validateFieldsErrors = array();
/**
* @var string ID to identify managed object
*/
@ -123,14 +128,12 @@ abstract class CommonObject
*/
protected $table_ref_field = '';
/**
* 0=Default, 1=View may be restricted to sales representative only if no permission to see all or to company of external user if external user
* @var integer
/**
* @var integer 0=Default, 1=View may be restricted to sales representative only if no permission to see all or to company of external user if external user
*/
public $restrictiononfksoc = 0;
// Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
/**
@ -6422,6 +6425,16 @@ abstract class CommonObject
$val = $this->fields[$key];
}
// Validation tests and output
$fieldValidationErrorMsg = '';
$validationClass = '';
$fieldValidationErrorMsg = $this->getFieldError($key);
if (!empty($fieldValidationErrorMsg)) {
$validationClass = ' --error'; // the -- is use as class state in css : .--error can't be be defined alone it must be define with another class like .my-class.--error or input.--error
} else {
$validationClass = ' --success'; // the -- is use as class state in css : .--success can't be be defined alone it must be define with another class like .my-class.--success or input.--success
}
$out = '';
$type = '';
$isDependList=0;
@ -6513,6 +6526,11 @@ abstract class CommonObject
}
}
// Add validation state class
if (!empty($validationClass)) {
$morecss.= ' '.$validationClass;
}
if (in_array($type, array('date'))) {
$tmp = explode(',', $size);
$newsize = $tmp[0];
@ -6979,6 +6997,12 @@ abstract class CommonObject
if ($type == 'date') $out.=' (YYYY-MM-DD)';
elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
*/
// Display error message for field
if (!empty($fieldValidationErrorMsg) && function_exists('getFieldErrorIcon')) {
$out .= ' '.getFieldErrorIcon($fieldValidationErrorMsg);
}
return $out;
}
@ -7318,6 +7342,228 @@ abstract class CommonObject
return $out;
}
/**
* clear validation message result for a field
*
* @param string $fieldKey Key of attribute to clear
* @return null
*/
public function clearFieldError($fieldKey)
{
$this->error = '';
unset($this->validateFieldsErrors[$fieldKey]);
}
/**
* set validation error message a field
*
* @param string $fieldKey Key of attribute
* @param string $msg the field error message
* @return null
*/
public function setFieldError($fieldKey, $msg = '')
{
global $langs;
if (empty($msg)) { $msg = $langs->trans("UnknowError"); }
$this->error = $this->validateFieldsErrors[$fieldKey] = $msg;
}
/**
* get field error message
*
* @param string $fieldKey Key of attribute
* @return string
*/
public function getFieldError($fieldKey)
{
if (!empty($this->validateFieldsErrors[$fieldKey])) {
return $this->validateFieldsErrors[$fieldKey];
}
return '';
}
/**
* Return validation test result for a field
*
* @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)
{
global $langs;
if (!class_exists('Validate')) { require_once DOL_DOCUMENT_ROOT . '/core/class/validate.class.php'; }
$this->clearFieldError($fieldKey);
if (!isset($val[$fieldKey])) {
$this->setFieldError($fieldKey, $langs->trans('FieldNotFoundInObject'));
return false;
}
$param = array();
$param['options'] = array();
$type = $val[$fieldKey]['type'];
$required = false;
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
//
// Convert var to be able to share same code than showOutputField of extrafields
if (preg_match('/varchar\((\d+)\)/', $type, $reg)) {
$type = 'varchar'; // convert varchar(xx) int varchar
$maxSize = $reg[1];
} elseif (preg_match('/varchar/', $type)) {
$type = 'varchar'; // convert varchar(xx) int varchar
}
if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
$type = 'select';
}
if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
$type = 'link';
}
if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
$param['options'] = $val['arrayofkeyval'];
}
if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
$type = 'link';
$param['options'] = array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
} elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
$param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
$type = 'sellist';
} elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
$param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
$type = 'sellist';
} elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
$param['options'] = array($reg[1].':'.$reg[2] => 'N');
$type = 'sellist';
}
//
// TEST Value
//
// 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->setFieldError($fieldKey, $validate->error);
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->setFieldError($fieldKey, $validate->error);
return false;
}
// MIN Size test
if (!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)) {
$this->setFieldError($fieldKey, $validate->error);
return false;
}
//
// TESTS for TYPE
//
if (in_array($type, array('date', 'datetime', 'timestamp'))) {
if (!$validate->isTimestamp($fieldValue)) {
$this->setFieldError($fieldKey, $validate->error);
return false;
} else { return true; }
} elseif ($type == 'duration') {
if (!$validate->isDuration($fieldValue)) {
$this->setFieldError($fieldKey, $validate->error);
return false;
} else { return true; }
} elseif (in_array($type, array('double', 'real', 'price'))) {
// is numeric
if (!$validate->isDuration($fieldValue)) {
$this->setFieldError($fieldKey, $validate->error);
return false;
} else { return true; }
} elseif ($type == 'boolean') {
if (!$validate->isBool($fieldValue)) {
$this->setFieldError($fieldKey, $validate->error);
return false;
} else { return true; }
} elseif ($type == 'mail') {
if (!$validate->isEmail($fieldValue)) {
$this->setFieldError($fieldKey, $validate->error);
return false;
}
} elseif ($type == 'url') {
if (!$validate->isUrl($fieldValue)) {
$this->setFieldError($fieldKey, $validate->error);
return false;
} else { return true; }
} elseif ($type == 'phone') {
if (!$validate->isPhone($fieldValue)) {
$this->setFieldError($fieldKey, $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);
$value_arr = array_map(array($this->db, 'escape'), $value_arr);
$selectkey = "rowid";
if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
$selectkey = $InfoFieldList[2];
}
if (!isInDb($value_arr, $InfoFieldList[0], $selectkey)) {
$this->setFieldError($fieldKey, $validate->error);
return false;
} 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->setFieldError($fieldKey, $validate->error);
return false;
} else { return true; }
}
// if no test failled all is ok
return true;
}
/**
* Function to show lines of extrafields with output datas.

View File

@ -0,0 +1,309 @@
<?php
/* Copyright (C) 2021 John BOTELLA <john.botella@atm-consulting.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* \file htdocs/core/class/validate.class.php
* \ingroup core
* \brief File for Utils class
*/
/**
* Class toolbox to validate values
*/
class Validate
{
/**
* @var DoliDb Database handler (result of a new DoliDB)
*/
public $db;
/**
* @var Translate $outputLang
*/
public $outputLang;
/**
* @var string Error string
* @see $errors
*/
public $error;
/**
* Constructor
*
* @param DoliDB $db Database handler
* @param Translate $outputLang output lang for error
* @return null
*/
public function __construct($db, $outputLang = false)
{
global $langs;
if ($outputLang) {
$this->outputLang = $langs;
} else {
$this->outputLang = $outputLang;
}
$outputLang->load('validate');
$this->db = $db;
}
/**
* Use to clear errors msg or other ghost vars
* @return null
*/
protected function clear()
{
$this->error = '';
}
/**
* Use to clear errors msg or other ghost vars
*
* @param string $errMsg your error message
* @return null
*/
protected function setError($errMsg)
{
$this->error = $errMsg;
}
/**
* Check for e-mail validity
*
* @param string $email e-mail address to validate
* @param int $maxLength string max length
* @return boolean Validity is ok or not
*/
public function isEmail($email, $maxLength = false)
{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->error = $this->outputLang->trans('RequireValidEmail');
return false;
}
return true;
}
/**
* Check for price validity
*
* @param string $price Price to validate
* @return boolean Validity is ok or not
*/
public function isPrice($price)
{
if (!preg_match('/^[0-9]{1,10}(\.[0-9]{1,9})?$/ui', $price)) {
$this->error = $this->outputLang->trans('RequireValidValue');
return false;
}
return true;
}
/**
* Check for timestamp validity
*
* @param string|int $stamp timestamp to validate
* @return boolean Validity is ok or not
*/
public function isTimestamp($stamp)
{
if (!is_numeric($stamp) && (int) $stamp == $stamp) {
$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;
}
/**
* Check for string max length validity
*
* @param string $string to validate
* @param int $length max length
* @return boolean Validity is ok or not
*/
public function isMaxLength($string, $length)
{
if (strlen($string) > $length) {
$this->error = $this->outputLang->trans('RequireMaxLength', $length);
return false;
}
return true;
}
/**
* Check for string not empty
*
* @param string $string to validate
* @return boolean Validity is ok or not
*/
public function isNotEmptyString($string)
{
if (!strlen($string)) {
$this->error = $this->outputLang->trans('RequireANotEmptyValue');
return false;
}
return true;
}
/**
* Check for string min length validity
*
* @param string $string to validate
* @param int $length max length
* @return boolean Validity is ok or not
*/
public function isMinLength($string, $length)
{
if (!strlen($string) < $length) {
$this->error = $this->outputLang->trans('RequireMinLength', $length);
return false;
}
return true;
}
/**
* Check url validity
*
* @param string $url to validate
* @return boolean Validity is ok or not
*/
public function isUrl($url)
{
if (!filter_var($url, FILTER_VALIDATE_URL)) {
$this->error = $this->outputLang->trans('RequireValidUrl');
return false;
}
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) {
$sql = 'SELECT ' . $col . ' FROM ' . MAIN_DB_PREFIX . $table . " WHERE " . $col ." = '" . $this->db->escape($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;
}
}

View File

@ -9921,6 +9921,24 @@ function dolGetButtonTitleSeparator($moreClass = "")
return '<span class="button-title-separator '.$moreClass.'" ></span>';
}
/**
* get field error icon
*
* @param string $fieldValidationErrorMsg message to add in tooltip
* @return string html output
*/
function getFieldErrorIcon($fieldValidationErrorMsg)
{
$out = '';
if (!empty($fieldValidationErrorMsg)) {
$out.= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
$out.= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
$out.= '</span>';
}
return $out;
}
/**
* Function dolGetButtonTitle : this kind of buttons are used in title in list
*

View File

@ -176,6 +176,9 @@ function rebuildObjectClass($destdir, $module, $objectname, $newmask, $readdir =
}
$texttoinsert .= "),";
}
if ($val['validate']) {
$texttoinsert .= " 'validate'=>'".$val['validate']."',";
}
if ($val['comment']) {
$texttoinsert .= " 'comment'=>\"".preg_replace('/"/', '', $val['comment'])."\"";
}

View File

@ -143,4 +143,5 @@ AsciiToHtmlConverter=Ascii to HTML converter
AsciiToPdfConverter=Ascii to PDF converter
TableNotEmptyDropCanceled=Table not empty. Drop has been canceled.
ModuleBuilderNotAllowed=The module builder is available but not allowed to your user.
ImportExportProfiles=Import and export profiles
ImportExportProfiles=Import and export profiles
ValidateModBuilderDesc=Put 1 if this field need to be validated with $this->validateField() or 0 if validation required

View File

@ -0,0 +1,19 @@
# Dolibarr language file - Source file is en_US - users
RequireValidValue = Value not valid
RequireAtLeastXString = Requires at least % character(s)
RequireXStringMax = Requires % character(s) max
RequireAtLeastXDigits = Requires at least % digit(s)
RequireXDigitsMax = Requires % digit(s) max
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
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

View File

@ -1303,7 +1303,8 @@ if ($dirins && $action == 'addproperty' && empty($cancel) && !empty($module) &&
'visible'=>GETPOST('propvisible', 'int'), 'enabled'=>GETPOST('propenabled', 'int'),
'position'=>GETPOST('propposition', 'int'), 'notnull'=>GETPOST('propnotnull', 'int'), 'index'=>GETPOST('propindex', 'int'), 'searchall'=>GETPOST('propsearchall', 'int'),
'isameasure'=>GETPOST('propisameasure', 'int'), 'comment'=>GETPOST('propcomment', 'alpha'), 'help'=>GETPOST('prophelp', 'alpha'),
'css'=>GETPOST('propcss', 'aZ09'), 'cssview'=>GETPOST('propcssview', 'aZ09'), 'csslist'=>GETPOST('propcsslist', 'aZ09')
'css'=>GETPOST('propcss', 'aZ09'), 'cssview'=>GETPOST('propcssview', 'aZ09'), 'csslist'=>GETPOST('propcsslist', 'aZ09'),
'validate' => GETPOST('propvalidate', 'int')
);
if (!empty($addfieldentry['arrayofkeyval']) && !is_array($addfieldentry['arrayofkeyval'])) {
@ -2680,6 +2681,7 @@ if ($module == 'initmodule') {
print '<th>'.$langs->trans("KeyForTooltip").'</th>';
print '<th class="center">'.$langs->trans("ShowOnCombobox").'</th>';
//print '<th class="center">'.$langs->trans("Disabled").'</th>';
print '<th>'.$form->textwithpicto($langs->trans("Validate"), $langs->trans("ValidateModBuilderDesc")).'</th>';
print '<th>'.$langs->trans("Comment").'</th>';
print '<th></th>';
print '</tr>';
@ -2712,6 +2714,7 @@ if ($module == 'initmodule') {
print '<td><input type="text" size="2" name="prophelp" value="'.dol_escape_htmltag(GETPOST('prophelp', 'alpha')).'"></td>';
print '<td class="center"><input type="text" class="center maxwidth50" name="propshowoncombobox" value="'.dol_escape_htmltag(GETPOST('propshowoncombobox', 'alpha')).'"></td>';
//print '<td class="center"><input type="text" size="2" name="propdisabled" value="'.dol_escape_htmltag(GETPOST('propdisabled', 'alpha')).'"></td>';
print '<td><input type="number" step="1" min="0" max="1" class="text maxwidth100" name="propvalidate" value="'.dol_escape_htmltag(GETPOST('propvalidate', 'alpha')).'"></td>';
print '<td><input class="text maxwidth100" name="propcomment" value="'.dol_escape_htmltag(GETPOST('propcomment', 'alpha')).'"></td>';
print '<td class="center">';
print '<input class="button" type="submit" name="add" value="'.$langs->trans("Add").'">';
@ -2753,6 +2756,7 @@ if ($module == 'initmodule') {
$prophelp = $propval['help'];
$propshowoncombobox = $propval['showoncombobox'];
//$propdisabled=$propval['disabled'];
$propvalidate = $propval['validate'];
$propcomment = $propval['comment'];
print '<tr class="oddeven">';
@ -2823,6 +2827,9 @@ if ($module == 'initmodule') {
print '<input class="center maxwidth50" name="propshowoncombobox" value="'.dol_escape_htmltag($propshowoncombobox).'">';
print '</td>';
print '<td>';
print '<input type="number" step="1" min="0" max="1" class="text maxwidth100" name="propvalidate" value="'.dol_escape_htmltag($propvalidate).'">';
print '</td>';
print '<td>';
print '<input class="maxwidth100" name="propcomment" value="'.dol_escape_htmltag($propcomment).'">';
print '</td>';
print '<td class="center">';
@ -2888,6 +2895,9 @@ if ($module == 'initmodule') {
/*print '<td class="center">';
print $propdisabled?$propdisabled:'';
print '</td>';*/
print '<td class="center">';
print $propvalidate ? dol_escape_htmltag($propvalidate) : '';
print '</td>';
print '<td class="tdoverflowmax200">';
print '<span title="'.dol_escape_htmltag($propcomment).'">';
print dol_escape_htmltag($propcomment);

View File

@ -91,7 +91,7 @@ class MyObject extends CommonObject
* 'arrayofkeyval' to set a list of values if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel"). Note that type can be 'integer' or 'varchar'
* 'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.
* 'comment' is not used. You can store here any text of your choice. It is not used by application.
*
* 'validate' is 1 if need to validate with $this->validateField()
* Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
*/
@ -102,15 +102,15 @@ class MyObject extends CommonObject
public $fields = array(
'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-2, 'noteditable'=>1, 'notnull'=> 1, 'index'=>1, 'position'=>1, 'comment'=>'Id', 'css'=>'left'),
'entity' => array('type'=>'integer', 'label'=>'Entity', 'enabled'=>1, 'visible'=>0, 'notnull'=> 1, 'default'=>1, 'index'=>1, 'position'=>10),
'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'noteditable'=>0, 'default'=>'', 'notnull'=> 1, 'showoncombobox'=>1, 'index'=>1, 'position'=>20, 'searchall'=>1, 'comment'=>'Reference of object'),
'label' => array('type'=>'varchar(255)', 'label'=>'Label', 'enabled'=>1, 'visible'=>1, 'position'=>30, 'searchall'=>1, 'css'=>'minwidth300', 'cssview'=>'wordbreak', 'help'=>'Help text', 'showoncombobox'=>2),
'amount' => array('type'=>'price', 'label'=>'Amount', 'enabled'=>1, 'visible'=>1, 'default'=>'null', 'position'=>40, 'searchall'=>0, 'isameasure'=>1, 'help'=>'Help text for amount'),
'qty' => array('type'=>'real', 'label'=>'Qty', 'enabled'=>1, 'visible'=>1, 'default'=>'0', 'position'=>45, 'searchall'=>0, 'isameasure'=>1, 'help'=>'Help text for quantity', 'css'=>'maxwidth75imp'),
'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php:1:status=1 AND entity IN (__SHARED_ENTITIES__)', 'picto'=>'company', 'label'=>'ThirdParty', 'visible'=> 1, 'enabled'=>1, 'position'=>50, 'notnull'=>-1, 'index'=>1, 'help'=>'LinkToThirparty'),
'fk_project' => array('type'=>'integer:Project:projet/class/project.class.php:1', 'label'=>'Project', 'picto'=>'project', 'enabled'=>1, 'visible'=>-1, 'position'=>52, 'notnull'=>-1, 'index'=>1),
'description' => array('type'=>'text', 'label'=>'Description', 'enabled'=>1, 'visible'=>3, 'position'=>60),
'note_public' => array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>61),
'note_private' => array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>62),
'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'noteditable'=>0, 'default'=>'', 'notnull'=> 1, 'showoncombobox'=>1, 'index'=>1, 'position'=>20, 'searchall'=>1, 'comment'=>'Reference of object', 'validate'=>1),
'label' => array('type'=>'varchar(255)', 'label'=>'Label', 'enabled'=>1, 'visible'=>1, 'position'=>30, 'searchall'=>1, 'css'=>'minwidth300', 'cssview'=>'wordbreak', 'help'=>'Help text', 'showoncombobox'=>2, 'validate'=>1),
'amount' => array('type'=>'price', 'label'=>'Amount', 'enabled'=>1, 'visible'=>1, 'default'=>'null', 'position'=>40, 'searchall'=>0, 'isameasure'=>1, 'help'=>'Help text for amount', 'validate'=>1),
'qty' => array('type'=>'real', 'label'=>'Qty', 'enabled'=>1, 'visible'=>1, 'default'=>'0', 'position'=>45, 'searchall'=>0, 'isameasure'=>1, 'help'=>'Help text for quantity', 'css'=>'maxwidth75imp', 'validate'=>1),
'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php:1:status=1 AND entity IN (__SHARED_ENTITIES__)', 'picto'=>'company', 'label'=>'ThirdParty', 'visible'=> 1, 'enabled'=>1, 'position'=>50, 'notnull'=>-1, 'index'=>1, 'help'=>'LinkToThirparty', 'validate'=>1),
'fk_project' => array('type'=>'integer:Project:projet/class/project.class.php:1', 'label'=>'Project', 'picto'=>'project', 'enabled'=>1, 'visible'=>-1, 'position'=>52, 'notnull'=>-1, 'index'=>1, 'validate'=>1),
'description' => array('type'=>'text', 'label'=>'Description', 'enabled'=>1, 'visible'=>3, 'position'=>60, 'validate'=>1),
'note_public' => array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>61, 'validate'=>1),
'note_private' => array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>62, 'validate'=>1),
'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'notnull'=> 1, 'position'=>500),
'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-2, 'notnull'=> 0, 'position'=>501),
//'date_validation ' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>502),
@ -120,7 +120,7 @@ class MyObject extends CommonObject
'last_main_doc' => array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>1, 'visible'=>0, 'notnull'=>0, 'position'=>600),
'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'index'=>0, 'position'=>1000),
'model_pdf' => array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'notnull'=>-1, 'position'=>1010),
'status' => array('type'=>'smallint', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'notnull'=> 1, 'default'=>0, 'index'=>1, 'position'=>1000, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Validated', 9=>'Canceled')),
'status' => array('type'=>'smallint', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'notnull'=> 1, 'default'=>0, 'index'=>1, 'position'=>1000, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Validated', 9=>'Canceled'), 'validate'=>1),
);
/**

View File

@ -234,10 +234,12 @@ input.button.massactionconfirmed {
margin: 4px;
}
input:invalid, select:invalid {
input:invalid, select:invalid, input.--error , select.--error {
border-color: #ea1212;
}
.field-error-icon { color: #ea1212; !important; }
/* Focus definitions must be after standard definition */
textarea:focus {
border: 1px solid #aaa !important;
@ -435,7 +437,7 @@ input.pageplusone {
transform: scale(-1, 1);
}
select:invalid {
select:invalid, select.--error {
color: gray;
}
input:disabled, textarea:disabled, select[disabled='disabled']
@ -2097,7 +2099,7 @@ span.widthpictotitle.pictotitle {
vertical-align: middle;
margin-top: -3px
}
.pictowarning, .pictoerror, .pictopreview {
.pictowarning, .pictoerror, .pictopreview, .picto.error {
padding-<?php echo $left; ?>: 3px;
}
.pictowarning {