浏览代码

- Added validation of the fields based on a regexp pattern
- Fields that depend on another field (in the same form) get refreshed when one of the fields they depend on is changed
- Updated the wizard to group all the mandatory fields on the first page, since having inter-dependent fields on one page is now supported

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@412 a333f486-631f-4898-b8df-5754b55c2be0

dflaven 15 年之前
父节点
当前提交
6f60b59d05

+ 40 - 12
application/cmdbabstract.class.inc.php

@@ -828,25 +828,29 @@ abstract class cmdbAbstractObject extends CMDBObject
 		if (!$oAttDef->IsExternalField())
 		{
 			$aCSSClasses = array();
+			$bMandatory = 0;
 			if ( (!$oAttDef->IsNullAllowed()) || ($iFlags & OPT_ATT_MANDATORY))
 			{
 				$aCSSClasses[] = 'mandatory';
+				$bMandatory = 1;
 			}
 			$sCSSClasses = self::GetCSSClasses($aCSSClasses);
+			$sValidationField = "<span id=\"v_{$iInputId}\"></span>";
 			switch($oAttDef->GetEditClass())
 			{
 				case 'Date':
+				case 'DateTime':
 				$aCSSClasses[] = 'date-pick';
 				$sCSSClasses = self::GetCSSClasses($aCSSClasses);
-				$sHTMLValue = "<input type=\"text\" size=\"20\" name=\"attr_{$sAttCode}{$sNameSuffix}\" value=\"$value\" id=\"$iInputId\"{$sCSSClasses}/>";
+				$sHTMLValue = "<input type=\"text\" size=\"20\" name=\"attr_{$sAttCode}{$sNameSuffix}\" value=\"$value\" id=\"$iInputId\"{$sCSSClasses}/>$sValidationField";
 				break;
 				
 				case 'Password':
-				$sHTMLValue = "<input type=\"password\" size=\"30\" name=\"attr_{$sAttCode}{$sNameSuffix}\" value=\"$value\" id=\"$iInputId\"{$sCSSClasses}/>";
+				$sHTMLValue = "<input type=\"password\" size=\"30\" name=\"attr_{$sAttCode}{$sNameSuffix}\" value=\"$value\" id=\"$iInputId\"{$sCSSClasses}/>$sValidationField";
 				break;
 				
 				case 'Text':
-					$sHTMLValue = "<textarea name=\"attr_{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iInputId\"{$sCSSClasses}>$value</textarea>";
+					$sHTMLValue = "<textarea name=\"attr_{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iInputId\"{$sCSSClasses}>$value</textarea>$sValidationField";
 				break;
 	
 				case 'List':
@@ -884,7 +888,7 @@ abstract class cmdbAbstractObject extends CMDBObject
 						{
 							// too many choices, use an autocomplete
 							// The input for the auto complete
-							$sHTMLValue = "<input count=\"".count($aAllowedValues)."\" type=\"text\" id=\"label_$iInputId\" size=\"30\" value=\"$sDisplayValue\"{$sCSSClasses}/>";
+							$sHTMLValue = "<input count=\"".count($aAllowedValues)."\" type=\"text\" id=\"label_$iInputId\" size=\"30\" value=\"$sDisplayValue\"{$sCSSClasses}/>$sValidationField";
 							// another hidden input to store & pass the object's Id
 							$sHTMLValue .= "<input type=\"hidden\" id=\"$iInputId\" name=\"attr_{$sAttCode}{$sNameSuffix}\" value=\"$value\" />\n";
 							$oPage->add_ready_script("\$('#label_$iInputId').autocomplete('./ajax.render.php', { scroll:true, minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#$iInputId', extraParams:{operation:'autocomplete', sclass:'$sClass',attCode:'".$sAttCode."'}});");
@@ -906,15 +910,22 @@ abstract class cmdbAbstractObject extends CMDBObject
 								$sSelected = ($value == $key) ? ' selected' : '';
 								$sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
 							}
-							$sHTMLValue .= "</select>\n";
+							$sHTMLValue .= "</select>$sValidationField\n";
 						}
 					}
 					else
 					{
-						$sHTMLValue = "<input type=\"text\" size=\"30\" name=\"attr_{$sAttCode}{$sNameSuffix}\" value=\"$value\" id=\"$iInputId\"{$sCSSClasses} />";
+						$sHTMLValue = "<input type=\"text\" size=\"30\" name=\"attr_{$sAttCode}{$sNameSuffix}\" value=\"$value\" id=\"$iInputId\"{$sCSSClasses} />$sValidationField";
 					}
 					break;
 			}
+			$sPattern = addslashes($oAttDef->GetValidationPattern()); //'^([0-9]+)$';
+			$oPage->add_ready_script("$('#$iInputId').bind('validate blur', function(evt, sFormId) { return ValidateField('$iInputId', '$sPattern', $bMandatory, sFormId) } );"); // Bind to a custom event: validate
+			$aDependencies = MetaModel::GetDependentAttributes($sClass, $sAttCode); // List of attributes that depend on the current one
+			if (count($aDependencies) > 0)
+			{
+				$oPage->add_ready_script("$('#$iInputId').bind('change', function(evt, sFormId) { return UpdateDependentFields(['".implode("','", $aDependencies)."']) } );"); // Bind to a custom event: validate
+			}
 		}
 		return $sHTMLValue;
 	}
@@ -923,12 +934,14 @@ abstract class cmdbAbstractObject extends CMDBObject
 	{
 		static $iFormId = 0;
 		$iFormId++;
+		$sClass = get_class($this);
 		$oAppContext = new ApplicationContext();
-		$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
+		$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
 		$iKey = $this->GetKey();
 		$aDetails = array();
-		$oPage->add("<form id=\"form_{$iFormId}\" enctype=\"multipart/form-data\" method=\"post\" onSubmit=\"return CheckMandatoryFields('form_{$iFormId}')\">\n");
-		foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
+		$aFieldsMap = array();
+		$oPage->add("<form id=\"form_{$iFormId}\" enctype=\"multipart/form-data\" method=\"post\" onSubmit=\"return CheckFields('form_{$iFormId}')\">\n");
+		foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef)
 		{
 			if ($oAttDef->IsWritable())
 			{
@@ -957,7 +970,10 @@ abstract class cmdbAbstractObject extends CMDBObject
 							$sValue = $this->Get($sAttCode);
 							$sDisplayValue = $this->GetEditValue($sAttCode);
 							$aArgs = array('this' => $this);
-							$sHTMLValue = self::GetFormElementForField($oPage, get_class($this), $sAttCode, $oAttDef, $sValue, $sDisplayValue, '', '', $iFlags, $aArgs);
+							$sInputId = $iFormId.'_'.$sAttCode;
+							$sHTMLValue = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs);
+							$aFieldsMap[$sAttCode] = $sInputId;
+							
 						}
 						$aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => $sHTMLValue);
 					}
@@ -966,7 +982,7 @@ abstract class cmdbAbstractObject extends CMDBObject
 		}
 		$oPage->details($aDetails);
 		$oPage->add("<input type=\"hidden\" name=\"id\" value=\"$iKey\">\n");
-		$oPage->add("<input type=\"hidden\" name=\"class\" value=\"".get_class($this)."\">\n");
+		$oPage->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">\n");
 		$oPage->add("<input type=\"hidden\" name=\"operation\" value=\"apply_modify\">\n");
 		$oPage->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\">\n");
 		foreach($aExtraParams as $sName => $value)
@@ -977,6 +993,18 @@ abstract class cmdbAbstractObject extends CMDBObject
 		$oPage->add("<button type=\"button\" class=\"action\" onClick=\"goBack()\"><span>".Dict::S('UI:Button:Cancel')."</span></button>&nbsp;&nbsp;&nbsp;&nbsp;\n");
 		$oPage->add("<button type=\"submit\" class=\"action\"><span>".Dict::S('UI:Button:Apply')."</span></button>\n");
 		$oPage->add("</form>\n");
+		
+		$iFieldsCount = count($aFieldsMap);
+		$sJsonFieldsMap = json_encode($aFieldsMap);
+
+		$oPage->add_script(
+<<<EOF
+		// Initializes the object once at the beginning of the page...
+		var oWizardHelper = new WizardHelper('$sClass');
+		oWizardHelper.SetFieldsMap($sJsonFieldsMap);
+		oWizardHelper.SetFieldsCount($iFieldsCount);
+EOF
+);
 	}
 	
 	public static function DisplayCreationForm(WebPage $oPage, $sClass, $oObjectToClone = null, $aArgs = array(), $aExtraParams = array())
@@ -989,7 +1017,7 @@ abstract class cmdbAbstractObject extends CMDBObject
 		$sOperation = ($oObjectToClone == null) ? 'apply_new' : 'apply_clone';
 		$sClass = ($oObjectToClone == null) ? $sClass : get_class($oObjectToClone);
 		$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
-		$oPage->add("<form id=\"creation_form_{$iCreationFormId}\" method=\"post\" enctype=\"multipart/form-data\" onSubmit=\"return CheckMandatoryFields('creation_form_{$iCreationFormId}')\">\n");
+		$oPage->add("<form id=\"creation_form_{$iCreationFormId}\" method=\"post\" enctype=\"multipart/form-data\" onSubmit=\"return CheckFields('creation_form_{$iCreationFormId}')\">\n");
 		$aStates = MetaModel::EnumStates($sClass);
 		if ($oObjectToClone == null)
 		{

+ 36 - 25
application/uiwizard.class.inc.php

@@ -61,8 +61,8 @@ class UIWizard
 				$sFieldFlag = (($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE)) || (!$oAttDef->IsNullAllowed()) )? ' <span class="hilite">*</span>' : '';
 				$oDefaultValuesSet = $oAttDef->GetDefaultValue(/* $oObject->ToArgs() */); // @@@ TO DO: get the object's current value if the object exists
 				$sHTMLValue = cmdbAbstractObject::GetFormElementForField($this->m_oPage, $this->m_sClass, $sAttCode, $oAttDef, $oDefaultValuesSet, '', "att_$iMaxInputId", '', $iOptions, $aArgs);
-				$aFieldsMap[$iMaxInputId] = $sAttCode;
-				$aDetails[] = array('label' => $oAttDef->GetLabel().$sFieldFlag, 'value' => "<div id=\"field_$iMaxInputId\">$sHTMLValue</div>");
+				$aFieldsMap["att_$iMaxInputId"] = $sAttCode;
+				$aDetails[] = array('label' => $oAttDef->GetLabel().$sFieldFlag, 'value' => "<span id=\"field_att_$iMaxInputId\">$sHTMLValue</span>");
 				if ($oAttDef->GetValuesDef() != null)
 				{
 					$sJSHandlerCode .= "\toWizardHelper.RequestAllowedValues('$sAttCode');\n";
@@ -128,6 +128,7 @@ $sJSHandlerCode
 		$this->m_oPage->add("<div id=\"object_preview\">\n");
 		$this->m_oPage->add("</div>\n");
 		$this->m_oPage->add($oAppContext->GetForForm());		
+		$this->m_oPage->add("<input type=\"button\" value=\"".Dict::S('UI:Button:Back')."\" onClick=\"GoToStep($iStepIndex, $iStepIndex - 1)\" />");
 		$this->m_oPage->add("<input type=\"submit\" value=\"Create ".MetaModel::GetName($this->m_sClass)."\" />\n");
 		$this->m_oPage->add("</div>\n");
 		$this->m_oPage->add("</form>\n");
@@ -201,35 +202,45 @@ $sJSHandlerCode
 		}
 			
 		// Now use the dependencies between the fields to order them
-		while(count($aFields) > 0)
+		// Start from the order of the 'details'
+		$aList = MetaModel::GetZListItems($this->m_sClass, 'details');
+		$index = 0;
+		$aOrder = array();
+		foreach($aFields as $sAttCode => $void)
 		{
-			$aCurrentStep = array();
-			foreach($aFields as $sAttCode => $aDependencies)
+				$aOrder[$sAttCode] = 999; // At the end of the list...
+		}
+		foreach($aList as $sAttCode)
+		{
+			if (array_key_exists($sAttCode, $aFields))
 			{
-				// All fields with no remaining dependencies can be entered at this
-				// step of the wizard
-				if (count($aDependencies) == 0)
-				{
-					$aCurrentStep[] = $sAttCode;
-					$aFieldsDone[$sAttCode] = '';
-					unset($aFields[$sAttCode]);
-					// Remove this field from the dependencies of the other fields
-					foreach($aFields as $sUpdatedCode => $aDummy)
-					{
-						// remove the dependency
-						unset($aFields[$sUpdatedCode][$sAttCode]);
-					}
-				}
+				$aOrder[$sAttCode] = $index;
 			}
-			if (count($aCurrentStep) == 0)
+			$index++;
+		}
+		foreach($aFields as $sAttCode => $aDependencies)
+		{
+			// All fields with no remaining dependencies can be entered at this
+			// step of the wizard
+			if (count($aDependencies) > 0)
 			{
-				// This step of the wizard would contain NO field !
-				echo "<strong>Error:</strong> Circular reference in the dependencies between the fields.";
-				print_r($aFields);
-				break;
+				$iMaxPos = 0;
+				// Remove this field from the dependencies of the other fields
+				foreach($aDependencies as $sDependentAttCode => $void)
+				{
+					// position the current field after the ones it depends on
+					$iMaxPos = max($iMaxPos, 1+$aOrder[$sDependentAttCode]);
+				}
 			}
-			$aWizardSteps['mandatory'][] = $aCurrentStep;
 		}
+		asort($aOrder);
+		$aCurrentStep = array();
+		foreach($aOrder as $sAttCode => $rank)
+		{
+			$aCurrentStep[] = $sAttCode;
+			$aFieldsDone[$sAttCode] = '';
+		}
+		$aWizardSteps['mandatory'][] = $aCurrentStep;
 
 
 		// Now computes the steps to fill the optional fields

+ 4 - 7
application/wizardhelper.class.inc.php

@@ -16,9 +16,8 @@ class WizardHelper
 	public function GetTargetObject($bReadUploadedFiles = false)
 	{
 		$oObj = MetaModel::NewObject($this->m_aData['m_sClass']);
-		foreach($this->m_aData['m_aCurrentValues'] as $iIndex => $value)
+		foreach($this->m_aData['m_oCurrentValues'] as $sAttCode => $value)
 		{
-			$sAttCode = array_search($iIndex, $this->m_aData['m_oFieldsMap']);
 			// Because this is stored in a Javascript array, unused indexes
 			// are filled with null values
 			if ( ($sAttCode !== false) && ($value !== null))
@@ -104,7 +103,6 @@ class WizardHelper
 		// Protect against a request for a non existing field
 		if (isset($this->m_aData['m_oFieldsMap'][$sAttCode]))
 		{
-			$iIndex = $this->m_aData['m_oFieldsMap'][$sAttCode];
 			$oAttDef = MetaModel::GetAttributeDef($this->m_aData['m_sClass'], $sAttCode);
 			if ($oAttDef->GetEditClass() == 'List')
 			{
@@ -124,13 +122,13 @@ class WizardHelper
 					}
 					$aData[] = $aRow;
 				}
-				$this->m_aData['m_aDefaultValue'][$iIndex] = json_encode($aData);
+				$this->m_aData['m_oDefaultValue'][$sAttCode] = json_encode($aData);
 				
 			}
 			else
 			{
 				// Normal handling for all other scalar attributes
-				$this->m_aData['m_aDefaultValue'][$iIndex] = $value;
+				$this->m_aData['m_oDefaultValue'][$sAttCode] = $value;
 			}
 		}
 	}
@@ -145,8 +143,7 @@ class WizardHelper
 		// Protect against a request for a non existing field
 		if (isset($this->m_aData['m_oFieldsMap'][$sAttCode]))
 		{
-			$iIndex = $this->m_aData['m_oFieldsMap'][$sAttCode];
-			$this->m_aData['m_aAllowedValues'][$iIndex] = $sHtml;
+			$this->m_aData['m_oAllowedValues'][$sAttCode] = $sHtml;
 		}
 	}
 	

+ 3 - 0
css/light-grey.css

@@ -478,6 +478,9 @@ div.wizStep span {
 	padding: 5px;
 }
 
+.wizContainer table tr td {
+	background: transparent;
+}
 .alignRight {
 	text-align: right;
 	padding: 3px;

二进制
images/validation_error.png


二进制
images/validation_ok.png


+ 93 - 48
js/forms-json-utils.js

@@ -60,7 +60,7 @@ function GoToStep(iCurrentStep, iNextStep)
 	if (iNextStep > iCurrentStep)
 	{
 		// Check the values when moving forward
-		if (CheckMandatoryFields('wizStep'+iCurrentStep))
+		if (CheckFields('wizStep'+iCurrentStep))
 		{
 			oCurrentStep.style.display = 'none';
 			ActivateStep(iNextStep);
@@ -85,70 +85,115 @@ function ActivateStep(iTargetStep)
 	}
 	oNextStep.style.display = '';
 	G_iCurrentStep = iTargetStep;
-	$('#wizStep'+(iTargetStep)).block({ message: null });
+	//$('#wizStep'+(iTargetStep)).block({ message: null });
 }
 
 
-function AjaxGetValuesDef(oObj, sClass, sAttCode, iFieldId)
-{
-	var oJSON = document.getElementById(sJsonFieldId);
-	$.get('ajax.render.php?class=' + sClass + '&json_obj=' + oJSON.value + '&att_code=' + sAttCode,
-	   { operation: "allowed_values" },
-	   function(data){
-		 //$('#field_'+iFieldId).html(data);
-		}
-	);
-}
+//function AjaxGetValuesDef(oObj, sClass, sAttCode, iFieldId)
+//{
+//	var oJSON = document.getElementById(sJsonFieldId);
+//	$.get('ajax.render.php?class=' + sClass + '&json_obj=' + oJSON.value + '&att_code=' + sAttCode,
+//	   { operation: "allowed_values" },
+//	   function(data){
+//		 //$('#field_'+iFieldId).html(data);
+//		}
+//	);
+//}
+//
+//function AjaxGetDefaultValue(oObj, sClass, sAttCode, iFieldId)
+//{
+//	// Asynchronously call the server to provide a default value if the field is
+//	// empty
+//	if (oObj['m_aCurrValues'][sAttCode] == '')
+//	{
+//		var oJSON = document.getElementById(sJsonFieldId);
+//		$.get('ajax.render.php?class=' + sClass + '&json_obj=' + oJSON.value + '&att_code=' + sAttCode,
+//		   { operation: "default_value" },
+//		   function(json_data){
+//			 var oObj = ReloadObjectFromServer(json_data);
+//			 UpdateFieldFromObject(iFieldId, aFieldsMap, oObj)
+//			}
+//		);
+//	}
+//}
 
-function AjaxGetDefaultValue(oObj, sClass, sAttCode, iFieldId)
-{
-	// Asynchronously call the server to provide a default value if the field is
-	// empty
-	if (oObj['m_aCurrValues'][sAttCode] == '')
-	{
-		var oJSON = document.getElementById(sJsonFieldId);
-		$.get('ajax.render.php?class=' + sClass + '&json_obj=' + oJSON.value + '&att_code=' + sAttCode,
-		   { operation: "default_value" },
-		   function(json_data){
-			 var oObj = ReloadObjectFromServer(json_data);
-			 UpdateFieldFromObject(iFieldId, aFieldsMap, oObj)
-			}
-		);
-	}
-}
+// Store the result of the form validation... there may be several forms per page, beware
+var oFormErrors = { err_form0: 0 };
 
-function CheckMandatoryFields(sFormId)
+function CheckFields(sFormId)
 {
 	$('#'+sFormId+' :submit').attr('disable', 'disabled');
 	$('#'+sFormId+' :button[type=submit]').attr('disable', 'disabled');
 	firstErrorId = '';
 	
-	var iErrorsCount = 0;
-	$('#'+sFormId+' :input.mandatory').each( function() {
-		if (( this.value == '') || (this.value == 0))
-		{
-			this.style.backgroundColor = '#fcc';
-			iErrorsCount++;
-			if (iErrorsCount == 1)
-			{
-				firstErrorId = this.id;
-			}
-		}
-		else
-		{
-			this.style.backgroundColor = '#fff';
-		}
+	// The two 'fields' below will be updated when the 'validate' event is processed
+	oFormErrors['err_'+sFormId] = 0;		// Number of errors encountered when validating the form
+	oFormErrors['input_'+sFormId] = null;	// First 'input' with an error, to set the focus to it
+	$('#'+sFormId+' :input').each( function()
+	{
+		validateEventResult = $(this).trigger('validate', sFormId);
 	}
 	);
-	if(iErrorsCount > 0)
+	if(oFormErrors['err_'+sFormId] > 0)
 	{
 		alert('Please fill-in all mandatory fields before continuing.');
 		$('#'+sFormId+' :submit').attr('disable', '');
 		$('#'+sFormId+' :button[type=submit]').attr('disable', '');
-		if (firstErrorId != '')
+		if (oFormErrors['input_'+sFormId] != null)
+		{
+			$('#'+oFormErrors['input_'+sFormId]).focus();
+		}
+	}
+	return (oFormErrors['err_'+sFormId] == 0); // If no error, submit the form
+}
+
+function ValidateField(sFieldId, sPattern, bMandatory, sFormId)
+{
+	var bValid = true;
+	var currentVal = $('#'+sFieldId).val();
+	if (bMandatory && ((currentVal == '') || (currentVal == 0)))
+	{
+		bValid = false;
+	}
+	else if (sPattern != '')
+	{
+		re = new RegExp(sPattern);
+		//console.log('Validating field: '+sFieldId + ' current value: '+currentVal + ' pattern: '+sPattern );
+		bValid = re.test(currentVal);
+	}
+	if (bValid)
+	{
+		// Visual feedback
+		$('#v_'+sFieldId).html('<img src="../images/validation_ok.png" />');
+	}
+	else
+	{
+		// Report the error...
+		oFormErrors['err_'+sFormId]++;
+		if (oFormErrors['input_'+sFormId] == null)
 		{
-			$('#'+firstErrorId).focus();
+			// Let's remember the first input with an error, so that we can put back the focus on it later
+			oFormErrors['input_'+sFormId] = sFieldId;
 		}
+		// Visual feedback
+		$('#v_'+sFieldId).html('<img src="../images/validation_error.png" />');
+	}
+	//console.log('Form: '+sFormId+' Validating field: '+sFieldId + ' current value: '+currentVal+' pattern: '+sPattern+' result: '+bValid );
+	return bValid;
+}
+
+function UpdateDependentFields(aFieldNames)
+{
+	//console.log('UpdateDependentFields:');
+	//console.log(aFieldNames);
+	index = 0;
+	oWizardHelper.ResetQuery();
+	oWizardHelper.UpdateWizard();
+	while(index < aFieldNames.length )
+	{
+		sAttCode = aFieldNames[index];
+		oWizardHelper.RequestAllowedValues(sAttCode);
+		index++;
 	}
-	return(iErrorsCount == 0);
+	oWizardHelper.AjaxQueryServer();
 }

+ 23 - 31
js/wizardhelper.js

@@ -3,11 +3,11 @@ function WizardHelper(sClass)
 {
 	this.m_oData = { 'm_sClass' : '',
 					 'm_oFieldsMap': {},
-					 'm_aCurrentValues': [],
+					 'm_oCurrentValues': {},
 					 'm_aDefaultValueRequested': [],
 					 'm_aAllowedValuesRequested': [],
-					 'm_aDefaultValue': [],
-					 'm_aAllowedValues': [],
+					 'm_oDefaultValue': {},
+					 'm_oAllowedValues': {},
 					 'm_iFieldsCount' : 0
 					};
 	this.m_oData.m_sClass = sClass;
@@ -16,7 +16,6 @@ function WizardHelper(sClass)
 	this.SetFieldsMap = function (oFieldsMap)
 	{
 		this.m_oData.m_oFieldsMap = oFieldsMap;
-		
 	}
 	
 	this.SetFieldsCount = function (count)
@@ -28,7 +27,6 @@ function WizardHelper(sClass)
 	this.RequestDefaultValue = function (sFieldName)
 	{
 		currentValue = this.UpdateCurrentValue(sFieldName);
-		sFieldId = this.m_oData.m_oFieldsMap[sFieldName];
 		if (currentValue == null)
 		{
 			this.m_oData.m_aDefaultValueRequested.push(sFieldName);
@@ -40,7 +38,7 @@ function WizardHelper(sClass)
 	}
 	this.SetCurrentValue = function (sFieldName, currentValue)
 	{
-		this.m_oData.m_aCurrentValues[this.m_oData.m_oFieldsMap[sFieldName]] = currentValue;
+		this.m_oData.m_oCurrentValues[sFieldName] = currentValue;
 	}
 	
 	this.ToJSON = function ()
@@ -57,44 +55,40 @@ function WizardHelper(sClass)
 	this.ResetQuery = function ()
 	{
 		this.m_oData.m_aDefaultValueRequested = [];
-		this.m_oData.m_aDefaultValue = [];
+		this.m_oData.m_oDefaultValue = {};
 		this.m_oData.m_aAllowedValuesRequested = [];
-		this.m_oData.m_aAllowedValues = [];
+		this.m_oData.m_oAllowedValues = {};
 	}
 	
 	this.UpdateFields = function ()
 	{
 		//console.log('** UpdateFields **');
-		//console.log(this.m_oData);
-		for(i=0; i< this.m_oData.m_aAllowedValuesRequested.length; i++)
+		// Set the full HTML for the input field
+		for(i=0; i<this.m_oData.m_aAllowedValuesRequested.length; i++)
 		{
 			sAttCode = this.m_oData.m_aAllowedValuesRequested[i];
 			sFieldId = this.m_oData.m_oFieldsMap[sAttCode];
-			$('#field_'+sFieldId).html(this.m_oData.m_aAllowedValues[sFieldId]);
+			//console.log('Setting #field_'+sFieldId+' to: '+this.m_oData.m_oAllowedValues[sAttCode]);
+			$('#field_'+sFieldId).html(this.m_oData.m_oAllowedValues[sAttCode]);
 		}
-		for(i=0; i< this.m_oData.m_aDefaultValueRequested.length; i++)
+		// Set the actual value of the input
+		for(i=0; i<this.m_oData.m_aDefaultValueRequested.length; i++)
 		{
 			sAttCode = this.m_oData.m_aDefaultValueRequested[i];
-			sFieldId = this.m_oData.m_oFieldsMap[sAttCode];
-			defaultValue = this.m_oData.m_aDefaultValue[sFieldId];
-			//console.log('Setting field:'+sFieldId+' ('+sAttCode+') to: '+defaultValue);
-			var oElement = document.getElementById('att_'+sFieldId);
-			oElement.value = defaultValue;
-			//console.log('att_'+sFieldId+', set to '+defaultValue);
+			defaultValue = this.m_oData.m_oDefaultValue[sAttCode];
+			sFieldId = this.m_oData.m_oFieldsMap[sAttCode];	
+			$('#'+sFieldId).val(defaultValue);
 		}
 	}
 	
 	this.UpdateWizard = function ()
 	{
 		//console.log('** UpdateWizard **')
-		for(i=0; i< this.m_oData.m_iFieldsCount; i++)
+		for(sFieldCode in this.m_oData.m_oFieldsMap)
 		{
-			value = $('#att_'+i).val();
-			if (value == '')
-			{
-				value = null;
-			}
-			this.m_oData.m_aCurrentValues[i] = value;
+			sCleanFieldCode = sFieldCode.replace('"', '');
+			//console.log(sFieldCode);
+			this.UpdateCurrentValue(sCleanFieldCode);
 		}
 	}
 	
@@ -110,7 +104,7 @@ function WizardHelper(sClass)
 				//oWizardHelper.FromJSON(json_data);
 				//oWizardHelper.UpdateFields(); // Is done directly in the html provided by ajax.render.php
 				//console.log(oWizardHelper);
-				$('#wizStep'+ G_iCurrentStep).unblock( {fadeOut: 0} );
+				//$('#wizStep'+ G_iCurrentStep).unblock( {fadeOut: 0} );
 			});
 	}
 	
@@ -125,16 +119,14 @@ function WizardHelper(sClass)
 			});
 	}
 	
-	this.UpdateCurrentValue = function (sFieldName)
+	this.UpdateCurrentValue = function (sFieldCode)
 	{
-		sFieldId = this.m_oData.m_oFieldsMap[sFieldName];
-		var oElement = document.getElementById('att_'+sFieldId);
-		value = oElement.value;
+		value = $('#'+this.m_oData.m_oFieldsMap[sFieldCode]).val();
 		if (value == '')
 		{
 			value = null;
 		}
-		this.m_oData.m_aCurrentValues[sFieldId] = value;
+		this.m_oData.m_oCurrentValues[sFieldCode] = value;
 		return value;		
 	}
 }

+ 27 - 5
pages/UI.php

@@ -179,7 +179,7 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed)
 			$sName = $oObj->GetName();
 			$sClassLabel = MetaModel::GetName(get_class($oObj));
 			$oObj->DBDeleteTracked($oMyChange);
-			$oP->add("<h1>".Dict::Format('UI:Delete:_Name_Class_Deleted')."</h1>\n");
+			$oP->add("<h1>".Dict::Format('UI:Delete:_Name_Class_Deleted', $sName, $sClassLabel)."</h1>\n");
 		}
 	}
 	else
@@ -995,6 +995,12 @@ try
 			$aTransition = $aTransitions[$sStimulus];
 			$sTargetState = $aTransition['target_state'];
 			$aTargetStates = MetaModel::EnumStates($sClass);
+			$oP->add_linked_script("../js/json.js");
+			$oP->add_linked_script("../js/forms-json-utils.js");
+			$oP->add_linked_script("../js/wizardhelper.js");
+			$oP->add_linked_script("../js/wizard.utils.js");
+			$oP->add_linked_script("../js/linkswidget.js");
+			$oP->add_linked_script("../js/jquery.blockUI.js");
 			$oP->add("<div class=\"page_header\">\n");
 			$oP->add("<h1>$sActionLabel - <span class=\"hilite\">{$oObj->GetName()}</span></h1>\n");
 			$oP->add("</div>\n");
@@ -1006,6 +1012,8 @@ try
 			$oP->add("<div class=\"wizContainer\">\n");
 			$oP->add("<form method=\"post\">\n");
 			$aDetails = array();
+			$iFieldIndex = 0;
+			$aFieldsMap = array();
 			foreach($aExpectedAttributes as $sAttCode => $iExpectCode)
 			{
 				// Prompt for an attribute if
@@ -1017,8 +1025,10 @@ try
 					$aAttributesDef = MetaModel::ListAttributeDefs($sClass);
 					$oAttDef = $aAttributesDef[$sAttCode];
 					$aArgs = array('this' => $oObj);
-					$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oP, $sClass, $sAttCode, $oAttDef, $oObj->Get($sAttCode), $oObj->GetEditValue($sAttCode), '', '', $iExpectCode, $aArgs);
-					$aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => $sHTMLValue);
+					$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oP, $sClass, $sAttCode, $oAttDef, $oObj->Get($sAttCode), $oObj->GetEditValue($sAttCode), 'att_'.$iFieldIndex, '', $iExpectCode, $aArgs);
+					$aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => "<span id=\"field_att_$iFieldIndex\">$sHTMLValue</span>");
+					$aFieldsMap[$sAttCode] = 'att_'.$iFieldIndex;
+					$iFieldIndex++;
 				}
 			}
 			$oP->details($aDetails);
@@ -1033,6 +1043,18 @@ try
 			$oP->add("</form>\n");
 			$oP->add("</div>\n");
 			$oP->add("</div>\n");
+
+			$iFieldsCount = count($aFieldsMap);
+			$sJsonFieldsMap = json_encode($aFieldsMap);
+	
+			$oP->add_script(
+<<<EOF
+			// Initializes the object once at the beginning of the page...
+			var oWizardHelper = new WizardHelper('$sClass');
+			oWizardHelper.SetFieldsMap($sJsonFieldsMap);
+			oWizardHelper.SetFieldsCount($iFieldsCount);
+EOF
+);
 		}
 		else
 		{
@@ -1073,7 +1095,7 @@ try
 				$oP->add("<div class=\"page_header\">\n");
 				$oP->add("<h1>$sActionLabel - <span class=\"hilite\">{$oObj->GetName()}</span></h1>\n");
 				$oP->add("<p>$sActionDetails</p>\n");
-				$oP->p(Dict::Format('UI:Apply_Stimulus_On_Object_In_State_ToTarget_State', $sACtionLabel, $oObj->GetName(), $oObj->GetStateLabel(), $sTargetState));
+				$oP->p(Dict::Format('UI:Apply_Stimulus_On_Object_In_State_ToTarget_State', $sActionLabel, $oObj->GetName(), $oObj->GetStateLabel(), $sTargetState));
 				$oP->add("</div>\n");
 				$aTargetState = $aTargetStates[$sTargetState];
 				$aExpectedAttributes = $aTargetState['attribute_list'];
@@ -1101,7 +1123,7 @@ try
 					$oMyChange->Set("userinfo", $sUserString);
 					$iChangeId = $oMyChange->DBInsert();
 					$oObj->DBUpdateTracked($oMyChange);
-					$oP->p(Dict::Format('UI:Class_Object_Updated'), get_class($oObj), $oObj->GetName());
+					$oP->p(Dict::Format('UI:Class_Object_Updated', get_class($oObj), $oObj->GetName()));
 				}
 				$oObj->DisplayDetails($oP);
 			}

+ 1 - 1
pages/ajax.render.php

@@ -91,7 +91,7 @@ switch($operation)
 		$value = $oObj->Get($sAttCode);
 		$displayValue = $oObj->GetEditValue($sAttCode);
 		$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
-		$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $value, $displayValue, 'att_'.$sId, '', 0, array('this' => $oObj));
+		$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $value, $displayValue, $sId, '', 0, array('this' => $oObj));
 
 		$oWizardHelper->SetAllowedValuesHtml($sAttCode, $sHTMLValue);
 	}