/**
  * 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) {
                     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 = $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 ($oAttDef->GetEditClass() == 'LinkedSet' && !$oAttDef->IsIndirect() && ($oAttDef->GetEditMode() == LINKSET_EDITMODE_INPLACE || $oAttDef->GetEditMode() == LINKSET_EDITMODE_ADDREMOVE)) {
                 $oLinkset = $this->Get($sAttCode);
                 $sLinkedClass = $oLinkset->GetClass();
                 $aObjSet = array();
                 $oLinkset->Rewind();
                 $bModified = false;
                 while ($oLink = $oLinkset->Fetch()) {
                     if (in_array($oLink->GetKey(), $value['to_be_deleted'])) {
                         // The link is to be deleted, don't copy it in the array
                         $bModified = true;
                     } else {
                         if (!array_key_exists('to_be_removed', $value) || !in_array($oLink->GetKey(), $value['to_be_removed'])) {
                             $aObjSet[] = $oLink;
                         }
                     }
                 }
                 if (array_key_exists('to_be_created', $value) && count($value['to_be_created']) > 0) {
                     // Now handle the links to be created
                     foreach ($value['to_be_created'] as $aData) {
                         $sSubClass = $aData['class'];
                         if ($sLinkedClass == $sSubClass || is_subclass_of($sSubClass, $sLinkedClass)) {
                             $aObjData = $aData['data'];
                             $oLink = new $sSubClass();
                             $oLink->UpdateObjectFromArray($aObjData);
                             $aObjSet[] = $oLink;
                             $bModified = true;
                         }
                     }
                 }
                 if (array_key_exists('to_be_added', $value) && count($value['to_be_added']) > 0) {
                     // Now handle the links to be added by making the remote object point to self
                     foreach ($value['to_be_added'] as $iObjKey) {
                         $oLink = MetaModel::GetObject($sLinkedClass, $iObjKey, false);
                         if ($oLink) {
                             $aObjSet[] = $oLink;
                             $bModified = true;
                         }
                     }
                 }
                 if (array_key_exists('to_be_removed', $value) && count($value['to_be_removed']) > 0) {
                     // Now handle the links to be removed by making the remote object point to nothing
                     // Keep them in the set (modified), DBWriteLinks will handle them
                     foreach ($value['to_be_removed'] as $iObjKey) {
                         $oLink = MetaModel::GetObject($sLinkedClass, $iObjKey, false);
                         if ($oLink) {
                             $sExtKeyToMe = $oAttDef->GetExtKeyToMe();
                             $oLink->Set($sExtKeyToMe, null);
                             $aObjSet[] = $oLink;
                             $bModified = true;
                         }
                     }
                 }
                 if ($bModified) {
                     $oNewSet = DBObjectSet::FromArray($oLinkset->GetClass(), $aObjSet);
                     $this->Set($sAttCode, $oNewSet);
                 }
             } else {
                 if (!is_null($value)) {
                     $aAttributes[$sAttCode] = trim($value);
                     $previousValue = $this->Get($sAttCode);
                     if ($previousValue !== $aAttributes[$sAttCode]) {
                         $this->Set($sAttCode, $aAttributes[$sAttCode]);
                     }
                 }
             }
         }
     }
 }
 /**
  * Constructs the PHP target object from the parameters sent to the web page by the wizard
  * @param boolean $bReadUploadedFiles True to also ready any uploaded file (for blob/document fields)
  * @return object
  */
 public function GetTargetObject($bReadUploadedFiles = false)
 {
     if (isset($this->m_aData['m_oCurrentValues']['id'])) {
         $oObj = MetaModel::GetObject($this->m_aData['m_sClass'], $this->m_aData['m_oCurrentValues']['id']);
     } else {
         $oObj = MetaModel::NewObject($this->m_aData['m_sClass']);
     }
     foreach ($this->m_aData['m_oCurrentValues'] as $sAttCode => $value) {
         // Because this is stored in a Javascript array, unused indexes
         // are filled with null values and unused keys (stored as strings) contain $$NULL$$
         if ($sAttCode != 'id' && $sAttCode !== false && $value !== null && $value !== '$$NULL$$') {
             $oAttDef = MetaModel::GetAttributeDef($this->m_aData['m_sClass'], $sAttCode);
             if ($oAttDef->IsLinkSet() && $value != '') {
                 // special handling for lists
                 // assumes this is handled as an array of objects
                 // thus encoded in json like: [ { name:'link1', 'id': 123}, { name:'link2', 'id': 124}...]
                 $aData = json_decode($value, true);
                 // true means decode as a hash array (not an object)
                 // Check what are the meaningful attributes
                 $aFields = $this->GetLinkedWizardStructure($oAttDef);
                 $sLinkedClass = $oAttDef->GetLinkedClass();
                 $aLinkedObjectsArray = array();
                 if (!is_array($aData)) {
                     echo "aData: '{$aData}' (value: '{$value}')\n";
                 }
                 foreach ($aData as $aLinkedObject) {
                     $oLinkedObj = MetaModel::NewObject($sLinkedClass);
                     foreach ($aFields as $sLinkedAttCode) {
                         if (isset($aLinkedObject[$sLinkedAttCode]) && $aLinkedObject[$sLinkedAttCode] !== null) {
                             $sLinkedAttDef = MetaModel::GetAttributeDef($sLinkedClass, $sLinkedAttCode);
                             if ($sLinkedAttDef->IsExternalKey() && $aLinkedObject[$sLinkedAttCode] != '' && $aLinkedObject[$sLinkedAttCode] > 0) {
                                 // For external keys: load the target object so that external fields
                                 // get filled too
                                 $oTargetObj = MetaModel::GetObject($sLinkedAttDef->GetTargetClass(), $aLinkedObject[$sLinkedAttCode]);
                                 $oLinkedObj->Set($sLinkedAttCode, $oTargetObj);
                             } else {
                                 $oLinkedObj->Set($sLinkedAttCode, $aLinkedObject[$sLinkedAttCode]);
                             }
                         }
                     }
                     $aLinkedObjectsArray[] = $oLinkedObj;
                 }
                 $oSet = DBObjectSet::FromArray($sLinkedClass, $aLinkedObjectsArray);
                 $oObj->Set($sAttCode, $oSet);
             } else {
                 if ($oAttDef->GetEditClass() == 'Document') {
                     if ($bReadUploadedFiles) {
                         $oDocument = utils::ReadPostedDocument('attr_' . $sAttCode, 'fcontents');
                         $oObj->Set($sAttCode, $oDocument);
                     } else {
                         // Create a new empty document, just for displaying the file name
                         $oDocument = new ormDocument(null, '', $value);
                         $oObj->Set($sAttCode, $oDocument);
                     }
                 } else {
                     if ($oAttDef->IsExternalKey() && !empty($value) && $value > 0) {
                         // For external keys: load the target object so that external fields
                         // get filled too
                         $oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value);
                         $oObj->Set($sAttCode, $oTargetObj);
                     } else {
                         $oObj->Set($sAttCode, $value);
                     }
                 }
             }
         }
     }
     if (isset($this->m_aData['m_sState']) && !empty($this->m_aData['m_sState'])) {
         $oObj->Set(MetaModel::GetStateAttributeCode($this->m_aData['m_sClass']), $this->m_aData['m_sState']);
     }
     $oObj->DoComputeValues();
     return $oObj;
 }
 /**
  * Helper to form a value, given JSON decoded data
  * The operation is the opposite to GetForJSON	 
  */
 public function FromJSONToValue($json)
 {
     $sTargetClass = $this->Get('linked_class');
     $aLinks = array();
     foreach ($json as $aValues) {
         if (isset($aValues['finalclass'])) {
             $sLinkClass = $aValues['finalclass'];
             if (!is_subclass_of($sLinkClass, $sTargetClass)) {
                 throw new CoreException('Wrong class for link attribute specification', array('requested_class' => $sLinkClass, 'expected_class' => $sTargetClass));
             }
         } elseif (MetaModel::IsAbstract($sTargetClass)) {
             throw new CoreException('Missing finalclass for link attribute specification');
         } else {
             $sLinkClass = $sTargetClass;
         }
         $oLink = MetaModel::NewObject($sLinkClass);
         foreach ($aValues as $sAttCode => $sValue) {
             $oLink->Set($sAttCode, $sValue);
         }
         // Check (roughly) if such a link is valid
         $aErrors = array();
         foreach (MetaModel::ListAttributeDefs($sTargetClass) as $sAttCode => $oAttDef) {
             if ($oAttDef->IsExternalKey()) {
                 if ($oAttDef->GetTargetClass() == $this->GetHostClass() || is_subclass_of($this->GetHostClass(), $oAttDef->GetTargetClass())) {
                     continue;
                     // Don't check the key to self
                 }
             }
             if ($oAttDef->IsWritable() && $oAttDef->IsNull($oLink->Get($sAttCode)) && !$oAttDef->IsNullAllowed()) {
                 $aErrors[] = $sAttCode;
             }
         }
         if (count($aErrors) > 0) {
             throw new CoreException("Missing value for mandatory attribute(s): " . implode(', ', $aErrors));
         }
         $aLinks[] = $oLink;
     }
     $oSet = DBObjectSet::FromArray($sTargetClass, $aLinks);
     return $oSet;
 }
 /**
  * Helper to link objects
  *
  * @param string sLinkAttCode
  * @param string sLinkedClass
  * @param array $aLinkList
  * @param DBObject oTargetObj
  * @param WebServiceResult oRes
  *
  * @return array List of objects that could not be found
  */
 protected function AddLinkedObjects($sLinkAttCode, $sParamName, $sLinkedClass, $aLinkList, &$oTargetObj, &$oRes)
 {
     $oLinkAtt = MetaModel::GetAttributeDef(get_class($oTargetObj), $sLinkAttCode);
     $sLinkClass = $oLinkAtt->GetLinkedClass();
     $sExtKeyToItem = $oLinkAtt->GetExtKeyToRemote();
     $aItemsFound = array();
     $aItemsNotFound = array();
     if (is_null($aLinkList)) {
         return $aItemsNotFound;
     }
     foreach ($aLinkList as $aItemData) {
         if (!array_key_exists('class', $aItemData)) {
             $oRes->LogWarning("Parameter {$sParamName}: missing 'class' specification");
             continue;
             // skip
         }
         $sTargetClass = $aItemData['class'];
         if (!MetaModel::IsValidClass($sTargetClass)) {
             $oRes->LogError("Parameter {$sParamName}: invalid class '{$sTargetClass}'");
             continue;
             // skip
         }
         if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass)) {
             $oRes->LogError("Parameter {$sParamName}: '{$sTargetClass}' is not a child class of '{$sLinkedClass}'");
             continue;
             // skip
         }
         $oReconFilter = new CMDBSearchFilter($sTargetClass);
         $aCIStringDesc = array();
         foreach ($aItemData['search'] as $sAttCode => $value) {
             if (!MetaModel::IsValidFilterCode($sTargetClass, $sAttCode)) {
                 $aCodes = array_keys(MetaModel::GetClassFilterDefs($sTargetClass));
                 $oRes->LogError("Parameter {$sParamName}: '{$sAttCode}' is not a valid filter code for class '{$sTargetClass}', expecting a value in {" . implode(', ', $aCodes) . "}");
                 continue 2;
                 // skip the entire item
             }
             $aCIStringDesc[] = "{$sAttCode}: {$value}";
             // The attribute is one of our reconciliation key
             $oReconFilter->AddCondition($sAttCode, $value, '=');
         }
         if (count($aCIStringDesc) == 1) {
             // take the last and unique value to describe the object
             $sItemDesc = $value;
         } else {
             // describe the object by the given keys
             $sItemDesc = $sTargetClass . '(' . implode('/', $aCIStringDesc) . ')';
         }
         $oExtObjects = new CMDBObjectSet($oReconFilter);
         switch ($oExtObjects->Count()) {
             case 0:
                 $oRes->LogWarning("Parameter {$sParamName}: object to link {$sLinkedClass} / {$sItemDesc} could not be found (searched: '" . $oReconFilter->ToOQL(true) . "')");
                 $aItemsNotFound[] = $sItemDesc;
                 break;
             case 1:
                 $aItemsFound[] = array('object' => $oExtObjects->Fetch(), 'link_values' => @$aItemData['link_values'], 'desc' => $sItemDesc);
                 break;
             default:
                 $oRes->LogWarning("Parameter {$sParamName}: Found " . $oExtObjects->Count() . " matches for item '{$sItemDesc}' (searched: '" . $oReconFilter->ToOQL(true) . "')");
                 $aItemsNotFound[] = $sItemDesc;
         }
     }
     if (count($aItemsFound) > 0) {
         $aLinks = array();
         foreach ($aItemsFound as $aItemData) {
             $oLink = MetaModel::NewObject($sLinkClass);
             $oLink->Set($sExtKeyToItem, $aItemData['object']->GetKey());
             foreach ($aItemData['link_values'] as $sKey => $value) {
                 if (!MetaModel::IsValidAttCode($sLinkClass, $sKey)) {
                     $oRes->LogWarning("Parameter {$sParamName}: Attaching item '" . $aItemData['desc'] . "', the attribute code '{$sKey}' is not valid ; check the class '{$sLinkClass}'");
                 } else {
                     $oLink->Set($sKey, $value);
                 }
             }
             $aLinks[] = $oLink;
         }
         $oImpactedInfraSet = DBObjectSet::FromArray($sLinkClass, $aLinks);
         $oTargetObj->Set($sLinkAttCode, $oImpactedInfraSet);
     }
     return $aItemsNotFound;
 }
 /**
  * Interpret the Rest/Json value and get a valid attribute value
  * 	 
  * @param string $sClass Name of the class
  * @param string $sAttCode Attribute code
  * @param mixed $value Depending on the type of attribute (a scalar, or search criteria, or list of related objects...)
  * @return mixed The value that can be used with DBObject::Set()
  * @throws Exception If the specification of the value is not valid.
  * @api
  */
 public static function MakeValue($sClass, $sAttCode, $value)
 {
     try {
         if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) {
             throw new Exception("Unknown attribute");
         }
         $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
         if ($oAttDef instanceof AttributeExternalKey) {
             $oExtKeyObject = static::FindObjectFromKey($oAttDef->GetTargetClass(), $value, true);
             $value = $oExtKeyObject != null ? $oExtKeyObject->GetKey() : 0;
         } elseif ($oAttDef instanceof AttributeLinkedSet) {
             if (!is_array($value)) {
                 throw new Exception("A link set must be defined by an array of objects");
             }
             $sLnkClass = $oAttDef->GetLinkedClass();
             $aLinks = array();
             foreach ($value as $oValues) {
                 $oLnk = static::MakeObjectFromFields($sLnkClass, $oValues);
                 $aLinks[] = $oLnk;
             }
             $value = DBObjectSet::FromArray($sLnkClass, $aLinks);
         } else {
             $value = $oAttDef->FromJSONToValue($value);
         }
     } catch (Exception $e) {
         throw new Exception("{$sAttCode}: " . $e->getMessage(), $e->getCode());
     }
     return $value;
 }
 public function MakeValueFromString($sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null, $sAttributeQualifier = null)
 {
     if (is_null($sSepItem) || empty($sSepItem)) {
         $sSepItem = MetaModel::GetConfig()->Get('link_set_item_separator');
     }
     if (is_null($sSepAttribute) || empty($sSepAttribute)) {
         $sSepAttribute = MetaModel::GetConfig()->Get('link_set_attribute_separator');
     }
     if (is_null($sSepValue) || empty($sSepValue)) {
         $sSepValue = MetaModel::GetConfig()->Get('link_set_value_separator');
     }
     if (is_null($sAttributeQualifier) || empty($sAttributeQualifier)) {
         $sAttributeQualifier = MetaModel::GetConfig()->Get('link_set_attribute_qualifier');
     }
     $sTargetClass = $this->Get('linked_class');
     $sInput = str_replace($sSepItem, "\n", $sProposedValue);
     $oCSVParser = new CSVParser($sInput, $sSepAttribute, $sAttributeQualifier);
     $aInput = $oCSVParser->ToArray(0);
     $aLinks = array();
     foreach ($aInput as $aRow) {
         // 1st - get the values, split the extkey->searchkey specs, and eventually get the finalclass value
         $aExtKeys = array();
         $aValues = array();
         foreach ($aRow as $sCell) {
             $iSepPos = strpos($sCell, $sSepValue);
             if ($iSepPos === false) {
                 // Houston...
                 throw new CoreException('Wrong format for link attribute specification', array('value' => $sCell));
             }
             $sAttCode = trim(substr($sCell, 0, $iSepPos));
             $sValue = substr($sCell, $iSepPos + strlen($sSepValue));
             if (preg_match('/^(.+)->(.+)$/', $sAttCode, $aMatches)) {
                 $sKeyAttCode = $aMatches[1];
                 $sRemoteAttCode = $aMatches[2];
                 $aExtKeys[$sKeyAttCode][$sRemoteAttCode] = $sValue;
                 if (!MetaModel::IsValidAttCode($sTargetClass, $sKeyAttCode)) {
                     throw new CoreException('Wrong attribute code for link attribute specification', array('class' => $sTargetClass, 'attcode' => $sKeyAttCode));
                 }
                 $oKeyAttDef = MetaModel::GetAttributeDef($sTargetClass, $sKeyAttCode);
                 $sRemoteClass = $oKeyAttDef->GetTargetClass();
                 if (!MetaModel::IsValidAttCode($sRemoteClass, $sRemoteAttCode)) {
                     throw new CoreException('Wrong attribute code for link attribute specification', array('class' => $sRemoteClass, 'attcode' => $sRemoteAttCode));
                 }
             } else {
                 if (!MetaModel::IsValidAttCode($sTargetClass, $sAttCode)) {
                     throw new CoreException('Wrong attribute code for link attribute specification', array('class' => $sTargetClass, 'attcode' => $sAttCode));
                 }
                 $aValues[$sAttCode] = $sValue;
             }
         }
         // 2nd - Instanciate the object and set the value
         if (isset($aValues['finalclass'])) {
             $sLinkClass = $aValues['finalclass'];
             if (!is_subclass_of($sLinkClass, $sTargetClass)) {
                 throw new CoreException('Wrong class for link attribute specification', array('requested_class' => $sLinkClass, 'expected_class' => $sTargetClass));
             }
         } elseif (MetaModel::IsAbstract($sTargetClass)) {
             throw new CoreException('Missing finalclass for link attribute specification');
         } else {
             $sLinkClass = $sTargetClass;
         }
         $oLink = MetaModel::NewObject($sLinkClass);
         foreach ($aValues as $sAttCode => $sValue) {
             $oLink->Set($sAttCode, $sValue);
         }
         // 3rd - Set external keys from search conditions
         foreach ($aExtKeys as $sKeyAttCode => $aReconciliation) {
             $oKeyAttDef = MetaModel::GetAttributeDef($sTargetClass, $sKeyAttCode);
             $sKeyClass = $oKeyAttDef->GetTargetClass();
             $oExtKeyFilter = new CMDBSearchFilter($sKeyClass);
             $aReconciliationDesc = array();
             foreach ($aReconciliation as $sRemoteAttCode => $sValue) {
                 $oExtKeyFilter->AddCondition($sRemoteAttCode, $sValue, '=');
                 $aReconciliationDesc[] = "{$sRemoteAttCode}={$sValue}";
             }
             $oExtKeySet = new CMDBObjectSet($oExtKeyFilter);
             switch ($oExtKeySet->Count()) {
                 case 0:
                     $sReconciliationDesc = implode(', ', $aReconciliationDesc);
                     throw new CoreException("Found no match", array('ext_key' => $sKeyAttCode, 'reconciliation' => $sReconciliationDesc));
                     break;
                 case 1:
                     $oRemoteObj = $oExtKeySet->Fetch();
                     $oLink->Set($sKeyAttCode, $oRemoteObj->GetKey());
                     break;
                 default:
                     $sReconciliationDesc = implode(', ', $aReconciliationDesc);
                     throw new CoreException("Found several matches", array('ext_key' => $sKeyAttCode, 'reconciliation' => $sReconciliationDesc));
                     // Found several matches, ambiguous
             }
         }
         // Check (roughly) if such a link is valid
         $aErrors = array();
         foreach (MetaModel::ListAttributeDefs($sTargetClass) as $sAttCode => $oAttDef) {
             if ($oAttDef->IsExternalKey()) {
                 if ($oAttDef->GetTargetClass() == $this->GetHostClass() || is_subclass_of($this->GetHostClass(), $oAttDef->GetTargetClass())) {
                     continue;
                     // Don't check the key to self
                 }
             }
             if ($oAttDef->IsWritable() && $oAttDef->IsNull($oLink->Get($sAttCode)) && !$oAttDef->IsNullAllowed()) {
                 $aErrors[] = $sAttCode;
             }
         }
         if (count($aErrors) > 0) {
             throw new CoreException("Missing value for mandatory attribute(s): " . implode(', ', $aErrors));
         }
         $aLinks[] = $oLink;
     }
     $oSet = DBObjectSet::FromArray($sTargetClass, $aLinks);
     return $oSet;
 }
Example #7
0
 // For archiving the modification
 $oFilter = DBObjectSearch::unserialize($sFilter);
 $sClass = $oFilter->GetClass();
 $aObjects = array();
 foreach ($aSelectObject as $iId) {
     $aObjects[] = MetaModel::GetObject($sClass, $iId);
 }
 $aTransitions = MetaModel::EnumTransitions($sClass, $sState);
 $aStimuli = MetaModel::EnumStimuli($sClass);
 $sActionLabel = $aStimuli[$sStimulus]->GetLabel();
 $sActionDetails = $aStimuli[$sStimulus]->GetDescription();
 $oP->set_title(Dict::Format('UI:StimulusModify_N_ObjectsOf_Class', $sActionLabel, count($aObjects), $sClass));
 $oP->add('<div class="page_header">');
 $oP->add('<h1>' . MetaModel::GetClassIcon($sClass) . '&nbsp;' . Dict::Format('UI:StimulusModify_N_ObjectsOf_Class', $sActionLabel, count($aObjects), $sClass) . '</h1>');
 $oP->add('</div>');
 $oSet = DBObjectSet::FromArray($sClass, $aObjects);
 // For reporting
 $aHeaders = array('object' => array('label' => MetaModel::GetName($sClass), 'description' => Dict::S('UI:ModifiedObject')), 'status' => array('label' => Dict::S('UI:BulkModifyStatus'), 'description' => Dict::S('UI:BulkModifyStatus+')), 'errors' => array('label' => Dict::S('UI:BulkModifyErrors'), 'description' => Dict::S('UI:BulkModifyErrors+')));
 $aRows = array();
 while ($oObj = $oSet->Fetch()) {
     $sError = Dict::S('UI:BulkModifyStatusOk');
     try {
         $aTransitions = $oObj->EnumTransitions();
         $aStimuli = MetaModel::EnumStimuli($sClass);
         if (!isset($aTransitions[$sStimulus])) {
             throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $oObj->GetName(), $oObj->GetStateLabel()));
         } else {
             $sActionLabel = $aStimuli[$sStimulus]->GetLabel();
             $sActionDetails = $aStimuli[$sStimulus]->GetDescription();
             $aTransition = $aTransitions[$sStimulus];
             $sTargetState = $aTransition['target_state'];
 protected function LoadValues($aArgs)
 {
     $this->m_aValues = array();
     if (!array_key_exists('this', $aArgs)) {
         throw new CoreException("Missing 'this' in arguments", array('args' => $aArgs));
     }
     $oTarget = $aArgs['this->object()'];
     // Nodes from which we will start the search for neighbourhood
     $oNodes = DBObjectSet::FromLinkSet($oTarget, $this->m_sLinkSetAttCode, $this->m_sExtKeyToRemote);
     // Neighbours, whatever their class
     $aRelated = $oNodes->GetRelatedObjects($this->m_sRelationCode, $this->m_iMaxDepth);
     $sRootClass = MetaModel::GetRootClass($this->m_sTargetClass);
     if (array_key_exists($sRootClass, $aRelated)) {
         $aLinksToCreate = array();
         foreach ($aRelated[$sRootClass] as $iKey => $oObject) {
             if (MetaModel::IsParentClass($this->m_sTargetClass, get_class($oObject))) {
                 $oNewLink = MetaModel::NewObject($this->m_sTargetLinkClass);
                 $oNewLink->Set($this->m_sTargetExtKey, $iKey);
                 //$oNewLink->Set('role', 'concerned by an impacted CI');
                 $aLinksToCreate[] = $oNewLink;
             }
         }
         // #@# or AddObjectArray($aObjects) ?
         $oSetToCreate = DBObjectSet::FromArray($this->m_sTargetLinkClass, $aLinksToCreate);
         $this->m_aValues[$oObject->GetKey()] = $oObject->GetName();
     }
     return true;
 }
 /**
  * Check if the speficied stimulus is allowed for the set of objects
  * @return UR_ALLOWED_YES, UR_ALLOWED_NO or UR_ALLOWED_DEPENDS
  */
 public function IsAllowed()
 {
     $sClass = $this->oFilter->GetClass();
     if (MetaModel::IsAbstract($sClass)) {
         return UR_ALLOWED_NO;
     }
     // Safeguard, not implemented if the base class of the set is abstract !
     $oSet = new DBObjectSet($this->oFilter);
     $iActionAllowed = UserRights::IsStimulusAllowed($sClass, $this->iActionCode, $oSet);
     if ($iActionAllowed == UR_ALLOWED_NO) {
         $this->iAllowedCount = 0;
         $this->aAllowedIDs = array();
     } else {
         // Hmmm, may not be needed right now because we limit the "multiple" action to object in
         // the same state... may be useful later on if we want to extend this behavior...
         // Check for each object if the action is allowed or not
         $this->aAllowedIDs = array();
         $oSet->Rewind();
         $iAllowedCount = 0;
         $iActionAllowed = UR_ALLOWED_DEPENDS;
         while ($oObj = $oSet->Fetch()) {
             $aTransitions = $oObj->EnumTransitions();
             if (array_key_exists($this->iActionCode, $aTransitions)) {
                 // Temporary optimization possible: since the current implementation
                 // of IsActionAllowed does not perform a 'per instance' check, we could
                 // skip this second validation phase and assume it would return UR_ALLOWED_YES
                 $oObjSet = DBObjectSet::FromArray($sClass, array($oObj));
                 if (!UserRights::IsStimulusAllowed($sClass, $this->iActionCode, $oObjSet)) {
                     $this->aAllowedIDs[$oObj->GetKey()] = false;
                 } else {
                     // Assume UR_ALLOWED_YES, since there is just one object !
                     $this->aAllowedIDs[$oObj->GetKey()] = true;
                     $this->iState = $oObj->GetState();
                     $this->iAllowedCount++;
                 }
             } else {
                 $this->aAllowedIDs[$oObj->GetKey()] = false;
             }
         }
     }
     if ($this->iAllowedCount == $oSet->Count()) {
         $iActionAllowed = UR_ALLOWED_YES;
     }
     if ($this->iAllowedCount == 0) {
         $iActionAllowed = UR_ALLOWED_NO;
     }
     return $iActionAllowed;
 }