diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php
index 42efdf3b4..fa753f45f 100644
--- a/datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php
+++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php
@@ -152,7 +152,7 @@ class UserProfileBrickController extends BrickController
// Preparing forms
$aData['forms']['contact'] = $this->ObjectFormHandlerHelper->HandleForm($oRequest, $sFormMode, $sCurContactClass,
$sCurContactId,
- $oBrick->GetForm());
+ );
$aData['forms']['preferences'] = $this->HandlePreferencesForm($oRequest, $sFormMode);
// - If user can change password, we display the form
$aData['forms']['password'] = (UserRights::CanChangePassword()) ? $this->HandlePasswordForm($oRequest, $sFormMode) : null;
@@ -428,7 +428,7 @@ class UserProfileBrickController extends BrickController
'sObjectField' => $sPictureAttCode,
'cache' => 86400,
's' => $oOrmDoc->GetSignature(),
- ]);
+ ]);
$aFormData['validation'] = array(
'valid' => true,
'messages' => array(),
diff --git a/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php b/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php
index 093e4c6f3..dbc46912b 100644
--- a/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php
+++ b/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php
@@ -96,6 +96,9 @@ class ObjectFormManager extends FormManager
/** @var array $aPlugins plugins data */
private array $aPlugins = array();
+ private array $aFieldsAtts = [];
+ private array $aExtraData = [];
+ private DOMDocument $oHtmlDocument;
/**
* @param string|array $formManagerData value of the formmanager_data portal parameter, either JSON or object
@@ -164,17 +167,6 @@ class ObjectFormManager extends FormManager
$oFormManager->SetActionRulesToken($aJson['formactionrulestoken']);
}
- // Retrieving form properties
- if (isset($aJson['formproperties']))
- {
- // As empty array are no passed through HTTP, this one is not always present and we have to ensure it is.
- if (!isset($aJson['formproperties']['fields']))
- {
- $aJson['formproperties']['fields'] = array();
- }
- $oFormManager->SetFormProperties($aJson['formproperties']);
- }
-
// Retrieving callback urls
if (!isset($aJson['formcallbacks'])) {
// TODO
@@ -330,7 +322,6 @@ class ObjectFormManager extends FormManager
}
$aJson['formmode'] = $this->sMode;
$aJson['formactionrulestoken'] = $this->sActionRulesToken;
- $aJson['formproperties'] = $this->aFormProperties;
return $aJson;
}
@@ -345,21 +336,6 @@ class ObjectFormManager extends FormManager
{
$sObjectClass = get_class($this->oObject);
- $aFieldsAtts = array();
- $aFieldsDMOnlyAttCodes = array();
- $aFieldsExtraData = array();
-
- if ($this->oForm !== null)
- {
- $oForm = $this->oForm;
- }
- else
- {
- $aFormId = 'objectform-'.((isset($this->aFormProperties['id'])) ? $this->aFormProperties['id'] : 'default').'-'.uniqid();
- $oForm = new Form($aFormId);
- $oForm->SetTransactionId(utils::GetNewTransactionId());
- }
-
// Building form from its properties
// - Consistency checks for stimulus form
if (isset($this->aFormProperties['stimulus_code']))
@@ -374,297 +350,21 @@ class ObjectFormManager extends FormManager
throw new Exception($sExceptionMessage);
}
}
- // - The fields
- switch ($this->aFormProperties['type'])
- {
- case 'custom_list':
- case 'static':
- foreach ($this->aFormProperties['fields'] as $sAttCode => $aOptions)
- {
- // When in a transition and no flags are specified for the field, we will retrieve its flags from DM later
- if ($this->IsTransitionForm() && empty($aOptions))
- {
- $aFieldsDMOnlyAttCodes[] = $sAttCode;
- continue;
- }
- // Otherwise we proceed as usual
- $iFieldFlags = OPT_ATT_NORMAL;
- // Checking if field should be slave
- if (isset($aOptions['slave']) && ($aOptions['slave'] === true))
- {
- $iFieldFlags = $iFieldFlags | OPT_ATT_SLAVE;
- }
- // Checking if field should be must_change
- if (isset($aOptions['must_change']) && ($aOptions['must_change'] === true))
- {
- $iFieldFlags = $iFieldFlags | OPT_ATT_MUSTCHANGE;
- }
- // Checking if field should be must prompt
- if (isset($aOptions['must_prompt']) && ($aOptions['must_prompt'] === true))
- {
- $iFieldFlags = $iFieldFlags | OPT_ATT_MUSTPROMPT;
- }
- // Checking if field should be hidden
- if (isset($aOptions['hidden']) && ($aOptions['hidden'] === true))
- {
- $iFieldFlags = $iFieldFlags | OPT_ATT_HIDDEN;
- }
- // Checking if field should be readonly
- if (isset($aOptions['read_only']) && ($aOptions['read_only'] === true))
- {
- $iFieldFlags = $iFieldFlags | OPT_ATT_READONLY;
- }
- // Checking if field should be mandatory
- if (isset($aOptions['mandatory']) && ($aOptions['mandatory'] === true))
- {
- $iFieldFlags = $iFieldFlags | OPT_ATT_MANDATORY;
- }
- // Finally, adding the attribute and its flags
- $aFieldsAtts[$sAttCode] = $iFieldFlags;
- }
- break;
- case 'zlist':
- foreach (MetaModel::FlattenZList(MetaModel::GetZListItems($sObjectClass, $this->aFormProperties['fields'])) as $sAttCode)
- {
- $aFieldsAtts[$sAttCode] = OPT_ATT_NORMAL;
- }
- break;
- }
- // - The layout
- if ($this->aFormProperties['layout'] !== null) {
- // Checking if we need to render the template from twig to html in order to parse the fields
- if ($this->aFormProperties['layout']['type'] === 'twig') {
-
- if ($this->oFormHandlerHelper !== null) {
- /** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $oObjectFormHandler */
- $sRendered = $this->oFormHandlerHelper->RenderFormFromTwig(
- $oForm->GetId(),
- $this->aFormProperties['layout']['content'],
- array('oRenderer' => $this->oRenderer, 'oObject' => $this->oObject)
- );
- } else {
- $sRendered = 'Form not rendered because of missing container';
- }
- } else {
- $sRendered = $this->aFormProperties['layout']['content'];
- }
-
- // Parsing rendered template to find the fields
- $oHtmlDocument = new DOMDocument();
- // Note: Loading as XML instead of HTML avoid some encoding issues (eg. 'é' was transformed to '˜©')
- $oHtmlDocument->loadXML(''.$sRendered.'');
-
- // Adding fields to the list
- $oXPath = new DOMXPath($oHtmlDocument);
- /** @var \DOMElement $oFieldNode */
- foreach ($oXPath->query('//div[contains(@class, "form_field")][@data-field-id]') as $oFieldNode)
- {
- $sFieldId = $oFieldNode->getAttribute('data-field-id');
- $sFieldFlags = $oFieldNode->getAttribute('data-field-flags');
- $iFieldFlags = OPT_ATT_NORMAL;
-
- // When in a transition and no flags are specified for the field, we will retrieve its flags from DM later
- if ($this->IsTransitionForm() && $sFieldFlags === '')
- {
- // (Might have already been added from the "fields" property)
- if (!in_array($sFieldId, $aFieldsDMOnlyAttCodes))
- {
- $aFieldsDMOnlyAttCodes[] = $sFieldId;
- }
- continue;
- }
-
- // Otherwise we proceed as usual
- foreach (explode(' ', $sFieldFlags) as $sFieldFlag)
- {
- if ($sFieldFlag !== '')
- {
- $sConst = 'OPT_ATT_'.strtoupper(str_replace('_', '', $sFieldFlag));
- if (defined($sConst))
- {
- $iFieldFlags = $iFieldFlags | constant($sConst);
- }
- else
- {
- IssueLog::Error(__METHOD__.' at line '.__LINE__.' : Flag "'.$sFieldFlag.'" is not valid for field [@data-field-id="'.$sFieldId.'"] in form[@id="'.$this->aFormProperties['id'].'"]');
- throw new Exception('Flag "'.$sFieldFlag.'" is not valid for field [@data-field-id="'.$sFieldId.'"] in form[@id="'.$this->aFormProperties['id'].'"]');
- }
- }
- }
-
- // Checking if field has form_path, if not, we add it
- if (!$oFieldNode->hasAttribute('data-form-path'))
- {
- $oFieldNode->setAttribute('data-form-path', $oForm->GetId());
- }
- // Checking if field should be displayed opened (For linked set)
- if ($oFieldNode->hasAttribute('data-field-opened') && ($oFieldNode->getAttribute('data-field-opened') === 'true'))
- {
- $aFieldsExtraData[$sFieldId]['opened'] = true;
- }
- // Checking if the field is handled by a plugin
- if ($oFieldNode->hasAttribute('data-field-plugin'))
- {
- $aFieldsExtraData[$sFieldId]['plugin'] = $oFieldNode->getAttribute('data-field-plugin');
- }
- // Checking if field allows to ignore scope (For linked set)
- if ($oFieldNode->hasAttribute('data-field-ignore-scopes') && ($oFieldNode->getAttribute('data-field-ignore-scopes') === 'true'))
- {
- $aFieldsExtraData[$sFieldId]['ignore_scopes'] = true;
- }
- // Checking field display mode
- if ($oFieldNode->hasAttribute('data-field-display-mode') && $oFieldNode->getAttribute('data-field-display-mode') !== '')
- {
- $aFieldsExtraData[$sFieldId]['display_mode'] = $oFieldNode->getAttribute('data-field-display-mode');
- }
- elseif (isset($this->aFormProperties['properties']['display_mode']))
- {
- $aFieldsExtraData[$sFieldId]['display_mode'] = $this->aFormProperties['properties']['display_mode'];
- }
- else
- {
- $aFieldsExtraData[$sFieldId]['display_mode'] = ApplicationHelper::FORM_DEFAULT_DISPLAY_MODE;
- }
-
- // Finally adding field to the list
- if (!array_key_exists($sFieldId, $aFieldsAtts))
- {
- $aFieldsAtts[$sFieldId] = OPT_ATT_NORMAL;
- }
- $aFieldsAtts[$sFieldId] = $aFieldsAtts[$sFieldId] | $iFieldFlags;
- }
-
- // Adding rendered template to the form renderer as the base layout
- $this->oRenderer->SetBaseLayout($oHtmlDocument->saveHTML());
- }
-
- // Merging flags from metamodel with those from the form
- // Also, retrieving mandatory attributes from metamodel to be able to complete the form with them if necessary
- //
- // Note: When in a transition, we don't do this for fields that should be set from DM
- if ($this->aFormProperties['type'] !== 'static')
- {
- if ($this->IsTransitionForm())
- {
- $aDatamodelAttCodes = $this->oObject->GetTransitionAttributes($this->aFormProperties['stimulus_code']);
- }
- else
- {
- $aDatamodelAttCodes = MetaModel::ListAttributeDefs($sObjectClass);
- }
-
- foreach ($aDatamodelAttCodes as $sAttCode => $value)
- {
- /** var AttributeDefinition $oAttDef */
-
- // Skipping fields that should come from DM only as they will be process later on
- if (in_array($sAttCode, $aFieldsDMOnlyAttCodes))
- {
- continue;
- }
-
- // Retrieving object flags
- if ($this->IsTransitionForm())
- {
- // Retrieving only mandatory flag from DM when on a transition
- $iFieldFlags = $value & OPT_ATT_MANDATORY;
- $oAttDef = MetaModel::GetAttributeDef(get_class($this->oObject), $sAttCode);
- }
- elseif ($this->oObject->IsNew())
- {
- $iFieldFlags = $this->oObject->GetInitialStateAttributeFlags($sAttCode);
- $oAttDef = $value;
- }
- else
- {
- $iFieldFlags = $this->oObject->GetAttributeFlags($sAttCode);
- $oAttDef = $value;
- }
-
- // Skipping fields that were not specified to DM only list (garbage collector)
- if ($this->IsTransitionForm() && !array_key_exists($sAttCode, $aFieldsAtts))
- {
- if ((($value & OPT_ATT_MANDATORY) === OPT_ATT_MANDATORY && $oAttDef->IsNull($this->oObject->Get($sAttCode)))
- || (($value & OPT_ATT_MUSTPROMPT) === OPT_ATT_MUSTPROMPT)
- || (($value & OPT_ATT_MUSTCHANGE) === OPT_ATT_MUSTCHANGE))
- {
- if (!in_array($sAttCode, $aFieldsDMOnlyAttCodes))
- {
- $aFieldsDMOnlyAttCodes[] = $sAttCode;
- }
- }
- continue;
- }
-
- // Merging flags with those from the form definition
- // - If the field is in fields list
- if (array_key_exists($sAttCode, $aFieldsAtts))
- {
- // .. We merge them all
- $aFieldsAtts[$sAttCode] = $aFieldsAtts[$sAttCode] | $iFieldFlags;
- }
- // - or it is mandatory and has no value
- if ((($iFieldFlags & OPT_ATT_MANDATORY) === OPT_ATT_MANDATORY) && ($this->oObject->Get($sAttCode) === ''))
- {
- if (!array_key_exists($sAttCode, $aFieldsAtts))
- {
- $aFieldsAtts[$sAttCode] = OPT_ATT_NORMAL;
- }
- $aFieldsAtts[$sAttCode] = $aFieldsAtts[$sAttCode] | OPT_ATT_MANDATORY;
- }
- }
- }
-
- // Adding fields with DM flags only
- // Note: This should only happen when in a transition
- foreach ($aFieldsDMOnlyAttCodes as $sAttCode)
- {
- // Retrieving object flags from DM
- if ($this->IsTransitionForm())
- {
- $aTransitionAtts = $this->oObject->GetTransitionAttributes($this->aFormProperties['stimulus_code']);
- $iFieldFlags = $aTransitionAtts[$sAttCode];
- }
- elseif ($this->oObject->IsNew())
- {
- $iFieldFlags = $this->oObject->GetInitialStateAttributeFlags($sAttCode);
- }
- else
- {
- $iFieldFlags = $this->oObject->GetAttributeFlags($sAttCode);
- }
-
- // Resetting/Forcing flag to read/write
- $aFieldsAtts[$sAttCode] = OPT_ATT_NORMAL;
- // Checking if field should be must_change
- if (($iFieldFlags & OPT_ATT_MUSTCHANGE) === OPT_ATT_MUSTCHANGE)
- {
- $aFieldsAtts[$sAttCode] = $aFieldsAtts[$sAttCode] | OPT_ATT_MUSTCHANGE;
- }
- // Checking if field should be must_prompt
- if (($iFieldFlags & OPT_ATT_MUSTPROMPT) === OPT_ATT_MUSTPROMPT)
- {
- $aFieldsAtts[$sAttCode] = $aFieldsAtts[$sAttCode] | OPT_ATT_MUSTPROMPT;
- }
- // Checking if field should be mandatory
- if (($iFieldFlags & OPT_ATT_MANDATORY) === OPT_ATT_MANDATORY)
- {
- $aFieldsAtts[$sAttCode] = $aFieldsAtts[$sAttCode] | OPT_ATT_MANDATORY;
- }
- }
+ // Adding rendered template to the form renderer as the base layout
+ $this->oRenderer->SetBaseLayout($this->oHtmlDocument->saveHTML());
// Building the form
- foreach ($aFieldsAtts as $sAttCode => $iFieldFlags)
+ foreach ($this->aFieldsAtts as $sAttCode => $iFieldFlags)
{
// handle plugins fields
- if(array_key_exists($sAttCode, $aFieldsExtraData)
- && array_key_exists('plugin', $aFieldsExtraData[$sAttCode])){
- $sPluginName = $aFieldsExtraData[$sAttCode]['plugin'];
+ if(array_key_exists($sAttCode, $this->aExtraData)
+ && array_key_exists('plugin', $this->aExtraData[$sAttCode])){
+ $sPluginName = $this->aExtraData[$sAttCode]['plugin'];
switch($sPluginName){
case AttachmentPlugIn::class:
- $this->AddAttachmentField($oForm, $sAttCode, $aFieldsExtraData);
+ $this->AddAttachmentField($this->oForm, $sAttCode, $this->aExtraData);
break;
default:
throw new Exception('Unknown plugin ' . $sPluginName);
@@ -690,7 +390,7 @@ class ObjectFormManager extends FormManager
$aFieldDependencies = $oAttDef->GetPrerequisiteAttributes();
if (!empty($aFieldDependencies))
{
- $oForm->AddFieldDependencies($oField->GetId(), $aFieldDependencies);
+ $this->oForm->AddFieldDependencies($oField->GetId(), $aFieldDependencies);
}
// Setting the field flags
@@ -754,7 +454,7 @@ class ObjectFormManager extends FormManager
array('Combodo\\iTop\\Form\\Field\\TextAreaField', 'Combodo\\iTop\\Form\\Field\\CaseLogField')))
{
/** @var \Combodo\iTop\Form\Field\TextAreaField|\Combodo\iTop\Form\Field\CaseLogField $oField */
- $oField->SetTransactionId($oForm->GetTransactionId());
+ $oField->SetTransactionId($this->oForm->GetTransactionId());
}
// - Field that require a search endpoint
if (in_array(get_class($oField),
@@ -916,12 +616,12 @@ class ObjectFormManager extends FormManager
$oField->SetLimitedAccessItemIDs($aLimitedAccessItemIDs);
}
// - Displaying as opened
- if (array_key_exists($sAttCode, $aFieldsExtraData) && array_key_exists('opened', $aFieldsExtraData[$sAttCode]))
+ if (array_key_exists($sAttCode, $this->aExtraData) && array_key_exists('opened', $this->aExtraData[$sAttCode]))
{
$oField->SetDisplayOpened(true);
}
// - Displaying out of scopes items
- if (array_key_exists($sAttCode, $aFieldsExtraData) && array_key_exists('ignore_scopes', $aFieldsExtraData[$sAttCode]))
+ if (array_key_exists($sAttCode, $this->aExtraData) && array_key_exists('ignore_scopes', $this->aExtraData[$sAttCode]))
{
$oField->SetDisplayLimitedAccessItems(true);
}
@@ -964,9 +664,9 @@ class ObjectFormManager extends FormManager
}
// Setting field display mode
- if (array_key_exists($sAttCode, $aFieldsExtraData) && array_key_exists('display_mode', $aFieldsExtraData[$sAttCode]))
+ if (array_key_exists($sAttCode, $this->aExtraData) && array_key_exists('display_mode', $this->aExtraData[$sAttCode]))
{
- $oField->SetDisplayMode($aFieldsExtraData[$sAttCode]['display_mode']);
+ $oField->SetDisplayMode($this->aExtraData[$sAttCode]['display_mode']);
}
// Overload (AttributeDefinition) flags metadata as they have been changed while building the form
@@ -979,7 +679,7 @@ class ObjectFormManager extends FormManager
// Note: We do this at the end because during the process an hidden field could have become writable if mandatory and empty for example.
if($oField->GetHidden() === false)
{
- $oForm->AddField($oField);
+ $this->oForm->AddField($oField);
} else {
$this->aHiddenFieldsId[]=$oField->GetId();
}
@@ -987,11 +687,11 @@ class ObjectFormManager extends FormManager
// Checking dependencies to ensure that all needed fields are in the form
// (This is kind of a garbage collector for dependencies)
- foreach ($oForm->GetDependencies() as $sImpactedFieldId => $aDependencies)
+ foreach ($this->oForm->GetDependencies() as $sImpactedFieldId => $aDependencies)
{
foreach ($aDependencies as $sDependencyFieldId)
{
- if (!$oForm->HasField($sDependencyFieldId))
+ if (!$this->oForm->HasField($sDependencyFieldId))
{
try
{
@@ -999,7 +699,7 @@ class ObjectFormManager extends FormManager
$oField = $oAttDef->MakeFormField($this->oObject);
$oField->SetHidden(true);
- $oForm->AddField($oField);
+ $this->oForm->AddField($oField);
}
catch (Exception $e)
{
@@ -1014,13 +714,12 @@ class ObjectFormManager extends FormManager
// fallback Checking if the instance has attachments
// (in case attachment is not explicitly declared in layout)
if (class_exists('Attachment') && class_exists('AttachmentPlugIn')
- && !$this->IsPluginInitialized(AttachmentPlugIn::class)
- && AttachmentPlugIn::IsAttachmentAllowedForObject($this->oObject)){
- $this->AddAttachmentField($oForm, 'attachments_plugin', $aFieldsExtraData);
+ && !$this->IsPluginInitialized(AttachmentPlugIn::class)
+ && AttachmentPlugIn::IsAttachmentAllowedForObject($this->oObject)){
+ $this->AddAttachmentField($this->oForm, 'attachments_plugin', $this->aExtraData);
}
- $oForm->Finalize();
- $this->oForm = $oForm;
+ $this->oForm->Finalize();
$this->oRenderer->SetForm($this->oForm);
}
@@ -1313,182 +1012,30 @@ class ObjectFormManager extends FormManager
*/
public function OnUpdate($aArgs = null)
{
- $aFormProperties = array();
+ $aFormProperties = [];
if (is_array($aArgs))
{
- // First we need to update the Object with its new values in order to enable the dependents fields to update
- if (isset($aArgs['currentValues']))
- {
- $aCurrentValues = $aArgs['currentValues'];
- $sObjectClass = get_class($this->oObject);
- foreach ($aCurrentValues as $sAttCode => $value)
- {
- if (MetaModel::IsValidAttCode($sObjectClass, $sAttCode))
- {
- /** @var \AttributeDefinition $oAttDef */
- $oAttDef = MetaModel::GetAttributeDef($sObjectClass, $sAttCode);
- if ($oAttDef->IsLinkSet())
- {
- /** @var \AttributeLinkedSet $oAttDef */
-
- // Parsing JSON value
- //
- // Note : The value was passed as a string instead of an array because the attribute would not be included in the $aCurrentValues when empty.
- // Which was an issue when deleting all objects from linkedset
- $value = json_decode($value, true);
-
- /** @var \ormLinkSet $oLinkSet */
- $oLinkSet = $this->oObject->Get($sAttCode);
- $sLinkedClass = $oAttDef->GetLinkedClass();
-
- // Checking links to remove
- if (isset($value['remove']))
- {
- foreach ($value['remove'] as $iObjKey => $aObjData)
- {
- $oLinkSet->RemoveItem($iObjKey);
- }
- }
-
- // Checking links to add
- if (isset($value['add']))
- {
- foreach ($value['add'] as $iObjKey => $aObjdata)
- {
- // Creating link when linkset is indirect...
- if ($oAttDef->IsIndirect())
- {
- /** @var \AttributeLinkedSetIndirect $oAttDef */
- $oLink = MetaModel::NewObject($sLinkedClass);
- $oLink->Set($oAttDef->GetExtKeyToRemote(), $iObjKey);
- $oLink->Set($oAttDef->GetExtKeyToMe(), $this->oObject->GetKey());
- // Set link attributes values...
- foreach ($aObjdata as $sLinkAttCode => $oAttValue) {
- if (!is_scalar($oAttValue)) {
- IssueLog::Debug("ObjectFormManager::OnUpdate invalid link attribute value, $sLinkAttCode is not a scalar value", LogChannels::PORTAL);
- continue;
- }
- $oLink->Set($sLinkAttCode, $oAttValue);
- }
- $oLinkSet->AddItem($oLink);
- }
- // ... or adding remote object when linkset id direct
- else
- {
- // Note : AllowAllData set to true here instead of checking scope's flag because we are displaying a value that has been set and validated
- $oLink = MetaModel::GetObject($sLinkedClass, $iObjKey, false, true);
- }
-
- if ($oLink !== null) {
- $oLinkSet->AddItem($oLink);
- }
- }
- }
-
- // Checking links to modify
- if ($oAttDef->IsIndirect() && isset($value['current'])) {
- foreach ($value['current'] as $iObjKey => $aObjData) {
- if ($iObjKey < 0) {
- continue;
- }
- $oLink = null;
- $oLinkSet->Rewind();
- foreach ($oLinkSet as $oItem) {
- if ($oItem->Get('id') != $iObjKey) {
- continue;
- }
- $oLink = $oItem;
- foreach ($aObjData as $sLinkAttCode => $oAttValue) {
- if (!is_scalar($oAttValue)) {
- IssueLog::Debug("ObjectFormManager::OnUpdate invalid link attribute value, $sLinkAttCode is not a scalar value", LogChannels::PORTAL);
- continue;
- }
- $oLink->Set($sLinkAttCode, $oAttValue);
- }
- $oLinkSet->ModifyItem($oLink);
- }
- }
- }
-
- // Setting value in the object
- $this->oObject->Set($sAttCode, $oLinkSet);
- } elseif ($oAttDef instanceof AttributeSet) {
- /** @var \ormSet $oTagSet */
- $oOrmSet = $this->oObject->Get($sAttCode);
- if (is_null($oOrmSet)) {
- $oOrmSet = new \ormSet(get_class($this->oObject), $sAttCode, $oAttDef->GetMaxItems());
- }
- $oOrmSet->ApplyDelta(json_decode($value, true));
- $this->oObject->Set($sAttCode, $oOrmSet);
- } elseif ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
- {
- if ($value != null) {
- $value = $oAttDef->GetFormat()->Parse($value);
- if (is_object($value)) {
- $value = $value->format($oAttDef->GetInternalFormat());
- }
- }
- $this->oObject->Set($sAttCode, $value);
- }
- elseif ($oAttDef->IsScalar() && is_array($value))
- {
- $this->oObject->Set($sAttCode, current($value));
- }
- elseif ($oAttDef->GetEditClass() === 'CustomFields')
- {
- // We don't update attribute as ormCustomField comparaison is not working as excepted.
- // When several templates available, "template_id" is not sent by the portal has it is a read-only select input
- // therefore, the TemplateFieldsHandler::CompareValues() doesn't work.
- // This use case works in the console as it always send all fields, even hidden and read-only.
-
- // Different templates
- if (isset($value['template_id'])
- && ($value['template_id'] != $value['current_template_id']))
- {
- $this->oObject->Set($sAttCode, $value);
- }
- // Same template, different fields
- elseif (isset($value['template_id'], $value['template_data'])
- && ($value['template_id'] == $value['current_template_id'])
- && ($value['template_data'] != $value['current_template_data']))
- {
- $this->oObject->Set($sAttCode, $value);
- }
- // Update of current values
- elseif (isset($value['user_data']))
- {
- $this->oObject->Set($sAttCode, $value);
- }
- // Else don't update! Otherwise we might loose current value
- }
- else
- {
- $this->oObject->Set($sAttCode, $value);
- }
- }
- }
- /** @var SecurityHelper $oSecurityHelper */
- $oSecurityHelper = $this->oFormHandlerHelper->GetSecurityHelper();
- // N°7023 - Note that we check for ext. key now as we want the check to be done on user inputs and NOT on ext. keys set programatically, so it must be done before the DoComputeValues
- $this->oObject->CheckChangedExtKeysValues(function ($sClass, $sId) use ($oSecurityHelper): bool {
- return $oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $sClass, $sId);
- });
- $this->oObject->DoComputeValues();
- }
-
// Then we retrieve properties of the form to build
if (isset($aArgs['formProperties']))
{
$aFormProperties = $aArgs['formProperties'];
}
}
+
// Then we build and update form
// - We update form properties only we don't have any yet. This is a fallback for cases when form properties where not among the JSON data
- if ($this->GetFormProperties() === null)
- {
- $this->SetFormProperties($aFormProperties);
+ $this->SetFormProperties($aFormProperties);
+ $this->PrepareFormAndHTMLDocument();
+ $this->PrepareFields();
+
+ if (is_array($aArgs)) {
+ // - We update the form with the current values only if we have some
+ if (isset($aArgs['currentValues'])) {
+ $this->UpdateObject($aArgs['currentValues']);
+ }
}
+
$this->Build();
}
@@ -1549,7 +1096,7 @@ class ObjectFormManager extends FormManager
$this->oObject->FireEvent(EVENT_ADD_ATTACHMENT_TO_OBJECT, $aData);
}
}
-
+
// Save changes to current object history
// inspired from itop-attachments/main.attachments.php / RecordHistory
foreach ($aActions as $oChangeOp)
@@ -1684,4 +1231,421 @@ class ObjectFormManager extends FormManager
return $aAttCodesToPrint;
}
+
+ private function UpdateObject(array $aCurrentValues)
+ {
+ $sObjectClass = get_class($this->oObject);
+
+ foreach ($aCurrentValues as $sAttCode => $value)
+ {
+ if (!array_key_exists($sAttCode, $this->aFieldsAtts)) {
+ continue;
+ }
+ $iAttributeFlags = $this->aFieldsAtts[$sAttCode];
+ if ($iAttributeFlags & OPT_ATT_HIDDEN) {
+ continue;
+ }
+ if ($iAttributeFlags & OPT_ATT_READONLY) {
+ continue;
+ }
+
+ if (MetaModel::IsValidAttCode($sObjectClass, $sAttCode)) {
+ /** @var \AttributeDefinition $oAttDef */
+ $oAttDef = MetaModel::GetAttributeDef($sObjectClass, $sAttCode);
+ if ($oAttDef->IsLinkSet()) {
+ /** @var \AttributeLinkedSet $oAttDef */
+
+ // Parsing JSON value
+ //
+ // Note : The value was passed as a string instead of an array because the attribute would not be included in the $aCurrentValues when empty.
+ // Which was an issue when deleting all objects from linkedset
+ $value = json_decode($value, true);
+
+ /** @var \ormLinkSet $oLinkSet */
+ $oLinkSet = $this->oObject->Get($sAttCode);
+ $sLinkedClass = $oAttDef->GetLinkedClass();
+
+ // Checking links to remove
+ if (isset($value['remove'])) {
+ foreach ($value['remove'] as $iObjKey => $aObjData) {
+ $oLinkSet->RemoveItem($iObjKey);
+ }
+ }
+
+ // Checking links to add
+ if (isset($value['add'])) {
+ foreach ($value['add'] as $iObjKey => $aObjdata) {
+ // Creating link when linkset is indirect...
+ if ($oAttDef->IsIndirect()) {
+ /** @var \AttributeLinkedSetIndirect $oAttDef */
+ $oLink = MetaModel::NewObject($sLinkedClass);
+ $oLink->Set($oAttDef->GetExtKeyToRemote(), $iObjKey);
+ $oLink->Set($oAttDef->GetExtKeyToMe(), $this->oObject->GetKey());
+ // Set link attributes values...
+ foreach ($aObjdata as $sLinkAttCode => $oAttValue) {
+ if (!is_scalar($oAttValue)) {
+ IssueLog::Debug("ObjectFormManager::OnUpdate invalid link attribute value, $sLinkAttCode is not a scalar value", LogChannels::PORTAL);
+ continue;
+ }
+ $oLink->Set($sLinkAttCode, $oAttValue);
+ }
+ $oLinkSet->AddItem($oLink);
+ }
+ // ... or adding remote object when linkset id direct
+ else {
+ // Note : AllowAllData set to true here instead of checking scope's flag because we are displaying a value that has been set and validated
+ $oLink = MetaModel::GetObject($sLinkedClass, $iObjKey, false, true);
+ }
+
+ if ($oLink !== null) {
+ $oLinkSet->AddItem($oLink);
+ }
+ }
+ }
+
+ // Checking links to modify
+ if ($oAttDef->IsIndirect() && isset($value['current'])) {
+ foreach ($value['current'] as $iObjKey => $aObjData) {
+ if ($iObjKey < 0) {
+ continue;
+ }
+ $oLink = null;
+ $oLinkSet->Rewind();
+ foreach ($oLinkSet as $oItem) {
+ if ($oItem->Get('id') != $iObjKey) {
+ continue;
+ }
+ $oLink = $oItem;
+ foreach ($aObjData as $sLinkAttCode => $oAttValue) {
+ if (!is_scalar($oAttValue)) {
+ IssueLog::Debug("ObjectFormManager::OnUpdate invalid link attribute value, $sLinkAttCode is not a scalar value", LogChannels::PORTAL);
+ continue;
+ }
+ $oLink->Set($sLinkAttCode, $oAttValue);
+ }
+ $oLinkSet->ModifyItem($oLink);
+ }
+ }
+ }
+
+ // Setting value in the object
+ $this->oObject->Set($sAttCode, $oLinkSet);
+ } elseif ($oAttDef instanceof AttributeSet) {
+ /** @var \ormSet $oTagSet */
+ $oOrmSet = $this->oObject->Get($sAttCode);
+ if (is_null($oOrmSet)) {
+ $oOrmSet = new \ormSet(get_class($this->oObject), $sAttCode, $oAttDef->GetMaxItems());
+ }
+ $oOrmSet->ApplyDelta(json_decode($value, true));
+ $this->oObject->Set($sAttCode, $oOrmSet);
+ } elseif ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
+ {
+ if ($value != null) {
+ $value = $oAttDef->GetFormat()->Parse($value);
+ if (is_object($value)) {
+ $value = $value->format($oAttDef->GetInternalFormat());
+ }
+ }
+ $this->oObject->Set($sAttCode, $value);
+ }
+ elseif ($oAttDef->IsScalar() && is_array($value)) {
+ $this->oObject->Set($sAttCode, current($value));
+ }
+ elseif ($oAttDef->GetEditClass() === 'CustomFields') {
+ // We don't update attribute as ormCustomField comparaison is not working as excepted.
+ // When several templates available, "template_id" is not sent by the portal has it is a read-only select input
+ // therefore, the TemplateFieldsHandler::CompareValues() doesn't work.
+ // This use case works in the console as it always send all fields, even hidden and read-only.
+
+ // Different templates
+ if (isset($value['template_id'])
+ && ($value['template_id'] != $value['current_template_id'])) {
+ $this->oObject->Set($sAttCode, $value);
+ }
+ // Same template, different fields
+ elseif (isset($value['template_id'], $value['template_data'])
+ && ($value['template_id'] == $value['current_template_id'])
+ && ($value['template_data'] != $value['current_template_data'])) {
+ $this->oObject->Set($sAttCode, $value);
+ }
+ // Update of current values
+ elseif (isset($value['user_data'])) {
+ $this->oObject->Set($sAttCode, $value);
+ }
+ // Else don't update! Otherwise we might loose current value
+ }
+ else {
+ $this->oObject->Set($sAttCode, $value);
+ }
+ }
+ }
+ /** @var SecurityHelper $oSecurityHelper */
+ $oSecurityHelper = $this->oFormHandlerHelper->GetSecurityHelper();
+ // N°7023 - Note that we check for ext. key now as we want the check to be done on user inputs and NOT on ext. keys set programatically, so it must be done before the DoComputeValues
+ $this->oObject->CheckChangedExtKeysValues(function ($sClass, $sId) use ($oSecurityHelper): bool {
+ return $oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $sClass, $sId);
+ });
+ $this->oObject->DoComputeValues();
+ }
+
+ /**
+ * @return void
+ * @throws \ArchivedObjectException
+ * @throws \CoreException
+ */
+ public function PrepareFields(): void
+ {
+ $sObjectClass = get_class($this->oObject);
+ $this->aFieldsAtts = array();
+ $this->aExtraData = array();
+ $aFieldsDMOnlyAttCodes = array();
+ switch ($this->aFormProperties['type']) {
+ case 'custom_list':
+ case 'static':
+ foreach ($this->aFormProperties['fields'] as $sAttCode => $aOptions) {
+ // When in a transition and no flags are specified for the field, we will retrieve its flags from DM later
+ if ($this->IsTransitionForm() && empty($aOptions)) {
+ $aFieldsDMOnlyAttCodes[] = $sAttCode;
+ continue;
+ }
+
+ // Otherwise we proceed as usual
+ $iFieldFlags = OPT_ATT_NORMAL;
+ // Checking if field should be slave
+ if (isset($aOptions['slave']) && ($aOptions['slave'] === true)) {
+ $iFieldFlags = $iFieldFlags | OPT_ATT_SLAVE;
+ }
+ // Checking if field should be must_change
+ if (isset($aOptions['must_change']) && ($aOptions['must_change'] === true)) {
+ $iFieldFlags = $iFieldFlags | OPT_ATT_MUSTCHANGE;
+ }
+ // Checking if field should be must prompt
+ if (isset($aOptions['must_prompt']) && ($aOptions['must_prompt'] === true)) {
+ $iFieldFlags = $iFieldFlags | OPT_ATT_MUSTPROMPT;
+ }
+ // Checking if field should be hidden
+ if (isset($aOptions['hidden']) && ($aOptions['hidden'] === true)) {
+ $iFieldFlags = $iFieldFlags | OPT_ATT_HIDDEN;
+ }
+ // Checking if field should be readonly
+ if (isset($aOptions['read_only']) && ($aOptions['read_only'] === true)) {
+ $iFieldFlags = $iFieldFlags | OPT_ATT_READONLY;
+ }
+ // Checking if field should be mandatory
+ if (isset($aOptions['mandatory']) && ($aOptions['mandatory'] === true)) {
+ $iFieldFlags = $iFieldFlags | OPT_ATT_MANDATORY;
+ }
+ // Finally, adding the attribute and its flags
+ $this->aFieldsAtts[$sAttCode] = $iFieldFlags;
+ }
+ break;
+
+ case 'zlist':
+ foreach (MetaModel::FlattenZList(MetaModel::GetZListItems($sObjectClass, $this->aFormProperties['fields'])) as $sAttCode) {
+ $this->aFieldsAtts[$sAttCode] = OPT_ATT_NORMAL;
+ }
+ break;
+ }
+
+ if ($this->aFormProperties['layout'] !== null) {
+ $oXPath = new DOMXPath($this->oHtmlDocument);
+ /** @var \DOMElement $oFieldNode */
+ foreach ($oXPath->query('//div[contains(@class, "form_field")][@data-field-id]') as $oFieldNode) {
+ $sFieldId = $oFieldNode->getAttribute('data-field-id');
+ $sFieldFlags = $oFieldNode->getAttribute('data-field-flags');
+ $iFieldFlags = OPT_ATT_NORMAL;
+
+ // When in a transition and no flags are specified for the field, we will retrieve its flags from DM later
+ if ($this->IsTransitionForm() && $sFieldFlags === '') {
+ // (Might have already been added from the "fields" property)
+ if (!in_array($sFieldId, $aFieldsDMOnlyAttCodes)) {
+ $aFieldsDMOnlyAttCodes[] = $sFieldId;
+ }
+ continue;
+ }
+
+ // Otherwise we proceed as usual
+ foreach (explode(' ', $sFieldFlags) as $sFieldFlag) {
+ if ($sFieldFlag !== '') {
+ $sConst = 'OPT_ATT_'.strtoupper(str_replace('_', '', $sFieldFlag));
+ if (defined($sConst)) {
+ $iFieldFlags = $iFieldFlags | constant($sConst);
+ } else {
+ IssueLog::Error(__METHOD__.' at line '.__LINE__.' : Flag "'.$sFieldFlag.'" is not valid for field [@data-field-id="'.$sFieldId.'"] in form[@id="'.$this->aFormProperties['id'].'"]');
+ throw new Exception('Flag "'.$sFieldFlag.'" is not valid for field [@data-field-id="'.$sFieldId.'"] in form[@id="'.$this->aFormProperties['id'].'"]');
+ }
+ }
+ }
+
+ // Checking if field should be displayed opened (For linked set)
+ if ($oFieldNode->hasAttribute('data-field-opened') && ($oFieldNode->getAttribute('data-field-opened') === 'true')) {
+ $this->aExtraData[$sFieldId]['opened'] = true;
+ }
+ // Checking if the field is handled by a plugin
+ if ($oFieldNode->hasAttribute('data-field-plugin')) {
+ $this->aExtraData[$sFieldId]['plugin'] = $oFieldNode->getAttribute('data-field-plugin');
+ }
+ // Checking if field allows to ignore scope (For linked set)
+ if ($oFieldNode->hasAttribute('data-field-ignore-scopes') && ($oFieldNode->getAttribute('data-field-ignore-scopes') === 'true')) {
+ $this->aExtraData[$sFieldId]['ignore_scopes'] = true;
+ }
+ // Checking field display mode
+ if ($oFieldNode->hasAttribute('data-field-display-mode') && $oFieldNode->getAttribute('data-field-display-mode') !== '') {
+ $this->aExtraData[$sFieldId]['display_mode'] = $oFieldNode->getAttribute('data-field-display-mode');
+ } elseif (isset($this->aFormProperties['properties']['display_mode'])) {
+ $this->aExtraData[$sFieldId]['display_mode'] = $this->aFormProperties['properties']['display_mode'];
+ } else {
+ $this->aExtraData[$sFieldId]['display_mode'] = ApplicationHelper::FORM_DEFAULT_DISPLAY_MODE;
+ }
+
+ // Finally adding field to the list
+ if (!array_key_exists($sFieldId, $this->aFieldsAtts)) {
+ $this->aFieldsAtts[$sFieldId] = OPT_ATT_NORMAL;
+ }
+ $this->aFieldsAtts[$sFieldId] = $this->aFieldsAtts[$sFieldId] | $iFieldFlags;
+ }
+ }
+
+ // Merging flags from metamodel with those from the form
+ // Also, retrieving mandatory attributes from metamodel to be able to complete the form with them if necessary
+ //
+ // Note: When in a transition, we don't do this for fields that should be set from DM
+ if ($this->aFormProperties['type'] !== 'static') {
+ if ($this->IsTransitionForm()) {
+ $aDatamodelAttCodes = $this->oObject->GetTransitionAttributes($this->aFormProperties['stimulus_code']);
+ }
+ else {
+ $aDatamodelAttCodes = MetaModel::ListAttributeDefs($sObjectClass);
+ }
+
+ foreach ($aDatamodelAttCodes as $sAttCode => $value) {
+ /** var AttributeDefinition $oAttDef */
+
+ // Skipping fields that should come from DM only as they will be process later on
+ if (in_array($sAttCode, $aFieldsDMOnlyAttCodes)) {
+ continue;
+ }
+
+ // Retrieving object flags
+ if ($this->IsTransitionForm()) {
+ // Retrieving only mandatory flag from DM when on a transition
+ $iFieldFlags = $value & OPT_ATT_MANDATORY;
+ $oAttDef = MetaModel::GetAttributeDef(get_class($this->oObject), $sAttCode);
+ }
+ elseif ($this->oObject->IsNew()) {
+ $iFieldFlags = $this->oObject->GetInitialStateAttributeFlags($sAttCode);
+ $oAttDef = $value;
+ }
+ else {
+ $iFieldFlags = $this->oObject->GetAttributeFlags($sAttCode);
+ $oAttDef = $value;
+ }
+
+ // Skipping fields that were not specified to DM only list (garbage collector)
+ if ($this->IsTransitionForm() && !array_key_exists($sAttCode, $this->aFieldsAtts)) {
+ if ((($value & OPT_ATT_MANDATORY) === OPT_ATT_MANDATORY && $oAttDef->IsNull($this->oObject->Get($sAttCode)))
+ || (($value & OPT_ATT_MUSTPROMPT) === OPT_ATT_MUSTPROMPT)
+ || (($value & OPT_ATT_MUSTCHANGE) === OPT_ATT_MUSTCHANGE)) {
+ if (!in_array($sAttCode, $aFieldsDMOnlyAttCodes)) {
+ $aFieldsDMOnlyAttCodes[] = $sAttCode;
+ }
+ }
+ continue;
+ }
+
+ // Merging flags with those from the form definition
+ // - If the field is in fields list
+ if (array_key_exists($sAttCode, $this->aFieldsAtts)) {
+ // .. We merge them all
+ $this->aFieldsAtts[$sAttCode] = $this->aFieldsAtts[$sAttCode] | $iFieldFlags;
+ }
+ // - or it is mandatory and has no value
+ if ((($iFieldFlags & OPT_ATT_MANDATORY) === OPT_ATT_MANDATORY) && ($this->oObject->Get($sAttCode) === '')) {
+ if (!array_key_exists($sAttCode, $this->aFieldsAtts)) {
+ $this->aFieldsAtts[$sAttCode] = OPT_ATT_NORMAL;
+ }
+ $this->aFieldsAtts[$sAttCode] = $this->aFieldsAtts[$sAttCode] | OPT_ATT_MANDATORY;
+ }
+ }
+ }
+
+ // Adding fields with DM flags only
+ // Note: This should only happen when in a transition
+ foreach ($aFieldsDMOnlyAttCodes as $sAttCode) {
+ // Retrieving object flags from DM
+ if ($this->IsTransitionForm()) {
+ $aTransitionAtts = $this->oObject->GetTransitionAttributes($this->aFormProperties['stimulus_code']);
+ $iFieldFlags = $aTransitionAtts[$sAttCode];
+ }
+ elseif ($this->oObject->IsNew()) {
+ $iFieldFlags = $this->oObject->GetInitialStateAttributeFlags($sAttCode);
+ }
+ else {
+ $iFieldFlags = $this->oObject->GetAttributeFlags($sAttCode);
+ }
+
+ // Resetting/Forcing flag to read/write
+ $this->aFieldsAtts[$sAttCode] = OPT_ATT_NORMAL;
+ // Checking if field should be must_change
+ if (($iFieldFlags & OPT_ATT_MUSTCHANGE) === OPT_ATT_MUSTCHANGE) {
+ $this->aFieldsAtts[$sAttCode] = $this->aFieldsAtts[$sAttCode] | OPT_ATT_MUSTCHANGE;
+ }
+ // Checking if field should be must_prompt
+ if (($iFieldFlags & OPT_ATT_MUSTPROMPT) === OPT_ATT_MUSTPROMPT) {
+ $this->aFieldsAtts[$sAttCode] = $this->aFieldsAtts[$sAttCode] | OPT_ATT_MUSTPROMPT;
+ }
+ // Checking if field should be mandatory
+ if (($iFieldFlags & OPT_ATT_MANDATORY) === OPT_ATT_MANDATORY) {
+ $this->aFieldsAtts[$sAttCode] = $this->aFieldsAtts[$sAttCode] | OPT_ATT_MANDATORY;
+ }
+ }
+ }
+
+ /**
+ * @return void
+ * @throws \Twig\Error\LoaderError
+ * @throws \Twig\Error\RuntimeError
+ * @throws \Twig\Error\SyntaxError
+ */
+ public function PrepareFormAndHTMLDocument(): void
+ {
+ if ($this->oForm === null) {
+ $aFormId = 'objectform-'.((isset($this->aFormProperties['id'])) ? $this->aFormProperties['id'] : 'default').'-'.uniqid();
+ $this->oForm = new Form($aFormId);
+ $this->oForm->SetTransactionId(utils::GetNewTransactionId());
+ }
+
+ $this->oHtmlDocument = new DOMDocument();
+ if ($this->aFormProperties['layout'] !== null) {
+ // Checking if we need to render the template from twig to html in order to parse the fields
+ if ($this->aFormProperties['layout']['type'] === 'twig') {
+ if ($this->oFormHandlerHelper !== null) {
+ /** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $oObjectFormHandler */
+ $sRendered = $this->oFormHandlerHelper->RenderFormFromTwig(
+ 999, // doesn't matter here
+ $this->aFormProperties['layout']['content'],
+ array('oRenderer' => $this->oRenderer, 'oObject' => $this->oObject)
+ );
+ } else {
+ $sRendered = 'Form not rendered because of missing container';
+ }
+ } else {
+ $sRendered = $this->aFormProperties['layout']['content'];
+ }
+
+ // Parsing rendered template to find the fields
+ // Note: Loading as XML instead of HTML avoid some encoding issues (eg. 'é' was transformed to '˜©')
+ $this->oHtmlDocument->loadXML(''.$sRendered.'');
+
+ // Adding fields to the list
+ $oXPath = new DOMXPath($this->oHtmlDocument);
+
+ /** @var \DOMElement $oFieldNode */
+ foreach ($oXPath->query('//div[contains(@class, "form_field")][@data-field-id]') as $oFieldNode) {
+ if (!$oFieldNode->hasAttribute('data-form-path')) {
+ $oFieldNode->setAttribute('data-form-path', $this->oForm->GetId());
+ }
+ }
+ }
+ }
}
diff --git a/datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php b/datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php
index 8d04ecdfe..2388fa5b6 100644
--- a/datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php
+++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php
@@ -281,7 +281,8 @@ class ObjectFormHandlerHelper
->SetActionRulesToken($sActionRulesToken)
->SetRenderer($oFormRenderer)
->SetFormProperties($aFormProperties);
-
+ $oFormManager->PrepareFormAndHTMLDocument();
+ $oFormManager->PrepareFields();
$oFormManager->Build();
$aFormData['hidden_fields'] = $oFormManager->GetHiddenFieldsId();
// Check the number of editable fields