diff --git a/application/datamodel.application.xml b/application/datamodel.application.xml index 2e198d7fc..c1028d42f 100644 --- a/application/datamodel.application.xml +++ b/application/datamodel.application.xml @@ -304,8 +304,8 @@ - - A stimulus is about to be applied to an object + + Manage the allowed transitions in current object state. The only action allowed is to deny transitions with DBObject::DenyTransition() cmdbAbstractObject @@ -314,89 +314,9 @@ The object where the stimulus is targeted DBObject - - Current stimulus applied - string - - - Object previous state - string - - - Object new state - string - - - The object must be saved in the database - boolean - - - Debug string - string - - - - - A stimulus has been applied to an object - - cmdbAbstractObject - - - - The object where the stimulus is targeted - DBObject - - - Current stimulus applied - string - - - Object previous state - string - - - Object new state - string - - - The object is asked to be saved in the database - boolean - - - Debug string - string - - - - - A stimulus has failed - - cmdbAbstractObject - - - - The action that failed to apply the stimulus - string - - - The object where the stimulus is targeted - DBObject - - - Current stimulus applied - string - - - Object previous state - string - - - Object new state - string - - - The object must be saved in the database - boolean + + The list of available stimuli in the current state + array Debug string diff --git a/core/dbobject.class.php b/core/dbobject.class.php index f898c6201..eb09138d8 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -208,6 +208,7 @@ abstract class DBObject implements iDisplay const MAX_UPDATE_LOOP_COUNT = 10; private $aEventListeners = []; + private array $aAllowedTransitions = []; /** * DBObject constructor. @@ -4400,9 +4401,32 @@ abstract class DBObject implements iDisplay ]); } + $this->aAllowedTransitions = $aSortedTransitions; + $this->FireEvent(EVENT_ENUM_TRANSITIONS, ['allowed_stimuli' => array_keys($aSortedTransitions)]); + $aSortedTransitions = $this->aAllowedTransitions; + $this->aAllowedTransitions = []; + return $aSortedTransitions; } + /** + * Remove a transition for a specific stimulus. + * This is only usable by EVENT_ENUM_TRANSITIONS listeners in order + * to manage the allowed transitions in the current object state. + * + * @param string $sStimulus + * + * @return void + * @api + * @since 3.1.2 + */ + public function DenyTransition(string $sStimulus): void + { + if (isset($this->aAllowedTransitions[$sStimulus])) { + unset($this->aAllowedTransitions[$sStimulus]); + } + } + /** * Helper to reset a stop-watch * Suitable for use as a lifecycle action @@ -4488,14 +4512,6 @@ abstract class DBObject implements iDisplay $sNewState = $aTransitionDef['target_state']; $this->Set($sStateAttCode, $sNewState); - $aEventData = [ - 'stimulus' => $sStimulusCode, - 'previous_state' => $sPreviousState, - 'new_state' => $sNewState, - 'save_object' => !$bDoNotWrite, - ]; - $this->FireEvent(EVENT_DB_BEFORE_APPLY_STIMULUS, $aEventData); - // $aTransitionDef is an // array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD @@ -4580,8 +4596,6 @@ abstract class DBObject implements iDisplay if (!$bDoNotWrite) { $this->DBWrite(); } - - $this->FireEvent(EVENT_DB_AFTER_APPLY_STIMULUS, $aEventData); } else { @@ -4590,8 +4604,6 @@ abstract class DBObject implements iDisplay { $this->m_aCurrValues[$sAttCode] = $aBackupValues[$sAttCode]; } - $aEventData['action'] = $sActionDesc; - $this->FireEvent(EVENT_DB_APPLY_STIMULUS_FAILED, $aEventData); } return $bSuccess; } diff --git a/tests/php-unit-tests/unitary-tests/core/CRUDEventTest.php b/tests/php-unit-tests/unitary-tests/core/CRUDEventTest.php index 67ba6e8f1..342364383 100644 --- a/tests/php-unit-tests/unitary-tests/core/CRUDEventTest.php +++ b/tests/php-unit-tests/unitary-tests/core/CRUDEventTest.php @@ -32,6 +32,7 @@ use const EVENT_DB_CHECK_TO_DELETE; use const EVENT_DB_CHECK_TO_WRITE; use const EVENT_DB_COMPUTE_VALUES; use const EVENT_DB_LINKS_CHANGED; +use const EVENT_ENUM_TRANSITIONS; class CRUDEventTest extends ItopDataTestCase { @@ -624,6 +625,29 @@ class CRUDEventTest extends ItopDataTestCase //echo($oDBObject->oEventDataReceived->Get('debug_info')); } + + public function testEnumTransitions() + { + $oEventReceiver = new CRUDEventReceiver($this); + $oEventReceiver->RegisterCRUDListeners(); + + // Object with no lifecycle + /** @var DBObject $oPerson */ + $oPerson = $this->CreatePerson(1); + $oEventReceiver->AddCallback(EVENT_ENUM_TRANSITIONS, Person::class, 'EnumTransitions'); + self::CleanCallCount(); + $oPerson->EnumTransitions(); + $this->assertEquals(0, self::$iEventCalls); + + // Object with lifecycle + $oTicket = $this->CreateTicket(1); + $oEventReceiver->AddCallback(EVENT_ENUM_TRANSITIONS, UserRequest::class, 'EnumTransitions'); + self::CleanCallCount(); + $aTransitions = $oTicket->EnumTransitions(); + $this->assertEquals(1, self::$aEventCalls[EVENT_ENUM_TRANSITIONS]); + $this->assertEquals(1, self::$iEventCalls); + $this->assertCount(0, $aTransitions); + } } /** @@ -713,7 +737,7 @@ class CRUDEventReceiver extends ClassesWithDebug $aCallBack = $this->aCallbacks[$sEvent][$sClass]; if ($aCallBack['count'] > 0) { $this->aCallbacks[$sEvent][$sClass]['count']--; - call_user_func($this->aCallbacks[$sEvent][$sClass]['callback'], $oObject); + call_user_func($this->aCallbacks[$sEvent][$sClass]['callback'], $oData); } } } @@ -730,6 +754,7 @@ class CRUDEventReceiver extends ClassesWithDebug $this->oTestCase->EventService_RegisterListener(EVENT_DB_ABOUT_TO_DELETE, [$this, 'OnEvent']); $this->oTestCase->EventService_RegisterListener(EVENT_DB_AFTER_DELETE, [$this, 'OnEvent']); $this->oTestCase->EventService_RegisterListener(EVENT_DB_LINKS_CHANGED, [$this, 'OnEvent']); + $this->oTestCase->EventService_RegisterListener(EVENT_ENUM_TRANSITIONS, [$this, 'OnEvent']); return; } @@ -739,9 +764,10 @@ class CRUDEventReceiver extends ClassesWithDebug /** * @noinspection PhpUnusedPrivateMethodInspection Used as a callback */ - private function AddRoleToLink($oObject): void + private function AddRoleToLink(EventData $oData): void { $this->Debug(__METHOD__); + $oObject = $oData->Get('object'); $oContactType = MetaModel::NewObject(ContactType::class, ['name' => 'test_'.$oObject->GetKey()]); $oContactType->DBInsert(); $oObject->Set('role_id', $oContactType->GetKey()); @@ -750,27 +776,44 @@ class CRUDEventReceiver extends ClassesWithDebug /** * @noinspection PhpUnusedPrivateMethodInspection Used as a callback */ - private function SetPersonFunction($oObject): void + private function SetPersonFunction(EventData $oData): void { $this->Debug(__METHOD__); + $oObject = $oData->Get('object'); $oObject->Set('function', 'CRUD_function_'.rand()); } /** * @noinspection PhpUnusedPrivateMethodInspection Used as a callback */ - private function SetPersonFirstName($oObject): void + private function SetPersonFirstName(EventData $oData): void { $this->Debug(__METHOD__); + $oObject = $oData->Get('object'); $oObject->Set('first_name', 'CRUD_first_name_'.rand()); } /** * @noinspection PhpUnusedPrivateMethodInspection Used as a callback */ - private function CheckCrudStack(DBObject $oObject): void + private function CheckCrudStack(EventData $oData): void { + $this->Debug(__METHOD__); + $oObject = $oData->Get('object'); self::$bIsObjectInCrudStack = DBObject::IsObjectCurrentlyInCrud(get_class($oObject), $oObject->GetKey()); } + private function EnumTransitions(EventData $oData): void + { + $this->Debug(__METHOD__); + /** @var \DBObject $oObject */ + $oObject = $oData->Get('object'); + $aAllowedStimuli = $oData->Get('allowed_stimuli'); + // Deny all transitions + foreach ($aAllowedStimuli as $sStimulus) { + $this->debug(" * Deny $sStimulus"); + $oObject->DenyTransition($sStimulus); + } + } + }