Preset the object creation form with values (scalars or lists) by the mean of GET arguments. E.g. .../UI.php&operation=new&class=Team&default[org_id]=161&default[member_list][-1234][id]=&default[member_list][-1234][role]=manager

SVN:trunk[1340]
This commit is contained in:
Romain Quetiez
2011-07-07 15:45:35 +00:00
parent 265ef1e1ce
commit 532912984d
9 changed files with 217 additions and 128 deletions

View File

@@ -1426,7 +1426,7 @@ EOF
}
$sHeader = '<div class="caselog_input_header">&nbsp;'.Dict::S('UI:CaseLogTypeYourTextHere').'</div>';
$sEditValue = $oAttDef->GetEditValue($value);
$sPreviousLog = $value->GetAsHTML();
$sPreviousLog = is_object($value) ? $value->GetAsHTML() : '';
$sHTMLValue = "<div class=\"caselog\" $sStyle><table style=\"width:100%;\"><tr><td>$sHeader<textarea style=\"border:0;width:100%\" title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iId\">$sEditValue</textarea>$sPreviousLog</td><td>{$sValidationField}</td></tr></table></div>";
break;
@@ -1453,9 +1453,9 @@ EOF
}
$iMaxFileSize = utils::ConvertToBytes(ini_get('upload_max_filesize'));
$sHTMLValue = "<input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"$iMaxFileSize\" />\n";
$sHTMLValue .= "<input name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" type=\"hidden\" id=\"$iId\" \" value=\"$sFileName\"/>\n";
$sHTMLValue .= "<input name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}[filename]\" type=\"hidden\" id=\"$iId\" \" value=\"$sFileName\"/>\n";
$sHTMLValue .= "<span id=\"name_$iInputId\">$sFileName</span><br/>\n";
$sHTMLValue .= "<input title=\"$sHelpText\" name=\"file_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" type=\"file\" id=\"file_$iId\" onChange=\"UpdateFileName('$iId', this.value)\"/>&nbsp;{$sValidationField}\n";
$sHTMLValue .= "<input title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}[fcontents]\" type=\"file\" id=\"file_$iId\" onChange=\"UpdateFileName('$iId', this.value)\"/>&nbsp;{$sValidationField}\n";
break;
case 'List':
@@ -1806,6 +1806,7 @@ EOF
);
$oPage->add_ready_script(
<<<EOF
oWizardHelper$sPrefix.UpdateWizard();
// Starts the validation when the page is ready
CheckFields('form_{$this->m_iFormId}', false);
@@ -2152,11 +2153,12 @@ EOF
}
/**
* Updates the object from the POSTed parameters
* Updates the object from a flat array of values
* @param array $aAttList array of attcode
* @return array of attcodes that can be used for writing on the current object
*/
public function UpdateObject($sFormPrefix = '', $aAttList = null)
public function GetWriteableAttList($aAttList, &$aErrors)
{
$aErrors = array();
if (!is_array($aAttList))
{
$aAttList = $this->FlattenZList(MetaModel::GetZListItems(get_class($this), 'details'));
@@ -2175,6 +2177,8 @@ EOF
}
}
}
$aWriteableAttList = array();
foreach($aAttList as $sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
@@ -2188,105 +2192,142 @@ EOF
}
elseif($iFlags & OPT_ATT_SLAVE)
{
$value = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null);
if (!is_null($value) && ($value != $this->Get($sAttCode)))
{
$aErrors[] = Dict::Format('UI:AttemptingToSetASlaveAttribute_Name', $oAttDef->GetLabel());
}
}
elseif ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect())
{
$aLinks = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null);
$sLinkedClass = $oAttDef->GetLinkedClass();
$sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
$oLinkedSet = DBObjectSet::FromScratch($sLinkedClass);
if (is_array($aLinks))
{
foreach($aLinks as $id => $aData)
{
if (is_numeric($id))
{
if ($id < 0)
{
// New link to be created, the opposite of the id (-$id) is the ID of the remote object
$oLink = MetaModel::NewObject($sLinkedClass);
$oLink->Set($sExtKeyToRemote, -$id);
$oLink->Set($sExtKeyToMe, $this->GetKey());
}
else
{
// Existing link, potentially to be updated...
$oLink = MetaModel::GetObject($sLinkedClass, $id);
}
// Now populate the attributes
foreach($aData as $sName => $value)
{
if (MetaModel::IsValidAttCode($sLinkedClass, $sName))
{
$oLinkAttDef = MetaModel::GetAttributeDef($sLinkedClass, $sName);
if ($oLinkAttDef->IsWritable())
{
$oLink->Set($sName, $value);
}
}
}
$oLinkedSet->AddObject($oLink);
}
}
}
$this->Set($sAttCode, $oLinkedSet);
}
elseif ($oAttDef->GetEditClass() == 'Document')
{
// There should be an uploaded file with the named attr_<attCode>
$oDocument = utils::ReadPostedDocument("file_{$sFormPrefix}{$sAttCode}");
if (!$oDocument->IsEmpty())
{
// A new file has been uploaded
$this->Set($sAttCode, $oDocument);
}
}
elseif ($oAttDef->GetEditClass() == 'One Way Password')
{
// Check if the password was typed/changed
$bChanged = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_changed", false);
if ($bChanged)
{
// The password has been changed or set
$rawValue = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null);
$this->Set($sAttCode, $rawValue);
}
}
elseif ($oAttDef->GetEditClass() == 'Duration')
{
$rawValue = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null);
if (!is_array($rawValue)) continue;
$iValue = (((24*$rawValue['d'])+$rawValue['h'])*60 +$rawValue['m'])*60 + $rawValue['s'];
$this->Set($sAttCode, $iValue);
$previousValue = $this->Get($sAttCode);
if ($previousValue !== $iValue)
{
$this->Set($sAttCode, $iValue);
}
$aErrors[] = Dict::Format('UI:AttemptingToSetASlaveAttribute_Name', $oAttDef->GetLabel());
}
else
{
$rawValue = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null);
if (!is_null($rawValue))
$aWriteableAttList[$sAttCode] = $oAttDef;
}
}
}
return $aWriteableAttList;
}
/**
* Updates the object from a flat array of values
* @param string $aValues array of attcode => scalar or array (N-N links)
* @return void
*/
public function UpdateObjectFromArray($aValues)
{
foreach($aValues as $sAttCode => $value)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect())
{
$aLinks = $value;
$sLinkedClass = $oAttDef->GetLinkedClass();
$sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
$oLinkedSet = DBObjectSet::FromScratch($sLinkedClass);
if (is_array($aLinks))
{
foreach($aLinks as $id => $aData)
{
$aAttributes[$sAttCode] = trim($rawValue);
$previousValue = $this->Get($sAttCode);
if ($previousValue !== $aAttributes[$sAttCode])
if (is_numeric($id))
{
$this->Set($sAttCode, $aAttributes[$sAttCode]);
if ($id < 0)
{
// New link to be created, the opposite of the id (-$id) is the ID of the remote object
$oLink = MetaModel::NewObject($sLinkedClass);
$oLink->Set($sExtKeyToRemote, -$id);
$oLink->Set($sExtKeyToMe, $this->GetKey());
}
else
{
// Existing link, potentially to be updated...
$oLink = MetaModel::GetObject($sLinkedClass, $id);
}
// Now populate the attributes
foreach($aData as $sName => $value)
{
if (MetaModel::IsValidAttCode($sLinkedClass, $sName))
{
$oLinkAttDef = MetaModel::GetAttributeDef($sLinkedClass, $sName);
if ($oLinkAttDef->IsWritable())
{
$oLink->Set($sName, $value);
}
}
}
$oLinkedSet->AddObject($oLink);
}
}
}
$this->Set($sAttCode, $oLinkedSet);
}
elseif ($oAttDef->GetEditClass() == 'Document')
{
// There should be an uploaded file with the named attr_<attCode>
$oDocument = $value['fcontents'];
if (!$oDocument->IsEmpty())
{
// A new file has been uploaded
$this->Set($sAttCode, $oDocument);
}
}
elseif ($oAttDef->GetEditClass() == 'One Way Password')
{
// Check if the password was typed/changed
$aPwdData = $value;
if (!is_null($aPwdData) && $aPwdData['changed'])
{
// The password has been changed or set
$this->Set($sAttCode, $aPwdData['value']);
}
}
elseif ($oAttDef->GetEditClass() == 'Duration')
{
$aDurationData = $value;
if (!is_array($aDurationData)) continue;
$iValue = (((24*$aDurationData['d'])+$aDurationData['h'])*60 +$aDurationData['m'])*60 + $aDurationData['s'];
$this->Set($sAttCode, $iValue);
$previousValue = $this->Get($sAttCode);
if ($previousValue !== $iValue)
{
$this->Set($sAttCode, $iValue);
}
}
else
{
if (!is_null($value))
{
$aAttributes[$sAttCode] = trim($value);
$previousValue = $this->Get($sAttCode);
if ($previousValue !== $aAttributes[$sAttCode])
{
$this->Set($sAttCode, $aAttributes[$sAttCode]);
}
}
}
}
}
/**
* Updates the object from the POSTed parameters (form)
*/
public function UpdateObject($sFormPrefix = '', $aAttList = null)
{
$aErrors = array();
$aValues = array();
foreach($this->GetWriteableAttList($aAttList, $aErrors) as $sAttCode => $oAttDef)
{
if ($oAttDef->GetEditClass() == 'Document')
{
$value = array('fcontents' => utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents'));
}
else
{
$value = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null);
}
if (!is_null($value))
{
$aValues[$sAttCode] = $value;
}
}
$this->UpdateObjectFromArray($aValues);
// Invoke extensions after the update of the object from the form
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
@@ -2296,6 +2337,27 @@ EOF
return $aErrors;
}
/**
* Updates the object from a given page argument
*/
public function UpdateObjectFromArg($sArgName, $aAttList = null)
{
$aErrors = array();
$aRawValues = utils::ReadParam($sArgName, array());
$aValues = array();
foreach($this->GetWriteableAttList($aAttList, $aErrors) as $sAttCode => $oAttDef)
{
if (isset($aRawValues[$sAttCode]))
{
$aValues[$sAttCode] = $aRawValues[$sAttCode];
}
}
$this->UpdateObjectFromArray($aValues);
return $aErrors;
}
protected function DBInsertTracked_Internal($bDoNotReload = false)
{
$res = parent::DBInsertTracked_Internal($bDoNotReload);

View File

@@ -167,6 +167,12 @@ EOF
window.location.href = '?operation=';
return false;
}
function SetWizardNextStep(sStep)
{
var next_step = $('input[id=next_step]');
next_step.val(sStep);
}
EOF
);
@@ -668,17 +674,17 @@ EOF
}
$sStepHistory = implode(',', $aPreviousSteps);
$this->add("<input type=\"hidden\" name=\"step_history\" value=\"$sStepHistory\">");
$this->add("<input type=\"hidden\" id=\"step_history\" name=\"step_history\" value=\"$sStepHistory\">");
if (!is_null($sNextStep))
{
$this->add("<input type=\"hidden\" name=\"next_step\" value=\"$sNextStep\">");
$this->add("<input type=\"hidden\" id=\"next_step\" name=\"next_step\" value=\"$sNextStep\">");
}
$this->add("<input type=\"hidden\" name=\"step_back\" value=\"0\">");
$this->add("<input type=\"hidden\" id=\"step_back\" name=\"step_back\" value=\"0\">");
$sTransactionId = utils::GetNewTransactionId();
$this->SetTransactionId($sTransactionId);
$this->add("<input type=\"hidden\" name=\"transaction_id\" value=\"$sTransactionId\">\n");
$this->add("<input type=\"hidden\" id=\"transaction_id\" name=\"transaction_id\" value=\"$sTransactionId\">\n");
$oPage->add_ready_script("$(window).unload(function() { OnUnload('$sTransactionId') } );\n");
}

View File

@@ -187,7 +187,7 @@ EOF
<<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper);
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
$('#label_$this->iId').autocomplete('../pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter', json: $sWizHelperJSON }});
$('#label_$this->iId').autocomplete('../pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter', json: function() { return $sWizHelperJSON; } }});
$('#label_$this->iId').blur(function() { $(this).search(); } );
$('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly !
$('#label_$this->iId').result( function(event, data, formatted) { OnAutoComplete('{$this->iId}', event, data, formatted); } );

View File

@@ -219,10 +219,18 @@ class UILinksWidget
while($oCurrentLink = $oValue->Fetch())
{
$aRow = array();
$key = $oCurrentLink->GetKey();
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote));
if ($oCurrentLink->IsNew())
{
$key = -$oLinkedObj->GetKey();
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $key, $aArgs);
}
else
{
$key = $oCurrentLink->GetKey();
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs);
}
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs);
}
$sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';

View File

@@ -52,13 +52,13 @@ class UIPasswordWidget
{
$sCode = $this->sAttCode.$this->sNameSuffix;
$iWidgetIndex = self::$iWidgetIndex;
$sPasswordValue = utils::ReadPostedParam("attr_$sCode", '*****');
$sConfirmPasswordValue = utils::ReadPostedParam("attr_{$sCode}_confirmed", '*****');
$sPasswordValue = utils::ReadPostedParam("attr_{$sCode}[value]", '*****');
$sConfirmPasswordValue = utils::ReadPostedParam("attr_{$sCode}[confirm]", '*****');
$sChangedValue = (($sPasswordValue != '*****') || ($sConfirmPasswordValue != '*****')) ? 1 : 0;
$sHtmlValue = '';
$sHtmlValue = '<input type="password" maxlength="255" name="attr_'.$sCode.'" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>&nbsp;<span class="form_validation" id="v_'.$this->iId.'"></span><br/>';
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'_confirmed"/> '.Dict::S('UI:PasswordConfirm').' <input id="'.$this->iId.'_reset" type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
$sHtmlValue .= '<input type="hidden" id="'.$this->iId.'_changed" name="attr_'.$sCode.'_changed" value="'.$sChangedValue.'"/>';
$sHtmlValue = '<input type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>&nbsp;<span class="form_validation" id="v_'.$this->iId.'"></span><br/>';
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/> '.Dict::S('UI:PasswordConfirm').' <input id="'.$this->iId.'_reset" type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
$sHtmlValue .= '<input type="hidden" id="'.$this->iId.'_changed" name="attr_'.$sCode.'[changed]" value="'.$sChangedValue.'"/>';
$oPage->add_ready_script("$('#$this->iId').bind('keyup change', function(evt) { return PasswordFieldChanged('$this->iId') } );"); // Bind to a custom event: validate
$oPage->add_ready_script("$('#$this->iId').bind('keyup change validate', function(evt, sFormId) { return ValidatePasswordField('$this->iId', sFormId) } );"); // Bind to a custom event: validate

View File

@@ -151,18 +151,25 @@ class utils
/**
* Reads an uploaded file and turns it into an ormDocument object - Triggers an exception in case of error
* @param string $sName Name of the input used from uploading the file
* @param string $sIndex If Name is an array of posted files, then the index must be used to point out the file
* @return ormDocument The uploaded file (can be 'empty' if nothing was uploaded)
*/
public static function ReadPostedDocument($sName)
public static function ReadPostedDocument($sName, $sIndex = null)
{
$oDocument = new ormDocument(); // an empty document
if(isset($_FILES[$sName]))
{
switch($_FILES[$sName]['error'])
$aFileInfo = $_FILES[$sName];
$sError = is_null($sIndex) ? $aFileInfo['error'] : $aFileInfo['error'][$sIndex];
switch($sError)
{
case UPLOAD_ERR_OK:
$doc_content = file_get_contents($_FILES[$sName]['tmp_name']);
$sMimeType = $_FILES[$sName]['type'];
$sTmpName = is_null($sIndex) ? $aFileInfo['tmp_name'] : $aFileInfo['tmp_name'][$sIndex];
$sMimeType = is_null($sIndex) ? $aFileInfo['type'] : $aFileInfo['type'][$sIndex];
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
$doc_content = file_get_contents($sTmpName);
if (function_exists('finfo_file'))
{
// as of PHP 5.3 the fileinfo extension is bundled within PHP
@@ -180,7 +187,7 @@ class utils
}
@finfo_close($rInfo);
}
$oDocument = new ormDocument($doc_content, $sMimeType, $_FILES[$sName]['name']);
$oDocument = new ormDocument($doc_content, $sMimeType, $sName);
break;
case UPLOAD_ERR_NO_FILE:
@@ -205,11 +212,12 @@ class utils
break;
case UPLOAD_ERR_EXTENSION:
throw new FileUploadException(Dict::Format('UI:Error:UploadStoppedByExtension_FileName', $_FILES[$sName]['name']));
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
throw new FileUploadException(Dict::Format('UI:Error:UploadStoppedByExtension_FileName', $sName));
break;
default:
throw new FileUploadException(Dict::Format('UI:Error:UploadFailedUnknownCause_Code', $_FILES[$sName]['error']));
throw new FileUploadException(Dict::Format('UI:Error:UploadFailedUnknownCause_Code', $sError));
break;
}

View File

@@ -98,7 +98,7 @@ class WizardHelper
{
if ($bReadUploadedFiles)
{
$oDocument = utils::ReadPostedDocument('file_'.$sAttCode);
$oDocument = utils::ReadPostedDocument('attr_'.$sAttCode, 'fcontents');
$oObj->Set($sAttCode, $oDocument);
}
else

View File

@@ -153,7 +153,7 @@ function CheckFields(sFormId, bDisplayAlert)
// 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[type!=hidden]').each( function()
$('#'+sFormId+' :input').each( function()
{
validateEventResult = $(this).trigger('validate', sFormId);
}

View File

@@ -1013,7 +1013,6 @@ EOF
{
$aArgs[$key] = $oAppContext->GetCurrentValue($key);
}
// If the specified class has subclasses, ask the user an instance of which class to create
$aSubClasses = MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
$aPossibleClasses = array();
@@ -1047,26 +1046,32 @@ EOF
$oP->set_title(Dict::Format('UI:CreationPageTitle_Class', $sClassLabel));
$oP->add("<h1>".MetaModel::GetClassIcon($sRealClass)."&nbsp;".Dict::Format('UI:CreationTitle_Class', $sClassLabel)."</h1>\n");
$oP->add("<div class=\"wizContainer\">\n");
$aDefaults = utils::ReadParam('default', array());
$aContext = $oAppContext->GetAsHash();
foreach( $oAppContext->GetNames() as $key)
{
$aDefaults[$key] = $oAppContext->GetCurrentValue($key);
}
// Set all the default values in an object and clone this "default" object
$oObjToClone = MetaModel::NewObject($sRealClass);
foreach($aDefaults as $sName => $value)
// 1st - set context values
$aContext = $oAppContext->GetAsHash();
foreach($oAppContext->GetNames() as $key)
{
if (MetaModel::IsValidAttCode($sRealClass, $sName))
if (MetaModel::IsValidAttCode($sRealClass, $key))
{
$oAttDef = MetaModel::GetAttributeDef($sRealClass, $sName);
$oAttDef = MetaModel::GetAttributeDef($sRealClass, $key);
if ($oAttDef->IsWritable())
{
$oObjToClone->Set($sName, $value);
$value = $oAppContext->GetCurrentValue($key, null);
if (!is_null($value))
{
$oObjToClone->Set($key, $value);
}
}
}
}
cmdbAbstractObject::DisplayCreationForm($oP, $sRealClass, $oObjToClone, array('default' => $aDefaults));
// 2nd - set values from the page argument 'default'
$oObjToClone->UpdateObjectFromArg('default');
cmdbAbstractObject::DisplayCreationForm($oP, $sRealClass, $oObjToClone, array('XXXXXXdefault' => $aDefaults=null));
$oP->add("</div>\n");
}
else