diff --git a/datamodels/2.x/itop-portal-base/portal/src/controllers/objectcontroller.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/controllers/objectcontroller.class.inc.php index b55c2c10e..495fac68e 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/controllers/objectcontroller.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/controllers/objectcontroller.class.inc.php @@ -332,11 +332,10 @@ class ObjectController extends AbstractController } // Checking security layers - // TODO : This should call the stimulus check in the security helper -// if (!SecurityHelper::IsActionAllowed($oApp, UR_ACTION_MODIFY, $sObjectClass, $sObjectId)) -// { -// $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); -// } + if(!SecurityHelper::IsStimulusAllowed($oApp, $sStimulusCode, $sObjectClass)) + { + $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + } // Retrieving object $oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */, $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass)); @@ -349,34 +348,54 @@ class ObjectController extends AbstractController // Retrieving request parameters $sOperation = $oRequest->request->get('operation'); - - // Preparing a dedicated form for the stimulus application - $aFormProperties = array( - 'id' => 'apply-stimulus', - 'type' => 'static', - 'fields' => array(), - 'layout' => null - ); - // Checking which fields need to be prompt - $aTransitions = MetaModel::EnumTransitions($sObjectClass, $oObject->GetState()); - $aTargetStates = MetaModel::EnumStates($sObjectClass); - $aTargetState = $aTargetStates[$aTransitions[$sStimulusCode]['target_state']]; - $aExpectedAttributes = $aTargetState['attribute_list']; - foreach ($aExpectedAttributes as $sAttCode => $iFlags) - { - if (($iFlags & (OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) || - (($iFlags & OPT_ATT_MANDATORY) && ($oObject->Get($sAttCode) == ''))) - { - $aFormProperties['fields'][$sAttCode] = array(); - // Settings flags for the field - if ($iFlags & OPT_ATT_MUSTCHANGE) - $aFormProperties['fields'][$sAttCode]['must_change'] = true; - if ($iFlags & OPT_ATT_MUSTPROMPT) - $aFormProperties['fields'][$sAttCode]['must_prompt'] = true; - if (($iFlags & OPT_ATT_MANDATORY) && ($oObject->Get($sAttCode) == '')) - $aFormProperties['fields'][$sAttCode]['mandatory'] = true; - } - } + + // Retrieving form properties + $aStimuliForms = ApplicationHelper::GetLoadedFormFromClass($oApp, $sObjectClass, 'apply_stimulus'); + if(array_key_exists($sStimulusCode, $aStimuliForms)) + { + $aFormProperties = $aStimuliForms[$sStimulusCode]; + } + // Or preparing a default form for the stimulus application + else + { + $aFormProperties = array( + 'id' => 'apply-stimulus', + 'type' => 'static', + 'fields' => array(), + 'layout' => null + ); + } + + // Adding stimulus code to form + $aFormProperties['stimulus_code'] = $sStimulusCode; + + // Checking which fields need to be prompt +// $aTransitions = MetaModel::EnumTransitions($sObjectClass, $oObject->GetState()); +// $aTargetStates = MetaModel::EnumStates($sObjectClass); +// $aTargetState = $aTargetStates[$aTransitions[$sStimulusCode]['target_state']]; +// $aExpectedAttributes = $oObject->GetTransitionAttributes($sStimulusCode /*, current state*/); +// IssueLog::Info($oObject->GetState()); +// IssueLog::Info(print_r($aExpectedAttributes, true)); +// foreach ($aExpectedAttributes as $sAttCode => $iFlags) +// { +// if (($iFlags & (OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) || +// (($iFlags & OPT_ATT_MANDATORY) && ($oObject->Get($sAttCode) == ''))) +// { +// if(!isset($aFormProperties['fields'][$sAttCode])) +// { +// $aFormProperties['fields'][$sAttCode] = array(); +// } +// +// // Settings flags for the field +// if ($iFlags & OPT_ATT_MUSTCHANGE) +// $aFormProperties['fields'][$sAttCode]['must_change'] = true; +// if ($iFlags & OPT_ATT_MUSTPROMPT) +// $aFormProperties['fields'][$sAttCode]['must_prompt'] = true; +// if (($iFlags & OPT_ATT_MANDATORY) && ($oObject->Get($sAttCode) == '')) +// $aFormProperties['fields'][$sAttCode]['mandatory'] = true; +// } +// } +// IssueLog::Info(print_r($aFormProperties['fields'], true)); // Adding target_state to current_values $oRequest->request->set('apply_stimulus', array('code' => $sStimulusCode)); @@ -487,12 +506,6 @@ class ObjectController extends AbstractController $aStimuli = Metamodel::EnumStimuli($sObjectClass); foreach ($oObject->EnumTransitions() as $sStimulusCode => $aTransitionDef) { -// $iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sObjectClass, $sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO; -// // Careful, $iAction is an integer whereas UR_ALLOWED_YES is a boolean, therefore we can't use a '===' operator. -// if ($iActionAllowed == UR_ALLOWED_YES) -// { -// $aFormData['buttons']['transitions'][$sStimulusCode] = $aStimuli[$sStimulusCode]->GetLabel(); -// } if(SecurityHelper::IsStimulusAllowed($oApp, $sStimulusCode, $sObjectClass, $oSetToCheckRights)) { $aFormData['buttons']['transitions'][$sStimulusCode] = $aStimuli[$sStimulusCode]->GetLabel(); @@ -555,12 +568,6 @@ class ObjectController extends AbstractController ->SetRenderer($oFormRenderer) ->SetFormProperties($aFormProperties); - if ($sMode === 'apply_stimulus') - { - $aEditFormProperties = ApplicationHelper::GetLoadedFormFromClass($oApp, $sObjectClass, ObjectFormManager::ENUM_MODE_APPLY_STIMULUS); - $oFormManager->MergeFormProperties($aEditFormProperties); - } - $oFormManager->Build(); // Check the number of editable fields diff --git a/datamodels/2.x/itop-portal-base/portal/src/forms/objectformmanager.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/forms/objectformmanager.class.inc.php index f4f2226fb..6ea350755 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/forms/objectformmanager.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/forms/objectformmanager.class.inc.php @@ -260,6 +260,16 @@ class ObjectFormManager extends FormManager return $this; } + /** + * Returns if the form manager is handling a transition form instead of a state form. + * + * @return bool + */ + public function IsTransitionForm() + { + return ($this->sMode === static::ENUM_MODE_APPLY_STIMULUS); + } + /** * Creates a JSON string from the current object including : * - formobject_class @@ -314,16 +324,16 @@ class ObjectFormManager extends FormManager { $iFieldFlags = $iFieldFlags | OPT_ATT_SLAVE; } - // Checking if field should be must prompt + // 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 must_change - if (isset($aOptions['must_change']) && ($aOptions['must_change'] === true)) - { - $iFieldFlags = $iFieldFlags | OPT_ATT_MUSTCHANGE; - } // Checking if field should be hidden if (isset($aOptions['hidden']) && ($aOptions['hidden'] === true)) { @@ -438,10 +448,24 @@ class ObjectFormManager extends FormManager // Also, retrieving mandatory attributes from metamodel to be able to complete the form with them if necessary if ($this->aFormProperties['type'] !== 'static') { - foreach (MetaModel::ListAttributeDefs($sObjectClass) as $sAttCode => $oAttDef) + if($this->IsTransitionForm()) + { + $aDatamodelAttCodes = $this->oObject->GetTransitionAttributes($this->aFormProperties['stimulus_code']); + } + else + { + $aDatamodelAttCodes = MetaModel::ListAttributeDefs($sObjectClass); + } + + foreach ($aDatamodelAttCodes as $sAttCode => $value) { // Retrieving object flags - if ($this->oObject->IsNew()) + if ($this->IsTransitionForm()) + { + // Retrieving only mandatory flag from DM when on a transition + $iFieldFlags = $value & OPT_ATT_MANDATORY; + } + elseif ($this->oObject->IsNew()) { $iFieldFlags = $this->oObject->GetInitialStateAttributeFlags($sAttCode); } @@ -454,7 +478,9 @@ class ObjectFormManager extends FormManager // - only if the field if it's in fields list if (array_key_exists($sAttCode, $aFieldsAtts)) { - $aFieldsAtts[$sAttCode] = $aFieldsAtts[$sAttCode] | $iFieldFlags; + if($this->IsTransitionForm()) { + $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) === '')) @@ -493,12 +519,19 @@ class ObjectFormManager extends FormManager { $oField->SetReadOnly(true); } - // - Else if it's mandatory and has no value, we force it as mandatory - elseif ((($iFieldFlags & OPT_ATT_MANDATORY) === OPT_ATT_MANDATORY) && $oAttDef->IsNull($this->oObject->Get($sAttCode))) - { - $oField->SetMandatory(true); - } - // - Else if it wasn't mandatory or already had a value, and it's hidden, we force it as hidden + // - Else if it's must change (transition), we force it as not readonly and not hidden + elseif (($iFieldFlags & OPT_ATT_MUSTCHANGE) === OPT_ATT_MUSTCHANGE && $this->IsTransitionForm()) + { + $oField->SetReadOnly(false); + $oField->SetHidden(false); + } + // - Else if it's must prompt (transition), we force it as not readonly and not hidden + elseif (($iFieldFlags & OPT_ATT_MUSTPROMPT) === OPT_ATT_MUSTPROMPT && $this->IsTransitionForm()) + { + $oField->SetReadOnly(false); + $oField->SetHidden(false); + } + // - Else if it wasn't mandatory or already had a value, and it's hidden, we force it as hidden elseif (($iFieldFlags & OPT_ATT_HIDDEN) === OPT_ATT_HIDDEN) { $oField->SetHidden(true); @@ -507,23 +540,17 @@ class ObjectFormManager extends FormManager { $oField->SetReadOnly(true); } - // - Else if it's must change, we force it as not readonly and not hidden - elseif (($iFieldFlags & OPT_ATT_MUSTCHANGE) === OPT_ATT_MUSTCHANGE) - { - $oField->SetReadOnly(false); - $oField->SetHidden(false); - } - // - Else if it's must prompt, we force it as not readonly and not hidden - elseif (($iFieldFlags & OPT_ATT_MUSTPROMPT) === OPT_ATT_MUSTPROMPT) - { - $oField->SetReadOnly(false); - $oField->SetHidden(false); - } else { // Normal field } + // Finally, if it's mandatory and has no value, we force it as mandatory + if ((($iFieldFlags & OPT_ATT_MANDATORY) === OPT_ATT_MANDATORY) && $oAttDef->IsNull($this->oObject->Get($sAttCode))) + { + $oField->SetMandatory(true); + } + // Specific operation on field // - Field that require a transaction id if (in_array(get_class($oField), array('Combodo\\iTop\\Form\\Field\\TextAreaField', 'Combodo\\iTop\\Form\\Field\\CaseLogField'))) @@ -719,7 +746,11 @@ class ObjectFormManager extends FormManager $oField->SetReadOnly(true); } - $oForm->AddField($oField); + // Adding attachements field in transition only if it is editable + if(!$this->IsTransitionForm() || ($this->IsTransitionForm() && !$oField->GetReadOnly()) ) + { + $oForm->AddField($oField); + } } } @@ -728,87 +759,6 @@ class ObjectFormManager extends FormManager $this->oRenderer->SetForm($this->oForm); } - /** - * Merging $this->aFormProperties with $aFormPropertiesToMerge. Merge only layout for now - * - * @param array $aFormPropertiesToMerge - * @throws Exception - */ - public function MergeFormProperties($aFormPropertiesToMerge) - { - if ($aFormPropertiesToMerge['layout'] !== null) - { - // Checking if we need to render the template from twig to html in order to parse the fields - if ($aFormPropertiesToMerge['layout']['type'] === 'twig') - { - // Creating sandbox twig env. to load and test the custom form template - $oTwig = new \Twig_Environment(new \Twig_Loader_String()); - $sRendered = $oTwig->render($aFormPropertiesToMerge['layout']['content'], array('oRenderer' => $this->oRenderer, 'oObject' => $this->oObject)); - } - else - { - $sRendered = $aFormPropertiesToMerge['layout']['content']; - } - - // Parsing rendered template to find the fields - $oHtmlDocument = new \DOMDocument(); - $oHtmlDocument->loadHTML('' . $sRendered . ''); - - // Adding fields to the list - $oXPath = new \DOMXPath($oHtmlDocument); - foreach ($oXPath->query('//div[@class="form_field"][@data-field-id]') as $oFieldNode) - { - $sFieldId = $oFieldNode->getAttribute('data-field-id'); - $sFieldFlags = $oFieldNode->getAttribute('data-field-flags'); -// $iFieldFlags = OPT_ATT_NORMAL; - -// // Checking if field has form_path, if not, we add it -// if (!$oFieldNode->hasAttribute('data-form-path')) -// { -// $oFieldNode->setAttribute('data-form-path', $oForm->GetId()); -// } - // Merging only fields that are already in the form - if (array_key_exists($sFieldId, $this->aFormProperties['fields'])) - { - // Settings field flags from the data-field-flags attribute - foreach (explode(' ', $sFieldFlags) as $sFieldFlag) - { - if ($sFieldFlag !== '') - { - $sConst = 'OPT_ATT_' . strtoupper(str_replace('_', '', $sFieldFlag)); - if (defined($sConst)) - { - switch ($sConst) - { - case 'OPT_ATT_SLAVE': - case 'OPT_ATT_HIDDEN': - if (!array_key_exists($sFieldId, $this->aFormProperties['fields'])) - { - $this->aFormProperties['fields'][$sFieldId] = array(); - } - $this->aFormProperties['fields'][$sFieldId]['hidden'] = true; - break; - case 'OPT_ATT_READONLY': - if (!array_key_exists($sFieldId, $this->aFormProperties['fields'])) - { - $this->aFormProperties['fields'][$sFieldId] = array(); - } - $this->aFormProperties['fields'][$sFieldId]['read_only'] = true; - break; - } - } - else - { - IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Flag "' . $sFieldFlag . '" is not valid for field [@data-field-id="' . $sFieldId . '"] in form[@id="' . $aFormPropertiesToMerge['id'] . '"]'); - throw new Exception('Flag "' . $sFieldFlag . '" is not valid for field [@data-field-id="' . $sFieldId . '"] in form[@id="' . $aFormPropertiesToMerge['id'] . '"]'); - } - } - } - } - } - } - } - /** * Calls all form fields OnCancel method in order to delegate them the cleanup; * diff --git a/datamodels/2.x/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php index 5003f903d..688350f6a 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php @@ -878,7 +878,8 @@ class ApplicationHelper } } - // Parsing availables modes for that form (view, edit, create) + // Parsing availables modes for that form (view, edit, create, apply_stimulus) + $aFormStimuli = array(); if (($oFormNode->GetOptionalElement('modes') !== null) && ($oFormNode->GetOptionalElement('modes')->GetNodes('mode')->length > 0)) { $aModes = array(); @@ -892,10 +893,25 @@ class ApplicationHelper { throw new DOMFormatException('Mode tag must have an id attribute', null, null, $oFormNode); } + + // If apply_stimulus mode, checking if stimuli are defined + if ($oModeNode->getAttribute('id') === 'apply_stimulus') + { + $oStimuliNode = $oModeNode->GetOptionalElement('stimuli'); + if($oStimuliNode !== null) + { + foreach ($oStimuliNode->GetNodes('stimulus') as $oStimulusNode) + { + $aFormStimuli[] = $oStimulusNode->getAttribute('id'); + } + } + } } } else { + // If no mode was specified, we set it all but stimuli as it would have no sense that every transition forms + // have as many fields displayed as a regular edit form for example. $aModes = array('view', 'edit', 'create'); } @@ -941,11 +957,6 @@ class ApplicationHelper } } } -// // ... or a specified zlist -// elseif ($oFormNode->GetOptionalElement('presentation') !== null) -// { -// // This is not implemented yet as it was rejected until futher notice. -// } // ... or the default zlist else { @@ -953,6 +964,25 @@ class ApplicationHelper $aFields['fields'] = 'details'; } + // Adding stimuli if explicitly defined + if(in_array('apply_stimulus', $aModes)) + { + // If stimuli are implicitly defined (empty tag), we define all those that have not already been by other forms. + if(empty($aFormStimuli)) + { + // Stimuli already declared + $aDeclaredStimuli = array(); + if(array_key_exists($sFormClass, $aForms) && array_key_exists('apply_stimulus', $aForms[$sFormClass])) + { + $aDeclaredStimuli = array_keys($aForms[$sFormClass]['apply_stimulus']); + } + // All stimuli + $aDatamodelStimuli = array_keys(MetaModel::EnumStimuli($sFormClass)); + // Missing stimuli + $aFormStimuli = array_diff($aDatamodelStimuli, $aDeclaredStimuli); + } + } + // Parsing presentation if ($oFormNode->GetOptionalElement('twig') !== null) { @@ -975,7 +1005,18 @@ class ApplicationHelper $aForms[$sFormClass] = array(); } - if (!isset($aForms[$sFormClass][$sMode])) + if ($sMode === 'apply_stimulus') + { + foreach($aFormStimuli as $sFormStimulus) + { + if(!isset($aForms[$sFormClass][$sMode][$sFormStimulus])) + { + $aForms[$sFormClass][$sMode][$sFormStimulus] = $aFields; + $aForms[$sFormClass][$sMode][$sFormStimulus]['id'] = 'apply_stimulus-'.$sFormClass.'-'.$sFormStimulus; + } + } + } + elseif (!isset($aForms[$sFormClass][$sMode])) { $aForms[$sFormClass][$sMode] = $aFields; }