diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php
index 8f7ea76e9..402cbd8ad 100644
--- a/application/cmdbabstract.class.inc.php
+++ b/application/cmdbabstract.class.inc.php
@@ -1854,7 +1854,8 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
{
$sNullValue = "'$sNullValue'"; // Add quotes to turn this into a JS string if it's not a number
}
- $oPage->add_ready_script("$('#$iId').bind('".implode(' ', $aEventsList)."', function(evt, sFormId) { return ValidateField('$iId', '$sPattern', $bMandatory, sFormId, $sNullValue) } );\n"); // Bind to a custom event: validate
+ $sOriginalValue = ($iFlags & OPT_ATT_MUSTCHANGE) ? "'".addslashes($value)."'" : 'undefined';
+ $oPage->add_ready_script("$('#$iId').bind('".implode(' ', $aEventsList)."', function(evt, sFormId) { return ValidateField('$iId', '$sPattern', $bMandatory, sFormId, $sNullValue, $sOriginalValue) } );\n"); // Bind to a custom event: validate
}
$aDependencies = MetaModel::GetDependentAttributes($sClass, $sAttCode); // List of attributes that depend on the current one
if (count($aDependencies) > 0)
@@ -1863,6 +1864,9 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$oPage->add_ready_script("$('#$iId').unbind('change.dependencies').bind('change.dependencies', function(evt, sFormId) { return oWizardHelper{$sFormPrefix}.UpdateDependentFields(['".implode("','", $aDependencies)."']) } );\n"); // Bind to a custom event: validate
}
}
+ $oPage->add_dict_entry('UI:ValueMustBeSet');
+ $oPage->add_dict_entry('UI:ValueMustBeChanged');
+ $oPage->add_dict_entry('UI:ValueInvalidFormat');
return "
{$sHTMLValue}
";
}
diff --git a/css/light-grey.css b/css/light-grey.css
index 24fa2f47f..53ae3c08f 100644
--- a/css/light-grey.css
+++ b/css/light-grey.css
@@ -1416,3 +1416,7 @@ div.ui-dialog-header {
padding-bottom: 10px;
padding-top: 7px;
}
+.form_field_error {
+ border: 1px solid #933;
+ background: #fcc;
+}
\ No newline at end of file
diff --git a/datamodels/1.x/itop-change-mgmt-1.0.0/datamodel.itop-change-mgmt.xml b/datamodels/1.x/itop-change-mgmt-1.0.0/datamodel.itop-change-mgmt.xml
index d77067f79..a9f958923 100644
--- a/datamodels/1.x/itop-change-mgmt-1.0.0/datamodel.itop-change-mgmt.xml
+++ b/datamodels/1.x/itop-change-mgmt-1.0.0/datamodel.itop-change-mgmt.xml
@@ -457,7 +457,7 @@
-
+
@@ -466,13 +466,13 @@
-
+
-
+
@@ -1488,7 +1488,7 @@
-
+
@@ -1497,13 +1497,13 @@
-
+
-
+
@@ -2465,7 +2465,7 @@
-
+
@@ -2474,13 +2474,13 @@
-
+
-
+
@@ -3471,7 +3471,7 @@
-
+
@@ -3480,13 +3480,13 @@
-
+
-
+
@@ -4547,7 +4547,7 @@
-
+
@@ -4556,13 +4556,13 @@
-
+
-
+
diff --git a/datamodels/1.x/itop-incident-mgmt-1.0.0/datamodel.itop-incident-mgmt.xml b/datamodels/1.x/itop-incident-mgmt-1.0.0/datamodel.itop-incident-mgmt.xml
index 1f451eb9f..0ad7356ca 100644
--- a/datamodels/1.x/itop-incident-mgmt-1.0.0/datamodel.itop-incident-mgmt.xml
+++ b/datamodels/1.x/itop-incident-mgmt-1.0.0/datamodel.itop-incident-mgmt.xml
@@ -54,7 +54,7 @@
-
+
@@ -63,28 +63,28 @@
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/datamodels/1.x/itop-problem-mgmt-1.0.0/datamodel.itop-problem-mgmt.xml b/datamodels/1.x/itop-problem-mgmt-1.0.0/datamodel.itop-problem-mgmt.xml
index cdc1be8c6..587c9dee0 100644
--- a/datamodels/1.x/itop-problem-mgmt-1.0.0/datamodel.itop-problem-mgmt.xml
+++ b/datamodels/1.x/itop-problem-mgmt-1.0.0/datamodel.itop-problem-mgmt.xml
@@ -213,7 +213,7 @@
-
+
@@ -222,28 +222,28 @@
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/datamodels/1.x/itop-request-mgmt-1.0.0/datamodel.itop-request-mgmt.xml b/datamodels/1.x/itop-request-mgmt-1.0.0/datamodel.itop-request-mgmt.xml
index b9d98e505..83ae51c66 100644
--- a/datamodels/1.x/itop-request-mgmt-1.0.0/datamodel.itop-request-mgmt.xml
+++ b/datamodels/1.x/itop-request-mgmt-1.0.0/datamodel.itop-request-mgmt.xml
@@ -91,7 +91,7 @@
-
+
@@ -100,28 +100,28 @@
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/datamodels/1.x/itop-tickets-1.0.0/datamodel.itop-tickets.xml b/datamodels/1.x/itop-tickets-1.0.0/datamodel.itop-tickets.xml
index 6a4bf2c9a..a5d2702bd 100644
--- a/datamodels/1.x/itop-tickets-1.0.0/datamodel.itop-tickets.xml
+++ b/datamodels/1.x/itop-tickets-1.0.0/datamodel.itop-tickets.xml
@@ -422,7 +422,7 @@
-
+
@@ -431,28 +431,28 @@
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/datamodels/2.x/itop-problem-mgmt/datamodel.itop-problem-mgmt.xml b/datamodels/2.x/itop-problem-mgmt/datamodel.itop-problem-mgmt.xml
index f4c046c62..dd02a249b 100755
--- a/datamodels/2.x/itop-problem-mgmt/datamodel.itop-problem-mgmt.xml
+++ b/datamodels/2.x/itop-problem-mgmt/datamodel.itop-problem-mgmt.xml
@@ -169,7 +169,7 @@
-
+
@@ -178,7 +178,7 @@
-
+
@@ -199,10 +199,10 @@
-
+
-
+
diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php
index f61763546..4aee9f27a 100644
--- a/dictionaries/dictionary.itop.ui.php
+++ b/dictionaries/dictionary.itop.ui.php
@@ -1196,7 +1196,10 @@ When associated with a trigger, each action is given an "order" number, specifyi
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'The minimum allowed is 5 seconds',
'UI:FillAllMandatoryFields' => 'Please fill all mandatory fields.',
-
+ 'UI:ValueMustBeSet' => 'Please specify a value',
+ 'UI:ValueMustBeChanged' => 'Please change the value',
+ 'UI:ValueInvalidFormat' => 'Invalid format',
+
'UI:CSVImportConfirmTitle' => 'Please confirm the operation',
'UI:CSVImportConfirmMessage' => 'Are you sure you want to do this?',
'UI:CSVImportError_items' => 'Errors: %1$d',
diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php
index 9fd8aebaf..409305d34 100644
--- a/dictionaries/fr.dictionary.itop.ui.php
+++ b/dictionaries/fr.dictionary.itop.ui.php
@@ -1036,6 +1036,9 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'Le minimum permis est de 5 secondes',
'UI:FillAllMandatoryFields' => 'Veuillez remplir tous les champs obligatoires.',
+ 'UI:ValueMustBeSet' => 'Veuillez spécifier une valeur pour ce champ',
+ 'UI:ValueMustBeChanged' => 'Veuillez modifier la valeur de ce champ',
+ 'UI:ValueInvalidFormat' => 'Format invalide',
'UI:CSVImportConfirmTitle' => 'Veuillez confirmer cette opération',
'UI:CSVImportConfirmMessage' => 'Etes-vous sûr(e) de vouloir faire cela ?',
diff --git a/js/forms-json-utils.js b/js/forms-json-utils.js
index d037395ec..3b7176888 100644
--- a/js/forms-json-utils.js
+++ b/js/forms-json-utils.js
@@ -146,7 +146,7 @@ function CheckFields(sFormId, bDisplayAlert)
return (oFormErrors['err_'+sFormId] == 0); // If no error, submit the form
}
-function ReportFieldValidationStatus(sFieldId, sFormId, bValid)
+function ReportFieldValidationStatus(sFieldId, sFormId, bValid, sExplain)
{
if (bValid)
{
@@ -163,13 +163,21 @@ function ReportFieldValidationStatus(sFieldId, sFormId, bValid)
oFormErrors['input_'+sFormId] = sFieldId;
}
// Visual feedback
- $('#v_'+sFieldId).html('
');
+ $('#v_'+sFieldId).html('
');
+ $('#v_'+sFieldId).tooltip({
+ items: 'span',
+ tooltipClass: 'form_field_error',
+ content: function() {
+ return $(this).find('img').attr('data-tooltip'); // As opposed to the default 'content' handler, do not escape the contents of 'title'
+ }
+ });
}
}
-function ValidateField(sFieldId, sPattern, bMandatory, sFormId, nullValue)
+function ValidateField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue)
{
var bValid = true;
+ var sExplain = '';
if ($('#'+sFieldId).attr('disabled'))
{
bValid = true; // disabled fields are not checked
@@ -185,6 +193,19 @@ function ValidateField(sFieldId, sPattern, bMandatory, sFormId, nullValue)
else if (bMandatory && (currentVal == nullValue))
{
bValid = false;
+ sExplain = Dict.S('UI:ValueMustBeSet');
+ }
+ else if ((originalValue != undefined) && (currentVal == originalValue))
+ {
+ bValid = false;
+ if (originalValue == nullValue)
+ {
+ sExplain = Dict.S('UI:ValueMustBeSet');
+ }
+ else
+ {
+ sExplain = Dict.S('UI:ValueMustBeChanged');
+ }
}
else if (currentVal == nullValue)
{
@@ -196,9 +217,10 @@ function ValidateField(sFieldId, sPattern, bMandatory, sFormId, nullValue)
re = new RegExp(sPattern);
//console.log('Validating field: '+sFieldId + ' current value: '+currentVal + ' pattern: '+sPattern );
bValid = re.test(currentVal);
+ sExplain = Dict.S('UI:ValueInvalidFormat');
}
}
- ReportFieldValidationStatus(sFieldId, sFormId, bValid);
+ ReportFieldValidationStatus(sFieldId, sFormId, bValid, sExplain);
//console.log('Form: '+sFormId+' Validating field: '+sFieldId + ' current value: '+currentVal+' pattern: '+sPattern+' result: '+bValid );
return true; // Do not stop propagation ??
}
@@ -229,7 +251,7 @@ function ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue)
bValid = true;
}
- ReportFieldValidationStatus(sFieldId, sFormId, bValid);
+ ReportFieldValidationStatus(sFieldId, sFormId, bValid, '');
setTimeout(function(){ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue);}, 500);
}
@@ -321,7 +343,7 @@ function ValidateCaseLogField(sFieldId, bMandatory, sFormId)
}
}
}
- ReportFieldValidationStatus(sFieldId, sFormId, bValid);
+ ReportFieldValidationStatus(sFieldId, sFormId, bValid, '');
return bValid;
}
// Manage a 'duration' field