N°642 Portal: Flags on transition

SVN:trunk[4774]
This commit is contained in:
Guillaume Lajarige
2017-06-29 08:03:35 +00:00
parent 1dbc2051cc
commit 374ce20d0c
3 changed files with 159 additions and 161 deletions

View File

@@ -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

View File

@@ -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('<root>' . $sRendered . '</root>');
// 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;
*

View File

@@ -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;
}