示例#1
0
/**
 * Helper to generate a Graphviz code for displaying the life cycle of a class
 * @param string $sClass The class to display
 * @return string The Graph description in Graphviz/Dot syntax   
 */
function GraphvizLifecycle($sClass)
{
    $sDotFileContent = "";
    $sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
    if (empty($sStateAttCode)) {
        //$oPage->p("no lifecycle for this class");
    } else {
        $aStates = MetaModel::EnumStates($sClass);
        $aStimuli = MetaModel::EnumStimuli($sClass);
        $sDotFileContent .= "digraph finite_state_machine {\r\n\tgraph [bgcolor = \"transparent\"];\r\n\trankdir=LR;\r\n\tsize=\"12,12\"\r\n\tnode [ fontname=Verdana style=filled fillcolor=\"#ffffff\" ];\r\n\tedge [ fontname=Verdana ];\r\n";
        $aStatesLinks = array();
        foreach ($aStates as $sStateCode => $aStateDef) {
            $aStatesLinks[$sStateCode] = array('in' => 0, 'out' => 0);
        }
        foreach ($aStates as $sStateCode => $aStateDef) {
            $sStateLabel = MetaModel::GetStateLabel($sClass, $sStateCode);
            $sStateDescription = MetaModel::GetStateDescription($sClass, $sStateCode);
            foreach (MetaModel::EnumTransitions($sClass, $sStateCode) as $sStimulusCode => $aTransitionDef) {
                $aStatesLinks[$sStateCode]['out']++;
                $aStatesLinks[$aTransitionDef['target_state']]['in']++;
                $sStimulusLabel = $aStimuli[$sStimulusCode]->GetLabel();
                $sTargetStateLabel = MetaModel::GetStateLabel($sClass, $aTransitionDef['target_state']);
                $sDotFileContent .= "\t{$sStateCode} -> {$aTransitionDef['target_state']} [ label=\"" . GraphvizEscape($sStimulusLabel) . "\"];\n";
            }
        }
        foreach ($aStates as $sStateCode => $aStateDef) {
            if ($aStatesLinks[$sStateCode]['out'] > 0 || $aStatesLinks[$sStateCode]['in'] > 0) {
                // Show only reachable states
                $sStateLabel = str_replace(' ', '\\n', MetaModel::GetStateLabel($sClass, $sStateCode));
                if ($aStatesLinks[$sStateCode]['in'] == 0 || $aStatesLinks[$sStateCode]['out'] == 0) {
                    // End or Start state, make it look different
                    $sDotFileContent .= "\t{$sStateCode} [ shape=doublecircle,label=\"" . GraphvizEscape($sStateLabel) . "\"];\n";
                } else {
                    $sDotFileContent .= "\t{$sStateCode} [ shape=circle,label=\"" . GraphvizEscape($sStateLabel) . "\"];\n";
                }
            }
        }
        $sDotFileContent .= "}\n";
    }
    return $sDotFileContent;
}
示例#2
0
/**
 * Apply the 'next-action' to the given object or redirect to the page that prompts for additional information if needed
 * @param $oP WebPage The page for the output
 * @param $oObj CMDBObject The object to process
 * @param $sNextAction string The code of the stimulus for the 'action' (i.e. Transition) to apply
 */
function ApplyNextAction(Webpage $oP, CMDBObject $oObj, $sNextAction)
{
    // Here handle the apply stimulus
    $aTransitions = $oObj->EnumTransitions();
    $aStimuli = MetaModel::EnumStimuli(get_class($oObj));
    if (!isset($aTransitions[$sNextAction])) {
        // Invalid stimulus
        throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sNextAction, $oObj->GetName(), $oObj->GetStateLabel()));
    }
    // Get the list of missing mandatory fields for the target state, considering only the changes from the previous form (i.e don't prompt twice)
    $aExpectedAttributes = $oObj->GetExpectedAttributes($oObj->GetState(), $sNextAction, true);
    if (count($aExpectedAttributes) == 0) {
        // If all the mandatory fields are already present, just apply the transition silently...
        if ($oObj->ApplyStimulus($sNextAction)) {
            $oObj->DBUpdate();
        }
        ReloadAndDisplay($oP, $oObj);
    } else {
        // redirect to the 'stimulus' action
        $oAppContext = new ApplicationContext();
        //echo "<p>Missing Attributes <pre>".print_r($aExpectedAttributes, true)."</pre></p>\n";
        $oP->add_header('Location: ' . utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?operation=stimulus&class=' . get_class($oObj) . '&stimulus=' . $sNextAction . '&id=' . $oObj->getKey() . '&' . $oAppContext->GetForLink());
    }
}
 public function GetExpectedAttributes($sCurrentState, $sStimulus, $bOnlyNewOnes)
 {
     $aTransitions = $this->EnumTransitions();
     $aStimuli = MetaModel::EnumStimuli(get_class($this));
     if (!isset($aTransitions[$sStimulus])) {
         // Invalid stimulus
         throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $this->GetName(), $this->GetStateLabel()));
     }
     $aTransition = $aTransitions[$sStimulus];
     $sTargetState = $aTransition['target_state'];
     $aTargetStates = MetaModel::EnumStates(get_class($this));
     $aTargetState = $aTargetStates[$sTargetState];
     $aCurrentState = $aTargetStates[$this->GetState()];
     $aExpectedAttributes = $aTargetState['attribute_list'];
     $aCurrentAttributes = $aCurrentState['attribute_list'];
     $aComputedAttributes = array();
     foreach ($aExpectedAttributes as $sAttCode => $iExpectCode) {
         if (!array_key_exists($sAttCode, $aCurrentAttributes)) {
             $aComputedAttributes[$sAttCode] = $iExpectCode;
         } else {
             if (!($aCurrentAttributes[$sAttCode] & (OPT_ATT_HIDDEN | OPT_ATT_READONLY))) {
                 $iExpectCode = $iExpectCode & ~(OPT_ATT_MUSTPROMPT | OPT_ATT_MUSTCHANGE);
                 // Already prompted/changed, reset the flags
             }
             //TODO: better check if the attribute is not *null*
             if ($iExpectCode & OPT_ATT_MANDATORY && $this->Get($sAttCode) != '') {
                 $iExpectCode = $iExpectCode & ~OPT_ATT_MANDATORY;
                 // If the attribute is present, then no need to request its presence
             }
             $aComputedAttributes[$sAttCode] = $iExpectCode;
         }
         $aComputedAttributes[$sAttCode] = $aComputedAttributes[$sAttCode] & ~(OPT_ATT_READONLY | OPT_ATT_HIDDEN);
         // Don't care about this form now
         if ($aComputedAttributes[$sAttCode] == 0) {
             unset($aComputedAttributes[$sAttCode]);
         }
     }
     return $aComputedAttributes;
 }
 function DoShowGrantSumary($oPage)
 {
     if ($this->GetRawName() == "Administrator") {
         // Looks dirty, but ok that's THE ONE
         $oPage->p(Dict::S('UI:UserManagement:AdminProfile+'));
         return;
     }
     // Note: for sure, we assume that the instance is derived from UserRightsProjection
     $oUserRights = UserRights::GetModuleInstance();
     $aDisplayData = array();
     foreach (MetaModel::GetClasses('bizmodel') as $sClass) {
         // Skip non instantiable classes
         if (MetaModel::IsAbstract($sClass)) {
             continue;
         }
         $aStimuli = array();
         foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) {
             $oGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode);
             if (is_object($oGrant) && $oGrant->Get('permission') == 'yes') {
                 $aStimuli[] = '<span title="' . $sStimulusCode . ': ' . htmlentities($oStimulus->GetDescription(), ENT_QUOTES, 'UTF-8') . '">' . htmlentities($oStimulus->GetLabel(), ENT_QUOTES, 'UTF-8') . '</span>';
             }
         }
         $sStimuli = implode(', ', $aStimuli);
         $aDisplayData[] = array('class' => MetaModel::GetName($sClass), 'read' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Read'), 'bulkread' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Bulk Read'), 'write' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Modify'), 'bulkwrite' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Bulk Modify'), 'delete' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Delete'), 'bulkdelete' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Bulk Delete'), 'stimuli' => $sStimuli);
     }
     $aDisplayConfig = array();
     $aDisplayConfig['class'] = array('label' => Dict::S('UI:UserManagement:Class'), 'description' => Dict::S('UI:UserManagement:Class+'));
     $aDisplayConfig['read'] = array('label' => Dict::S('UI:UserManagement:Action:Read'), 'description' => Dict::S('UI:UserManagement:Action:Read+'));
     $aDisplayConfig['bulkread'] = array('label' => Dict::S('UI:UserManagement:Action:BulkRead'), 'description' => Dict::S('UI:UserManagement:Action:BulkRead+'));
     $aDisplayConfig['write'] = array('label' => Dict::S('UI:UserManagement:Action:Modify'), 'description' => Dict::S('UI:UserManagement:Action:Modify+'));
     $aDisplayConfig['bulkwrite'] = array('label' => Dict::S('UI:UserManagement:Action:BulkModify'), 'description' => Dict::S('UI:UserManagement:Action:BulkModify+'));
     $aDisplayConfig['delete'] = array('label' => Dict::S('UI:UserManagement:Action:Delete'), 'description' => Dict::S('UI:UserManagement:Action:Delete+'));
     $aDisplayConfig['bulkdelete'] = array('label' => Dict::S('UI:UserManagement:Action:BulkDelete'), 'description' => Dict::S('UI:UserManagement:Action:BulkDelete+'));
     $aDisplayConfig['stimuli'] = array('label' => Dict::S('UI:UserManagement:Action:Stimuli'), 'description' => Dict::S('UI:UserManagement:Action:Stimuli+'));
     $oPage->table($aDisplayConfig, $aDisplayData);
 }
示例#5
0
 /**
  * Designed as an action to be called when a stop watch threshold times out
  * or from within the framework	
  */
 public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false)
 {
     $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
     if (empty($sStateAttCode)) {
         return false;
     }
     MyHelpers::CheckKeyInArray('object lifecycle stimulus', $sStimulusCode, MetaModel::EnumStimuli(get_class($this)));
     $aStateTransitions = $this->EnumTransitions();
     if (!array_key_exists($sStimulusCode, $aStateTransitions)) {
         // This simulus has no effect in the current state... do nothing
         return;
     }
     $aTransitionDef = $aStateTransitions[$sStimulusCode];
     // Change the state before proceeding to the actions, this is necessary because an action might
     // trigger another stimuli (alternative: push the stimuli into a queue)
     $sPreviousState = $this->Get($sStateAttCode);
     $sNewState = $aTransitionDef['target_state'];
     $this->Set($sStateAttCode, $sNewState);
     // $aTransitionDef is an
     //    array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD
     $bSuccess = true;
     foreach ($aTransitionDef['actions'] as $sActionHandler) {
         // std PHP spec
         $aActionCallSpec = array($this, $sActionHandler);
         if (!is_callable($aActionCallSpec)) {
             throw new CoreException("Unable to call action: " . get_class($this) . "::{$sActionHandler}");
             return;
         }
         $bRet = call_user_func($aActionCallSpec, $sStimulusCode);
         // if one call fails, the whole is considered as failed
         if (!$bRet) {
             $bSuccess = false;
         }
     }
     if ($bSuccess) {
         $sClass = get_class($this);
         // Stop watches
         foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
             if ($oAttDef instanceof AttributeStopWatch) {
                 $oSW = $this->Get($sAttCode);
                 if (in_array($sNewState, $oAttDef->GetStates())) {
                     $oSW->Start($this, $oAttDef);
                 } else {
                     $oSW->Stop($this, $oAttDef);
                 }
                 $this->Set($sAttCode, $oSW);
             }
         }
         if (!$bDoNotWrite) {
             $this->DBWrite();
         }
         // Change state triggers...
         $sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
         $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN ('{$sClassList}') AND t.state='{$sPreviousState}'"));
         while ($oTrigger = $oSet->Fetch()) {
             $oTrigger->DoActivate($this->ToArgs('this'));
         }
         $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN ('{$sClassList}') AND t.state='{$sNewState}'"));
         while ($oTrigger = $oSet->Fetch()) {
             $oTrigger->DoActivate($this->ToArgs('this'));
         }
     }
     return $bSuccess;
 }
 protected function SetupUser($iUserId, $bNewUser = false)
 {
     foreach (array('bizmodel', 'application', 'gui', 'core/cmdb') as $sCategory) {
         foreach (MetaModel::GetClasses($sCategory) as $sClass) {
             foreach (self::$m_aActionCodes as $iActionCode => $sAction) {
                 if ($bNewUser) {
                     $bAddCell = true;
                 } else {
                     $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassGrant WHERE class = '{$sClass}' AND action = '{$sAction}' AND userid = {$iUserId}"));
                     $bAddCell = $oSet->Count() < 1;
                 }
                 if ($bAddCell) {
                     // Create a new entry
                     $oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassGrant");
                     $oMyClassGrant->Set("userid", $iUserId);
                     $oMyClassGrant->Set("class", $sClass);
                     $oMyClassGrant->Set("action", $sAction);
                     $oMyClassGrant->Set("permission", "yes");
                     $iId = $oMyClassGrant->DBInsertNoReload();
                 }
             }
             foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) {
                 if ($bNewUser) {
                     $bAddCell = true;
                 } else {
                     $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassStimulusGrant WHERE class = '{$sClass}' AND stimulus = '{$sStimulusCode}' AND userid = {$iUserId}"));
                     $bAddCell = $oSet->Count() < 1;
                 }
                 if ($bAddCell) {
                     // Create a new entry
                     $oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassStimulusGrant");
                     $oMyClassGrant->Set("userid", $iUserId);
                     $oMyClassGrant->Set("class", $sClass);
                     $oMyClassGrant->Set("stimulus", $sStimulusCode);
                     $oMyClassGrant->Set("permission", "yes");
                     $iId = $oMyClassGrant->DBInsertNoReload();
                 }
             }
             foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) {
                 if ($bNewUser) {
                     $bAddCell = true;
                 } else {
                     $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixAttributeGrant WHERE class = '{$sClass}' AND attcode = '{$sAttCode}' AND userid = {$iUserId}"));
                     $bAddCell = $oSet->Count() < 1;
                 }
                 if ($bAddCell) {
                     foreach (array('read', 'modify') as $sAction) {
                         // Create a new entry
                         $oMyAttGrant = MetaModel::NewObject("UserRightsMatrixAttributeGrant");
                         $oMyAttGrant->Set("userid", $iUserId);
                         $oMyAttGrant->Set("class", $sClass);
                         $oMyAttGrant->Set("attcode", $sAttCode);
                         $oMyAttGrant->Set("action", $sAction);
                         $oMyAttGrant->Set("permission", "yes");
                         $iId = $oMyAttGrant->DBInsertNoReload();
                     }
                 }
             }
         }
     }
     /*
     // Create the "My Bookmarks" menu item (parent_id = 0, rank = 6)
     if ($bNewUser)
     {
     	$bAddMenu = true;
     }
     else
     {
     	$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT menuNode WHERE type = 'user' AND parent_id = 0 AND user_id = $iUserId"));
     	$bAddMenu = ($oSet->Count() < 1);
     }
     if ($bAddMenu)
     {
     	$oMenu = MetaModel::NewObject('menuNode');
     	$oMenu->Set('type', 'user');
     	$oMenu->Set('parent_id', 0);	// It's a toplevel entry
     	$oMenu->Set('rank', 6);			// Located just above the Admin Tools section (=7)
     	$oMenu->Set('name', 'My Bookmarks');
     	$oMenu->Set('label', 'My Favorite Items');
     	$oMenu->Set('hyperlink', 'UI.php');
     	$oMenu->Set('template', '<p></p><p></p><p style="text-align:center; font-family:Georgia, Times, serif; font-size:32px;">My bookmarks</p><p style="text-align:center; font-family:Georgia, Times, serif; font-size:14px;"><i>This section contains my most favorite search results</i></p>');
     	$oMenu->Set('user_id', $iUserId);
     	$oMenu->DBInsert();
     }
     */
 }
 /**
  * Enumerate services delivered by this class
  * @param string $sVersion The version (e.g. 1.0) supported by the services
  * @return RestResult The standardized result structure (at least a message)
  * @throws Exception in case of internal failure.	 
  */
 public function ExecOperation($sVersion, $sVerb, $aParams)
 {
     $oResult = new RestResultWithObjects();
     switch ($sVerb) {
         case 'core/create':
             RestUtils::InitTrackingComment($aParams);
             $sClass = RestUtils::GetClass($aParams, 'class');
             $aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
             $aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
             $bExtendedOutput = RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+';
             $oObject = RestUtils::MakeObjectFromFields($sClass, $aFields);
             $oObject->DBInsert();
             $oResult->AddObject(0, 'created', $oObject, $aShowFields, $bExtendedOutput);
             break;
         case 'core/update':
             RestUtils::InitTrackingComment($aParams);
             $sClass = RestUtils::GetClass($aParams, 'class');
             $key = RestUtils::GetMandatoryParam($aParams, 'key');
             $aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
             $aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
             $bExtendedOutput = RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+';
             $oObject = RestUtils::FindObjectFromKey($sClass, $key);
             RestUtils::UpdateObjectFromFields($oObject, $aFields);
             $oObject->DBUpdate();
             $oResult->AddObject(0, 'updated', $oObject, $aShowFields, $bExtendedOutput);
             break;
         case 'core/apply_stimulus':
             RestUtils::InitTrackingComment($aParams);
             $sClass = RestUtils::GetClass($aParams, 'class');
             $key = RestUtils::GetMandatoryParam($aParams, 'key');
             $aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
             $aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
             $bExtendedOutput = RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+';
             $sStimulus = RestUtils::GetMandatoryParam($aParams, 'stimulus');
             $oObject = RestUtils::FindObjectFromKey($sClass, $key);
             RestUtils::UpdateObjectFromFields($oObject, $aFields);
             $aTransitions = $oObject->EnumTransitions();
             $aStimuli = MetaModel::EnumStimuli(get_class($oObject));
             if (!isset($aTransitions[$sStimulus])) {
                 // Invalid stimulus
                 $oResult->code = RestResult::INTERNAL_ERROR;
                 $oResult->message = "Invalid stimulus: '{$sStimulus}' on the object " . $oObject->GetName() . " in state '" . $oObject->GetState() . "'";
             } else {
                 $aTransition = $aTransitions[$sStimulus];
                 $sTargetState = $aTransition['target_state'];
                 $aStates = MetaModel::EnumStates($sClass);
                 $aTargetStateDef = $aStates[$sTargetState];
                 $aExpectedAttributes = $aTargetStateDef['attribute_list'];
                 $aMissingMandatory = array();
                 foreach ($aExpectedAttributes as $sAttCode => $iExpectCode) {
                     if ($iExpectCode & OPT_ATT_MANDATORY && $oObject->Get($sAttCode) == '') {
                         $aMissingMandatory[] = $sAttCode;
                     }
                 }
                 if (count($aMissingMandatory) == 0) {
                     // If all the mandatory fields are already present, just apply the transition silently...
                     if ($oObject->ApplyStimulus($sStimulus)) {
                         $oObject->DBUpdate();
                         $oResult->AddObject(0, 'updated', $oObject, $aShowFields, $bExtendedOutput);
                     }
                 } else {
                     // Missing mandatory attributes for the transition
                     $oResult->code = RestResult::INTERNAL_ERROR;
                     $oResult->message = 'Missing mandatory attribute(s) for applying the stimulus: ' . implode(', ', $aMissingMandatory) . '.';
                 }
             }
             break;
         case 'core/get':
             $sClass = RestUtils::GetClass($aParams, 'class');
             $key = RestUtils::GetMandatoryParam($aParams, 'key');
             $aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
             $bExtendedOutput = RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+';
             $oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
             while ($oObject = $oObjectSet->Fetch()) {
                 $oResult->AddObject(0, '', $oObject, $aShowFields, $bExtendedOutput);
             }
             $oResult->message = "Found: " . $oObjectSet->Count();
             break;
         case 'core/delete':
             $sClass = RestUtils::GetClass($aParams, 'class');
             $key = RestUtils::GetMandatoryParam($aParams, 'key');
             $bSimulate = RestUtils::GetOptionalParam($aParams, 'simulate', false);
             $oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
             $aObjects = $oObjectSet->ToArray();
             $this->DeleteObjects($oResult, $aObjects, $bSimulate);
             break;
         case 'core/get_related':
             $oResult = new RestResultWithRelations();
             $sClass = RestUtils::GetClass($aParams, 'class');
             $key = RestUtils::GetMandatoryParam($aParams, 'key');
             $sRelation = RestUtils::GetMandatoryParam($aParams, 'relation');
             $iMaxRecursionDepth = RestUtils::GetOptionalParam($aParams, 'depth', 20);
             $sDirection = RestUtils::GetOptionalParam($aParams, 'direction', null);
             $bEnableRedundancy = RestUtils::GetOptionalParam($aParams, 'redundancy', false);
             $bReverse = false;
             if (is_null($sDirection) && $sRelation == 'depends on') {
                 // Legacy behavior, consider "depends on" as a forward relation
                 $sRelation = 'impacts';
                 $sDirection = 'up';
                 $bReverse = true;
                 // emulate the legacy behavior by returning the edges
             } else {
                 if (is_null($sDirection)) {
                     $sDirection = 'down';
                 }
             }
             $oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
             if ($sDirection == 'down') {
                 $oRelationGraph = $oObjectSet->GetRelatedObjectsDown($sRelation, $iMaxRecursionDepth, $bEnableRedundancy);
             } else {
                 if ($sDirection == 'up') {
                     $oRelationGraph = $oObjectSet->GetRelatedObjectsUp($sRelation, $iMaxRecursionDepth, $bEnableRedundancy);
                 } else {
                     $oResult->code = RestResult::INTERNAL_ERROR;
                     $oResult->message = "Invalid value: '{$sDirection}' for the parameter 'direction'. Valid values are 'up' and 'down'";
                     return $oResult;
                 }
             }
             if ($bEnableRedundancy) {
                 // Remove the redundancy nodes from the output
                 $oIterator = new RelationTypeIterator($oRelationGraph, 'Node');
                 foreach ($oIterator as $oNode) {
                     if ($oNode instanceof RelationRedundancyNode) {
                         $oRelationGraph->FilterNode($oNode);
                     }
                 }
             }
             $aIndexByClass = array();
             $oIterator = new RelationTypeIterator($oRelationGraph);
             foreach ($oIterator as $oElement) {
                 if ($oElement instanceof RelationObjectNode) {
                     $oObject = $oElement->GetProperty('object');
                     if ($oObject) {
                         if ($bEnableRedundancy) {
                             // Add only the "reached" objects
                             if ($oElement->GetProperty('is_reached')) {
                                 $aIndexByClass[get_class($oObject)][$oObject->GetKey()] = null;
                                 $oResult->AddObject(0, '', $oObject);
                             }
                         } else {
                             $aIndexByClass[get_class($oObject)][$oObject->GetKey()] = null;
                             $oResult->AddObject(0, '', $oObject);
                         }
                     }
                 } else {
                     if ($oElement instanceof RelationEdge) {
                         $oSrcObj = $oElement->GetSourceNode()->GetProperty('object');
                         $oDestObj = $oElement->GetSinkNode()->GetProperty('object');
                         $sSrcKey = get_class($oSrcObj) . '::' . $oSrcObj->GetKey();
                         $sDestKey = get_class($oDestObj) . '::' . $oDestObj->GetKey();
                         if ($bEnableRedundancy) {
                             // Add only the edges where both source and destination are "reached"
                             if ($oElement->GetSourceNode()->GetProperty('is_reached') && $oElement->GetSinkNode()->GetProperty('is_reached')) {
                                 if ($bReverse) {
                                     $oResult->AddRelation($sDestKey, $sSrcKey);
                                 } else {
                                     $oResult->AddRelation($sSrcKey, $sDestKey);
                                 }
                             }
                         } else {
                             if ($bReverse) {
                                 $oResult->AddRelation($sDestKey, $sSrcKey);
                             } else {
                                 $oResult->AddRelation($sSrcKey, $sDestKey);
                             }
                         }
                     }
                 }
             }
             if (count($aIndexByClass) > 0) {
                 $aStats = array();
                 foreach ($aIndexByClass as $sClass => $aIds) {
                     $aStats[] = $sClass . '= ' . count($aIds);
                 }
                 $oResult->message = "Scope: " . $oObjectSet->Count() . "; Related objects: " . implode(', ', $aStats);
             } else {
                 $oResult->message = "Nothing found";
             }
             break;
         case 'core/check_credentials':
             $oResult = new RestResult();
             $sUser = RestUtils::GetMandatoryParam($aParams, 'user');
             $sPassword = RestUtils::GetMandatoryParam($aParams, 'password');
             if (UserRights::CheckCredentials($sUser, $sPassword) !== true) {
                 $oResult->authorized = false;
             } else {
                 $oResult->authorized = true;
             }
             break;
         default:
             // unknown operation: handled at a higher level
     }
     return $oResult;
 }
示例#8
0
/**
 * Helper for the lifecycle details of a given class
 */
function DisplayLifecycle($oPage, $sClass)
{
    $sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
    if (empty($sStateAttCode)) {
        $oPage->p(Dict::S('UI:Schema:NoLifeCyle'));
    } else {
        $aStates = MetaModel::EnumStates($sClass);
        $aStimuli = MetaModel::EnumStimuli($sClass);
        $oPage->add("<img src=\"" . utils::GetAbsoluteUrlAppRoot() . "pages/graphviz.php?class={$sClass}\">\n");
        $oPage->add("<h3>" . Dict::S('UI:Schema:LifeCycleTransitions') . "</h3>\n");
        $oPage->add("<ul>\n");
        foreach ($aStates as $sStateCode => $aStateDef) {
            $sStateLabel = MetaModel::GetStateLabel($sClass, $sStateCode);
            $sStateDescription = MetaModel::GetStateDescription($sClass, $sStateCode);
            $oPage->add("<li title=\"code: {$sStateCode}\">{$sStateLabel} <span style=\"color:grey;\">({$sStateCode}) {$sStateDescription}</span></li>\n");
            $oPage->add("<ul>\n");
            foreach (MetaModel::EnumTransitions($sClass, $sStateCode) as $sStimulusCode => $aTransitionDef) {
                $sStimulusLabel = $aStimuli[$sStimulusCode]->GetLabel();
                $sTargetStateLabel = MetaModel::GetStateLabel($sClass, $aTransitionDef['target_state']);
                if (count($aTransitionDef['actions']) > 0) {
                    $sActions = " <em>(" . implode(', ', $aTransitionDef['actions']) . ")</em>";
                } else {
                    $sActions = "";
                }
                $oPage->add("<li><span style=\"color:red;font-weight=bold;\">{$sStimulusLabel}</span> =&gt; {$sTargetStateLabel} {$sActions}</li>\n");
            }
            $oPage->add("</ul>\n");
        }
        $oPage->add("</ul>\n");
        $oPage->add("<h3>" . Dict::S('UI:Schema:LifeCyleAttributeOptions') . "</h3>\n");
        $oPage->add("<ul>\n");
        foreach ($aStates as $sStateCode => $aStateDef) {
            $sStateLabel = MetaModel::GetStateLabel($sClass, $sStateCode);
            $sStateDescription = MetaModel::GetStateDescription($sClass, $sStateCode);
            $oPage->add("<li title=\"code: {$sStateCode}\">{$sStateLabel} <span style=\"color:grey;\">({$sStateCode}) {$sStateDescription}</span></li>\n");
            if (count($aStates[$sStateCode]['attribute_list']) > 0) {
                $oPage->add("<ul>\n");
                foreach ($aStates[$sStateCode]['attribute_list'] as $sAttCode => $iOptions) {
                    $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
                    $sAttLabel = $oAttDef->GetLabel();
                    $aOptions = array();
                    if ($iOptions & OPT_ATT_HIDDEN) {
                        $aOptions[] = Dict::S('UI:Schema:LifeCycleHiddenAttribute');
                    }
                    if ($iOptions & OPT_ATT_READONLY) {
                        $aOptions[] = Dict::S('UI:Schema:LifeCycleReadOnlyAttribute');
                    }
                    if ($iOptions & OPT_ATT_MANDATORY) {
                        $aOptions[] = Dict::S('UI:Schema:LifeCycleMandatoryAttribute');
                    }
                    if ($iOptions & OPT_ATT_MUSTCHANGE) {
                        $aOptions[] = Dict::S('UI:Schema:LifeCycleAttributeMustChange');
                    }
                    if ($iOptions & OPT_ATT_MUSTPROMPT) {
                        $aOptions[] = Dict::S('UI:Schema:LifeCycleAttributeMustPrompt');
                    }
                    if (count($aOptions)) {
                        $sOptions = implode(', ', $aOptions);
                    } else {
                        $sOptions = "";
                    }
                    $oPage->add("<li><span style=\"color:purple;font-weight=bold;\">{$sAttLabel}</span> {$sOptions}</li>\n");
                }
                $oPage->add("</ul>\n");
            } else {
                $oPage->p("<em>" . Dict::S('UI:Schema:LifeCycleEmptyList') . "</em>");
            }
        }
        $oPage->add("</ul>\n");
    }
}
 function DoShowGrantSumary($oPage, $sClassCategory)
 {
     if (UserRights::IsAdministrator($this)) {
         // Looks dirty, but ok that's THE ONE
         $oPage->p(Dict::S('UI:UserManagement:AdminProfile+'));
         return;
     }
     $oKPI = new ExecutionKPI();
     $aDisplayData = array();
     foreach (MetaModel::GetClasses($sClassCategory) as $sClass) {
         $aClassStimuli = MetaModel::EnumStimuli($sClass);
         if (count($aClassStimuli) > 0) {
             $aStimuli = array();
             foreach ($aClassStimuli as $sStimulusCode => $oStimulus) {
                 if (UserRights::IsStimulusAllowed($sClass, $sStimulusCode, null, $this)) {
                     $aStimuli[] = '<span title="' . $sStimulusCode . ': ' . htmlentities($oStimulus->GetDescription(), ENT_QUOTES, 'UTF-8') . '">' . htmlentities($oStimulus->GetLabel(), ENT_QUOTES, 'UTF-8') . '</span>';
                 }
             }
             $sStimuli = implode(', ', $aStimuli);
         } else {
             $sStimuli = '<em title="' . Dict::S('UI:UserManagement:NoLifeCycleApplicable+') . '">' . Dict::S('UI:UserManagement:NoLifeCycleApplicable') . '</em>';
         }
         $aDisplayData[] = array('class' => MetaModel::GetName($sClass), 'read' => $this->GetGrantAsHtml($sClass, UR_ACTION_READ), 'bulkread' => $this->GetGrantAsHtml($sClass, UR_ACTION_BULK_READ), 'write' => $this->GetGrantAsHtml($sClass, UR_ACTION_MODIFY), 'bulkwrite' => $this->GetGrantAsHtml($sClass, UR_ACTION_BULK_MODIFY), 'stimuli' => $sStimuli);
     }
     $oKPI->ComputeAndReport('Computation of user rights');
     $aDisplayConfig = array();
     $aDisplayConfig['class'] = array('label' => Dict::S('UI:UserManagement:Class'), 'description' => Dict::S('UI:UserManagement:Class+'));
     $aDisplayConfig['read'] = array('label' => Dict::S('UI:UserManagement:Action:Read'), 'description' => Dict::S('UI:UserManagement:Action:Read+'));
     $aDisplayConfig['bulkread'] = array('label' => Dict::S('UI:UserManagement:Action:BulkRead'), 'description' => Dict::S('UI:UserManagement:Action:BulkRead+'));
     $aDisplayConfig['write'] = array('label' => Dict::S('UI:UserManagement:Action:Modify'), 'description' => Dict::S('UI:UserManagement:Action:Modify+'));
     $aDisplayConfig['bulkwrite'] = array('label' => Dict::S('UI:UserManagement:Action:BulkModify'), 'description' => Dict::S('UI:UserManagement:Action:BulkModify+'));
     $aDisplayConfig['stimuli'] = array('label' => Dict::S('UI:UserManagement:Action:Stimuli'), 'description' => Dict::S('UI:UserManagement:Action:Stimuli+'));
     $oPage->table($aDisplayConfig, $aDisplayData);
 }
示例#10
0
function MakeDictionaryTemplate($sModules = '', $sLanguage = 'EN US')
{
    $sRes = '';
    Dict::SetDefaultLanguage($sLanguage);
    $aAvailableLanguages = Dict::GetLanguages();
    $sDesc = $aAvailableLanguages[$sLanguage]['description'];
    $sLocalizedDesc = $aAvailableLanguages[$sLanguage]['localized_description'];
    $sRes .= "// Dictionary conventions\n";
    $sRes .= htmlentities("// Class:<class_name>\n", ENT_QUOTES, 'UTF-8');
    $sRes .= htmlentities("// Class:<class_name>+\n", ENT_QUOTES, 'UTF-8');
    $sRes .= htmlentities("// Class:<class_name>/Attribute:<attribute_code>\n", ENT_QUOTES, 'UTF-8');
    $sRes .= htmlentities("// Class:<class_name>/Attribute:<attribute_code>+\n", ENT_QUOTES, 'UTF-8');
    $sRes .= htmlentities("// Class:<class_name>/Attribute:<attribute_code>/Value:<value>\n", ENT_QUOTES, 'UTF-8');
    $sRes .= htmlentities("// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+\n", ENT_QUOTES, 'UTF-8');
    $sRes .= htmlentities("// Class:<class_name>/Stimulus:<stimulus_code>\n", ENT_QUOTES, 'UTF-8');
    $sRes .= htmlentities("// Class:<class_name>/Stimulus:<stimulus_code>+\n", ENT_QUOTES, 'UTF-8');
    $sRes .= "\n";
    // Note: I did not use EnumCategories(), because a given class maybe found in several categories
    // Need to invent the "module", to characterize the origins of a class
    if (strlen($sModules) == 0) {
        $aModules = array('bizmodel', 'core/cmdb', 'gui', 'application', 'addon/userrights', 'monitoring');
    } else {
        $aModules = explode(', ', $sModules);
    }
    $sRes .= "//////////////////////////////////////////////////////////////////////\n";
    $sRes .= "// Note: The classes have been grouped by categories: " . implode(', ', $aModules) . "\n";
    $sRes .= "//////////////////////////////////////////////////////////////////////\n";
    foreach ($aModules as $sCategory) {
        $sRes .= "//////////////////////////////////////////////////////////////////////\n";
        $sRes .= "// Classes in '{$sCategory}'\n";
        $sRes .= "//////////////////////////////////////////////////////////////////////\n";
        $sRes .= "//\n";
        $sRes .= "\n";
        foreach (MetaModel::GetClasses($sCategory) as $sClass) {
            if (!MetaModel::HasTable($sClass)) {
                continue;
            }
            $bNotInDico = false;
            $bNotImportant = true;
            $sClassRes = "//\n";
            $sClassRes .= "// Class: {$sClass}\n";
            $sClassRes .= "//\n";
            $sClassRes .= "\n";
            $sClassRes .= "Dict::Add('{$sLanguage}', '{$sDesc}', '{$sLocalizedDesc}', array(\n";
            $sClassRes .= MakeDictEntry("Class:{$sClass}", MetaModel::GetName_Obsolete($sClass), $sClass, $bNotInDico);
            $sClassRes .= MakeDictEntry("Class:{$sClass}+", MetaModel::GetClassDescription_Obsolete($sClass), '', $bNotImportant);
            foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
                if ($sAttCode == 'friendlyname') {
                    continue;
                }
                // Skip this attribute if not originaly defined in this class
                if (MetaModel::GetAttributeOrigin($sClass, $sAttCode) != $sClass) {
                    continue;
                }
                $sClassRes .= MakeDictEntry("Class:{$sClass}/Attribute:{$sAttCode}", $oAttDef->GetLabel_Obsolete(), $sAttCode, $bNotInDico);
                $sClassRes .= MakeDictEntry("Class:{$sClass}/Attribute:{$sAttCode}+", $oAttDef->GetDescription_Obsolete(), '', $bNotImportant);
                if ($oAttDef instanceof AttributeEnum) {
                    if (MetaModel::GetStateAttributeCode($sClass) == $sAttCode) {
                        foreach (MetaModel::EnumStates($sClass) as $sStateCode => $aStateData) {
                            if (array_key_exists('label', $aStateData)) {
                                $sValue = $aStateData['label'];
                            } else {
                                $sValue = MetaModel::GetStateLabel($sClass, $sStateCode);
                            }
                            if (array_key_exists('description', $aStateData)) {
                                $sValuePlus = $aStateData['description'];
                            } else {
                                $sValuePlus = MetaModel::GetStateDescription($sClass, $sStateCode);
                            }
                            $sClassRes .= MakeDictEntry("Class:{$sClass}/Attribute:{$sAttCode}/Value:{$sStateCode}", $sValue, '', $bNotInDico);
                            $sClassRes .= MakeDictEntry("Class:{$sClass}/Attribute:{$sAttCode}/Value:{$sStateCode}+", $sValuePlus, '', $bNotImportant);
                        }
                    } else {
                        foreach ($oAttDef->GetAllowedValues() as $sKey => $value) {
                            $sClassRes .= MakeDictEntry("Class:{$sClass}/Attribute:{$sAttCode}/Value:{$sKey}", $value, '', $bNotInDico);
                            $sClassRes .= MakeDictEntry("Class:{$sClass}/Attribute:{$sAttCode}/Value:{$sKey}+", $value, '', $bNotImportant);
                        }
                    }
                }
            }
            foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) {
                $sClassRes .= MakeDictEntry("Class:{$sClass}/Stimulus:{$sStimulusCode}", $oStimulus->GetLabel_Obsolete(), '', $bNotInDico);
                $sClassRes .= MakeDictEntry("Class:{$sClass}/Stimulus:{$sStimulusCode}+", $oStimulus->GetDescription_Obsolete(), '', $bNotImportant);
            }
            $sClassRes .= "));\n";
            $sClassRes .= "\n";
            $sRes .= $sClassRes;
        }
    }
    return $sRes;
}
示例#11
0
/**
 * Create form to apply a stimulus
 * @param WebPage $oP The current web page
 * @param Object $oObj The target object
 * @param String $sStimulusCode Stimulus that will be applied
 * @param Array $aEditAtt List of attributes to edit
 * @return void
 */
function MakeStimulusForm(WebPage $oP, $oObj, $sStimulusCode, $aEditAtt)
{
    static $bHasStimulusForm = false;
    $sDialogId = $sStimulusCode . "_dialog";
    $sFormId = $sStimulusCode . "_form";
    $sCancelButtonLabel = Dict::S('UI:Button:Cancel');
    $oP->add('<div id="' . $sDialogId . '" style="display: none;">');
    $sClass = get_class($oObj);
    $oP->add('<form id="' . $sFormId . '" method="post">');
    $sTransactionId = utils::GetNewTransactionId();
    $oP->add("<input type=\"hidden\" id=\"transaction_id\" name=\"transaction_id\" value=\"{$sTransactionId}\">\n");
    $oP->add("<input type=\"hidden\" name=\"class\" value=\"{$sClass}\">");
    $oP->add("<input type=\"hidden\" name=\"id\" value=\"" . $oObj->GetKey() . "\">");
    $oP->add("<input type=\"hidden\" name=\"operation\" value=\"update_request\">");
    $oP->add("<input type=\"hidden\" id=\"stimulus_to_apply\" name=\"apply_stimulus\" value=\"{$sStimulusCode}\">\n");
    $aTransitions = $oObj->EnumTransitions();
    $aStimuli = MetaModel::EnumStimuli($sClass);
    if (!isset($aTransitions[$sStimulusCode])) {
        // Invalid stimulus
        throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulusCode, $oObj->GetName(), $oObj->GetStateLabel()));
    }
    // Compute the attribute flags in the target state
    $aTransition = $aTransitions[$sStimulusCode];
    $sTargetState = $aTransition['target_state'];
    $aTargetStates = MetaModel::EnumStates($sClass);
    $aTargetState = $aTargetStates[$sTargetState];
    $aExpectedAttributes = $aTargetState['attribute_list'];
    foreach ($aEditAtt as $sAttCode) {
        $sValue = $oObj->Get($sAttCode);
        $sDisplayValue = $oObj->GetEditValue($sAttCode);
        $aArgs = array('this' => $oObj, 'formPrefix' => '');
        $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
        $sInputId = 'input_' . $sAttCode;
        $iFlags = array_key_exists($sAttCode, $aExpectedAttributes) ? $aExpectedAttributes[$sAttCode] : 0;
        $sHTMLValue = "<span id=\"field_{$sStimulusCode}_{$sInputId}\">" . cmdbAbstractObject::GetFormElementForField($oP, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs) . '</span>';
        $oP->add('<h1>' . MetaModel::GetLabel($sClass, $sAttCode) . '</h1>');
        $oP->add($sHTMLValue);
    }
    $oP->add('</form>');
    $oP->add('</div>');
    if (!$bHasStimulusForm) {
        $bHasStimulusForm = true;
        $oP->add_script(<<<EOF

function RunStimulusDialog(sStimulusCode, sTitle, sOkButtonLabel)
{
\tvar sWidth = 'auto';
\tif (sStimulusCode == 'ev_reopen')
\t{
\t\t// Avoid having a dialog spanning the complete width of the window
\t\t// just because it contains a CaseLog entry
\t\tsWidth = '80%';
\t}
\t\t\t\t
\t\$('#'+sStimulusCode+'_dialog').dialog({
\t\theight: 'auto',
\t\twidth: sWidth,
\t\tmodal: true,
\t\ttitle: sTitle,
\t\tbuttons: [
\t\t{ text: sOkButtonLabel, click: function() {
\t\t\t\$(this).find('#'+sStimulusCode+'_form').submit();
\t\t} },
\t\t{ text: "{$sCancelButtonLabel}", click: function() {
\t\t\t\$(this).dialog( "close" );
\t\t} }
\t\t]
\t});
\t// Start the validation
\tCheckFields(sStimulusCode+'_form', false);
\t\$('#'+sStimulusCode+'_form').submit( function() {
\t\treturn OnSubmit(sStimulusCode+'_form');
\t});
}
EOF
);
    }
}
示例#12
0
 function test_object_lifecycle()
 {
     echo "<h4>Test object lifecycle</h4>";
     self::DumpVariable(MetaModel::GetStateAttributeCode("cmdbContact"));
     self::DumpVariable(MetaModel::EnumStates("cmdbContact"));
     self::DumpVariable(MetaModel::EnumStimuli("cmdbContact"));
     foreach (MetaModel::EnumStates("cmdbContact") as $sStateCode => $aStateDef) {
         echo "<p>Transition from <strong>{$sStateCode}</strong></p>\n";
         self::DumpVariable(MetaModel::EnumTransitions("cmdbContact", $sStateCode));
     }
     $oObj = MetaModel::GetObject("cmdbContact", 18);
     echo "Current state: " . $oObj->GetState() . "... let's go to school...";
     self::DumpVariable($oObj->EnumTransitions());
     $oObj->ApplyStimulus("toschool");
     echo "New state: " . $oObj->GetState() . "... let's get older...";
     self::DumpVariable($oObj->EnumTransitions());
     $oObj->ApplyStimulus("raise");
     echo "New state: " . $oObj->GetState() . "... let's try to go further... (should give an error)";
     self::DumpVariable($oObj->EnumTransitions());
     $oObj->ApplyStimulus("raise");
     // should give an error
 }
 /**
  * Enumerate services delivered by this class
  * @param string $sVersion The version (e.g. 1.0) supported by the services
  * @return RestResult The standardized result structure (at least a message)
  * @throws Exception in case of internal failure.	 
  */
 public function ExecOperation($sVersion, $sVerb, $aParams)
 {
     $oResult = new RestResultWithObjects();
     switch ($sVerb) {
         case 'core/create':
             RestUtils::InitTrackingComment($aParams);
             $sClass = RestUtils::GetClass($aParams, 'class');
             $aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
             $aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
             $bExtendedOutput = RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+';
             $oObject = RestUtils::MakeObjectFromFields($sClass, $aFields);
             $oObject->DBInsert();
             $oResult->AddObject(0, 'created', $oObject, $aShowFields, $bExtendedOutput);
             break;
         case 'core/update':
             RestUtils::InitTrackingComment($aParams);
             $sClass = RestUtils::GetClass($aParams, 'class');
             $key = RestUtils::GetMandatoryParam($aParams, 'key');
             $aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
             $aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
             $bExtendedOutput = RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+';
             $oObject = RestUtils::FindObjectFromKey($sClass, $key);
             RestUtils::UpdateObjectFromFields($oObject, $aFields);
             $oObject->DBUpdate();
             $oResult->AddObject(0, 'updated', $oObject, $aShowFields, $bExtendedOutput);
             break;
         case 'core/apply_stimulus':
             RestUtils::InitTrackingComment($aParams);
             $sClass = RestUtils::GetClass($aParams, 'class');
             $key = RestUtils::GetMandatoryParam($aParams, 'key');
             $aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
             $aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
             $bExtendedOutput = RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+';
             $sStimulus = RestUtils::GetMandatoryParam($aParams, 'stimulus');
             $oObject = RestUtils::FindObjectFromKey($sClass, $key);
             RestUtils::UpdateObjectFromFields($oObject, $aFields);
             $aTransitions = $oObject->EnumTransitions();
             $aStimuli = MetaModel::EnumStimuli(get_class($oObject));
             if (!isset($aTransitions[$sStimulus])) {
                 // Invalid stimulus
                 $oResult->code = RestResult::INTERNAL_ERROR;
                 $oResult->message = "Invalid stimulus: '{$sStimulus}' on the object " . $oObject->GetName() . " in state '" . $oObject->GetState() . "'";
             } else {
                 $aTransition = $aTransitions[$sStimulus];
                 $sTargetState = $aTransition['target_state'];
                 $aStates = MetaModel::EnumStates($sClass);
                 $aTargetStateDef = $aStates[$sTargetState];
                 $aExpectedAttributes = $aTargetStateDef['attribute_list'];
                 $aMissingMandatory = array();
                 foreach ($aExpectedAttributes as $sAttCode => $iExpectCode) {
                     if ($iExpectCode & OPT_ATT_MANDATORY && $oObject->Get($sAttCode) == '') {
                         $aMissingMandatory[] = $sAttCode;
                     }
                 }
                 if (count($aMissingMandatory) == 0) {
                     // If all the mandatory fields are already present, just apply the transition silently...
                     if ($oObject->ApplyStimulus($sStimulus)) {
                         $oObject->DBUpdate();
                         $oResult->AddObject(0, 'updated', $oObject, $aShowFields, $bExtendedOutput);
                     }
                 } else {
                     // Missing mandatory attributes for the transition
                     $oResult->code = RestResult::INTERNAL_ERROR;
                     $oResult->message = 'Missing mandatory attribute(s) for applying the stimulus: ' . implode(', ', $aMissingMandatory) . '.';
                 }
             }
             break;
         case 'core/get':
             $sClass = RestUtils::GetClass($aParams, 'class');
             $key = RestUtils::GetMandatoryParam($aParams, 'key');
             $aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
             $bExtendedOutput = RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+';
             $oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
             while ($oObject = $oObjectSet->Fetch()) {
                 $oResult->AddObject(0, '', $oObject, $aShowFields, $bExtendedOutput);
             }
             $oResult->message = "Found: " . $oObjectSet->Count();
             break;
         case 'core/delete':
             $sClass = RestUtils::GetClass($aParams, 'class');
             $key = RestUtils::GetMandatoryParam($aParams, 'key');
             $bSimulate = RestUtils::GetOptionalParam($aParams, 'simulate', false);
             $oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
             $aObjects = $oObjectSet->ToArray();
             $this->DeleteObjects($oResult, $aObjects, $bSimulate);
             break;
         case 'core/get_related':
             $oResult = new RestResultWithRelations();
             $sClass = RestUtils::GetClass($aParams, 'class');
             $key = RestUtils::GetMandatoryParam($aParams, 'key');
             $sRelation = RestUtils::GetMandatoryParam($aParams, 'relation');
             $iMaxRecursionDepth = RestUtils::GetOptionalParam($aParams, 'depth', 20);
             $oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
             $aIndexByClass = array();
             while ($oObject = $oObjectSet->Fetch()) {
                 $aRelated = array();
                 $aGraph = array();
                 $aIndexByClass[get_class($oObject)][$oObject->GetKey()] = null;
                 $oResult->AddObject(0, '', $oObject);
                 $this->GetRelatedObjects($oObject, $sRelation, $iMaxRecursionDepth, $aRelated, $aGraph);
                 foreach ($aRelated as $sClass => $aObjects) {
                     foreach ($aObjects as $oRelatedObj) {
                         $aIndexByClass[get_class($oRelatedObj)][$oRelatedObj->GetKey()] = null;
                         $oResult->AddObject(0, '', $oRelatedObj);
                     }
                 }
                 foreach ($aGraph as $sSrcKey => $aDestinations) {
                     foreach ($aDestinations as $sDestKey) {
                         $oResult->AddRelation($sSrcKey, $sDestKey);
                     }
                 }
             }
             if (count($aIndexByClass) > 0) {
                 $aStats = array();
                 foreach ($aIndexByClass as $sClass => $aIds) {
                     $aStats[] = $sClass . '= ' . count($aIds);
                 }
                 $oResult->message = "Scope: " . $oObjectSet->Count() . "; Related objects: " . implode(', ', $aStats);
             } else {
                 $oResult->message = "Nothing found";
             }
             break;
         case 'core/check_credentials':
             $oResult = new RestResult();
             $sUser = RestUtils::GetMandatoryParam($aParams, 'user');
             $sPassword = RestUtils::GetMandatoryParam($aParams, 'password');
             if (UserRights::CheckCredentials($sUser, $sPassword) !== true) {
                 $oResult->authorized = false;
             } else {
                 $oResult->authorized = true;
             }
             break;
         default:
             // unknown operation: handled at a higher level
     }
     return $oResult;
 }